xref: /linux/fs/ceph/quota.c (revision 1260ed77798502de9c98020040d2995008de10cc)
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