xref: /linux/fs/ceph/quota.c (revision cafe21a4fb3075fb2980caba8fdb533a1bdb52b0)
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  * This program is free software; you can redistribute it and/or
8fb18a575SLuis Henriques  * modify it under the terms of the GNU General Public License
9fb18a575SLuis Henriques  * as published by the Free Software Foundation; either version 2
10fb18a575SLuis Henriques  * of the License, or (at your option) any later version.
11fb18a575SLuis Henriques  *
12fb18a575SLuis Henriques  * This program is distributed in the hope that it will be useful,
13fb18a575SLuis Henriques  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14fb18a575SLuis Henriques  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15fb18a575SLuis Henriques  * GNU General Public License for more details.
16fb18a575SLuis Henriques  *
17fb18a575SLuis Henriques  * You should have received a copy of the GNU General Public License
18fb18a575SLuis Henriques  * along with this program; if not, see <http://www.gnu.org/licenses/>.
19fb18a575SLuis Henriques  */
20fb18a575SLuis Henriques 
21fb18a575SLuis Henriques #include "super.h"
22fb18a575SLuis Henriques #include "mds_client.h"
23fb18a575SLuis Henriques 
24*cafe21a4SLuis Henriques static inline bool ceph_has_quota(struct ceph_inode_info *ci)
25*cafe21a4SLuis Henriques {
26*cafe21a4SLuis Henriques 	return (ci && (ci->i_max_files || ci->i_max_bytes));
27*cafe21a4SLuis Henriques }
28*cafe21a4SLuis Henriques 
29fb18a575SLuis Henriques void ceph_handle_quota(struct ceph_mds_client *mdsc,
30fb18a575SLuis Henriques 		       struct ceph_mds_session *session,
31fb18a575SLuis Henriques 		       struct ceph_msg *msg)
32fb18a575SLuis Henriques {
33fb18a575SLuis Henriques 	struct super_block *sb = mdsc->fsc->sb;
34fb18a575SLuis Henriques 	struct ceph_mds_quota *h = msg->front.iov_base;
35fb18a575SLuis Henriques 	struct ceph_vino vino;
36fb18a575SLuis Henriques 	struct inode *inode;
37fb18a575SLuis Henriques 	struct ceph_inode_info *ci;
38fb18a575SLuis Henriques 
39fb18a575SLuis Henriques 	if (msg->front.iov_len != sizeof(*h)) {
40fb18a575SLuis Henriques 		pr_err("%s corrupt message mds%d len %d\n", __func__,
41fb18a575SLuis Henriques 		       session->s_mds, (int)msg->front.iov_len);
42fb18a575SLuis Henriques 		ceph_msg_dump(msg);
43fb18a575SLuis Henriques 		return;
44fb18a575SLuis Henriques 	}
45fb18a575SLuis Henriques 
46fb18a575SLuis Henriques 	/* increment msg sequence number */
47fb18a575SLuis Henriques 	mutex_lock(&session->s_mutex);
48fb18a575SLuis Henriques 	session->s_seq++;
49fb18a575SLuis Henriques 	mutex_unlock(&session->s_mutex);
50fb18a575SLuis Henriques 
51fb18a575SLuis Henriques 	/* lookup inode */
52fb18a575SLuis Henriques 	vino.ino = le64_to_cpu(h->ino);
53fb18a575SLuis Henriques 	vino.snap = CEPH_NOSNAP;
54fb18a575SLuis Henriques 	inode = ceph_find_inode(sb, vino);
55fb18a575SLuis Henriques 	if (!inode) {
56fb18a575SLuis Henriques 		pr_warn("Failed to find inode %llu\n", vino.ino);
57fb18a575SLuis Henriques 		return;
58fb18a575SLuis Henriques 	}
59fb18a575SLuis Henriques 	ci = ceph_inode(inode);
60fb18a575SLuis Henriques 
61fb18a575SLuis Henriques 	spin_lock(&ci->i_ceph_lock);
62fb18a575SLuis Henriques 	ci->i_rbytes = le64_to_cpu(h->rbytes);
63fb18a575SLuis Henriques 	ci->i_rfiles = le64_to_cpu(h->rfiles);
64fb18a575SLuis Henriques 	ci->i_rsubdirs = le64_to_cpu(h->rsubdirs);
65fb18a575SLuis Henriques 	ci->i_max_bytes = le64_to_cpu(h->max_bytes);
66fb18a575SLuis Henriques 	ci->i_max_files = le64_to_cpu(h->max_files);
67fb18a575SLuis Henriques 	spin_unlock(&ci->i_ceph_lock);
68fb18a575SLuis Henriques 
69fb18a575SLuis Henriques 	iput(inode);
70fb18a575SLuis Henriques }
71b7a29217SLuis Henriques 
72*cafe21a4SLuis Henriques /*
73*cafe21a4SLuis Henriques  * This function walks through the snaprealm for an inode and returns the
74*cafe21a4SLuis Henriques  * ceph_snap_realm for the first snaprealm that has quotas set (either max_files
75*cafe21a4SLuis Henriques  * or max_bytes).  If the root is reached, return the root ceph_snap_realm
76*cafe21a4SLuis Henriques  * instead.
77*cafe21a4SLuis Henriques  *
78*cafe21a4SLuis Henriques  * Note that the caller is responsible for calling ceph_put_snap_realm() on the
79*cafe21a4SLuis Henriques  * returned realm.
80*cafe21a4SLuis Henriques  */
81*cafe21a4SLuis Henriques static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc,
82*cafe21a4SLuis Henriques 					       struct inode *inode)
83*cafe21a4SLuis Henriques {
84*cafe21a4SLuis Henriques 	struct ceph_inode_info *ci = NULL;
85*cafe21a4SLuis Henriques 	struct ceph_snap_realm *realm, *next;
86*cafe21a4SLuis Henriques 	struct ceph_vino vino;
87*cafe21a4SLuis Henriques 	struct inode *in;
88*cafe21a4SLuis Henriques 
89*cafe21a4SLuis Henriques 	realm = ceph_inode(inode)->i_snap_realm;
90*cafe21a4SLuis Henriques 	ceph_get_snap_realm(mdsc, realm);
91*cafe21a4SLuis Henriques 	while (realm) {
92*cafe21a4SLuis Henriques 		vino.ino = realm->ino;
93*cafe21a4SLuis Henriques 		vino.snap = CEPH_NOSNAP;
94*cafe21a4SLuis Henriques 		in = ceph_find_inode(inode->i_sb, vino);
95*cafe21a4SLuis Henriques 		if (!in) {
96*cafe21a4SLuis Henriques 			pr_warn("Failed to find inode for %llu\n", vino.ino);
97*cafe21a4SLuis Henriques 			break;
98*cafe21a4SLuis Henriques 		}
99*cafe21a4SLuis Henriques 		ci = ceph_inode(in);
100*cafe21a4SLuis Henriques 		if (ceph_has_quota(ci) || (ci->i_vino.ino == CEPH_INO_ROOT)) {
101*cafe21a4SLuis Henriques 			iput(in);
102*cafe21a4SLuis Henriques 			return realm;
103*cafe21a4SLuis Henriques 		}
104*cafe21a4SLuis Henriques 		iput(in);
105*cafe21a4SLuis Henriques 		next = realm->parent;
106*cafe21a4SLuis Henriques 		ceph_get_snap_realm(mdsc, next);
107*cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
108*cafe21a4SLuis Henriques 		realm = next;
109*cafe21a4SLuis Henriques 	}
110*cafe21a4SLuis Henriques 	if (realm)
111*cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
112*cafe21a4SLuis Henriques 
113*cafe21a4SLuis Henriques 	return NULL;
114*cafe21a4SLuis Henriques }
115*cafe21a4SLuis Henriques 
116*cafe21a4SLuis Henriques bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
117*cafe21a4SLuis Henriques {
118*cafe21a4SLuis Henriques 	struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc;
119*cafe21a4SLuis Henriques 	struct ceph_snap_realm *old_realm, *new_realm;
120*cafe21a4SLuis Henriques 	bool is_same;
121*cafe21a4SLuis Henriques 
122*cafe21a4SLuis Henriques 	down_read(&mdsc->snap_rwsem);
123*cafe21a4SLuis Henriques 	old_realm = get_quota_realm(mdsc, old);
124*cafe21a4SLuis Henriques 	new_realm = get_quota_realm(mdsc, new);
125*cafe21a4SLuis Henriques 	is_same = (old_realm == new_realm);
126*cafe21a4SLuis Henriques 	up_read(&mdsc->snap_rwsem);
127*cafe21a4SLuis Henriques 
128*cafe21a4SLuis Henriques 	if (old_realm)
129*cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, old_realm);
130*cafe21a4SLuis Henriques 	if (new_realm)
131*cafe21a4SLuis Henriques 		ceph_put_snap_realm(mdsc, new_realm);
132*cafe21a4SLuis Henriques 
133*cafe21a4SLuis Henriques 	return is_same;
134*cafe21a4SLuis Henriques }
135*cafe21a4SLuis Henriques 
136b7a29217SLuis Henriques enum quota_check_op {
137b7a29217SLuis Henriques 	QUOTA_CHECK_MAX_FILES_OP	/* check quota max_files limit */
138b7a29217SLuis Henriques };
139b7a29217SLuis Henriques 
140b7a29217SLuis Henriques /*
141b7a29217SLuis Henriques  * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each
142b7a29217SLuis Henriques  * realm, it will execute quota check operation defined by the 'op' parameter.
143b7a29217SLuis Henriques  * The snaprealm walk is interrupted if the quota check detects that the quota
144b7a29217SLuis Henriques  * is exceeded or if the root inode is reached.
145b7a29217SLuis Henriques  */
146b7a29217SLuis Henriques static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
147b7a29217SLuis Henriques 				 loff_t delta)
148b7a29217SLuis Henriques {
149b7a29217SLuis Henriques 	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
150b7a29217SLuis Henriques 	struct ceph_inode_info *ci;
151b7a29217SLuis Henriques 	struct ceph_snap_realm *realm, *next;
152b7a29217SLuis Henriques 	struct ceph_vino vino;
153b7a29217SLuis Henriques 	struct inode *in;
154b7a29217SLuis Henriques 	u64 max, rvalue;
155b7a29217SLuis Henriques 	bool is_root;
156b7a29217SLuis Henriques 	bool exceeded = false;
157b7a29217SLuis Henriques 
158b7a29217SLuis Henriques 	down_read(&mdsc->snap_rwsem);
159b7a29217SLuis Henriques 	realm = ceph_inode(inode)->i_snap_realm;
160b7a29217SLuis Henriques 	ceph_get_snap_realm(mdsc, realm);
161b7a29217SLuis Henriques 	while (realm) {
162b7a29217SLuis Henriques 		vino.ino = realm->ino;
163b7a29217SLuis Henriques 		vino.snap = CEPH_NOSNAP;
164b7a29217SLuis Henriques 		in = ceph_find_inode(inode->i_sb, vino);
165b7a29217SLuis Henriques 		if (!in) {
166b7a29217SLuis Henriques 			pr_warn("Failed to find inode for %llu\n", vino.ino);
167b7a29217SLuis Henriques 			break;
168b7a29217SLuis Henriques 		}
169b7a29217SLuis Henriques 		ci = ceph_inode(in);
170b7a29217SLuis Henriques 		spin_lock(&ci->i_ceph_lock);
171b7a29217SLuis Henriques 		if (op == QUOTA_CHECK_MAX_FILES_OP) {
172b7a29217SLuis Henriques 			max = ci->i_max_files;
173b7a29217SLuis Henriques 			rvalue = ci->i_rfiles + ci->i_rsubdirs;
174b7a29217SLuis Henriques 		}
175b7a29217SLuis Henriques 		is_root = (ci->i_vino.ino == CEPH_INO_ROOT);
176b7a29217SLuis Henriques 		spin_unlock(&ci->i_ceph_lock);
177b7a29217SLuis Henriques 		switch (op) {
178b7a29217SLuis Henriques 		case QUOTA_CHECK_MAX_FILES_OP:
179b7a29217SLuis Henriques 			exceeded = (max && (rvalue >= max));
180b7a29217SLuis Henriques 			break;
181b7a29217SLuis Henriques 		default:
182b7a29217SLuis Henriques 			/* Shouldn't happen */
183b7a29217SLuis Henriques 			pr_warn("Invalid quota check op (%d)\n", op);
184b7a29217SLuis Henriques 			exceeded = true; /* Just break the loop */
185b7a29217SLuis Henriques 		}
186b7a29217SLuis Henriques 		iput(in);
187b7a29217SLuis Henriques 
188b7a29217SLuis Henriques 		if (is_root || exceeded)
189b7a29217SLuis Henriques 			break;
190b7a29217SLuis Henriques 		next = realm->parent;
191b7a29217SLuis Henriques 		ceph_get_snap_realm(mdsc, next);
192b7a29217SLuis Henriques 		ceph_put_snap_realm(mdsc, realm);
193b7a29217SLuis Henriques 		realm = next;
194b7a29217SLuis Henriques 	}
195b7a29217SLuis Henriques 	ceph_put_snap_realm(mdsc, realm);
196b7a29217SLuis Henriques 	up_read(&mdsc->snap_rwsem);
197b7a29217SLuis Henriques 
198b7a29217SLuis Henriques 	return exceeded;
199b7a29217SLuis Henriques }
200b7a29217SLuis Henriques 
201b7a29217SLuis Henriques /*
202b7a29217SLuis Henriques  * ceph_quota_is_max_files_exceeded - check if we can create a new file
203b7a29217SLuis Henriques  * @inode:	directory where a new file is being created
204b7a29217SLuis Henriques  *
205b7a29217SLuis Henriques  * This functions returns true is max_files quota allows a new file to be
206b7a29217SLuis Henriques  * created.  It is necessary to walk through the snaprealm hierarchy (until the
207b7a29217SLuis Henriques  * FS root) to check all realms with quotas set.
208b7a29217SLuis Henriques  */
209b7a29217SLuis Henriques bool ceph_quota_is_max_files_exceeded(struct inode *inode)
210b7a29217SLuis Henriques {
211b7a29217SLuis Henriques 	WARN_ON(!S_ISDIR(inode->i_mode));
212b7a29217SLuis Henriques 
213b7a29217SLuis Henriques 	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0);
214b7a29217SLuis Henriques }
215