xref: /openbmc/linux/fs/ceph/quota.c (revision 0c44a8e0)
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 
77fb18a575SLuis Henriques 	iput(inode);
78fb18a575SLuis Henriques }
79b7a29217SLuis Henriques 
800c44a8e0SLuis Henriques static struct ceph_quotarealm_inode *
810c44a8e0SLuis Henriques find_quotarealm_inode(struct ceph_mds_client *mdsc, u64 ino)
820c44a8e0SLuis Henriques {
830c44a8e0SLuis Henriques 	struct ceph_quotarealm_inode *qri = NULL;
840c44a8e0SLuis Henriques 	struct rb_node **node, *parent = NULL;
850c44a8e0SLuis Henriques 
860c44a8e0SLuis Henriques 	mutex_lock(&mdsc->quotarealms_inodes_mutex);
870c44a8e0SLuis Henriques 	node = &(mdsc->quotarealms_inodes.rb_node);
880c44a8e0SLuis Henriques 	while (*node) {
890c44a8e0SLuis Henriques 		parent = *node;
900c44a8e0SLuis Henriques 		qri = container_of(*node, struct ceph_quotarealm_inode, node);
910c44a8e0SLuis Henriques 
920c44a8e0SLuis Henriques 		if (ino < qri->ino)
930c44a8e0SLuis Henriques 			node = &((*node)->rb_left);
940c44a8e0SLuis Henriques 		else if (ino > qri->ino)
950c44a8e0SLuis Henriques 			node = &((*node)->rb_right);
960c44a8e0SLuis Henriques 		else
970c44a8e0SLuis Henriques 			break;
980c44a8e0SLuis Henriques 	}
990c44a8e0SLuis Henriques 	if (!qri || (qri->ino != ino)) {
1000c44a8e0SLuis Henriques 		/* Not found, create a new one and insert it */
1010c44a8e0SLuis Henriques 		qri = kmalloc(sizeof(*qri), GFP_KERNEL);
1020c44a8e0SLuis Henriques 		if (qri) {
1030c44a8e0SLuis Henriques 			qri->ino = ino;
1040c44a8e0SLuis Henriques 			qri->inode = NULL;
1050c44a8e0SLuis Henriques 			qri->timeout = 0;
1060c44a8e0SLuis Henriques 			mutex_init(&qri->mutex);
1070c44a8e0SLuis Henriques 			rb_link_node(&qri->node, parent, node);
1080c44a8e0SLuis Henriques 			rb_insert_color(&qri->node, &mdsc->quotarealms_inodes);
1090c44a8e0SLuis Henriques 		} else
1100c44a8e0SLuis Henriques 			pr_warn("Failed to alloc quotarealms_inode\n");
1110c44a8e0SLuis Henriques 	}
1120c44a8e0SLuis Henriques 	mutex_unlock(&mdsc->quotarealms_inodes_mutex);
1130c44a8e0SLuis Henriques 
1140c44a8e0SLuis Henriques 	return qri;
1150c44a8e0SLuis Henriques }
1160c44a8e0SLuis Henriques 
1170c44a8e0SLuis Henriques /*
1180c44a8e0SLuis Henriques  * This function will try to lookup a realm inode which isn't visible in the
1190c44a8e0SLuis Henriques  * filesystem mountpoint.  A list of these kind of inodes (not visible) is
1200c44a8e0SLuis Henriques  * maintained in the mdsc and freed only when the filesystem is umounted.
1210c44a8e0SLuis Henriques  *
1220c44a8e0SLuis Henriques  * Note that these inodes are kept in this list even if the lookup fails, which
1230c44a8e0SLuis Henriques  * allows to prevent useless lookup requests.
1240c44a8e0SLuis Henriques  */
1250c44a8e0SLuis Henriques static struct inode *lookup_quotarealm_inode(struct ceph_mds_client *mdsc,
1260c44a8e0SLuis Henriques 					     struct super_block *sb,
1270c44a8e0SLuis Henriques 					     struct ceph_snap_realm *realm)
1280c44a8e0SLuis Henriques {
1290c44a8e0SLuis Henriques 	struct ceph_quotarealm_inode *qri;
1300c44a8e0SLuis Henriques 	struct inode *in;
1310c44a8e0SLuis Henriques 
1320c44a8e0SLuis Henriques 	qri = find_quotarealm_inode(mdsc, realm->ino);
1330c44a8e0SLuis Henriques 	if (!qri)
1340c44a8e0SLuis Henriques 		return NULL;
1350c44a8e0SLuis Henriques 
1360c44a8e0SLuis Henriques 	mutex_lock(&qri->mutex);
1370c44a8e0SLuis Henriques 	if (qri->inode) {
1380c44a8e0SLuis Henriques 		/* A request has already returned the inode */
1390c44a8e0SLuis Henriques 		mutex_unlock(&qri->mutex);
1400c44a8e0SLuis Henriques 		return qri->inode;
1410c44a8e0SLuis Henriques 	}
1420c44a8e0SLuis Henriques 	/* Check if this inode lookup has failed recently */
1430c44a8e0SLuis Henriques 	if (qri->timeout &&
1440c44a8e0SLuis Henriques 	    time_before_eq(jiffies, qri->timeout)) {
1450c44a8e0SLuis Henriques 		mutex_unlock(&qri->mutex);
1460c44a8e0SLuis Henriques 		return NULL;
1470c44a8e0SLuis Henriques 	}
1480c44a8e0SLuis Henriques 	in = ceph_lookup_inode(sb, realm->ino);
1490c44a8e0SLuis Henriques 	if (IS_ERR(in)) {
1500c44a8e0SLuis Henriques 		pr_warn("Can't lookup inode %llx (err: %ld)\n",
1510c44a8e0SLuis Henriques 			realm->ino, PTR_ERR(in));
1520c44a8e0SLuis Henriques 		qri->timeout = jiffies + msecs_to_jiffies(60 * 1000); /* XXX */
1530c44a8e0SLuis Henriques 	} else {
1540c44a8e0SLuis Henriques 		qri->timeout = 0;
1550c44a8e0SLuis Henriques 		qri->inode = in;
1560c44a8e0SLuis Henriques 	}
1570c44a8e0SLuis Henriques 	mutex_unlock(&qri->mutex);
1580c44a8e0SLuis Henriques 
1590c44a8e0SLuis Henriques 	return in;
1600c44a8e0SLuis Henriques }
1610c44a8e0SLuis Henriques 
1620c44a8e0SLuis Henriques void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc)
1630c44a8e0SLuis Henriques {
1640c44a8e0SLuis Henriques 	struct ceph_quotarealm_inode *qri;
1650c44a8e0SLuis Henriques 	struct rb_node *node;
1660c44a8e0SLuis Henriques 
1670c44a8e0SLuis Henriques 	/*
1680c44a8e0SLuis Henriques 	 * It should now be safe to clean quotarealms_inode tree without holding
1690c44a8e0SLuis Henriques 	 * mdsc->quotarealms_inodes_mutex...
1700c44a8e0SLuis Henriques 	 */
1710c44a8e0SLuis Henriques 	mutex_lock(&mdsc->quotarealms_inodes_mutex);
1720c44a8e0SLuis Henriques 	while (!RB_EMPTY_ROOT(&mdsc->quotarealms_inodes)) {
1730c44a8e0SLuis Henriques 		node = rb_first(&mdsc->quotarealms_inodes);
1740c44a8e0SLuis Henriques 		qri = rb_entry(node, struct ceph_quotarealm_inode, node);
1750c44a8e0SLuis Henriques 		rb_erase(node, &mdsc->quotarealms_inodes);
1760c44a8e0SLuis Henriques 		iput(qri->inode);
1770c44a8e0SLuis Henriques 		kfree(qri);
1780c44a8e0SLuis Henriques 	}
1790c44a8e0SLuis Henriques 	mutex_unlock(&mdsc->quotarealms_inodes_mutex);
1800c44a8e0SLuis Henriques }
1810c44a8e0SLuis Henriques 
182cafe21a4SLuis Henriques /*
183cafe21a4SLuis Henriques  * This function walks through the snaprealm for an inode and returns the
184cafe21a4SLuis Henriques  * ceph_snap_realm for the first snaprealm that has quotas set (either max_files
185cafe21a4SLuis Henriques  * or max_bytes).  If the root is reached, return the root ceph_snap_realm
186cafe21a4SLuis Henriques  * instead.
187cafe21a4SLuis Henriques  *
188cafe21a4SLuis Henriques  * Note that the caller is responsible for calling ceph_put_snap_realm() on the
189cafe21a4SLuis Henriques  * returned realm.
1900c44a8e0SLuis Henriques  *
1910c44a8e0SLuis Henriques  * Callers of this function need to hold mdsc->snap_rwsem.  However, if there's
1920c44a8e0SLuis Henriques  * a need to do an inode lookup, this rwsem will be temporarily dropped.  Hence
1930c44a8e0SLuis Henriques  * the 'retry' argument: if rwsem needs to be dropped and 'retry' is 'false'
1940c44a8e0SLuis Henriques  * this function will return -EAGAIN; otherwise, the snaprealms walk-through
1950c44a8e0SLuis Henriques  * will be restarted.
196cafe21a4SLuis Henriques  */
197cafe21a4SLuis Henriques static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc,
1980c44a8e0SLuis Henriques 					       struct inode *inode, bool retry)
199cafe21a4SLuis Henriques {
200cafe21a4SLuis Henriques 	struct ceph_inode_info *ci = NULL;
201cafe21a4SLuis Henriques 	struct ceph_snap_realm *realm, *next;
202cafe21a4SLuis Henriques 	struct inode *in;
2030eb6bbe4SYan, Zheng 	bool has_quota;
204cafe21a4SLuis Henriques 
20525963669SYan, Zheng 	if (ceph_snap(inode) != CEPH_NOSNAP)
20625963669SYan, Zheng 		return NULL;
20725963669SYan, Zheng 
2080c44a8e0SLuis Henriques restart:
209cafe21a4SLuis Henriques 	realm = ceph_inode(inode)->i_snap_realm;
21025963669SYan, Zheng 	if (realm)
211cafe21a4SLuis Henriques 		ceph_get_snap_realm(mdsc, realm);
21225963669SYan, Zheng 	else
21325963669SYan, Zheng 		pr_err_ratelimited("get_quota_realm: ino (%llx.%llx) "
21425963669SYan, Zheng 				   "null i_snap_realm\n", ceph_vinop(inode));
215cafe21a4SLuis Henriques 	while (realm) {
2160c44a8e0SLuis Henriques 		bool has_inode;
2170c44a8e0SLuis Henriques 
218e3161f17SLuis Henriques 		spin_lock(&realm->inodes_with_caps_lock);
2190c44a8e0SLuis Henriques 		has_inode = realm->inode;
2200c44a8e0SLuis Henriques 		in = has_inode ? igrab(realm->inode) : NULL;
221e3161f17SLuis Henriques 		spin_unlock(&realm->inodes_with_caps_lock);
2220c44a8e0SLuis Henriques 		if (has_inode && !in)
223cafe21a4SLuis Henriques 			break;
2240c44a8e0SLuis Henriques 		if (!in) {
2250c44a8e0SLuis Henriques 			up_read(&mdsc->snap_rwsem);
2260c44a8e0SLuis Henriques 			in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm);
2270c44a8e0SLuis Henriques 			down_read(&mdsc->snap_rwsem);
2280c44a8e0SLuis Henriques 			if (IS_ERR_OR_NULL(in))
2290c44a8e0SLuis Henriques 				break;
2300c44a8e0SLuis Henriques 			ceph_put_snap_realm(mdsc, realm);
2310c44a8e0SLuis Henriques 			if (!retry)
2320c44a8e0SLuis Henriques 				return ERR_PTR(-EAGAIN);
2330c44a8e0SLuis Henriques 			goto restart;
2340c44a8e0SLuis Henriques 		}
235e3161f17SLuis Henriques 
236cafe21a4SLuis Henriques 		ci = ceph_inode(in);
237d557c48dSLuis Henriques 		has_quota = __ceph_has_any_quota(ci);
238cafe21a4SLuis Henriques 		iput(in);
2390eb6bbe4SYan, Zheng 
240cafe21a4SLuis Henriques 		next = realm->parent;
2410eb6bbe4SYan, Zheng 		if (has_quota || !next)
2420eb6bbe4SYan, Zheng 		       return realm;
2430eb6bbe4SYan, Zheng 
244cafe21a4SLuis Henriques 		ceph_get_snap_realm(mdsc, next);
245cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
246cafe21a4SLuis Henriques 		realm = next;
247cafe21a4SLuis Henriques 	}
248cafe21a4SLuis Henriques 	if (realm)
249cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
250cafe21a4SLuis Henriques 
251cafe21a4SLuis Henriques 	return NULL;
252cafe21a4SLuis Henriques }
253cafe21a4SLuis Henriques 
254cafe21a4SLuis Henriques bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
255cafe21a4SLuis Henriques {
256cafe21a4SLuis Henriques 	struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc;
257cafe21a4SLuis Henriques 	struct ceph_snap_realm *old_realm, *new_realm;
258cafe21a4SLuis Henriques 	bool is_same;
259cafe21a4SLuis Henriques 
2600c44a8e0SLuis Henriques restart:
2610c44a8e0SLuis Henriques 	/*
2620c44a8e0SLuis Henriques 	 * We need to lookup 2 quota realms atomically, i.e. with snap_rwsem.
2630c44a8e0SLuis Henriques 	 * However, get_quota_realm may drop it temporarily.  By setting the
2640c44a8e0SLuis Henriques 	 * 'retry' parameter to 'false', we'll get -EAGAIN if the rwsem was
2650c44a8e0SLuis Henriques 	 * dropped and we can then restart the whole operation.
2660c44a8e0SLuis Henriques 	 */
267cafe21a4SLuis Henriques 	down_read(&mdsc->snap_rwsem);
2680c44a8e0SLuis Henriques 	old_realm = get_quota_realm(mdsc, old, true);
2690c44a8e0SLuis Henriques 	new_realm = get_quota_realm(mdsc, new, false);
2700c44a8e0SLuis Henriques 	if (PTR_ERR(new_realm) == -EAGAIN) {
2710c44a8e0SLuis Henriques 		up_read(&mdsc->snap_rwsem);
2720c44a8e0SLuis Henriques 		if (old_realm)
2730c44a8e0SLuis Henriques 			ceph_put_snap_realm(mdsc, old_realm);
2740c44a8e0SLuis Henriques 		goto restart;
2750c44a8e0SLuis Henriques 	}
276cafe21a4SLuis Henriques 	is_same = (old_realm == new_realm);
277cafe21a4SLuis Henriques 	up_read(&mdsc->snap_rwsem);
278cafe21a4SLuis Henriques 
279cafe21a4SLuis Henriques 	if (old_realm)
280cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, old_realm);
281cafe21a4SLuis Henriques 	if (new_realm)
282cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, new_realm);
283cafe21a4SLuis Henriques 
284cafe21a4SLuis Henriques 	return is_same;
285cafe21a4SLuis Henriques }
286cafe21a4SLuis Henriques 
287b7a29217SLuis Henriques enum quota_check_op {
2882b83845fSLuis Henriques 	QUOTA_CHECK_MAX_FILES_OP,	/* check quota max_files limit */
2891ab302a0SLuis Henriques 	QUOTA_CHECK_MAX_BYTES_OP,	/* check quota max_files limit */
2901ab302a0SLuis Henriques 	QUOTA_CHECK_MAX_BYTES_APPROACHING_OP	/* check if quota max_files
2911ab302a0SLuis Henriques 						   limit is approaching */
292b7a29217SLuis Henriques };
293b7a29217SLuis Henriques 
294b7a29217SLuis Henriques /*
295b7a29217SLuis Henriques  * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each
296b7a29217SLuis Henriques  * realm, it will execute quota check operation defined by the 'op' parameter.
297b7a29217SLuis Henriques  * The snaprealm walk is interrupted if the quota check detects that the quota
298b7a29217SLuis Henriques  * is exceeded or if the root inode is reached.
299b7a29217SLuis Henriques  */
300b7a29217SLuis Henriques static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
301b7a29217SLuis Henriques 				 loff_t delta)
302b7a29217SLuis Henriques {
303b7a29217SLuis Henriques 	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
304b7a29217SLuis Henriques 	struct ceph_inode_info *ci;
305b7a29217SLuis Henriques 	struct ceph_snap_realm *realm, *next;
306b7a29217SLuis Henriques 	struct inode *in;
307b7a29217SLuis Henriques 	u64 max, rvalue;
308b7a29217SLuis Henriques 	bool exceeded = false;
309b7a29217SLuis Henriques 
31025963669SYan, Zheng 	if (ceph_snap(inode) != CEPH_NOSNAP)
31125963669SYan, Zheng 		return false;
31225963669SYan, Zheng 
313b7a29217SLuis Henriques 	down_read(&mdsc->snap_rwsem);
3140c44a8e0SLuis Henriques restart:
315b7a29217SLuis Henriques 	realm = ceph_inode(inode)->i_snap_realm;
31625963669SYan, Zheng 	if (realm)
317b7a29217SLuis Henriques 		ceph_get_snap_realm(mdsc, realm);
31825963669SYan, Zheng 	else
31925963669SYan, Zheng 		pr_err_ratelimited("check_quota_exceeded: ino (%llx.%llx) "
32025963669SYan, Zheng 				   "null i_snap_realm\n", ceph_vinop(inode));
321b7a29217SLuis Henriques 	while (realm) {
3220c44a8e0SLuis Henriques 		bool has_inode;
323e3161f17SLuis Henriques 
3240c44a8e0SLuis Henriques 		spin_lock(&realm->inodes_with_caps_lock);
3250c44a8e0SLuis Henriques 		has_inode = realm->inode;
3260c44a8e0SLuis Henriques 		in = has_inode ? igrab(realm->inode) : NULL;
3270c44a8e0SLuis Henriques 		spin_unlock(&realm->inodes_with_caps_lock);
3280c44a8e0SLuis Henriques 		if (has_inode && !in)
3290c44a8e0SLuis Henriques 			break;
3300c44a8e0SLuis Henriques 		if (!in) {
3310c44a8e0SLuis Henriques 			up_read(&mdsc->snap_rwsem);
3320c44a8e0SLuis Henriques 			in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm);
3330c44a8e0SLuis Henriques 			down_read(&mdsc->snap_rwsem);
3340c44a8e0SLuis Henriques 			if (IS_ERR_OR_NULL(in))
3350c44a8e0SLuis Henriques 				break;
3360c44a8e0SLuis Henriques 			ceph_put_snap_realm(mdsc, realm);
3370c44a8e0SLuis Henriques 			goto restart;
3380c44a8e0SLuis Henriques 		}
339b7a29217SLuis Henriques 		ci = ceph_inode(in);
340b7a29217SLuis Henriques 		spin_lock(&ci->i_ceph_lock);
341b7a29217SLuis Henriques 		if (op == QUOTA_CHECK_MAX_FILES_OP) {
342b7a29217SLuis Henriques 			max = ci->i_max_files;
343b7a29217SLuis Henriques 			rvalue = ci->i_rfiles + ci->i_rsubdirs;
3442b83845fSLuis Henriques 		} else {
3452b83845fSLuis Henriques 			max = ci->i_max_bytes;
3462b83845fSLuis Henriques 			rvalue = ci->i_rbytes;
347b7a29217SLuis Henriques 		}
348b7a29217SLuis Henriques 		spin_unlock(&ci->i_ceph_lock);
349b7a29217SLuis Henriques 		switch (op) {
350b7a29217SLuis Henriques 		case QUOTA_CHECK_MAX_FILES_OP:
351b7a29217SLuis Henriques 			exceeded = (max && (rvalue >= max));
352b7a29217SLuis Henriques 			break;
3532b83845fSLuis Henriques 		case QUOTA_CHECK_MAX_BYTES_OP:
3542b83845fSLuis Henriques 			exceeded = (max && (rvalue + delta > max));
3552b83845fSLuis Henriques 			break;
3561ab302a0SLuis Henriques 		case QUOTA_CHECK_MAX_BYTES_APPROACHING_OP:
3571ab302a0SLuis Henriques 			if (max) {
3581ab302a0SLuis Henriques 				if (rvalue >= max)
3591ab302a0SLuis Henriques 					exceeded = true;
3601ab302a0SLuis Henriques 				else {
3611ab302a0SLuis Henriques 					/*
3621ab302a0SLuis Henriques 					 * when we're writing more that 1/16th
3631ab302a0SLuis Henriques 					 * of the available space
3641ab302a0SLuis Henriques 					 */
3651ab302a0SLuis Henriques 					exceeded =
3661ab302a0SLuis Henriques 						(((max - rvalue) >> 4) < delta);
3671ab302a0SLuis Henriques 				}
3681ab302a0SLuis Henriques 			}
3691ab302a0SLuis Henriques 			break;
370b7a29217SLuis Henriques 		default:
371b7a29217SLuis Henriques 			/* Shouldn't happen */
372b7a29217SLuis Henriques 			pr_warn("Invalid quota check op (%d)\n", op);
373b7a29217SLuis Henriques 			exceeded = true; /* Just break the loop */
374b7a29217SLuis Henriques 		}
375b7a29217SLuis Henriques 		iput(in);
376b7a29217SLuis Henriques 
377b7a29217SLuis Henriques 		next = realm->parent;
3780eb6bbe4SYan, Zheng 		if (exceeded || !next)
3790eb6bbe4SYan, Zheng 			break;
380b7a29217SLuis Henriques 		ceph_get_snap_realm(mdsc, next);
381b7a29217SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
382b7a29217SLuis Henriques 		realm = next;
383b7a29217SLuis Henriques 	}
38471f2cc64SLuis Henriques 	if (realm)
385b7a29217SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
386b7a29217SLuis Henriques 	up_read(&mdsc->snap_rwsem);
387b7a29217SLuis Henriques 
388b7a29217SLuis Henriques 	return exceeded;
389b7a29217SLuis Henriques }
390b7a29217SLuis Henriques 
391b7a29217SLuis Henriques /*
392b7a29217SLuis Henriques  * ceph_quota_is_max_files_exceeded - check if we can create a new file
393b7a29217SLuis Henriques  * @inode:	directory where a new file is being created
394b7a29217SLuis Henriques  *
395b7a29217SLuis Henriques  * This functions returns true is max_files quota allows a new file to be
396b7a29217SLuis Henriques  * created.  It is necessary to walk through the snaprealm hierarchy (until the
397b7a29217SLuis Henriques  * FS root) to check all realms with quotas set.
398b7a29217SLuis Henriques  */
399b7a29217SLuis Henriques bool ceph_quota_is_max_files_exceeded(struct inode *inode)
400b7a29217SLuis Henriques {
401d557c48dSLuis Henriques 	if (!ceph_has_realms_with_quotas(inode))
402d557c48dSLuis Henriques 		return false;
403d557c48dSLuis Henriques 
404b7a29217SLuis Henriques 	WARN_ON(!S_ISDIR(inode->i_mode));
405b7a29217SLuis Henriques 
406b7a29217SLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0);
407b7a29217SLuis Henriques }
4082b83845fSLuis Henriques 
4092b83845fSLuis Henriques /*
4102b83845fSLuis Henriques  * ceph_quota_is_max_bytes_exceeded - check if we can write to a file
4112b83845fSLuis Henriques  * @inode:	inode being written
4122b83845fSLuis Henriques  * @newsize:	new size if write succeeds
4132b83845fSLuis Henriques  *
4142b83845fSLuis Henriques  * This functions returns true is max_bytes quota allows a file size to reach
4152b83845fSLuis Henriques  * @newsize; it returns false otherwise.
4162b83845fSLuis Henriques  */
4172b83845fSLuis Henriques bool ceph_quota_is_max_bytes_exceeded(struct inode *inode, loff_t newsize)
4182b83845fSLuis Henriques {
4192b83845fSLuis Henriques 	loff_t size = i_size_read(inode);
4202b83845fSLuis Henriques 
421d557c48dSLuis Henriques 	if (!ceph_has_realms_with_quotas(inode))
422d557c48dSLuis Henriques 		return false;
423d557c48dSLuis Henriques 
4242b83845fSLuis Henriques 	/* return immediately if we're decreasing file size */
4252b83845fSLuis Henriques 	if (newsize <= size)
4262b83845fSLuis Henriques 		return false;
4272b83845fSLuis Henriques 
4282b83845fSLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_OP, (newsize - size));
4292b83845fSLuis Henriques }
4301ab302a0SLuis Henriques 
4311ab302a0SLuis Henriques /*
4321ab302a0SLuis Henriques  * ceph_quota_is_max_bytes_approaching - check if we're reaching max_bytes
4331ab302a0SLuis Henriques  * @inode:	inode being written
4341ab302a0SLuis Henriques  * @newsize:	new size if write succeeds
4351ab302a0SLuis Henriques  *
4361ab302a0SLuis Henriques  * This function returns true if the new file size @newsize will be consuming
4371ab302a0SLuis Henriques  * more than 1/16th of the available quota space; it returns false otherwise.
4381ab302a0SLuis Henriques  */
4391ab302a0SLuis Henriques bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize)
4401ab302a0SLuis Henriques {
4411ab302a0SLuis Henriques 	loff_t size = ceph_inode(inode)->i_reported_size;
4421ab302a0SLuis Henriques 
443d557c48dSLuis Henriques 	if (!ceph_has_realms_with_quotas(inode))
444d557c48dSLuis Henriques 		return false;
445d557c48dSLuis Henriques 
4461ab302a0SLuis Henriques 	/* return immediately if we're decreasing file size */
4471ab302a0SLuis Henriques 	if (newsize <= size)
4481ab302a0SLuis Henriques 		return false;
4491ab302a0SLuis Henriques 
4501ab302a0SLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP,
4511ab302a0SLuis Henriques 				    (newsize - size));
4521ab302a0SLuis Henriques }
4539122eed5SLuis Henriques 
4549122eed5SLuis Henriques /*
4559122eed5SLuis Henriques  * ceph_quota_update_statfs - if root has quota update statfs with quota status
4569122eed5SLuis Henriques  * @fsc:	filesystem client instance
4579122eed5SLuis Henriques  * @buf:	statfs to update
4589122eed5SLuis Henriques  *
4599122eed5SLuis Henriques  * If the mounted filesystem root has max_bytes quota set, update the filesystem
4609122eed5SLuis Henriques  * statistics with the quota status.
4619122eed5SLuis Henriques  *
4629122eed5SLuis Henriques  * This function returns true if the stats have been updated, false otherwise.
4639122eed5SLuis Henriques  */
4649122eed5SLuis Henriques bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
4659122eed5SLuis Henriques {
4669122eed5SLuis Henriques 	struct ceph_mds_client *mdsc = fsc->mdsc;
4679122eed5SLuis Henriques 	struct ceph_inode_info *ci;
4689122eed5SLuis Henriques 	struct ceph_snap_realm *realm;
4699122eed5SLuis Henriques 	struct inode *in;
4709122eed5SLuis Henriques 	u64 total = 0, used, free;
4719122eed5SLuis Henriques 	bool is_updated = false;
4729122eed5SLuis Henriques 
4739122eed5SLuis Henriques 	down_read(&mdsc->snap_rwsem);
4740c44a8e0SLuis Henriques 	realm = get_quota_realm(mdsc, d_inode(fsc->sb->s_root), true);
4759122eed5SLuis Henriques 	up_read(&mdsc->snap_rwsem);
4769122eed5SLuis Henriques 	if (!realm)
4779122eed5SLuis Henriques 		return false;
4789122eed5SLuis Henriques 
4799122eed5SLuis Henriques 	spin_lock(&realm->inodes_with_caps_lock);
4809122eed5SLuis Henriques 	in = realm->inode ? igrab(realm->inode) : NULL;
4819122eed5SLuis Henriques 	spin_unlock(&realm->inodes_with_caps_lock);
4829122eed5SLuis Henriques 	if (in) {
4839122eed5SLuis Henriques 		ci = ceph_inode(in);
4849122eed5SLuis Henriques 		spin_lock(&ci->i_ceph_lock);
4859122eed5SLuis Henriques 		if (ci->i_max_bytes) {
4869122eed5SLuis Henriques 			total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT;
4879122eed5SLuis Henriques 			used = ci->i_rbytes >> CEPH_BLOCK_SHIFT;
4889122eed5SLuis Henriques 			/* It is possible for a quota to be exceeded.
4899122eed5SLuis Henriques 			 * Report 'zero' in that case
4909122eed5SLuis Henriques 			 */
4919122eed5SLuis Henriques 			free = total > used ? total - used : 0;
4929122eed5SLuis Henriques 		}
4939122eed5SLuis Henriques 		spin_unlock(&ci->i_ceph_lock);
4949122eed5SLuis Henriques 		if (total) {
4959122eed5SLuis Henriques 			buf->f_blocks = total;
4969122eed5SLuis Henriques 			buf->f_bfree = free;
4979122eed5SLuis Henriques 			buf->f_bavail = free;
4989122eed5SLuis Henriques 			is_updated = true;
4999122eed5SLuis Henriques 		}
5009122eed5SLuis Henriques 		iput(in);
5019122eed5SLuis Henriques 	}
5029122eed5SLuis Henriques 	ceph_put_snap_realm(mdsc, realm);
5039122eed5SLuis Henriques 
5049122eed5SLuis Henriques 	return is_updated;
5059122eed5SLuis Henriques }
5069122eed5SLuis Henriques 
507