Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.5.6.
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * quota.c - CephFS quota
  4 *
  5 * Copyright (C) 2017-2018 SUSE
  6 *
  7 * This program is free software; you can redistribute it and/or
  8 * modify it under the terms of the GNU General Public License
  9 * as published by the Free Software Foundation; either version 2
 10 * of the License, or (at your option) any later version.
 11 *
 12 * This program is distributed in the hope that it will be useful,
 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 15 * GNU General Public License for more details.
 16 *
 17 * You should have received a copy of the GNU General Public License
 18 * along with this program; if not, see <http://www.gnu.org/licenses/>.
 19 */
 20
 21#include <linux/statfs.h>
 22
 23#include "super.h"
 24#include "mds_client.h"
 25
 26void ceph_adjust_quota_realms_count(struct inode *inode, bool inc)
 27{
 28	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
 29	if (inc)
 30		atomic64_inc(&mdsc->quotarealms_count);
 31	else
 32		atomic64_dec(&mdsc->quotarealms_count);
 33}
 34
 35static inline bool ceph_has_realms_with_quotas(struct inode *inode)
 36{
 37	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
 38	return atomic64_read(&mdsc->quotarealms_count) > 0;
 39}
 40
 41void ceph_handle_quota(struct ceph_mds_client *mdsc,
 42		       struct ceph_mds_session *session,
 43		       struct ceph_msg *msg)
 44{
 45	struct super_block *sb = mdsc->fsc->sb;
 46	struct ceph_mds_quota *h = msg->front.iov_base;
 47	struct ceph_vino vino;
 48	struct inode *inode;
 49	struct ceph_inode_info *ci;
 50
 51	if (msg->front.iov_len != sizeof(*h)) {
 52		pr_err("%s corrupt message mds%d len %d\n", __func__,
 53		       session->s_mds, (int)msg->front.iov_len);
 54		ceph_msg_dump(msg);
 55		return;
 56	}
 57
 58	/* increment msg sequence number */
 59	mutex_lock(&session->s_mutex);
 60	session->s_seq++;
 61	mutex_unlock(&session->s_mutex);
 62
 63	/* lookup inode */
 64	vino.ino = le64_to_cpu(h->ino);
 65	vino.snap = CEPH_NOSNAP;
 66	inode = ceph_find_inode(sb, vino);
 67	if (!inode) {
 68		pr_warn("Failed to find inode %llu\n", vino.ino);
 69		return;
 70	}
 71	ci = ceph_inode(inode);
 72
 73	spin_lock(&ci->i_ceph_lock);
 74	ci->i_rbytes = le64_to_cpu(h->rbytes);
 75	ci->i_rfiles = le64_to_cpu(h->rfiles);
 76	ci->i_rsubdirs = le64_to_cpu(h->rsubdirs);
 77	__ceph_update_quota(ci, le64_to_cpu(h->max_bytes),
 78		            le64_to_cpu(h->max_files));
 79	spin_unlock(&ci->i_ceph_lock);
 80
 81	iput(inode);
 82}
 83
 84/*
 85 * This function walks through the snaprealm for an inode and returns the
 86 * ceph_snap_realm for the first snaprealm that has quotas set (either max_files
 87 * or max_bytes).  If the root is reached, return the root ceph_snap_realm
 88 * instead.
 89 *
 90 * Note that the caller is responsible for calling ceph_put_snap_realm() on the
 91 * returned realm.
 92 */
 93static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc,
 94					       struct inode *inode)
 95{
 96	struct ceph_inode_info *ci = NULL;
 97	struct ceph_snap_realm *realm, *next;
 98	struct inode *in;
 99	bool has_quota;
100
101	if (ceph_snap(inode) != CEPH_NOSNAP)
102		return NULL;
103
104	realm = ceph_inode(inode)->i_snap_realm;
105	if (realm)
106		ceph_get_snap_realm(mdsc, realm);
107	else
108		pr_err_ratelimited("get_quota_realm: ino (%llx.%llx) "
109				   "null i_snap_realm\n", ceph_vinop(inode));
110	while (realm) {
111		spin_lock(&realm->inodes_with_caps_lock);
112		in = realm->inode ? igrab(realm->inode) : NULL;
113		spin_unlock(&realm->inodes_with_caps_lock);
114		if (!in)
115			break;
116
117		ci = ceph_inode(in);
118		has_quota = __ceph_has_any_quota(ci);
119		iput(in);
120
121		next = realm->parent;
122		if (has_quota || !next)
123		       return realm;
124
125		ceph_get_snap_realm(mdsc, next);
126		ceph_put_snap_realm(mdsc, realm);
127		realm = next;
128	}
129	if (realm)
130		ceph_put_snap_realm(mdsc, realm);
131
132	return NULL;
133}
134
135bool ceph_quota_is_same_realm(struct inode *old, struct inode *new)
136{
137	struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc;
138	struct ceph_snap_realm *old_realm, *new_realm;
139	bool is_same;
140
141	down_read(&mdsc->snap_rwsem);
142	old_realm = get_quota_realm(mdsc, old);
143	new_realm = get_quota_realm(mdsc, new);
144	is_same = (old_realm == new_realm);
145	up_read(&mdsc->snap_rwsem);
146
147	if (old_realm)
148		ceph_put_snap_realm(mdsc, old_realm);
149	if (new_realm)
150		ceph_put_snap_realm(mdsc, new_realm);
151
152	return is_same;
153}
154
155enum quota_check_op {
156	QUOTA_CHECK_MAX_FILES_OP,	/* check quota max_files limit */
157	QUOTA_CHECK_MAX_BYTES_OP,	/* check quota max_files limit */
158	QUOTA_CHECK_MAX_BYTES_APPROACHING_OP	/* check if quota max_files
159						   limit is approaching */
160};
161
162/*
163 * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each
164 * realm, it will execute quota check operation defined by the 'op' parameter.
165 * The snaprealm walk is interrupted if the quota check detects that the quota
166 * is exceeded or if the root inode is reached.
167 */
168static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op,
169				 loff_t delta)
170{
171	struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc;
172	struct ceph_inode_info *ci;
173	struct ceph_snap_realm *realm, *next;
174	struct inode *in;
175	u64 max, rvalue;
176	bool exceeded = false;
177
178	if (ceph_snap(inode) != CEPH_NOSNAP)
179		return false;
180
181	down_read(&mdsc->snap_rwsem);
182	realm = ceph_inode(inode)->i_snap_realm;
183	if (realm)
184		ceph_get_snap_realm(mdsc, realm);
185	else
186		pr_err_ratelimited("check_quota_exceeded: ino (%llx.%llx) "
187				   "null i_snap_realm\n", ceph_vinop(inode));
188	while (realm) {
189		spin_lock(&realm->inodes_with_caps_lock);
190		in = realm->inode ? igrab(realm->inode) : NULL;
191		spin_unlock(&realm->inodes_with_caps_lock);
192		if (!in)
193			break;
194
195		ci = ceph_inode(in);
196		spin_lock(&ci->i_ceph_lock);
197		if (op == QUOTA_CHECK_MAX_FILES_OP) {
198			max = ci->i_max_files;
199			rvalue = ci->i_rfiles + ci->i_rsubdirs;
200		} else {
201			max = ci->i_max_bytes;
202			rvalue = ci->i_rbytes;
203		}
204		spin_unlock(&ci->i_ceph_lock);
205		switch (op) {
206		case QUOTA_CHECK_MAX_FILES_OP:
207			exceeded = (max && (rvalue >= max));
208			break;
209		case QUOTA_CHECK_MAX_BYTES_OP:
210			exceeded = (max && (rvalue + delta > max));
211			break;
212		case QUOTA_CHECK_MAX_BYTES_APPROACHING_OP:
213			if (max) {
214				if (rvalue >= max)
215					exceeded = true;
216				else {
217					/*
218					 * when we're writing more that 1/16th
219					 * of the available space
220					 */
221					exceeded =
222						(((max - rvalue) >> 4) < delta);
223				}
224			}
225			break;
226		default:
227			/* Shouldn't happen */
228			pr_warn("Invalid quota check op (%d)\n", op);
229			exceeded = true; /* Just break the loop */
230		}
231		iput(in);
232
233		next = realm->parent;
234		if (exceeded || !next)
235			break;
236		ceph_get_snap_realm(mdsc, next);
237		ceph_put_snap_realm(mdsc, realm);
238		realm = next;
239	}
240	ceph_put_snap_realm(mdsc, realm);
241	up_read(&mdsc->snap_rwsem);
242
243	return exceeded;
244}
245
246/*
247 * ceph_quota_is_max_files_exceeded - check if we can create a new file
248 * @inode:	directory where a new file is being created
249 *
250 * This functions returns true is max_files quota allows a new file to be
251 * created.  It is necessary to walk through the snaprealm hierarchy (until the
252 * FS root) to check all realms with quotas set.
253 */
254bool ceph_quota_is_max_files_exceeded(struct inode *inode)
255{
256	if (!ceph_has_realms_with_quotas(inode))
257		return false;
258
259	WARN_ON(!S_ISDIR(inode->i_mode));
260
261	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0);
262}
263
264/*
265 * ceph_quota_is_max_bytes_exceeded - check if we can write to a file
266 * @inode:	inode being written
267 * @newsize:	new size if write succeeds
268 *
269 * This functions returns true is max_bytes quota allows a file size to reach
270 * @newsize; it returns false otherwise.
271 */
272bool ceph_quota_is_max_bytes_exceeded(struct inode *inode, loff_t newsize)
273{
274	loff_t size = i_size_read(inode);
275
276	if (!ceph_has_realms_with_quotas(inode))
277		return false;
278
279	/* return immediately if we're decreasing file size */
280	if (newsize <= size)
281		return false;
282
283	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_OP, (newsize - size));
284}
285
286/*
287 * ceph_quota_is_max_bytes_approaching - check if we're reaching max_bytes
288 * @inode:	inode being written
289 * @newsize:	new size if write succeeds
290 *
291 * This function returns true if the new file size @newsize will be consuming
292 * more than 1/16th of the available quota space; it returns false otherwise.
293 */
294bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize)
295{
296	loff_t size = ceph_inode(inode)->i_reported_size;
297
298	if (!ceph_has_realms_with_quotas(inode))
299		return false;
300
301	/* return immediately if we're decreasing file size */
302	if (newsize <= size)
303		return false;
304
305	return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP,
306				    (newsize - size));
307}
308
309/*
310 * ceph_quota_update_statfs - if root has quota update statfs with quota status
311 * @fsc:	filesystem client instance
312 * @buf:	statfs to update
313 *
314 * If the mounted filesystem root has max_bytes quota set, update the filesystem
315 * statistics with the quota status.
316 *
317 * This function returns true if the stats have been updated, false otherwise.
318 */
319bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf)
320{
321	struct ceph_mds_client *mdsc = fsc->mdsc;
322	struct ceph_inode_info *ci;
323	struct ceph_snap_realm *realm;
324	struct inode *in;
325	u64 total = 0, used, free;
326	bool is_updated = false;
327
328	down_read(&mdsc->snap_rwsem);
329	realm = get_quota_realm(mdsc, d_inode(fsc->sb->s_root));
330	up_read(&mdsc->snap_rwsem);
331	if (!realm)
332		return false;
333
334	spin_lock(&realm->inodes_with_caps_lock);
335	in = realm->inode ? igrab(realm->inode) : NULL;
336	spin_unlock(&realm->inodes_with_caps_lock);
337	if (in) {
338		ci = ceph_inode(in);
339		spin_lock(&ci->i_ceph_lock);
340		if (ci->i_max_bytes) {
341			total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT;
342			used = ci->i_rbytes >> CEPH_BLOCK_SHIFT;
343			/* It is possible for a quota to be exceeded.
344			 * Report 'zero' in that case
345			 */
346			free = total > used ? total - used : 0;
347		}
348		spin_unlock(&ci->i_ceph_lock);
349		if (total) {
350			buf->f_blocks = total;
351			buf->f_bfree = free;
352			buf->f_bavail = free;
353			is_updated = true;
354		}
355		iput(in);
356	}
357	ceph_put_snap_realm(mdsc, realm);
358
359	return is_updated;
360}
361