xref: /openbmc/linux/fs/ceph/quota.c (revision d8fedfb4)
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;
46fb18a575SLuis Henriques 	struct ceph_vino vino;
47fb18a575SLuis Henriques 	struct inode *inode;
48fb18a575SLuis Henriques 	struct ceph_inode_info *ci;
49fb18a575SLuis Henriques 
50e3dfcab2SXiubo Li 	if (!ceph_inc_mds_stopping_blocker(mdsc, session))
51e3dfcab2SXiubo Li 		return;
52e3dfcab2SXiubo Li 
530fcf6c02SYan, Zheng 	if (msg->front.iov_len < sizeof(*h)) {
54fb18a575SLuis Henriques 		pr_err("%s corrupt message mds%d len %d\n", __func__,
55fb18a575SLuis Henriques 		       session->s_mds, (int)msg->front.iov_len);
56fb18a575SLuis Henriques 		ceph_msg_dump(msg);
57e3dfcab2SXiubo Li 		goto out;
58fb18a575SLuis Henriques 	}
59fb18a575SLuis Henriques 
60fb18a575SLuis Henriques 	/* lookup inode */
61fb18a575SLuis Henriques 	vino.ino = le64_to_cpu(h->ino);
62fb18a575SLuis Henriques 	vino.snap = CEPH_NOSNAP;
63fb18a575SLuis Henriques 	inode = ceph_find_inode(sb, vino);
64fb18a575SLuis Henriques 	if (!inode) {
65fb18a575SLuis Henriques 		pr_warn("Failed to find inode %llu\n", vino.ino);
66e3dfcab2SXiubo Li 		goto out;
67fb18a575SLuis Henriques 	}
68fb18a575SLuis Henriques 	ci = ceph_inode(inode);
69fb18a575SLuis Henriques 
70fb18a575SLuis Henriques 	spin_lock(&ci->i_ceph_lock);
71fb18a575SLuis Henriques 	ci->i_rbytes = le64_to_cpu(h->rbytes);
72fb18a575SLuis Henriques 	ci->i_rfiles = le64_to_cpu(h->rfiles);
73fb18a575SLuis Henriques 	ci->i_rsubdirs = le64_to_cpu(h->rsubdirs);
74d557c48dSLuis Henriques 	__ceph_update_quota(ci, le64_to_cpu(h->max_bytes),
75d557c48dSLuis Henriques 		            le64_to_cpu(h->max_files));
76fb18a575SLuis Henriques 	spin_unlock(&ci->i_ceph_lock);
77fb18a575SLuis Henriques 
7823c2c76eSJeff Layton 	iput(inode);
79e3dfcab2SXiubo Li out:
80e3dfcab2SXiubo Li 	ceph_dec_mds_stopping_blocker(mdsc);
81fb18a575SLuis Henriques }
82b7a29217SLuis Henriques 
830c44a8e0SLuis Henriques static struct ceph_quotarealm_inode *
find_quotarealm_inode(struct ceph_mds_client * mdsc,u64 ino)840c44a8e0SLuis Henriques find_quotarealm_inode(struct ceph_mds_client *mdsc, u64 ino)
850c44a8e0SLuis Henriques {
860c44a8e0SLuis Henriques 	struct ceph_quotarealm_inode *qri = NULL;
870c44a8e0SLuis Henriques 	struct rb_node **node, *parent = NULL;
880c44a8e0SLuis Henriques 
890c44a8e0SLuis Henriques 	mutex_lock(&mdsc->quotarealms_inodes_mutex);
900c44a8e0SLuis Henriques 	node = &(mdsc->quotarealms_inodes.rb_node);
910c44a8e0SLuis Henriques 	while (*node) {
920c44a8e0SLuis Henriques 		parent = *node;
930c44a8e0SLuis Henriques 		qri = container_of(*node, struct ceph_quotarealm_inode, node);
940c44a8e0SLuis Henriques 
950c44a8e0SLuis Henriques 		if (ino < qri->ino)
960c44a8e0SLuis Henriques 			node = &((*node)->rb_left);
970c44a8e0SLuis Henriques 		else if (ino > qri->ino)
980c44a8e0SLuis Henriques 			node = &((*node)->rb_right);
990c44a8e0SLuis Henriques 		else
1000c44a8e0SLuis Henriques 			break;
1010c44a8e0SLuis Henriques 	}
1020c44a8e0SLuis Henriques 	if (!qri || (qri->ino != ino)) {
1030c44a8e0SLuis Henriques 		/* Not found, create a new one and insert it */
1040c44a8e0SLuis Henriques 		qri = kmalloc(sizeof(*qri), GFP_KERNEL);
1050c44a8e0SLuis Henriques 		if (qri) {
1060c44a8e0SLuis Henriques 			qri->ino = ino;
1070c44a8e0SLuis Henriques 			qri->inode = NULL;
1080c44a8e0SLuis Henriques 			qri->timeout = 0;
1090c44a8e0SLuis Henriques 			mutex_init(&qri->mutex);
1100c44a8e0SLuis Henriques 			rb_link_node(&qri->node, parent, node);
1110c44a8e0SLuis Henriques 			rb_insert_color(&qri->node, &mdsc->quotarealms_inodes);
1120c44a8e0SLuis Henriques 		} else
1130c44a8e0SLuis Henriques 			pr_warn("Failed to alloc quotarealms_inode\n");
1140c44a8e0SLuis Henriques 	}
1150c44a8e0SLuis Henriques 	mutex_unlock(&mdsc->quotarealms_inodes_mutex);
1160c44a8e0SLuis Henriques 
1170c44a8e0SLuis Henriques 	return qri;
1180c44a8e0SLuis Henriques }
1190c44a8e0SLuis Henriques 
1200c44a8e0SLuis Henriques /*
1210c44a8e0SLuis Henriques  * This function will try to lookup a realm inode which isn't visible in the
1220c44a8e0SLuis Henriques  * filesystem mountpoint.  A list of these kind of inodes (not visible) is
1230c44a8e0SLuis Henriques  * maintained in the mdsc and freed only when the filesystem is umounted.
1240c44a8e0SLuis Henriques  *
1250c44a8e0SLuis Henriques  * Note that these inodes are kept in this list even if the lookup fails, which
1260c44a8e0SLuis Henriques  * allows to prevent useless lookup requests.
1270c44a8e0SLuis Henriques  */
lookup_quotarealm_inode(struct ceph_mds_client * mdsc,struct super_block * sb,struct ceph_snap_realm * realm)1280c44a8e0SLuis Henriques static struct inode *lookup_quotarealm_inode(struct ceph_mds_client *mdsc,
1290c44a8e0SLuis Henriques 					     struct super_block *sb,
1300c44a8e0SLuis Henriques 					     struct ceph_snap_realm *realm)
1310c44a8e0SLuis Henriques {
1320c44a8e0SLuis Henriques 	struct ceph_quotarealm_inode *qri;
1330c44a8e0SLuis Henriques 	struct inode *in;
1340c44a8e0SLuis Henriques 
1350c44a8e0SLuis Henriques 	qri = find_quotarealm_inode(mdsc, realm->ino);
1360c44a8e0SLuis Henriques 	if (!qri)
1370c44a8e0SLuis Henriques 		return NULL;
1380c44a8e0SLuis Henriques 
1390c44a8e0SLuis Henriques 	mutex_lock(&qri->mutex);
1402ef5df1aSYan, Zheng 	if (qri->inode && ceph_is_any_caps(qri->inode)) {
1410c44a8e0SLuis Henriques 		/* A request has already returned the inode */
1420c44a8e0SLuis Henriques 		mutex_unlock(&qri->mutex);
1430c44a8e0SLuis Henriques 		return qri->inode;
1440c44a8e0SLuis Henriques 	}
1450c44a8e0SLuis Henriques 	/* Check if this inode lookup has failed recently */
1460c44a8e0SLuis Henriques 	if (qri->timeout &&
1470c44a8e0SLuis Henriques 	    time_before_eq(jiffies, qri->timeout)) {
1480c44a8e0SLuis Henriques 		mutex_unlock(&qri->mutex);
1490c44a8e0SLuis Henriques 		return NULL;
1500c44a8e0SLuis Henriques 	}
1512ef5df1aSYan, Zheng 	if (qri->inode) {
1522ef5df1aSYan, Zheng 		/* get caps */
1532ef5df1aSYan, Zheng 		int ret = __ceph_do_getattr(qri->inode, NULL,
1542ef5df1aSYan, Zheng 					    CEPH_STAT_CAP_INODE, true);
1552ef5df1aSYan, Zheng 		if (ret >= 0)
1562ef5df1aSYan, Zheng 			in = qri->inode;
1572ef5df1aSYan, Zheng 		else
1582ef5df1aSYan, Zheng 			in = ERR_PTR(ret);
1592ef5df1aSYan, Zheng 	}  else {
1600c44a8e0SLuis Henriques 		in = ceph_lookup_inode(sb, realm->ino);
1612ef5df1aSYan, Zheng 	}
1622ef5df1aSYan, Zheng 
1630c44a8e0SLuis Henriques 	if (IS_ERR(in)) {
16412ae44a4SLuis Henriques 		dout("Can't lookup inode %llx (err: %ld)\n",
1650c44a8e0SLuis Henriques 		     realm->ino, PTR_ERR(in));
1660c44a8e0SLuis Henriques 		qri->timeout = jiffies + msecs_to_jiffies(60 * 1000); /* XXX */
1670c44a8e0SLuis Henriques 	} else {
1680c44a8e0SLuis Henriques 		qri->timeout = 0;
1690c44a8e0SLuis Henriques 		qri->inode = in;
1700c44a8e0SLuis Henriques 	}
1710c44a8e0SLuis Henriques 	mutex_unlock(&qri->mutex);
1720c44a8e0SLuis Henriques 
1730c44a8e0SLuis Henriques 	return in;
1740c44a8e0SLuis Henriques }
1750c44a8e0SLuis Henriques 
ceph_cleanup_quotarealms_inodes(struct ceph_mds_client * mdsc)1760c44a8e0SLuis Henriques void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc)
1770c44a8e0SLuis Henriques {
1780c44a8e0SLuis Henriques 	struct ceph_quotarealm_inode *qri;
1790c44a8e0SLuis Henriques 	struct rb_node *node;
1800c44a8e0SLuis Henriques 
1810c44a8e0SLuis Henriques 	/*
1820c44a8e0SLuis Henriques 	 * It should now be safe to clean quotarealms_inode tree without holding
1830c44a8e0SLuis Henriques 	 * mdsc->quotarealms_inodes_mutex...
1840c44a8e0SLuis Henriques 	 */
1850c44a8e0SLuis Henriques 	mutex_lock(&mdsc->quotarealms_inodes_mutex);
1860c44a8e0SLuis Henriques 	while (!RB_EMPTY_ROOT(&mdsc->quotarealms_inodes)) {
1870c44a8e0SLuis Henriques 		node = rb_first(&mdsc->quotarealms_inodes);
1880c44a8e0SLuis Henriques 		qri = rb_entry(node, struct ceph_quotarealm_inode, node);
1890c44a8e0SLuis Henriques 		rb_erase(node, &mdsc->quotarealms_inodes);
1900c44a8e0SLuis Henriques 		iput(qri->inode);
1910c44a8e0SLuis Henriques 		kfree(qri);
1920c44a8e0SLuis Henriques 	}
1930c44a8e0SLuis Henriques 	mutex_unlock(&mdsc->quotarealms_inodes_mutex);
1940c44a8e0SLuis Henriques }
1950c44a8e0SLuis Henriques 
196cafe21a4SLuis Henriques /*
197*d8fedfb4SWenchao Hao  * This function walks through the snaprealm for an inode and set the
198*d8fedfb4SWenchao Hao  * realmp with the first snaprealm that has quotas set (max_files,
19955ab5520SLuís Henriques  * max_bytes, or any, depending on the 'which_quota' argument).  If the root is
200*d8fedfb4SWenchao Hao  * reached, set the realmp with the root ceph_snap_realm instead.
201cafe21a4SLuis Henriques  *
202cafe21a4SLuis Henriques  * Note that the caller is responsible for calling ceph_put_snap_realm() on the
203cafe21a4SLuis Henriques  * returned realm.
2040c44a8e0SLuis Henriques  *
2050c44a8e0SLuis Henriques  * Callers of this function need to hold mdsc->snap_rwsem.  However, if there's
2060c44a8e0SLuis Henriques  * a need to do an inode lookup, this rwsem will be temporarily dropped.  Hence
2070c44a8e0SLuis Henriques  * the 'retry' argument: if rwsem needs to be dropped and 'retry' is 'false'
2080c44a8e0SLuis Henriques  * this function will return -EAGAIN; otherwise, the snaprealms walk-through
2090c44a8e0SLuis Henriques  * will be restarted.
210cafe21a4SLuis 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)211*d8fedfb4SWenchao Hao static int get_quota_realm(struct ceph_mds_client *mdsc, struct inode *inode,
21255ab5520SLuís Henriques 			   enum quota_get_realm which_quota,
213*d8fedfb4SWenchao Hao 			   struct ceph_snap_realm **realmp, bool retry)
214cafe21a4SLuis Henriques {
215cafe21a4SLuis Henriques 	struct ceph_inode_info *ci = NULL;
216cafe21a4SLuis Henriques 	struct ceph_snap_realm *realm, *next;
217cafe21a4SLuis Henriques 	struct inode *in;
2180eb6bbe4SYan, Zheng 	bool has_quota;
219cafe21a4SLuis Henriques 
220*d8fedfb4SWenchao Hao 	if (realmp)
221*d8fedfb4SWenchao Hao 		*realmp = NULL;
22225963669SYan, Zheng 	if (ceph_snap(inode) != CEPH_NOSNAP)
223*d8fedfb4SWenchao Hao 		return 0;
22425963669SYan, Zheng 
2250c44a8e0SLuis Henriques restart:
226cafe21a4SLuis Henriques 	realm = ceph_inode(inode)->i_snap_realm;
22725963669SYan, Zheng 	if (realm)
228cafe21a4SLuis Henriques 		ceph_get_snap_realm(mdsc, realm);
22925963669SYan, Zheng 	else
23025963669SYan, Zheng 		pr_err_ratelimited("get_quota_realm: ino (%llx.%llx) "
23125963669SYan, Zheng 				   "null i_snap_realm\n", ceph_vinop(inode));
232cafe21a4SLuis Henriques 	while (realm) {
2330c44a8e0SLuis Henriques 		bool has_inode;
2340c44a8e0SLuis Henriques 
235e3161f17SLuis Henriques 		spin_lock(&realm->inodes_with_caps_lock);
2360c44a8e0SLuis Henriques 		has_inode = realm->inode;
2370c44a8e0SLuis Henriques 		in = has_inode ? igrab(realm->inode) : NULL;
238e3161f17SLuis Henriques 		spin_unlock(&realm->inodes_with_caps_lock);
2390c44a8e0SLuis Henriques 		if (has_inode && !in)
240cafe21a4SLuis Henriques 			break;
2410c44a8e0SLuis Henriques 		if (!in) {
2420c44a8e0SLuis Henriques 			up_read(&mdsc->snap_rwsem);
2430c44a8e0SLuis Henriques 			in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm);
2440c44a8e0SLuis Henriques 			down_read(&mdsc->snap_rwsem);
2450c44a8e0SLuis Henriques 			if (IS_ERR_OR_NULL(in))
2460c44a8e0SLuis Henriques 				break;
2470c44a8e0SLuis Henriques 			ceph_put_snap_realm(mdsc, realm);
2480c44a8e0SLuis Henriques 			if (!retry)
249*d8fedfb4SWenchao Hao 				return -EAGAIN;
2500c44a8e0SLuis Henriques 			goto restart;
2510c44a8e0SLuis Henriques 		}
252e3161f17SLuis Henriques 
253cafe21a4SLuis Henriques 		ci = ceph_inode(in);
25455ab5520SLuís Henriques 		has_quota = __ceph_has_quota(ci, which_quota);
25523c2c76eSJeff Layton 		iput(in);
2560eb6bbe4SYan, Zheng 
257cafe21a4SLuis Henriques 		next = realm->parent;
258*d8fedfb4SWenchao Hao 		if (has_quota || !next) {
259*d8fedfb4SWenchao Hao 			if (realmp)
260*d8fedfb4SWenchao Hao 				*realmp = realm;
261*d8fedfb4SWenchao Hao 			return 0;
262*d8fedfb4SWenchao Hao 		}
2630eb6bbe4SYan, Zheng 
264cafe21a4SLuis Henriques 		ceph_get_snap_realm(mdsc, next);
265cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
266cafe21a4SLuis Henriques 		realm = next;
267cafe21a4SLuis Henriques 	}
268cafe21a4SLuis Henriques 	if (realm)
269cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
270cafe21a4SLuis Henriques 
271*d8fedfb4SWenchao Hao 	return 0;
272cafe21a4SLuis Henriques }
273cafe21a4SLuis Henriques 
ceph_quota_is_same_realm(struct inode * old,struct inode * new)2746646ea1cSLuis Henriques bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
275cafe21a4SLuis Henriques {
2762678da88SXiubo Li 	struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(old->i_sb);
277cafe21a4SLuis Henriques 	struct ceph_snap_realm *old_realm, *new_realm;
278cafe21a4SLuis Henriques 	bool is_same;
279*d8fedfb4SWenchao Hao 	int ret;
280cafe21a4SLuis Henriques 
2810c44a8e0SLuis Henriques restart:
2820c44a8e0SLuis Henriques 	/*
2830c44a8e0SLuis Henriques 	 * We need to lookup 2 quota realms atomically, i.e. with snap_rwsem.
2840c44a8e0SLuis Henriques 	 * However, get_quota_realm may drop it temporarily.  By setting the
2850c44a8e0SLuis Henriques 	 * 'retry' parameter to 'false', we'll get -EAGAIN if the rwsem was
2860c44a8e0SLuis Henriques 	 * dropped and we can then restart the whole operation.
2870c44a8e0SLuis Henriques 	 */
288cafe21a4SLuis Henriques 	down_read(&mdsc->snap_rwsem);
289*d8fedfb4SWenchao Hao 	get_quota_realm(mdsc, old, QUOTA_GET_ANY, &old_realm, true);
290*d8fedfb4SWenchao Hao 	ret = get_quota_realm(mdsc, new, QUOTA_GET_ANY, &new_realm, false);
291*d8fedfb4SWenchao Hao 	if (ret == -EAGAIN) {
2920c44a8e0SLuis Henriques 		up_read(&mdsc->snap_rwsem);
2930c44a8e0SLuis Henriques 		if (old_realm)
2940c44a8e0SLuis Henriques 			ceph_put_snap_realm(mdsc, old_realm);
2950c44a8e0SLuis Henriques 		goto restart;
2960c44a8e0SLuis Henriques 	}
297cafe21a4SLuis Henriques 	is_same = (old_realm == new_realm);
298cafe21a4SLuis Henriques 	up_read(&mdsc->snap_rwsem);
299cafe21a4SLuis Henriques 
300cafe21a4SLuis Henriques 	if (old_realm)
301cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, old_realm);
302cafe21a4SLuis Henriques 	if (new_realm)
303cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, new_realm);
304cafe21a4SLuis Henriques 
305cafe21a4SLuis Henriques 	return is_same;
306cafe21a4SLuis Henriques }
307cafe21a4SLuis Henriques 
308b7a29217SLuis Henriques enum quota_check_op {
3092b83845fSLuis Henriques 	QUOTA_CHECK_MAX_FILES_OP,	/* check quota max_files limit */
3101ab302a0SLuis Henriques 	QUOTA_CHECK_MAX_BYTES_OP,	/* check quota max_files limit */
3111ab302a0SLuis Henriques 	QUOTA_CHECK_MAX_BYTES_APPROACHING_OP	/* check if quota max_files
3121ab302a0SLuis Henriques 						   limit is approaching */
313b7a29217SLuis Henriques };
314b7a29217SLuis Henriques 
315b7a29217SLuis Henriques /*
316b7a29217SLuis Henriques  * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each
317b7a29217SLuis Henriques  * realm, it will execute quota check operation defined by the 'op' parameter.
318b7a29217SLuis Henriques  * The snaprealm walk is interrupted if the quota check detects that the quota
319b7a29217SLuis Henriques  * is exceeded or if the root inode is reached.
320b7a29217SLuis Henriques  */
check_quota_exceeded(struct inode * inode,enum quota_check_op op,loff_t delta)321b7a29217SLuis Henriques static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
322b7a29217SLuis Henriques 				 loff_t delta)
323b7a29217SLuis Henriques {
3242678da88SXiubo Li 	struct ceph_mds_client *mdsc = ceph_sb_to_mdsc(inode->i_sb);
325b7a29217SLuis Henriques 	struct ceph_inode_info *ci;
326b7a29217SLuis Henriques 	struct ceph_snap_realm *realm, *next;
327b7a29217SLuis Henriques 	struct inode *in;
328b7a29217SLuis Henriques 	u64 max, rvalue;
329b7a29217SLuis Henriques 	bool exceeded = false;
330b7a29217SLuis Henriques 
33125963669SYan, Zheng 	if (ceph_snap(inode) != CEPH_NOSNAP)
33225963669SYan, Zheng 		return false;
33325963669SYan, Zheng 
334b7a29217SLuis Henriques 	down_read(&mdsc->snap_rwsem);
3350c44a8e0SLuis Henriques restart:
336b7a29217SLuis Henriques 	realm = ceph_inode(inode)->i_snap_realm;
33725963669SYan, Zheng 	if (realm)
338b7a29217SLuis Henriques 		ceph_get_snap_realm(mdsc, realm);
33925963669SYan, Zheng 	else
34025963669SYan, Zheng 		pr_err_ratelimited("check_quota_exceeded: ino (%llx.%llx) "
34125963669SYan, Zheng 				   "null i_snap_realm\n", ceph_vinop(inode));
342b7a29217SLuis Henriques 	while (realm) {
3430c44a8e0SLuis Henriques 		bool has_inode;
344e3161f17SLuis Henriques 
3450c44a8e0SLuis Henriques 		spin_lock(&realm->inodes_with_caps_lock);
3460c44a8e0SLuis Henriques 		has_inode = realm->inode;
3470c44a8e0SLuis Henriques 		in = has_inode ? igrab(realm->inode) : NULL;
3480c44a8e0SLuis Henriques 		spin_unlock(&realm->inodes_with_caps_lock);
3490c44a8e0SLuis Henriques 		if (has_inode && !in)
3500c44a8e0SLuis Henriques 			break;
3510c44a8e0SLuis Henriques 		if (!in) {
3520c44a8e0SLuis Henriques 			up_read(&mdsc->snap_rwsem);
3530c44a8e0SLuis Henriques 			in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm);
3540c44a8e0SLuis Henriques 			down_read(&mdsc->snap_rwsem);
3550c44a8e0SLuis Henriques 			if (IS_ERR_OR_NULL(in))
3560c44a8e0SLuis Henriques 				break;
3570c44a8e0SLuis Henriques 			ceph_put_snap_realm(mdsc, realm);
3580c44a8e0SLuis Henriques 			goto restart;
3590c44a8e0SLuis Henriques 		}
360b7a29217SLuis Henriques 		ci = ceph_inode(in);
361b7a29217SLuis Henriques 		spin_lock(&ci->i_ceph_lock);
362b7a29217SLuis Henriques 		if (op == QUOTA_CHECK_MAX_FILES_OP) {
363b7a29217SLuis Henriques 			max = ci->i_max_files;
364b7a29217SLuis Henriques 			rvalue = ci->i_rfiles + ci->i_rsubdirs;
3652b83845fSLuis Henriques 		} else {
3662b83845fSLuis Henriques 			max = ci->i_max_bytes;
3672b83845fSLuis Henriques 			rvalue = ci->i_rbytes;
368b7a29217SLuis Henriques 		}
369b7a29217SLuis Henriques 		spin_unlock(&ci->i_ceph_lock);
370b7a29217SLuis Henriques 		switch (op) {
371b7a29217SLuis Henriques 		case QUOTA_CHECK_MAX_FILES_OP:
3722b83845fSLuis Henriques 		case QUOTA_CHECK_MAX_BYTES_OP:
3732b83845fSLuis Henriques 			exceeded = (max && (rvalue + delta > max));
3742b83845fSLuis Henriques 			break;
3751ab302a0SLuis Henriques 		case QUOTA_CHECK_MAX_BYTES_APPROACHING_OP:
3761ab302a0SLuis Henriques 			if (max) {
3771ab302a0SLuis Henriques 				if (rvalue >= max)
3781ab302a0SLuis Henriques 					exceeded = true;
3791ab302a0SLuis Henriques 				else {
3801ab302a0SLuis Henriques 					/*
3811ab302a0SLuis Henriques 					 * when we're writing more that 1/16th
3821ab302a0SLuis Henriques 					 * of the available space
3831ab302a0SLuis Henriques 					 */
3841ab302a0SLuis Henriques 					exceeded =
3851ab302a0SLuis Henriques 						(((max - rvalue) >> 4) < delta);
3861ab302a0SLuis Henriques 				}
3871ab302a0SLuis Henriques 			}
3881ab302a0SLuis Henriques 			break;
389b7a29217SLuis Henriques 		default:
390b7a29217SLuis Henriques 			/* Shouldn't happen */
391b7a29217SLuis Henriques 			pr_warn("Invalid quota check op (%d)\n", op);
392b7a29217SLuis Henriques 			exceeded = true; /* Just break the loop */
393b7a29217SLuis Henriques 		}
39423c2c76eSJeff Layton 		iput(in);
395b7a29217SLuis Henriques 
396b7a29217SLuis Henriques 		next = realm->parent;
3970eb6bbe4SYan, Zheng 		if (exceeded || !next)
3980eb6bbe4SYan, Zheng 			break;
399b7a29217SLuis Henriques 		ceph_get_snap_realm(mdsc, next);
400b7a29217SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
401b7a29217SLuis Henriques 		realm = next;
402b7a29217SLuis Henriques 	}
40371f2cc64SLuis Henriques 	if (realm)
404b7a29217SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
405b7a29217SLuis Henriques 	up_read(&mdsc->snap_rwsem);
406b7a29217SLuis Henriques 
407b7a29217SLuis Henriques 	return exceeded;
408b7a29217SLuis Henriques }
409b7a29217SLuis Henriques 
410b7a29217SLuis Henriques /*
411b7a29217SLuis Henriques  * ceph_quota_is_max_files_exceeded - check if we can create a new file
412b7a29217SLuis Henriques  * @inode:	directory where a new file is being created
413b7a29217SLuis Henriques  *
414b7a29217SLuis Henriques  * This functions returns true is max_files quota allows a new file to be
415b7a29217SLuis Henriques  * created.  It is necessary to walk through the snaprealm hierarchy (until the
416b7a29217SLuis Henriques  * FS root) to check all realms with quotas set.
417b7a29217SLuis Henriques  */
ceph_quota_is_max_files_exceeded(struct inode * inode)418b7a29217SLuis Henriques bool ceph_quota_is_max_files_exceeded(struct inode *inode)
419b7a29217SLuis Henriques {
420d557c48dSLuis Henriques 	if (!ceph_has_realms_with_quotas(inode))
421d557c48dSLuis Henriques 		return false;
422d557c48dSLuis Henriques 
423b7a29217SLuis Henriques 	WARN_ON(!S_ISDIR(inode->i_mode));
424b7a29217SLuis Henriques 
425daa668fbSLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 1);
426b7a29217SLuis Henriques }
4272b83845fSLuis Henriques 
4282b83845fSLuis Henriques /*
4292b83845fSLuis Henriques  * ceph_quota_is_max_bytes_exceeded - check if we can write to a file
4302b83845fSLuis Henriques  * @inode:	inode being written
4312b83845fSLuis Henriques  * @newsize:	new size if write succeeds
4322b83845fSLuis Henriques  *
4332b83845fSLuis Henriques  * This functions returns true is max_bytes quota allows a file size to reach
4342b83845fSLuis Henriques  * @newsize; it returns false otherwise.
4352b83845fSLuis Henriques  */
ceph_quota_is_max_bytes_exceeded(struct inode * inode,loff_t newsize)4362b83845fSLuis Henriques bool ceph_quota_is_max_bytes_exceeded(struct inode *inode, loff_t newsize)
4372b83845fSLuis Henriques {
4382b83845fSLuis Henriques 	loff_t size = i_size_read(inode);
4392b83845fSLuis Henriques 
440d557c48dSLuis Henriques 	if (!ceph_has_realms_with_quotas(inode))
441d557c48dSLuis Henriques 		return false;
442d557c48dSLuis Henriques 
4432b83845fSLuis Henriques 	/* return immediately if we're decreasing file size */
4442b83845fSLuis Henriques 	if (newsize <= size)
4452b83845fSLuis Henriques 		return false;
4462b83845fSLuis Henriques 
4472b83845fSLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_OP, (newsize - size));
4482b83845fSLuis Henriques }
4491ab302a0SLuis Henriques 
4501ab302a0SLuis Henriques /*
4511ab302a0SLuis Henriques  * ceph_quota_is_max_bytes_approaching - check if we're reaching max_bytes
4521ab302a0SLuis Henriques  * @inode:	inode being written
4531ab302a0SLuis Henriques  * @newsize:	new size if write succeeds
4541ab302a0SLuis Henriques  *
4551ab302a0SLuis Henriques  * This function returns true if the new file size @newsize will be consuming
4561ab302a0SLuis Henriques  * more than 1/16th of the available quota space; it returns false otherwise.
4571ab302a0SLuis Henriques  */
ceph_quota_is_max_bytes_approaching(struct inode * inode,loff_t newsize)4581ab302a0SLuis Henriques bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize)
4591ab302a0SLuis Henriques {
4601ab302a0SLuis Henriques 	loff_t size = ceph_inode(inode)->i_reported_size;
4611ab302a0SLuis Henriques 
462d557c48dSLuis Henriques 	if (!ceph_has_realms_with_quotas(inode))
463d557c48dSLuis Henriques 		return false;
464d557c48dSLuis Henriques 
4651ab302a0SLuis Henriques 	/* return immediately if we're decreasing file size */
4661ab302a0SLuis Henriques 	if (newsize <= size)
4671ab302a0SLuis Henriques 		return false;
4681ab302a0SLuis Henriques 
4691ab302a0SLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP,
4701ab302a0SLuis Henriques 				    (newsize - size));
4711ab302a0SLuis Henriques }
4729122eed5SLuis Henriques 
4739122eed5SLuis Henriques /*
4749122eed5SLuis Henriques  * ceph_quota_update_statfs - if root has quota update statfs with quota status
4759122eed5SLuis Henriques  * @fsc:	filesystem client instance
4769122eed5SLuis Henriques  * @buf:	statfs to update
4779122eed5SLuis Henriques  *
4789122eed5SLuis Henriques  * If the mounted filesystem root has max_bytes quota set, update the filesystem
4799122eed5SLuis Henriques  * statistics with the quota status.
4809122eed5SLuis Henriques  *
4819122eed5SLuis Henriques  * This function returns true if the stats have been updated, false otherwise.
4829122eed5SLuis Henriques  */
ceph_quota_update_statfs(struct ceph_fs_client * fsc,struct kstatfs * buf)4839122eed5SLuis Henriques bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
4849122eed5SLuis Henriques {
4859122eed5SLuis Henriques 	struct ceph_mds_client *mdsc = fsc->mdsc;
4869122eed5SLuis Henriques 	struct ceph_inode_info *ci;
4879122eed5SLuis Henriques 	struct ceph_snap_realm *realm;
4889122eed5SLuis Henriques 	struct inode *in;
4899122eed5SLuis Henriques 	u64 total = 0, used, free;
4909122eed5SLuis Henriques 	bool is_updated = false;
4919122eed5SLuis Henriques 
4929122eed5SLuis Henriques 	down_read(&mdsc->snap_rwsem);
493*d8fedfb4SWenchao Hao 	get_quota_realm(mdsc, d_inode(fsc->sb->s_root), QUOTA_GET_MAX_BYTES,
494*d8fedfb4SWenchao Hao 			&realm, true);
4959122eed5SLuis Henriques 	up_read(&mdsc->snap_rwsem);
4969122eed5SLuis Henriques 	if (!realm)
4979122eed5SLuis Henriques 		return false;
4989122eed5SLuis Henriques 
4999122eed5SLuis Henriques 	spin_lock(&realm->inodes_with_caps_lock);
5009122eed5SLuis Henriques 	in = realm->inode ? igrab(realm->inode) : NULL;
5019122eed5SLuis Henriques 	spin_unlock(&realm->inodes_with_caps_lock);
5029122eed5SLuis Henriques 	if (in) {
5039122eed5SLuis Henriques 		ci = ceph_inode(in);
5049122eed5SLuis Henriques 		spin_lock(&ci->i_ceph_lock);
5059122eed5SLuis Henriques 		if (ci->i_max_bytes) {
5069122eed5SLuis Henriques 			total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT;
5079122eed5SLuis Henriques 			used = ci->i_rbytes >> CEPH_BLOCK_SHIFT;
5088e55ba8cSKotresh HR 			/* For quota size less than 4MB, use 4KB block size */
5098e55ba8cSKotresh HR 			if (!total) {
5108e55ba8cSKotresh HR 				total = ci->i_max_bytes >> CEPH_4K_BLOCK_SHIFT;
5118e55ba8cSKotresh HR 				used = ci->i_rbytes >> CEPH_4K_BLOCK_SHIFT;
5128e55ba8cSKotresh HR 	                        buf->f_frsize = 1 << CEPH_4K_BLOCK_SHIFT;
5138e55ba8cSKotresh HR 			}
5149122eed5SLuis Henriques 			/* It is possible for a quota to be exceeded.
5159122eed5SLuis Henriques 			 * Report 'zero' in that case
5169122eed5SLuis Henriques 			 */
5179122eed5SLuis Henriques 			free = total > used ? total - used : 0;
5188e55ba8cSKotresh HR 			/* For quota size less than 4KB, report the
5198e55ba8cSKotresh HR 			 * total=used=4KB,free=0 when quota is full
5208e55ba8cSKotresh HR 			 * and total=free=4KB, used=0 otherwise */
5218e55ba8cSKotresh HR 			if (!total) {
5228e55ba8cSKotresh HR 				total = 1;
5238e55ba8cSKotresh HR 				free = ci->i_max_bytes > ci->i_rbytes ? 1 : 0;
5248e55ba8cSKotresh HR 	                        buf->f_frsize = 1 << CEPH_4K_BLOCK_SHIFT;
5258e55ba8cSKotresh HR 			}
5269122eed5SLuis Henriques 		}
5279122eed5SLuis Henriques 		spin_unlock(&ci->i_ceph_lock);
5289122eed5SLuis Henriques 		if (total) {
5299122eed5SLuis Henriques 			buf->f_blocks = total;
5309122eed5SLuis Henriques 			buf->f_bfree = free;
5319122eed5SLuis Henriques 			buf->f_bavail = free;
5329122eed5SLuis Henriques 			is_updated = true;
5339122eed5SLuis Henriques 		}
5349122eed5SLuis Henriques 		iput(in);
5359122eed5SLuis Henriques 	}
5369122eed5SLuis Henriques 	ceph_put_snap_realm(mdsc, realm);
5379122eed5SLuis Henriques 
5389122eed5SLuis Henriques 	return is_updated;
5399122eed5SLuis Henriques }
5409122eed5SLuis Henriques 
541