xref: /openbmc/linux/fs/ceph/quota.c (revision 12ae44a4)
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 
13d557c48dSLuis Henriques void ceph_adjust_quota_realms_count(struct inode *inode, bool inc)
14cafe21a4SLuis Henriques {
15d557c48dSLuis Henriques 	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
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 
22d557c48dSLuis Henriques static inline bool ceph_has_realms_with_quotas(struct inode *inode)
23d557c48dSLuis Henriques {
24d557c48dSLuis Henriques 	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
250c44a8e0SLuis Henriques 	struct super_block *sb = mdsc->fsc->sb;
260c44a8e0SLuis Henriques 
270c44a8e0SLuis Henriques 	if (atomic64_read(&mdsc->quotarealms_count) > 0)
280c44a8e0SLuis Henriques 		return true;
290c44a8e0SLuis Henriques 	/* if root is the real CephFS root, we don't have quota realms */
300c44a8e0SLuis Henriques 	if (sb->s_root->d_inode &&
310c44a8e0SLuis Henriques 	    (sb->s_root->d_inode->i_ino == CEPH_INO_ROOT))
320c44a8e0SLuis Henriques 		return false;
330c44a8e0SLuis Henriques 	/* otherwise, we can't know for sure */
340c44a8e0SLuis Henriques 	return true;
35cafe21a4SLuis Henriques }
36cafe21a4SLuis Henriques 
37fb18a575SLuis Henriques void ceph_handle_quota(struct ceph_mds_client *mdsc,
38fb18a575SLuis Henriques 		       struct ceph_mds_session *session,
39fb18a575SLuis Henriques 		       struct ceph_msg *msg)
40fb18a575SLuis Henriques {
41fb18a575SLuis Henriques 	struct super_block *sb = mdsc->fsc->sb;
42fb18a575SLuis Henriques 	struct ceph_mds_quota *h = msg->front.iov_base;
43fb18a575SLuis Henriques 	struct ceph_vino vino;
44fb18a575SLuis Henriques 	struct inode *inode;
45fb18a575SLuis Henriques 	struct ceph_inode_info *ci;
46fb18a575SLuis Henriques 
470fcf6c02SYan, Zheng 	if (msg->front.iov_len < sizeof(*h)) {
48fb18a575SLuis Henriques 		pr_err("%s corrupt message mds%d len %d\n", __func__,
49fb18a575SLuis Henriques 		       session->s_mds, (int)msg->front.iov_len);
50fb18a575SLuis Henriques 		ceph_msg_dump(msg);
51fb18a575SLuis Henriques 		return;
52fb18a575SLuis Henriques 	}
53fb18a575SLuis Henriques 
54fb18a575SLuis Henriques 	/* increment msg sequence number */
55fb18a575SLuis Henriques 	mutex_lock(&session->s_mutex);
56fb18a575SLuis Henriques 	session->s_seq++;
57fb18a575SLuis Henriques 	mutex_unlock(&session->s_mutex);
58fb18a575SLuis Henriques 
59fb18a575SLuis Henriques 	/* lookup inode */
60fb18a575SLuis Henriques 	vino.ino = le64_to_cpu(h->ino);
61fb18a575SLuis Henriques 	vino.snap = CEPH_NOSNAP;
62fb18a575SLuis Henriques 	inode = ceph_find_inode(sb, vino);
63fb18a575SLuis Henriques 	if (!inode) {
64fb18a575SLuis Henriques 		pr_warn("Failed to find inode %llu\n", vino.ino);
65fb18a575SLuis Henriques 		return;
66fb18a575SLuis Henriques 	}
67fb18a575SLuis Henriques 	ci = ceph_inode(inode);
68fb18a575SLuis Henriques 
69fb18a575SLuis Henriques 	spin_lock(&ci->i_ceph_lock);
70fb18a575SLuis Henriques 	ci->i_rbytes = le64_to_cpu(h->rbytes);
71fb18a575SLuis Henriques 	ci->i_rfiles = le64_to_cpu(h->rfiles);
72fb18a575SLuis Henriques 	ci->i_rsubdirs = le64_to_cpu(h->rsubdirs);
73d557c48dSLuis Henriques 	__ceph_update_quota(ci, le64_to_cpu(h->max_bytes),
74d557c48dSLuis Henriques 		            le64_to_cpu(h->max_files));
75fb18a575SLuis Henriques 	spin_unlock(&ci->i_ceph_lock);
76fb18a575SLuis Henriques 
773e1d0452SYan, Zheng 	/* avoid calling iput_final() in dispatch thread */
783e1d0452SYan, Zheng 	ceph_async_iput(inode);
79fb18a575SLuis Henriques }
80b7a29217SLuis Henriques 
810c44a8e0SLuis Henriques static struct ceph_quotarealm_inode *
820c44a8e0SLuis Henriques find_quotarealm_inode(struct ceph_mds_client *mdsc, u64 ino)
830c44a8e0SLuis Henriques {
840c44a8e0SLuis Henriques 	struct ceph_quotarealm_inode *qri = NULL;
850c44a8e0SLuis Henriques 	struct rb_node **node, *parent = NULL;
860c44a8e0SLuis Henriques 
870c44a8e0SLuis Henriques 	mutex_lock(&mdsc->quotarealms_inodes_mutex);
880c44a8e0SLuis Henriques 	node = &(mdsc->quotarealms_inodes.rb_node);
890c44a8e0SLuis Henriques 	while (*node) {
900c44a8e0SLuis Henriques 		parent = *node;
910c44a8e0SLuis Henriques 		qri = container_of(*node, struct ceph_quotarealm_inode, node);
920c44a8e0SLuis Henriques 
930c44a8e0SLuis Henriques 		if (ino < qri->ino)
940c44a8e0SLuis Henriques 			node = &((*node)->rb_left);
950c44a8e0SLuis Henriques 		else if (ino > qri->ino)
960c44a8e0SLuis Henriques 			node = &((*node)->rb_right);
970c44a8e0SLuis Henriques 		else
980c44a8e0SLuis Henriques 			break;
990c44a8e0SLuis Henriques 	}
1000c44a8e0SLuis Henriques 	if (!qri || (qri->ino != ino)) {
1010c44a8e0SLuis Henriques 		/* Not found, create a new one and insert it */
1020c44a8e0SLuis Henriques 		qri = kmalloc(sizeof(*qri), GFP_KERNEL);
1030c44a8e0SLuis Henriques 		if (qri) {
1040c44a8e0SLuis Henriques 			qri->ino = ino;
1050c44a8e0SLuis Henriques 			qri->inode = NULL;
1060c44a8e0SLuis Henriques 			qri->timeout = 0;
1070c44a8e0SLuis Henriques 			mutex_init(&qri->mutex);
1080c44a8e0SLuis Henriques 			rb_link_node(&qri->node, parent, node);
1090c44a8e0SLuis Henriques 			rb_insert_color(&qri->node, &mdsc->quotarealms_inodes);
1100c44a8e0SLuis Henriques 		} else
1110c44a8e0SLuis Henriques 			pr_warn("Failed to alloc quotarealms_inode\n");
1120c44a8e0SLuis Henriques 	}
1130c44a8e0SLuis Henriques 	mutex_unlock(&mdsc->quotarealms_inodes_mutex);
1140c44a8e0SLuis Henriques 
1150c44a8e0SLuis Henriques 	return qri;
1160c44a8e0SLuis Henriques }
1170c44a8e0SLuis Henriques 
1180c44a8e0SLuis Henriques /*
1190c44a8e0SLuis Henriques  * This function will try to lookup a realm inode which isn't visible in the
1200c44a8e0SLuis Henriques  * filesystem mountpoint.  A list of these kind of inodes (not visible) is
1210c44a8e0SLuis Henriques  * maintained in the mdsc and freed only when the filesystem is umounted.
1220c44a8e0SLuis Henriques  *
1230c44a8e0SLuis Henriques  * Note that these inodes are kept in this list even if the lookup fails, which
1240c44a8e0SLuis Henriques  * allows to prevent useless lookup requests.
1250c44a8e0SLuis Henriques  */
1260c44a8e0SLuis Henriques static struct inode *lookup_quotarealm_inode(struct ceph_mds_client *mdsc,
1270c44a8e0SLuis Henriques 					     struct super_block *sb,
1280c44a8e0SLuis Henriques 					     struct ceph_snap_realm *realm)
1290c44a8e0SLuis Henriques {
1300c44a8e0SLuis Henriques 	struct ceph_quotarealm_inode *qri;
1310c44a8e0SLuis Henriques 	struct inode *in;
1320c44a8e0SLuis Henriques 
1330c44a8e0SLuis Henriques 	qri = find_quotarealm_inode(mdsc, realm->ino);
1340c44a8e0SLuis Henriques 	if (!qri)
1350c44a8e0SLuis Henriques 		return NULL;
1360c44a8e0SLuis Henriques 
1370c44a8e0SLuis Henriques 	mutex_lock(&qri->mutex);
1382ef5df1aSYan, Zheng 	if (qri->inode && ceph_is_any_caps(qri->inode)) {
1390c44a8e0SLuis Henriques 		/* A request has already returned the inode */
1400c44a8e0SLuis Henriques 		mutex_unlock(&qri->mutex);
1410c44a8e0SLuis Henriques 		return qri->inode;
1420c44a8e0SLuis Henriques 	}
1430c44a8e0SLuis Henriques 	/* Check if this inode lookup has failed recently */
1440c44a8e0SLuis Henriques 	if (qri->timeout &&
1450c44a8e0SLuis Henriques 	    time_before_eq(jiffies, qri->timeout)) {
1460c44a8e0SLuis Henriques 		mutex_unlock(&qri->mutex);
1470c44a8e0SLuis Henriques 		return NULL;
1480c44a8e0SLuis Henriques 	}
1492ef5df1aSYan, Zheng 	if (qri->inode) {
1502ef5df1aSYan, Zheng 		/* get caps */
1512ef5df1aSYan, Zheng 		int ret = __ceph_do_getattr(qri->inode, NULL,
1522ef5df1aSYan, Zheng 					    CEPH_STAT_CAP_INODE, true);
1532ef5df1aSYan, Zheng 		if (ret >= 0)
1542ef5df1aSYan, Zheng 			in = qri->inode;
1552ef5df1aSYan, Zheng 		else
1562ef5df1aSYan, Zheng 			in = ERR_PTR(ret);
1572ef5df1aSYan, Zheng 	}  else {
1580c44a8e0SLuis Henriques 		in = ceph_lookup_inode(sb, realm->ino);
1592ef5df1aSYan, Zheng 	}
1602ef5df1aSYan, Zheng 
1610c44a8e0SLuis Henriques 	if (IS_ERR(in)) {
16212ae44a4SLuis Henriques 		dout("Can't lookup inode %llx (err: %ld)\n",
1630c44a8e0SLuis Henriques 		     realm->ino, PTR_ERR(in));
1640c44a8e0SLuis Henriques 		qri->timeout = jiffies + msecs_to_jiffies(60 * 1000); /* XXX */
1650c44a8e0SLuis Henriques 	} else {
1660c44a8e0SLuis Henriques 		qri->timeout = 0;
1670c44a8e0SLuis Henriques 		qri->inode = in;
1680c44a8e0SLuis Henriques 	}
1690c44a8e0SLuis Henriques 	mutex_unlock(&qri->mutex);
1700c44a8e0SLuis Henriques 
1710c44a8e0SLuis Henriques 	return in;
1720c44a8e0SLuis Henriques }
1730c44a8e0SLuis Henriques 
1740c44a8e0SLuis Henriques void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc)
1750c44a8e0SLuis Henriques {
1760c44a8e0SLuis Henriques 	struct ceph_quotarealm_inode *qri;
1770c44a8e0SLuis Henriques 	struct rb_node *node;
1780c44a8e0SLuis Henriques 
1790c44a8e0SLuis Henriques 	/*
1800c44a8e0SLuis Henriques 	 * It should now be safe to clean quotarealms_inode tree without holding
1810c44a8e0SLuis Henriques 	 * mdsc->quotarealms_inodes_mutex...
1820c44a8e0SLuis Henriques 	 */
1830c44a8e0SLuis Henriques 	mutex_lock(&mdsc->quotarealms_inodes_mutex);
1840c44a8e0SLuis Henriques 	while (!RB_EMPTY_ROOT(&mdsc->quotarealms_inodes)) {
1850c44a8e0SLuis Henriques 		node = rb_first(&mdsc->quotarealms_inodes);
1860c44a8e0SLuis Henriques 		qri = rb_entry(node, struct ceph_quotarealm_inode, node);
1870c44a8e0SLuis Henriques 		rb_erase(node, &mdsc->quotarealms_inodes);
1880c44a8e0SLuis Henriques 		iput(qri->inode);
1890c44a8e0SLuis Henriques 		kfree(qri);
1900c44a8e0SLuis Henriques 	}
1910c44a8e0SLuis Henriques 	mutex_unlock(&mdsc->quotarealms_inodes_mutex);
1920c44a8e0SLuis Henriques }
1930c44a8e0SLuis Henriques 
194cafe21a4SLuis Henriques /*
195cafe21a4SLuis Henriques  * This function walks through the snaprealm for an inode and returns the
196cafe21a4SLuis Henriques  * ceph_snap_realm for the first snaprealm that has quotas set (either max_files
197cafe21a4SLuis Henriques  * or max_bytes).  If the root is reached, return the root ceph_snap_realm
198cafe21a4SLuis Henriques  * instead.
199cafe21a4SLuis Henriques  *
200cafe21a4SLuis Henriques  * Note that the caller is responsible for calling ceph_put_snap_realm() on the
201cafe21a4SLuis Henriques  * returned realm.
2020c44a8e0SLuis Henriques  *
2030c44a8e0SLuis Henriques  * Callers of this function need to hold mdsc->snap_rwsem.  However, if there's
2040c44a8e0SLuis Henriques  * a need to do an inode lookup, this rwsem will be temporarily dropped.  Hence
2050c44a8e0SLuis Henriques  * the 'retry' argument: if rwsem needs to be dropped and 'retry' is 'false'
2060c44a8e0SLuis Henriques  * this function will return -EAGAIN; otherwise, the snaprealms walk-through
2070c44a8e0SLuis Henriques  * will be restarted.
208cafe21a4SLuis Henriques  */
209cafe21a4SLuis Henriques static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc,
2100c44a8e0SLuis Henriques 					       struct inode *inode, bool retry)
211cafe21a4SLuis Henriques {
212cafe21a4SLuis Henriques 	struct ceph_inode_info *ci = NULL;
213cafe21a4SLuis Henriques 	struct ceph_snap_realm *realm, *next;
214cafe21a4SLuis Henriques 	struct inode *in;
2150eb6bbe4SYan, Zheng 	bool has_quota;
216cafe21a4SLuis Henriques 
21725963669SYan, Zheng 	if (ceph_snap(inode) != CEPH_NOSNAP)
21825963669SYan, Zheng 		return NULL;
21925963669SYan, Zheng 
2200c44a8e0SLuis Henriques restart:
221cafe21a4SLuis Henriques 	realm = ceph_inode(inode)->i_snap_realm;
22225963669SYan, Zheng 	if (realm)
223cafe21a4SLuis Henriques 		ceph_get_snap_realm(mdsc, realm);
22425963669SYan, Zheng 	else
22525963669SYan, Zheng 		pr_err_ratelimited("get_quota_realm: ino (%llx.%llx) "
22625963669SYan, Zheng 				   "null i_snap_realm\n", ceph_vinop(inode));
227cafe21a4SLuis Henriques 	while (realm) {
2280c44a8e0SLuis Henriques 		bool has_inode;
2290c44a8e0SLuis Henriques 
230e3161f17SLuis Henriques 		spin_lock(&realm->inodes_with_caps_lock);
2310c44a8e0SLuis Henriques 		has_inode = realm->inode;
2320c44a8e0SLuis Henriques 		in = has_inode ? igrab(realm->inode) : NULL;
233e3161f17SLuis Henriques 		spin_unlock(&realm->inodes_with_caps_lock);
2340c44a8e0SLuis Henriques 		if (has_inode && !in)
235cafe21a4SLuis Henriques 			break;
2360c44a8e0SLuis Henriques 		if (!in) {
2370c44a8e0SLuis Henriques 			up_read(&mdsc->snap_rwsem);
2380c44a8e0SLuis Henriques 			in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm);
2390c44a8e0SLuis Henriques 			down_read(&mdsc->snap_rwsem);
2400c44a8e0SLuis Henriques 			if (IS_ERR_OR_NULL(in))
2410c44a8e0SLuis Henriques 				break;
2420c44a8e0SLuis Henriques 			ceph_put_snap_realm(mdsc, realm);
2430c44a8e0SLuis Henriques 			if (!retry)
2440c44a8e0SLuis Henriques 				return ERR_PTR(-EAGAIN);
2450c44a8e0SLuis Henriques 			goto restart;
2460c44a8e0SLuis Henriques 		}
247e3161f17SLuis Henriques 
248cafe21a4SLuis Henriques 		ci = ceph_inode(in);
249d557c48dSLuis Henriques 		has_quota = __ceph_has_any_quota(ci);
2503e1d0452SYan, Zheng 		/* avoid calling iput_final() while holding mdsc->snap_rwsem */
2513e1d0452SYan, Zheng 		ceph_async_iput(in);
2520eb6bbe4SYan, Zheng 
253cafe21a4SLuis Henriques 		next = realm->parent;
2540eb6bbe4SYan, Zheng 		if (has_quota || !next)
2550eb6bbe4SYan, Zheng 		       return realm;
2560eb6bbe4SYan, Zheng 
257cafe21a4SLuis Henriques 		ceph_get_snap_realm(mdsc, next);
258cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
259cafe21a4SLuis Henriques 		realm = next;
260cafe21a4SLuis Henriques 	}
261cafe21a4SLuis Henriques 	if (realm)
262cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
263cafe21a4SLuis Henriques 
264cafe21a4SLuis Henriques 	return NULL;
265cafe21a4SLuis Henriques }
266cafe21a4SLuis Henriques 
267cafe21a4SLuis Henriques bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
268cafe21a4SLuis Henriques {
269cafe21a4SLuis Henriques 	struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc;
270cafe21a4SLuis Henriques 	struct ceph_snap_realm *old_realm, *new_realm;
271cafe21a4SLuis Henriques 	bool is_same;
272cafe21a4SLuis Henriques 
2730c44a8e0SLuis Henriques restart:
2740c44a8e0SLuis Henriques 	/*
2750c44a8e0SLuis Henriques 	 * We need to lookup 2 quota realms atomically, i.e. with snap_rwsem.
2760c44a8e0SLuis Henriques 	 * However, get_quota_realm may drop it temporarily.  By setting the
2770c44a8e0SLuis Henriques 	 * 'retry' parameter to 'false', we'll get -EAGAIN if the rwsem was
2780c44a8e0SLuis Henriques 	 * dropped and we can then restart the whole operation.
2790c44a8e0SLuis Henriques 	 */
280cafe21a4SLuis Henriques 	down_read(&mdsc->snap_rwsem);
2810c44a8e0SLuis Henriques 	old_realm = get_quota_realm(mdsc, old, true);
2820c44a8e0SLuis Henriques 	new_realm = get_quota_realm(mdsc, new, false);
2830c44a8e0SLuis Henriques 	if (PTR_ERR(new_realm) == -EAGAIN) {
2840c44a8e0SLuis Henriques 		up_read(&mdsc->snap_rwsem);
2850c44a8e0SLuis Henriques 		if (old_realm)
2860c44a8e0SLuis Henriques 			ceph_put_snap_realm(mdsc, old_realm);
2870c44a8e0SLuis Henriques 		goto restart;
2880c44a8e0SLuis Henriques 	}
289cafe21a4SLuis Henriques 	is_same = (old_realm == new_realm);
290cafe21a4SLuis Henriques 	up_read(&mdsc->snap_rwsem);
291cafe21a4SLuis Henriques 
292cafe21a4SLuis Henriques 	if (old_realm)
293cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, old_realm);
294cafe21a4SLuis Henriques 	if (new_realm)
295cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, new_realm);
296cafe21a4SLuis Henriques 
297cafe21a4SLuis Henriques 	return is_same;
298cafe21a4SLuis Henriques }
299cafe21a4SLuis Henriques 
300b7a29217SLuis Henriques enum quota_check_op {
3012b83845fSLuis Henriques 	QUOTA_CHECK_MAX_FILES_OP,	/* check quota max_files limit */
3021ab302a0SLuis Henriques 	QUOTA_CHECK_MAX_BYTES_OP,	/* check quota max_files limit */
3031ab302a0SLuis Henriques 	QUOTA_CHECK_MAX_BYTES_APPROACHING_OP	/* check if quota max_files
3041ab302a0SLuis Henriques 						   limit is approaching */
305b7a29217SLuis Henriques };
306b7a29217SLuis Henriques 
307b7a29217SLuis Henriques /*
308b7a29217SLuis Henriques  * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each
309b7a29217SLuis Henriques  * realm, it will execute quota check operation defined by the 'op' parameter.
310b7a29217SLuis Henriques  * The snaprealm walk is interrupted if the quota check detects that the quota
311b7a29217SLuis Henriques  * is exceeded or if the root inode is reached.
312b7a29217SLuis Henriques  */
313b7a29217SLuis Henriques static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
314b7a29217SLuis Henriques 				 loff_t delta)
315b7a29217SLuis Henriques {
316b7a29217SLuis Henriques 	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
317b7a29217SLuis Henriques 	struct ceph_inode_info *ci;
318b7a29217SLuis Henriques 	struct ceph_snap_realm *realm, *next;
319b7a29217SLuis Henriques 	struct inode *in;
320b7a29217SLuis Henriques 	u64 max, rvalue;
321b7a29217SLuis Henriques 	bool exceeded = false;
322b7a29217SLuis Henriques 
32325963669SYan, Zheng 	if (ceph_snap(inode) != CEPH_NOSNAP)
32425963669SYan, Zheng 		return false;
32525963669SYan, Zheng 
326b7a29217SLuis Henriques 	down_read(&mdsc->snap_rwsem);
3270c44a8e0SLuis Henriques restart:
328b7a29217SLuis Henriques 	realm = ceph_inode(inode)->i_snap_realm;
32925963669SYan, Zheng 	if (realm)
330b7a29217SLuis Henriques 		ceph_get_snap_realm(mdsc, realm);
33125963669SYan, Zheng 	else
33225963669SYan, Zheng 		pr_err_ratelimited("check_quota_exceeded: ino (%llx.%llx) "
33325963669SYan, Zheng 				   "null i_snap_realm\n", ceph_vinop(inode));
334b7a29217SLuis Henriques 	while (realm) {
3350c44a8e0SLuis Henriques 		bool has_inode;
336e3161f17SLuis Henriques 
3370c44a8e0SLuis Henriques 		spin_lock(&realm->inodes_with_caps_lock);
3380c44a8e0SLuis Henriques 		has_inode = realm->inode;
3390c44a8e0SLuis Henriques 		in = has_inode ? igrab(realm->inode) : NULL;
3400c44a8e0SLuis Henriques 		spin_unlock(&realm->inodes_with_caps_lock);
3410c44a8e0SLuis Henriques 		if (has_inode && !in)
3420c44a8e0SLuis Henriques 			break;
3430c44a8e0SLuis Henriques 		if (!in) {
3440c44a8e0SLuis Henriques 			up_read(&mdsc->snap_rwsem);
3450c44a8e0SLuis Henriques 			in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm);
3460c44a8e0SLuis Henriques 			down_read(&mdsc->snap_rwsem);
3470c44a8e0SLuis Henriques 			if (IS_ERR_OR_NULL(in))
3480c44a8e0SLuis Henriques 				break;
3490c44a8e0SLuis Henriques 			ceph_put_snap_realm(mdsc, realm);
3500c44a8e0SLuis Henriques 			goto restart;
3510c44a8e0SLuis Henriques 		}
352b7a29217SLuis Henriques 		ci = ceph_inode(in);
353b7a29217SLuis Henriques 		spin_lock(&ci->i_ceph_lock);
354b7a29217SLuis Henriques 		if (op == QUOTA_CHECK_MAX_FILES_OP) {
355b7a29217SLuis Henriques 			max = ci->i_max_files;
356b7a29217SLuis Henriques 			rvalue = ci->i_rfiles + ci->i_rsubdirs;
3572b83845fSLuis Henriques 		} else {
3582b83845fSLuis Henriques 			max = ci->i_max_bytes;
3592b83845fSLuis Henriques 			rvalue = ci->i_rbytes;
360b7a29217SLuis Henriques 		}
361b7a29217SLuis Henriques 		spin_unlock(&ci->i_ceph_lock);
362b7a29217SLuis Henriques 		switch (op) {
363b7a29217SLuis Henriques 		case QUOTA_CHECK_MAX_FILES_OP:
364b7a29217SLuis Henriques 			exceeded = (max && (rvalue >= max));
365b7a29217SLuis Henriques 			break;
3662b83845fSLuis Henriques 		case QUOTA_CHECK_MAX_BYTES_OP:
3672b83845fSLuis Henriques 			exceeded = (max && (rvalue + delta > max));
3682b83845fSLuis Henriques 			break;
3691ab302a0SLuis Henriques 		case QUOTA_CHECK_MAX_BYTES_APPROACHING_OP:
3701ab302a0SLuis Henriques 			if (max) {
3711ab302a0SLuis Henriques 				if (rvalue >= max)
3721ab302a0SLuis Henriques 					exceeded = true;
3731ab302a0SLuis Henriques 				else {
3741ab302a0SLuis Henriques 					/*
3751ab302a0SLuis Henriques 					 * when we're writing more that 1/16th
3761ab302a0SLuis Henriques 					 * of the available space
3771ab302a0SLuis Henriques 					 */
3781ab302a0SLuis Henriques 					exceeded =
3791ab302a0SLuis Henriques 						(((max - rvalue) >> 4) < delta);
3801ab302a0SLuis Henriques 				}
3811ab302a0SLuis Henriques 			}
3821ab302a0SLuis Henriques 			break;
383b7a29217SLuis Henriques 		default:
384b7a29217SLuis Henriques 			/* Shouldn't happen */
385b7a29217SLuis Henriques 			pr_warn("Invalid quota check op (%d)\n", op);
386b7a29217SLuis Henriques 			exceeded = true; /* Just break the loop */
387b7a29217SLuis Henriques 		}
3883e1d0452SYan, Zheng 		/* avoid calling iput_final() while holding mdsc->snap_rwsem */
3893e1d0452SYan, Zheng 		ceph_async_iput(in);
390b7a29217SLuis Henriques 
391b7a29217SLuis Henriques 		next = realm->parent;
3920eb6bbe4SYan, Zheng 		if (exceeded || !next)
3930eb6bbe4SYan, Zheng 			break;
394b7a29217SLuis Henriques 		ceph_get_snap_realm(mdsc, next);
395b7a29217SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
396b7a29217SLuis Henriques 		realm = next;
397b7a29217SLuis Henriques 	}
39871f2cc64SLuis Henriques 	if (realm)
399b7a29217SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
400b7a29217SLuis Henriques 	up_read(&mdsc->snap_rwsem);
401b7a29217SLuis Henriques 
402b7a29217SLuis Henriques 	return exceeded;
403b7a29217SLuis Henriques }
404b7a29217SLuis Henriques 
405b7a29217SLuis Henriques /*
406b7a29217SLuis Henriques  * ceph_quota_is_max_files_exceeded - check if we can create a new file
407b7a29217SLuis Henriques  * @inode:	directory where a new file is being created
408b7a29217SLuis Henriques  *
409b7a29217SLuis Henriques  * This functions returns true is max_files quota allows a new file to be
410b7a29217SLuis Henriques  * created.  It is necessary to walk through the snaprealm hierarchy (until the
411b7a29217SLuis Henriques  * FS root) to check all realms with quotas set.
412b7a29217SLuis Henriques  */
413b7a29217SLuis Henriques bool ceph_quota_is_max_files_exceeded(struct inode *inode)
414b7a29217SLuis Henriques {
415d557c48dSLuis Henriques 	if (!ceph_has_realms_with_quotas(inode))
416d557c48dSLuis Henriques 		return false;
417d557c48dSLuis Henriques 
418b7a29217SLuis Henriques 	WARN_ON(!S_ISDIR(inode->i_mode));
419b7a29217SLuis Henriques 
420b7a29217SLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0);
421b7a29217SLuis Henriques }
4222b83845fSLuis Henriques 
4232b83845fSLuis Henriques /*
4242b83845fSLuis Henriques  * ceph_quota_is_max_bytes_exceeded - check if we can write to a file
4252b83845fSLuis Henriques  * @inode:	inode being written
4262b83845fSLuis Henriques  * @newsize:	new size if write succeeds
4272b83845fSLuis Henriques  *
4282b83845fSLuis Henriques  * This functions returns true is max_bytes quota allows a file size to reach
4292b83845fSLuis Henriques  * @newsize; it returns false otherwise.
4302b83845fSLuis Henriques  */
4312b83845fSLuis Henriques bool ceph_quota_is_max_bytes_exceeded(struct inode *inode, loff_t newsize)
4322b83845fSLuis Henriques {
4332b83845fSLuis Henriques 	loff_t size = i_size_read(inode);
4342b83845fSLuis Henriques 
435d557c48dSLuis Henriques 	if (!ceph_has_realms_with_quotas(inode))
436d557c48dSLuis Henriques 		return false;
437d557c48dSLuis Henriques 
4382b83845fSLuis Henriques 	/* return immediately if we're decreasing file size */
4392b83845fSLuis Henriques 	if (newsize <= size)
4402b83845fSLuis Henriques 		return false;
4412b83845fSLuis Henriques 
4422b83845fSLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_OP, (newsize - size));
4432b83845fSLuis Henriques }
4441ab302a0SLuis Henriques 
4451ab302a0SLuis Henriques /*
4461ab302a0SLuis Henriques  * ceph_quota_is_max_bytes_approaching - check if we're reaching max_bytes
4471ab302a0SLuis Henriques  * @inode:	inode being written
4481ab302a0SLuis Henriques  * @newsize:	new size if write succeeds
4491ab302a0SLuis Henriques  *
4501ab302a0SLuis Henriques  * This function returns true if the new file size @newsize will be consuming
4511ab302a0SLuis Henriques  * more than 1/16th of the available quota space; it returns false otherwise.
4521ab302a0SLuis Henriques  */
4531ab302a0SLuis Henriques bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize)
4541ab302a0SLuis Henriques {
4551ab302a0SLuis Henriques 	loff_t size = ceph_inode(inode)->i_reported_size;
4561ab302a0SLuis Henriques 
457d557c48dSLuis Henriques 	if (!ceph_has_realms_with_quotas(inode))
458d557c48dSLuis Henriques 		return false;
459d557c48dSLuis Henriques 
4601ab302a0SLuis Henriques 	/* return immediately if we're decreasing file size */
4611ab302a0SLuis Henriques 	if (newsize <= size)
4621ab302a0SLuis Henriques 		return false;
4631ab302a0SLuis Henriques 
4641ab302a0SLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP,
4651ab302a0SLuis Henriques 				    (newsize - size));
4661ab302a0SLuis Henriques }
4679122eed5SLuis Henriques 
4689122eed5SLuis Henriques /*
4699122eed5SLuis Henriques  * ceph_quota_update_statfs - if root has quota update statfs with quota status
4709122eed5SLuis Henriques  * @fsc:	filesystem client instance
4719122eed5SLuis Henriques  * @buf:	statfs to update
4729122eed5SLuis Henriques  *
4739122eed5SLuis Henriques  * If the mounted filesystem root has max_bytes quota set, update the filesystem
4749122eed5SLuis Henriques  * statistics with the quota status.
4759122eed5SLuis Henriques  *
4769122eed5SLuis Henriques  * This function returns true if the stats have been updated, false otherwise.
4779122eed5SLuis Henriques  */
4789122eed5SLuis Henriques bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
4799122eed5SLuis Henriques {
4809122eed5SLuis Henriques 	struct ceph_mds_client *mdsc = fsc->mdsc;
4819122eed5SLuis Henriques 	struct ceph_inode_info *ci;
4829122eed5SLuis Henriques 	struct ceph_snap_realm *realm;
4839122eed5SLuis Henriques 	struct inode *in;
4849122eed5SLuis Henriques 	u64 total = 0, used, free;
4859122eed5SLuis Henriques 	bool is_updated = false;
4869122eed5SLuis Henriques 
4879122eed5SLuis Henriques 	down_read(&mdsc->snap_rwsem);
4880c44a8e0SLuis Henriques 	realm = get_quota_realm(mdsc, d_inode(fsc->sb->s_root), true);
4899122eed5SLuis Henriques 	up_read(&mdsc->snap_rwsem);
4909122eed5SLuis Henriques 	if (!realm)
4919122eed5SLuis Henriques 		return false;
4929122eed5SLuis Henriques 
4939122eed5SLuis Henriques 	spin_lock(&realm->inodes_with_caps_lock);
4949122eed5SLuis Henriques 	in = realm->inode ? igrab(realm->inode) : NULL;
4959122eed5SLuis Henriques 	spin_unlock(&realm->inodes_with_caps_lock);
4969122eed5SLuis Henriques 	if (in) {
4979122eed5SLuis Henriques 		ci = ceph_inode(in);
4989122eed5SLuis Henriques 		spin_lock(&ci->i_ceph_lock);
4999122eed5SLuis Henriques 		if (ci->i_max_bytes) {
5009122eed5SLuis Henriques 			total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT;
5019122eed5SLuis Henriques 			used = ci->i_rbytes >> CEPH_BLOCK_SHIFT;
5029122eed5SLuis Henriques 			/* It is possible for a quota to be exceeded.
5039122eed5SLuis Henriques 			 * Report 'zero' in that case
5049122eed5SLuis Henriques 			 */
5059122eed5SLuis Henriques 			free = total > used ? total - used : 0;
5069122eed5SLuis Henriques 		}
5079122eed5SLuis Henriques 		spin_unlock(&ci->i_ceph_lock);
5089122eed5SLuis Henriques 		if (total) {
5099122eed5SLuis Henriques 			buf->f_blocks = total;
5109122eed5SLuis Henriques 			buf->f_bfree = free;
5119122eed5SLuis Henriques 			buf->f_bavail = free;
5129122eed5SLuis Henriques 			is_updated = true;
5139122eed5SLuis Henriques 		}
5149122eed5SLuis Henriques 		iput(in);
5159122eed5SLuis Henriques 	}
5169122eed5SLuis Henriques 	ceph_put_snap_realm(mdsc, realm);
5179122eed5SLuis Henriques 
5189122eed5SLuis Henriques 	return is_updated;
5199122eed5SLuis Henriques }
5209122eed5SLuis Henriques 
521