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