1fb18a575SLuis Henriques // SPDX-License-Identifier: GPL-2.0
2fb18a575SLuis Henriques /*
3fb18a575SLuis Henriques * quota.c - CephFS quota
4fb18a575SLuis Henriques *
5fb18a575SLuis Henriques * Copyright (C) 2017-2018 SUSE
6fb18a575SLuis Henriques */
7fb18a575SLuis Henriques
89122eed5SLuis Henriques #include <linux/statfs.h>
99122eed5SLuis Henriques
10fb18a575SLuis Henriques #include "super.h"
11fb18a575SLuis Henriques #include "mds_client.h"
12fb18a575SLuis Henriques
ceph_adjust_quota_realms_count(struct inode * inode,bool inc)13d557c48dSLuis Henriques void ceph_adjust_quota_realms_count(struct inode *inode, bool inc)
14cafe21a4SLuis Henriques {
152678da88SXiubo Li struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
16d557c48dSLuis Henriques if (inc)
17d557c48dSLuis Henriques atomic64_inc(&mdsc->quotarealms_count);
18d557c48dSLuis Henriques else
19d557c48dSLuis Henriques atomic64_dec(&mdsc->quotarealms_count);
20d557c48dSLuis Henriques }
21d557c48dSLuis Henriques
ceph_has_realms_with_quotas(struct inode * inode)22d557c48dSLuis Henriques static inline bool ceph_has_realms_with_quotas(struct inode *inode)
23d557c48dSLuis Henriques {
242678da88SXiubo Li struct super_block *sb = inode->i_sb;
252678da88SXiubo Li struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(sb);
26ebce3eb2SJeff Layton struct inode *root = d_inode(sb->s_root);
270c44a8e0SLuis Henriques
280c44a8e0SLuis Henriques if (atomic64_read(&mdsc->quotarealms_count) > 0)
290c44a8e0SLuis Henriques return true;
300c44a8e0SLuis Henriques /* if root is the real CephFS root, we don't have quota realms */
31ebce3eb2SJeff Layton if (root && ceph_ino(root) == CEPH_INO_ROOT)
320c44a8e0SLuis Henriques return false;
330078ea3bSJeff Layton /* MDS stray dirs have no quota realms */
340078ea3bSJeff Layton if (ceph_vino_is_reserved(ceph_inode(inode)->i_vino))
350078ea3bSJeff Layton return false;
360c44a8e0SLuis Henriques /* otherwise, we can't know for sure */
370c44a8e0SLuis Henriques return true;
38cafe21a4SLuis Henriques }
39cafe21a4SLuis Henriques
ceph_handle_quota(struct ceph_mds_client * mdsc,struct ceph_mds_session * session,struct ceph_msg * msg)40fb18a575SLuis Henriques void ceph_handle_quota(struct ceph_mds_client *mdsc,
41fb18a575SLuis Henriques struct ceph_mds_session *session,
42fb18a575SLuis Henriques struct ceph_msg *msg)
43fb18a575SLuis Henriques {
44fb18a575SLuis Henriques struct super_block *sb = mdsc->fsc->sb;
45fb18a575SLuis Henriques struct ceph_mds_quota *h = msg->front.iov_base;
4638d46409SXiubo Li struct ceph_client *cl = mdsc->fsc->client;
47fb18a575SLuis Henriques struct ceph_vino vino;
48fb18a575SLuis Henriques struct inode *inode;
49fb18a575SLuis Henriques struct ceph_inode_info *ci;
50fb18a575SLuis Henriques
51e3dfcab2SXiubo Li if (!ceph_inc_mds_stopping_blocker(mdsc, session))
52e3dfcab2SXiubo Li return;
53e3dfcab2SXiubo Li
540fcf6c02SYan, Zheng if (msg->front.iov_len < sizeof(*h)) {
5538d46409SXiubo Li pr_err_client(cl, "corrupt message mds%d len %d\n",
56fb18a575SLuis Henriques session->s_mds, (int)msg->front.iov_len);
57fb18a575SLuis Henriques ceph_msg_dump(msg);
58e3dfcab2SXiubo Li goto out;
59fb18a575SLuis Henriques }
60fb18a575SLuis Henriques
61fb18a575SLuis Henriques /* lookup inode */
62fb18a575SLuis Henriques vino.ino = le64_to_cpu(h->ino);
63fb18a575SLuis Henriques vino.snap = CEPH_NOSNAP;
64fb18a575SLuis Henriques inode = ceph_find_inode(sb, vino);
65fb18a575SLuis Henriques if (!inode) {
6638d46409SXiubo Li pr_warn_client(cl, "failed to find inode %llx\n", vino.ino);
67e3dfcab2SXiubo Li goto out;
68fb18a575SLuis Henriques }
69fb18a575SLuis Henriques ci = ceph_inode(inode);
70fb18a575SLuis Henriques
71fb18a575SLuis Henriques spin_lock(&ci->i_ceph_lock);
72fb18a575SLuis Henriques ci->i_rbytes = le64_to_cpu(h->rbytes);
73fb18a575SLuis Henriques ci->i_rfiles = le64_to_cpu(h->rfiles);
74fb18a575SLuis Henriques ci->i_rsubdirs = le64_to_cpu(h->rsubdirs);
75d557c48dSLuis Henriques __ceph_update_quota(ci, le64_to_cpu(h->max_bytes),
76d557c48dSLuis Henriques le64_to_cpu(h->max_files));
77fb18a575SLuis Henriques spin_unlock(&ci->i_ceph_lock);
78fb18a575SLuis Henriques
7923c2c76eSJeff Layton iput(inode);
80e3dfcab2SXiubo Li out:
81e3dfcab2SXiubo Li ceph_dec_mds_stopping_blocker(mdsc);
82fb18a575SLuis Henriques }
83b7a29217SLuis Henriques
840c44a8e0SLuis Henriques static struct ceph_quotarealm_inode *
find_quotarealm_inode(struct ceph_mds_client * mdsc,u64 ino)850c44a8e0SLuis Henriques find_quotarealm_inode(struct ceph_mds_client *mdsc, u64 ino)
860c44a8e0SLuis Henriques {
870c44a8e0SLuis Henriques struct ceph_quotarealm_inode *qri = NULL;
880c44a8e0SLuis Henriques struct rb_node **node, *parent = NULL;
8938d46409SXiubo Li struct ceph_client *cl = mdsc->fsc->client;
900c44a8e0SLuis Henriques
910c44a8e0SLuis Henriques mutex_lock(&mdsc->quotarealms_inodes_mutex);
920c44a8e0SLuis Henriques node = &(mdsc->quotarealms_inodes.rb_node);
930c44a8e0SLuis Henriques while (*node) {
940c44a8e0SLuis Henriques parent = *node;
950c44a8e0SLuis Henriques qri = container_of(*node, struct ceph_quotarealm_inode, node);
960c44a8e0SLuis Henriques
970c44a8e0SLuis Henriques if (ino < qri->ino)
980c44a8e0SLuis Henriques node = &((*node)->rb_left);
990c44a8e0SLuis Henriques else if (ino > qri->ino)
1000c44a8e0SLuis Henriques node = &((*node)->rb_right);
1010c44a8e0SLuis Henriques else
1020c44a8e0SLuis Henriques break;
1030c44a8e0SLuis Henriques }
1040c44a8e0SLuis Henriques if (!qri || (qri->ino != ino)) {
1050c44a8e0SLuis Henriques /* Not found, create a new one and insert it */
1060c44a8e0SLuis Henriques qri = kmalloc(sizeof(*qri), GFP_KERNEL);
1070c44a8e0SLuis Henriques if (qri) {
1080c44a8e0SLuis Henriques qri->ino = ino;
1090c44a8e0SLuis Henriques qri->inode = NULL;
1100c44a8e0SLuis Henriques qri->timeout = 0;
1110c44a8e0SLuis Henriques mutex_init(&qri->mutex);
1120c44a8e0SLuis Henriques rb_link_node(&qri->node, parent, node);
1130c44a8e0SLuis Henriques rb_insert_color(&qri->node, &mdsc->quotarealms_inodes);
1140c44a8e0SLuis Henriques } else
11538d46409SXiubo Li pr_warn_client(cl, "Failed to alloc quotarealms_inode\n");
1160c44a8e0SLuis Henriques }
1170c44a8e0SLuis Henriques mutex_unlock(&mdsc->quotarealms_inodes_mutex);
1180c44a8e0SLuis Henriques
1190c44a8e0SLuis Henriques return qri;
1200c44a8e0SLuis Henriques }
1210c44a8e0SLuis Henriques
1220c44a8e0SLuis Henriques /*
1230c44a8e0SLuis Henriques * This function will try to lookup a realm inode which isn't visible in the
1240c44a8e0SLuis Henriques * filesystem mountpoint. A list of these kind of inodes (not visible) is
1250c44a8e0SLuis Henriques * maintained in the mdsc and freed only when the filesystem is umounted.
1260c44a8e0SLuis Henriques *
1270c44a8e0SLuis Henriques * Note that these inodes are kept in this list even if the lookup fails, which
1280c44a8e0SLuis Henriques * allows to prevent useless lookup requests.
1290c44a8e0SLuis Henriques */
lookup_quotarealm_inode(struct ceph_mds_client * mdsc,struct super_block * sb,struct ceph_snap_realm * realm)1300c44a8e0SLuis Henriques static struct inode *lookup_quotarealm_inode(struct ceph_mds_client *mdsc,
1310c44a8e0SLuis Henriques struct super_block *sb,
1320c44a8e0SLuis Henriques struct ceph_snap_realm *realm)
1330c44a8e0SLuis Henriques {
13438d46409SXiubo Li struct ceph_client *cl = mdsc->fsc->client;
1350c44a8e0SLuis Henriques struct ceph_quotarealm_inode *qri;
1360c44a8e0SLuis Henriques struct inode *in;
1370c44a8e0SLuis Henriques
1380c44a8e0SLuis Henriques qri = find_quotarealm_inode(mdsc, realm->ino);
1390c44a8e0SLuis Henriques if (!qri)
1400c44a8e0SLuis Henriques return NULL;
1410c44a8e0SLuis Henriques
1420c44a8e0SLuis Henriques mutex_lock(&qri->mutex);
1432ef5df1aSYan, Zheng if (qri->inode && ceph_is_any_caps(qri->inode)) {
1440c44a8e0SLuis Henriques /* A request has already returned the inode */
1450c44a8e0SLuis Henriques mutex_unlock(&qri->mutex);
1460c44a8e0SLuis Henriques return qri->inode;
1470c44a8e0SLuis Henriques }
1480c44a8e0SLuis Henriques /* Check if this inode lookup has failed recently */
1490c44a8e0SLuis Henriques if (qri->timeout &&
1500c44a8e0SLuis Henriques time_before_eq(jiffies, qri->timeout)) {
1510c44a8e0SLuis Henriques mutex_unlock(&qri->mutex);
1520c44a8e0SLuis Henriques return NULL;
1530c44a8e0SLuis Henriques }
1542ef5df1aSYan, Zheng if (qri->inode) {
1552ef5df1aSYan, Zheng /* get caps */
1562ef5df1aSYan, Zheng int ret = __ceph_do_getattr(qri->inode, NULL,
1572ef5df1aSYan, Zheng CEPH_STAT_CAP_INODE, true);
1582ef5df1aSYan, Zheng if (ret >= 0)
1592ef5df1aSYan, Zheng in = qri->inode;
1602ef5df1aSYan, Zheng else
1612ef5df1aSYan, Zheng in = ERR_PTR(ret);
1622ef5df1aSYan, Zheng } else {
1630c44a8e0SLuis Henriques in = ceph_lookup_inode(sb, realm->ino);
1642ef5df1aSYan, Zheng }
1652ef5df1aSYan, Zheng
1660c44a8e0SLuis Henriques if (IS_ERR(in)) {
16738d46409SXiubo Li doutc(cl, "Can't lookup inode %llx (err: %ld)\n", realm->ino,
16838d46409SXiubo Li PTR_ERR(in));
169*f5ea0319SEaswar Hariharan qri->timeout = jiffies + secs_to_jiffies(60); /* XXX */
1700c44a8e0SLuis Henriques } else {
1710c44a8e0SLuis Henriques qri->timeout = 0;
1720c44a8e0SLuis Henriques qri->inode = in;
1730c44a8e0SLuis Henriques }
1740c44a8e0SLuis Henriques mutex_unlock(&qri->mutex);
1750c44a8e0SLuis Henriques
1760c44a8e0SLuis Henriques return in;
1770c44a8e0SLuis Henriques }
1780c44a8e0SLuis Henriques
ceph_cleanup_quotarealms_inodes(struct ceph_mds_client * mdsc)1790c44a8e0SLuis Henriques void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc)
1800c44a8e0SLuis Henriques {
1810c44a8e0SLuis Henriques struct ceph_quotarealm_inode *qri;
1820c44a8e0SLuis Henriques struct rb_node *node;
1830c44a8e0SLuis Henriques
1840c44a8e0SLuis Henriques /*
1850c44a8e0SLuis Henriques * It should now be safe to clean quotarealms_inode tree without holding
1860c44a8e0SLuis Henriques * mdsc->quotarealms_inodes_mutex...
1870c44a8e0SLuis Henriques */
1880c44a8e0SLuis Henriques mutex_lock(&mdsc->quotarealms_inodes_mutex);
1890c44a8e0SLuis Henriques while (!RB_EMPTY_ROOT(&mdsc->quotarealms_inodes)) {
1900c44a8e0SLuis Henriques node = rb_first(&mdsc->quotarealms_inodes);
1910c44a8e0SLuis Henriques qri = rb_entry(node, struct ceph_quotarealm_inode, node);
1920c44a8e0SLuis Henriques rb_erase(node, &mdsc->quotarealms_inodes);
1930c44a8e0SLuis Henriques iput(qri->inode);
1940c44a8e0SLuis Henriques kfree(qri);
1950c44a8e0SLuis Henriques }
1960c44a8e0SLuis Henriques mutex_unlock(&mdsc->quotarealms_inodes_mutex);
1970c44a8e0SLuis Henriques }
1980c44a8e0SLuis Henriques
199cafe21a4SLuis Henriques /*
2000f4cf64eSWenchao Hao * This function walks through the snaprealm for an inode and set the
2010f4cf64eSWenchao Hao * realmp with the first snaprealm that has quotas set (max_files,
20255ab5520SLuís Henriques * max_bytes, or any, depending on the 'which_quota' argument). If the root is
2030f4cf64eSWenchao Hao * reached, set the realmp with the root ceph_snap_realm instead.
204cafe21a4SLuis Henriques *
205cafe21a4SLuis Henriques * Note that the caller is responsible for calling ceph_put_snap_realm() on the
206cafe21a4SLuis Henriques * returned realm.
2070c44a8e0SLuis Henriques *
2080c44a8e0SLuis Henriques * Callers of this function need to hold mdsc->snap_rwsem. However, if there's
2090c44a8e0SLuis Henriques * a need to do an inode lookup, this rwsem will be temporarily dropped. Hence
2100c44a8e0SLuis Henriques * the 'retry' argument: if rwsem needs to be dropped and 'retry' is 'false'
2110c44a8e0SLuis Henriques * this function will return -EAGAIN; otherwise, the snaprealms walk-through
2120c44a8e0SLuis Henriques * will be restarted.
213cafe21a4SLuis Henriques */
get_quota_realm(struct ceph_mds_client * mdsc,struct inode * inode,enum quota_get_realm which_quota,struct ceph_snap_realm ** realmp,bool retry)2140f4cf64eSWenchao Hao static int get_quota_realm(struct ceph_mds_client *mdsc, struct inode *inode,
21555ab5520SLuís Henriques enum quota_get_realm which_quota,
2160f4cf64eSWenchao Hao struct ceph_snap_realm **realmp, bool retry)
217cafe21a4SLuis Henriques {
21838d46409SXiubo Li struct ceph_client *cl = mdsc->fsc->client;
219cafe21a4SLuis Henriques struct ceph_inode_info *ci = NULL;
220cafe21a4SLuis Henriques struct ceph_snap_realm *realm, *next;
221cafe21a4SLuis Henriques struct inode *in;
2220eb6bbe4SYan, Zheng bool has_quota;
223cafe21a4SLuis Henriques
2240f4cf64eSWenchao Hao if (realmp)
2250f4cf64eSWenchao Hao *realmp = NULL;
22625963669SYan, Zheng if (ceph_snap(inode) != CEPH_NOSNAP)
2270f4cf64eSWenchao Hao return 0;
22825963669SYan, Zheng
2290c44a8e0SLuis Henriques restart:
230cafe21a4SLuis Henriques realm = ceph_inode(inode)->i_snap_realm;
23125963669SYan, Zheng if (realm)
232cafe21a4SLuis Henriques ceph_get_snap_realm(mdsc, realm);
23325963669SYan, Zheng else
23438d46409SXiubo Li pr_err_ratelimited_client(cl,
23538d46409SXiubo Li "%p %llx.%llx null i_snap_realm\n",
23638d46409SXiubo Li inode, ceph_vinop(inode));
237cafe21a4SLuis Henriques while (realm) {
2380c44a8e0SLuis Henriques bool has_inode;
2390c44a8e0SLuis Henriques
240e3161f17SLuis Henriques spin_lock(&realm->inodes_with_caps_lock);
2410c44a8e0SLuis Henriques has_inode = realm->inode;
2420c44a8e0SLuis Henriques in = has_inode ? igrab(realm->inode) : NULL;
243e3161f17SLuis Henriques spin_unlock(&realm->inodes_with_caps_lock);
2440c44a8e0SLuis Henriques if (has_inode && !in)
245cafe21a4SLuis Henriques break;
2460c44a8e0SLuis Henriques if (!in) {
2470c44a8e0SLuis Henriques up_read(&mdsc->snap_rwsem);
2480c44a8e0SLuis Henriques in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm);
2490c44a8e0SLuis Henriques down_read(&mdsc->snap_rwsem);
2500c44a8e0SLuis Henriques if (IS_ERR_OR_NULL(in))
2510c44a8e0SLuis Henriques break;
2520c44a8e0SLuis Henriques ceph_put_snap_realm(mdsc, realm);
2530c44a8e0SLuis Henriques if (!retry)
2540f4cf64eSWenchao Hao return -EAGAIN;
2550c44a8e0SLuis Henriques goto restart;
2560c44a8e0SLuis Henriques }
257e3161f17SLuis Henriques
258cafe21a4SLuis Henriques ci = ceph_inode(in);
25955ab5520SLuís Henriques has_quota = __ceph_has_quota(ci, which_quota);
26023c2c76eSJeff Layton iput(in);
2610eb6bbe4SYan, Zheng
262cafe21a4SLuis Henriques next = realm->parent;
2630f4cf64eSWenchao Hao if (has_quota || !next) {
2640f4cf64eSWenchao Hao if (realmp)
2650f4cf64eSWenchao Hao *realmp = realm;
2660f4cf64eSWenchao Hao return 0;
2670f4cf64eSWenchao Hao }
2680eb6bbe4SYan, Zheng
269cafe21a4SLuis Henriques ceph_get_snap_realm(mdsc, next);
270cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, realm);
271cafe21a4SLuis Henriques realm = next;
272cafe21a4SLuis Henriques }
273cafe21a4SLuis Henriques if (realm)
274cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, realm);
275cafe21a4SLuis Henriques
2760f4cf64eSWenchao Hao return 0;
277cafe21a4SLuis Henriques }
278cafe21a4SLuis Henriques
ceph_quota_is_same_realm(struct inode * old,struct inode * new)2796646ea1cSLuis Henriques bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
280cafe21a4SLuis Henriques {
2812678da88SXiubo Li struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(old->i_sb);
282cafe21a4SLuis Henriques struct ceph_snap_realm *old_realm, *new_realm;
283cafe21a4SLuis Henriques bool is_same;
2840f4cf64eSWenchao Hao int ret;
285cafe21a4SLuis Henriques
2860c44a8e0SLuis Henriques restart:
2870c44a8e0SLuis Henriques /*
2880c44a8e0SLuis Henriques * We need to lookup 2 quota realms atomically, i.e. with snap_rwsem.
2890c44a8e0SLuis Henriques * However, get_quota_realm may drop it temporarily. By setting the
2900c44a8e0SLuis Henriques * 'retry' parameter to 'false', we'll get -EAGAIN if the rwsem was
2910c44a8e0SLuis Henriques * dropped and we can then restart the whole operation.
2920c44a8e0SLuis Henriques */
293cafe21a4SLuis Henriques down_read(&mdsc->snap_rwsem);
2940f4cf64eSWenchao Hao get_quota_realm(mdsc, old, QUOTA_GET_ANY, &old_realm, true);
2950f4cf64eSWenchao Hao ret = get_quota_realm(mdsc, new, QUOTA_GET_ANY, &new_realm, false);
2960f4cf64eSWenchao Hao if (ret == -EAGAIN) {
2970c44a8e0SLuis Henriques up_read(&mdsc->snap_rwsem);
2980c44a8e0SLuis Henriques if (old_realm)
2990c44a8e0SLuis Henriques ceph_put_snap_realm(mdsc, old_realm);
3000c44a8e0SLuis Henriques goto restart;
3010c44a8e0SLuis Henriques }
302cafe21a4SLuis Henriques is_same = (old_realm == new_realm);
303cafe21a4SLuis Henriques up_read(&mdsc->snap_rwsem);
304cafe21a4SLuis Henriques
305cafe21a4SLuis Henriques if (old_realm)
306cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, old_realm);
307cafe21a4SLuis Henriques if (new_realm)
308cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, new_realm);
309cafe21a4SLuis Henriques
310cafe21a4SLuis Henriques return is_same;
311cafe21a4SLuis Henriques }
312cafe21a4SLuis Henriques
313b7a29217SLuis Henriques enum quota_check_op {
3142b83845fSLuis Henriques QUOTA_CHECK_MAX_FILES_OP, /* check quota max_files limit */
3151ab302a0SLuis Henriques QUOTA_CHECK_MAX_BYTES_OP, /* check quota max_files limit */
3161ab302a0SLuis Henriques QUOTA_CHECK_MAX_BYTES_APPROACHING_OP /* check if quota max_files
3171ab302a0SLuis Henriques limit is approaching */
318b7a29217SLuis Henriques };
319b7a29217SLuis Henriques
320b7a29217SLuis Henriques /*
321b7a29217SLuis Henriques * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each
322b7a29217SLuis Henriques * realm, it will execute quota check operation defined by the 'op' parameter.
323b7a29217SLuis Henriques * The snaprealm walk is interrupted if the quota check detects that the quota
324b7a29217SLuis Henriques * is exceeded or if the root inode is reached.
325b7a29217SLuis Henriques */
check_quota_exceeded(struct inode * inode,enum quota_check_op op,loff_t delta)326b7a29217SLuis Henriques static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
327b7a29217SLuis Henriques loff_t delta)
328b7a29217SLuis Henriques {
3292678da88SXiubo Li struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
33038d46409SXiubo Li struct ceph_client *cl = mdsc->fsc->client;
331b7a29217SLuis Henriques struct ceph_inode_info *ci;
332b7a29217SLuis Henriques struct ceph_snap_realm *realm, *next;
333b7a29217SLuis Henriques struct inode *in;
334b7a29217SLuis Henriques u64 max, rvalue;
335b7a29217SLuis Henriques bool exceeded = false;
336b7a29217SLuis Henriques
33725963669SYan, Zheng if (ceph_snap(inode) != CEPH_NOSNAP)
33825963669SYan, Zheng return false;
33925963669SYan, Zheng
340b7a29217SLuis Henriques down_read(&mdsc->snap_rwsem);
3410c44a8e0SLuis Henriques restart:
342b7a29217SLuis Henriques realm = ceph_inode(inode)->i_snap_realm;
34325963669SYan, Zheng if (realm)
344b7a29217SLuis Henriques ceph_get_snap_realm(mdsc, realm);
34525963669SYan, Zheng else
34638d46409SXiubo Li pr_err_ratelimited_client(cl,
34738d46409SXiubo Li "%p %llx.%llx null i_snap_realm\n",
34838d46409SXiubo Li inode, ceph_vinop(inode));
349b7a29217SLuis Henriques while (realm) {
3500c44a8e0SLuis Henriques bool has_inode;
351e3161f17SLuis Henriques
3520c44a8e0SLuis Henriques spin_lock(&realm->inodes_with_caps_lock);
3530c44a8e0SLuis Henriques has_inode = realm->inode;
3540c44a8e0SLuis Henriques in = has_inode ? igrab(realm->inode) : NULL;
3550c44a8e0SLuis Henriques spin_unlock(&realm->inodes_with_caps_lock);
3560c44a8e0SLuis Henriques if (has_inode && !in)
3570c44a8e0SLuis Henriques break;
3580c44a8e0SLuis Henriques if (!in) {
3590c44a8e0SLuis Henriques up_read(&mdsc->snap_rwsem);
3600c44a8e0SLuis Henriques in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm);
3610c44a8e0SLuis Henriques down_read(&mdsc->snap_rwsem);
3620c44a8e0SLuis Henriques if (IS_ERR_OR_NULL(in))
3630c44a8e0SLuis Henriques break;
3640c44a8e0SLuis Henriques ceph_put_snap_realm(mdsc, realm);
3650c44a8e0SLuis Henriques goto restart;
3660c44a8e0SLuis Henriques }
367b7a29217SLuis Henriques ci = ceph_inode(in);
368b7a29217SLuis Henriques spin_lock(&ci->i_ceph_lock);
369b7a29217SLuis Henriques if (op == QUOTA_CHECK_MAX_FILES_OP) {
370b7a29217SLuis Henriques max = ci->i_max_files;
371b7a29217SLuis Henriques rvalue = ci->i_rfiles + ci->i_rsubdirs;
3722b83845fSLuis Henriques } else {
3732b83845fSLuis Henriques max = ci->i_max_bytes;
3742b83845fSLuis Henriques rvalue = ci->i_rbytes;
375b7a29217SLuis Henriques }
376b7a29217SLuis Henriques spin_unlock(&ci->i_ceph_lock);
377b7a29217SLuis Henriques switch (op) {
378b7a29217SLuis Henriques case QUOTA_CHECK_MAX_FILES_OP:
3792b83845fSLuis Henriques case QUOTA_CHECK_MAX_BYTES_OP:
3802b83845fSLuis Henriques exceeded = (max && (rvalue + delta > max));
3812b83845fSLuis Henriques break;
3821ab302a0SLuis Henriques case QUOTA_CHECK_MAX_BYTES_APPROACHING_OP:
3831ab302a0SLuis Henriques if (max) {
3841ab302a0SLuis Henriques if (rvalue >= max)
3851ab302a0SLuis Henriques exceeded = true;
3861ab302a0SLuis Henriques else {
3871ab302a0SLuis Henriques /*
3881ab302a0SLuis Henriques * when we're writing more that 1/16th
3891ab302a0SLuis Henriques * of the available space
3901ab302a0SLuis Henriques */
3911ab302a0SLuis Henriques exceeded =
3921ab302a0SLuis Henriques (((max - rvalue) >> 4) < delta);
3931ab302a0SLuis Henriques }
3941ab302a0SLuis Henriques }
3951ab302a0SLuis Henriques break;
396b7a29217SLuis Henriques default:
397b7a29217SLuis Henriques /* Shouldn't happen */
39838d46409SXiubo Li pr_warn_client(cl, "Invalid quota check op (%d)\n", op);
399b7a29217SLuis Henriques exceeded = true; /* Just break the loop */
400b7a29217SLuis Henriques }
40123c2c76eSJeff Layton iput(in);
402b7a29217SLuis Henriques
403b7a29217SLuis Henriques next = realm->parent;
4040eb6bbe4SYan, Zheng if (exceeded || !next)
4050eb6bbe4SYan, Zheng break;
406b7a29217SLuis Henriques ceph_get_snap_realm(mdsc, next);
407b7a29217SLuis Henriques ceph_put_snap_realm(mdsc, realm);
408b7a29217SLuis Henriques realm = next;
409b7a29217SLuis Henriques }
41071f2cc64SLuis Henriques if (realm)
411b7a29217SLuis Henriques ceph_put_snap_realm(mdsc, realm);
412b7a29217SLuis Henriques up_read(&mdsc->snap_rwsem);
413b7a29217SLuis Henriques
414b7a29217SLuis Henriques return exceeded;
415b7a29217SLuis Henriques }
416b7a29217SLuis Henriques
417b7a29217SLuis Henriques /*
418b7a29217SLuis Henriques * ceph_quota_is_max_files_exceeded - check if we can create a new file
419b7a29217SLuis Henriques * @inode: directory where a new file is being created
420b7a29217SLuis Henriques *
421b7a29217SLuis Henriques * This functions returns true is max_files quota allows a new file to be
422b7a29217SLuis Henriques * created. It is necessary to walk through the snaprealm hierarchy (until the
423b7a29217SLuis Henriques * FS root) to check all realms with quotas set.
424b7a29217SLuis Henriques */
ceph_quota_is_max_files_exceeded(struct inode * inode)425b7a29217SLuis Henriques bool ceph_quota_is_max_files_exceeded(struct inode *inode)
426b7a29217SLuis Henriques {
427d557c48dSLuis Henriques if (!ceph_has_realms_with_quotas(inode))
428d557c48dSLuis Henriques return false;
429d557c48dSLuis Henriques
430b7a29217SLuis Henriques WARN_ON(!S_ISDIR(inode->i_mode));
431b7a29217SLuis Henriques
432daa668fbSLuis Henriques return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 1);
433b7a29217SLuis Henriques }
4342b83845fSLuis Henriques
4352b83845fSLuis Henriques /*
4362b83845fSLuis Henriques * ceph_quota_is_max_bytes_exceeded - check if we can write to a file
4372b83845fSLuis Henriques * @inode: inode being written
4382b83845fSLuis Henriques * @newsize: new size if write succeeds
4392b83845fSLuis Henriques *
4402b83845fSLuis Henriques * This functions returns true is max_bytes quota allows a file size to reach
4412b83845fSLuis Henriques * @newsize; it returns false otherwise.
4422b83845fSLuis Henriques */
ceph_quota_is_max_bytes_exceeded(struct inode * inode,loff_t newsize)4432b83845fSLuis Henriques bool ceph_quota_is_max_bytes_exceeded(struct inode *inode, loff_t newsize)
4442b83845fSLuis Henriques {
4452b83845fSLuis Henriques loff_t size = i_size_read(inode);
4462b83845fSLuis Henriques
447d557c48dSLuis Henriques if (!ceph_has_realms_with_quotas(inode))
448d557c48dSLuis Henriques return false;
449d557c48dSLuis Henriques
4502b83845fSLuis Henriques /* return immediately if we're decreasing file size */
4512b83845fSLuis Henriques if (newsize <= size)
4522b83845fSLuis Henriques return false;
4532b83845fSLuis Henriques
4542b83845fSLuis Henriques return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_OP, (newsize - size));
4552b83845fSLuis Henriques }
4561ab302a0SLuis Henriques
4571ab302a0SLuis Henriques /*
4581ab302a0SLuis Henriques * ceph_quota_is_max_bytes_approaching - check if we're reaching max_bytes
4591ab302a0SLuis Henriques * @inode: inode being written
4601ab302a0SLuis Henriques * @newsize: new size if write succeeds
4611ab302a0SLuis Henriques *
4621ab302a0SLuis Henriques * This function returns true if the new file size @newsize will be consuming
4631ab302a0SLuis Henriques * more than 1/16th of the available quota space; it returns false otherwise.
4641ab302a0SLuis Henriques */
ceph_quota_is_max_bytes_approaching(struct inode * inode,loff_t newsize)4651ab302a0SLuis Henriques bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize)
4661ab302a0SLuis Henriques {
4671ab302a0SLuis Henriques loff_t size = ceph_inode(inode)->i_reported_size;
4681ab302a0SLuis Henriques
469d557c48dSLuis Henriques if (!ceph_has_realms_with_quotas(inode))
470d557c48dSLuis Henriques return false;
471d557c48dSLuis Henriques
4721ab302a0SLuis Henriques /* return immediately if we're decreasing file size */
4731ab302a0SLuis Henriques if (newsize <= size)
4741ab302a0SLuis Henriques return false;
4751ab302a0SLuis Henriques
4761ab302a0SLuis Henriques return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP,
4771ab302a0SLuis Henriques (newsize - size));
4781ab302a0SLuis Henriques }
4799122eed5SLuis Henriques
4809122eed5SLuis Henriques /*
4819122eed5SLuis Henriques * ceph_quota_update_statfs - if root has quota update statfs with quota status
4829122eed5SLuis Henriques * @fsc: filesystem client instance
4839122eed5SLuis Henriques * @buf: statfs to update
4849122eed5SLuis Henriques *
4859122eed5SLuis Henriques * If the mounted filesystem root has max_bytes quota set, update the filesystem
4869122eed5SLuis Henriques * statistics with the quota status.
4879122eed5SLuis Henriques *
4889122eed5SLuis Henriques * This function returns true if the stats have been updated, false otherwise.
4899122eed5SLuis Henriques */
ceph_quota_update_statfs(struct ceph_fs_client * fsc,struct kstatfs * buf)4909122eed5SLuis Henriques bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
4919122eed5SLuis Henriques {
4929122eed5SLuis Henriques struct ceph_mds_client *mdsc = fsc->mdsc;
4939122eed5SLuis Henriques struct ceph_inode_info *ci;
4949122eed5SLuis Henriques struct ceph_snap_realm *realm;
4959122eed5SLuis Henriques struct inode *in;
4969122eed5SLuis Henriques u64 total = 0, used, free;
4979122eed5SLuis Henriques bool is_updated = false;
4989122eed5SLuis Henriques
4999122eed5SLuis Henriques down_read(&mdsc->snap_rwsem);
5000f4cf64eSWenchao Hao get_quota_realm(mdsc, d_inode(fsc->sb->s_root), QUOTA_GET_MAX_BYTES,
5010f4cf64eSWenchao Hao &realm, true);
5029122eed5SLuis Henriques up_read(&mdsc->snap_rwsem);
5039122eed5SLuis Henriques if (!realm)
5049122eed5SLuis Henriques return false;
5059122eed5SLuis Henriques
5069122eed5SLuis Henriques spin_lock(&realm->inodes_with_caps_lock);
5079122eed5SLuis Henriques in = realm->inode ? igrab(realm->inode) : NULL;
5089122eed5SLuis Henriques spin_unlock(&realm->inodes_with_caps_lock);
5099122eed5SLuis Henriques if (in) {
5109122eed5SLuis Henriques ci = ceph_inode(in);
5119122eed5SLuis Henriques spin_lock(&ci->i_ceph_lock);
5129122eed5SLuis Henriques if (ci->i_max_bytes) {
5139122eed5SLuis Henriques total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT;
5149122eed5SLuis Henriques used = ci->i_rbytes >> CEPH_BLOCK_SHIFT;
5158e55ba8cSKotresh HR /* For quota size less than 4MB, use 4KB block size */
5168e55ba8cSKotresh HR if (!total) {
5178e55ba8cSKotresh HR total = ci->i_max_bytes >> CEPH_4K_BLOCK_SHIFT;
5188e55ba8cSKotresh HR used = ci->i_rbytes >> CEPH_4K_BLOCK_SHIFT;
5198e55ba8cSKotresh HR buf->f_frsize = 1 << CEPH_4K_BLOCK_SHIFT;
5208e55ba8cSKotresh HR }
5219122eed5SLuis Henriques /* It is possible for a quota to be exceeded.
5229122eed5SLuis Henriques * Report 'zero' in that case
5239122eed5SLuis Henriques */
5249122eed5SLuis Henriques free = total > used ? total - used : 0;
5258e55ba8cSKotresh HR /* For quota size less than 4KB, report the
5268e55ba8cSKotresh HR * total=used=4KB,free=0 when quota is full
5278e55ba8cSKotresh HR * and total=free=4KB, used=0 otherwise */
5288e55ba8cSKotresh HR if (!total) {
5298e55ba8cSKotresh HR total = 1;
5308e55ba8cSKotresh HR free = ci->i_max_bytes > ci->i_rbytes ? 1 : 0;
5318e55ba8cSKotresh HR buf->f_frsize = 1 << CEPH_4K_BLOCK_SHIFT;
5328e55ba8cSKotresh HR }
5339122eed5SLuis Henriques }
5349122eed5SLuis Henriques spin_unlock(&ci->i_ceph_lock);
5359122eed5SLuis Henriques if (total) {
5369122eed5SLuis Henriques buf->f_blocks = total;
5379122eed5SLuis Henriques buf->f_bfree = free;
5389122eed5SLuis Henriques buf->f_bavail = free;
5399122eed5SLuis Henriques is_updated = true;
5409122eed5SLuis Henriques }
5419122eed5SLuis Henriques iput(in);
5429122eed5SLuis Henriques }
5439122eed5SLuis Henriques ceph_put_snap_realm(mdsc, realm);
5449122eed5SLuis Henriques
5459122eed5SLuis Henriques return is_updated;
5469122eed5SLuis Henriques }
5479122eed5SLuis Henriques
548