Skip to content

Commit fb18a57

Browse files
Luis Henriquesidryomov
authored andcommitted
ceph: quota: add initial infrastructure to support cephfs quotas
This patch adds the infrastructure required to support cephfs quotas as it is currently implemented in the ceph fuse client. Cephfs quotas can be set on any directory, and can restrict the number of bytes or the number of files stored beneath that point in the directory hierarchy. Quotas are set using the extended attributes 'ceph.quota.max_files' and 'ceph.quota.max_bytes', and can be removed by setting these attributes to '0'. Link: http://tracker.ceph.com/issues/22372 Signed-off-by: Luis Henriques <[email protected]> Reviewed-by: "Yan, Zheng" <[email protected]> Signed-off-by: Ilya Dryomov <[email protected]>
1 parent 08a7910 commit fb18a57

File tree

11 files changed

+180
-1
lines changed

11 files changed

+180
-1
lines changed

Documentation/filesystems/ceph.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ subdirectories, and a summation of all nested file sizes. This makes
6262
the identification of large disk space consumers relatively quick, as
6363
no 'du' or similar recursive scan of the file system is required.
6464

65+
Finally, Ceph also allows quotas to be set on any directory in the system.
66+
The quota can restrict the number of bytes or the number of files stored
67+
beneath that point in the directory hierarchy. Quotas can be set using
68+
extended attributes 'ceph.quota.max_files' and 'ceph.quota.max_bytes', eg:
69+
70+
setfattr -n ceph.quota.max_bytes -v 100000000 /some/dir
71+
getfattr -n ceph.quota.max_bytes /some/dir
72+
73+
A limitation of the current quotas implementation is that it relies on the
74+
cooperation of the client mounting the file system to stop writers when a
75+
limit is reached. A modified or adversarial client cannot be prevented
76+
from writing as much data as it needs.
6577

6678
Mount Syntax
6779
============

fs/ceph/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
obj-$(CONFIG_CEPH_FS) += ceph.o
77

88
ceph-y := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \
9-
export.o caps.o snap.o xattr.o \
9+
export.o caps.o snap.o xattr.o quota.o \
1010
mds_client.o mdsmap.o strings.o ceph_frag.o \
1111
debugfs.o
1212

fs/ceph/inode.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -441,6 +441,9 @@ struct inode *ceph_alloc_inode(struct super_block *sb)
441441
atomic64_set(&ci->i_complete_seq[1], 0);
442442
ci->i_symlink = NULL;
443443

444+
ci->i_max_bytes = 0;
445+
ci->i_max_files = 0;
446+
444447
memset(&ci->i_dir_layout, 0, sizeof(ci->i_dir_layout));
445448
RCU_INIT_POINTER(ci->i_layout.pool_ns, NULL);
446449

@@ -790,6 +793,9 @@ static int fill_inode(struct inode *inode, struct page *locked_page,
790793
inode->i_rdev = le32_to_cpu(info->rdev);
791794
inode->i_blkbits = fls(le32_to_cpu(info->layout.fl_stripe_unit)) - 1;
792795

796+
ci->i_max_bytes = iinfo->max_bytes;
797+
ci->i_max_files = iinfo->max_files;
798+
793799
if ((new_version || (new_issued & CEPH_CAP_AUTH_SHARED)) &&
794800
(issued & CEPH_CAP_AUTH_EXCL) == 0) {
795801
inode->i_mode = le32_to_cpu(info->mode);

fs/ceph/mds_client.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,26 @@ static int parse_reply_info_in(void **p, void *end,
100100
} else
101101
info->inline_version = CEPH_INLINE_NONE;
102102

103+
if (features & CEPH_FEATURE_MDS_QUOTA) {
104+
u8 struct_v, struct_compat;
105+
u32 struct_len;
106+
107+
/*
108+
* both struct_v and struct_compat are expected to be >= 1
109+
*/
110+
ceph_decode_8_safe(p, end, struct_v, bad);
111+
ceph_decode_8_safe(p, end, struct_compat, bad);
112+
if (!struct_v || !struct_compat)
113+
goto bad;
114+
ceph_decode_32_safe(p, end, struct_len, bad);
115+
ceph_decode_need(p, end, struct_len, bad);
116+
ceph_decode_64_safe(p, end, info->max_bytes, bad);
117+
ceph_decode_64_safe(p, end, info->max_files, bad);
118+
} else {
119+
info->max_bytes = 0;
120+
info->max_files = 0;
121+
}
122+
103123
info->pool_ns_len = 0;
104124
info->pool_ns_data = NULL;
105125
if (features & CEPH_FEATURE_FS_FILE_LAYOUT_V2) {
@@ -4082,6 +4102,9 @@ static void dispatch(struct ceph_connection *con, struct ceph_msg *msg)
40824102
case CEPH_MSG_CLIENT_LEASE:
40834103
handle_lease(mdsc, s, msg);
40844104
break;
4105+
case CEPH_MSG_CLIENT_QUOTA:
4106+
ceph_handle_quota(mdsc, s, msg);
4107+
break;
40854108

40864109
default:
40874110
pr_err("received unknown message type %d %s\n", type,

fs/ceph/mds_client.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ struct ceph_mds_reply_info_in {
4949
char *inline_data;
5050
u32 pool_ns_len;
5151
char *pool_ns_data;
52+
u64 max_bytes;
53+
u64 max_files;
5254
};
5355

5456
struct ceph_mds_reply_dir_entry {

fs/ceph/quota.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* quota.c - CephFS quota
4+
*
5+
* Copyright (C) 2017-2018 SUSE
6+
*
7+
* This program is free software; you can redistribute it and/or
8+
* modify it under the terms of the GNU General Public License
9+
* as published by the Free Software Foundation; either version 2
10+
* of the License, or (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program; if not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
#include "super.h"
22+
#include "mds_client.h"
23+
24+
void ceph_handle_quota(struct ceph_mds_client *mdsc,
25+
struct ceph_mds_session *session,
26+
struct ceph_msg *msg)
27+
{
28+
struct super_block *sb = mdsc->fsc->sb;
29+
struct ceph_mds_quota *h = msg->front.iov_base;
30+
struct ceph_vino vino;
31+
struct inode *inode;
32+
struct ceph_inode_info *ci;
33+
34+
if (msg->front.iov_len != sizeof(*h)) {
35+
pr_err("%s corrupt message mds%d len %d\n", __func__,
36+
session->s_mds, (int)msg->front.iov_len);
37+
ceph_msg_dump(msg);
38+
return;
39+
}
40+
41+
/* increment msg sequence number */
42+
mutex_lock(&session->s_mutex);
43+
session->s_seq++;
44+
mutex_unlock(&session->s_mutex);
45+
46+
/* lookup inode */
47+
vino.ino = le64_to_cpu(h->ino);
48+
vino.snap = CEPH_NOSNAP;
49+
inode = ceph_find_inode(sb, vino);
50+
if (!inode) {
51+
pr_warn("Failed to find inode %llu\n", vino.ino);
52+
return;
53+
}
54+
ci = ceph_inode(inode);
55+
56+
spin_lock(&ci->i_ceph_lock);
57+
ci->i_rbytes = le64_to_cpu(h->rbytes);
58+
ci->i_rfiles = le64_to_cpu(h->rfiles);
59+
ci->i_rsubdirs = le64_to_cpu(h->rsubdirs);
60+
ci->i_max_bytes = le64_to_cpu(h->max_bytes);
61+
ci->i_max_files = le64_to_cpu(h->max_files);
62+
spin_unlock(&ci->i_ceph_lock);
63+
64+
iput(inode);
65+
}

fs/ceph/super.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,9 @@ struct ceph_inode_info {
310310
u64 i_rbytes, i_rfiles, i_rsubdirs;
311311
u64 i_files, i_subdirs;
312312

313+
/* quotas */
314+
u64 i_max_bytes, i_max_files;
315+
313316
struct rb_root i_fragtree;
314317
int i_fragtree_nsplits;
315318
struct mutex i_fragtree_mutex;
@@ -1070,4 +1073,9 @@ extern int ceph_locks_to_pagelist(struct ceph_filelock *flocks,
10701073
extern int ceph_fs_debugfs_init(struct ceph_fs_client *client);
10711074
extern void ceph_fs_debugfs_cleanup(struct ceph_fs_client *client);
10721075

1076+
/* quota.c */
1077+
extern void ceph_handle_quota(struct ceph_mds_client *mdsc,
1078+
struct ceph_mds_session *session,
1079+
struct ceph_msg *msg);
1080+
10731081
#endif /* _FS_CEPH_SUPER_H */

fs/ceph/xattr.c

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -224,6 +224,31 @@ static size_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val,
224224
(long)ci->i_rctime.tv_nsec);
225225
}
226226

227+
/* quotas */
228+
229+
static bool ceph_vxattrcb_quota_exists(struct ceph_inode_info *ci)
230+
{
231+
return (ci->i_max_files || ci->i_max_bytes);
232+
}
233+
234+
static size_t ceph_vxattrcb_quota(struct ceph_inode_info *ci, char *val,
235+
size_t size)
236+
{
237+
return snprintf(val, size, "max_bytes=%llu max_files=%llu",
238+
ci->i_max_bytes, ci->i_max_files);
239+
}
240+
241+
static size_t ceph_vxattrcb_quota_max_bytes(struct ceph_inode_info *ci,
242+
char *val, size_t size)
243+
{
244+
return snprintf(val, size, "%llu", ci->i_max_bytes);
245+
}
246+
247+
static size_t ceph_vxattrcb_quota_max_files(struct ceph_inode_info *ci,
248+
char *val, size_t size)
249+
{
250+
return snprintf(val, size, "%llu", ci->i_max_files);
251+
}
227252

228253
#define CEPH_XATTR_NAME(_type, _name) XATTR_CEPH_PREFIX #_type "." #_name
229254
#define CEPH_XATTR_NAME2(_type, _name, _name2) \
@@ -247,6 +272,15 @@ static size_t ceph_vxattrcb_dir_rctime(struct ceph_inode_info *ci, char *val,
247272
.hidden = true, \
248273
.exists_cb = ceph_vxattrcb_layout_exists, \
249274
}
275+
#define XATTR_QUOTA_FIELD(_type, _name) \
276+
{ \
277+
.name = CEPH_XATTR_NAME(_type, _name), \
278+
.name_size = sizeof(CEPH_XATTR_NAME(_type, _name)), \
279+
.getxattr_cb = ceph_vxattrcb_ ## _type ## _ ## _name, \
280+
.readonly = false, \
281+
.hidden = true, \
282+
.exists_cb = ceph_vxattrcb_quota_exists, \
283+
}
250284

251285
static struct ceph_vxattr ceph_dir_vxattrs[] = {
252286
{
@@ -270,6 +304,16 @@ static struct ceph_vxattr ceph_dir_vxattrs[] = {
270304
XATTR_NAME_CEPH(dir, rsubdirs),
271305
XATTR_NAME_CEPH(dir, rbytes),
272306
XATTR_NAME_CEPH(dir, rctime),
307+
{
308+
.name = "ceph.quota",
309+
.name_size = sizeof("ceph.quota"),
310+
.getxattr_cb = ceph_vxattrcb_quota,
311+
.readonly = false,
312+
.hidden = true,
313+
.exists_cb = ceph_vxattrcb_quota_exists,
314+
},
315+
XATTR_QUOTA_FIELD(quota, max_bytes),
316+
XATTR_QUOTA_FIELD(quota, max_files),
273317
{ .name = NULL, 0 } /* Required table terminator */
274318
};
275319
static size_t ceph_dir_vxattrs_name_size; /* total size of all names */

include/linux/ceph/ceph_features.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,7 @@ DEFINE_CEPH_FEATURE_DEPRECATED(63, 1, RESERVED_BROKEN, LUMINOUS) // client-facin
204204
CEPH_FEATURE_OSD_PRIMARY_AFFINITY | \
205205
CEPH_FEATURE_MSGR_KEEPALIVE2 | \
206206
CEPH_FEATURE_OSD_POOLRESEND | \
207+
CEPH_FEATURE_MDS_QUOTA | \
207208
CEPH_FEATURE_CRUSH_V4 | \
208209
CEPH_FEATURE_NEW_OSDOP_ENCODING | \
209210
CEPH_FEATURE_SERVER_JEWEL | \

include/linux/ceph/ceph_fs.h

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@ struct ceph_dir_layout {
134134
#define CEPH_MSG_CLIENT_LEASE 0x311
135135
#define CEPH_MSG_CLIENT_SNAP 0x312
136136
#define CEPH_MSG_CLIENT_CAPRELEASE 0x313
137+
#define CEPH_MSG_CLIENT_QUOTA 0x314
137138

138139
/* pool ops */
139140
#define CEPH_MSG_POOLOP_REPLY 48
@@ -807,4 +808,20 @@ struct ceph_mds_snap_realm {
807808
} __attribute__ ((packed));
808809
/* followed by my snap list, then prior parent snap list */
809810

811+
/*
812+
* quotas
813+
*/
814+
struct ceph_mds_quota {
815+
__le64 ino; /* ino */
816+
struct ceph_timespec rctime;
817+
__le64 rbytes; /* dir stats */
818+
__le64 rfiles;
819+
__le64 rsubdirs;
820+
__u8 struct_v; /* compat */
821+
__u8 struct_compat;
822+
__le32 struct_len;
823+
__le64 max_bytes; /* quota max. bytes */
824+
__le64 max_files; /* quota max. files */
825+
} __attribute__ ((packed));
826+
810827
#endif

net/ceph/ceph_common.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ const char *ceph_msg_type_name(int type)
8080
case CEPH_MSG_CLIENT_REPLY: return "client_reply";
8181
case CEPH_MSG_CLIENT_CAPS: return "client_caps";
8282
case CEPH_MSG_CLIENT_CAPRELEASE: return "client_cap_release";
83+
case CEPH_MSG_CLIENT_QUOTA: return "client_quota";
8384
case CEPH_MSG_CLIENT_SNAP: return "client_snap";
8485
case CEPH_MSG_CLIENT_LEASE: return "client_lease";
8586
case CEPH_MSG_POOLOP_REPLY: return "poolop_reply";

0 commit comments

Comments
 (0)