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 13d557c48dSLuis Henriques void ceph_adjust_quota_realms_count(struct inode *inode, bool inc) 14cafe21a4SLuis Henriques { 15d557c48dSLuis Henriques struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; 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 22d557c48dSLuis Henriques static inline bool ceph_has_realms_with_quotas(struct inode *inode) 23d557c48dSLuis Henriques { 24d557c48dSLuis Henriques struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; 250c44a8e0SLuis Henriques struct super_block *sb = mdsc->fsc->sb; 260c44a8e0SLuis Henriques 270c44a8e0SLuis Henriques if (atomic64_read(&mdsc->quotarealms_count) > 0) 280c44a8e0SLuis Henriques return true; 290c44a8e0SLuis Henriques /* if root is the real CephFS root, we don't have quota realms */ 300c44a8e0SLuis Henriques if (sb->s_root->d_inode && 310c44a8e0SLuis Henriques (sb->s_root->d_inode->i_ino == CEPH_INO_ROOT)) 320c44a8e0SLuis Henriques return false; 330c44a8e0SLuis Henriques /* otherwise, we can't know for sure */ 340c44a8e0SLuis Henriques return true; 35cafe21a4SLuis Henriques } 36cafe21a4SLuis Henriques 37fb18a575SLuis Henriques void ceph_handle_quota(struct ceph_mds_client *mdsc, 38fb18a575SLuis Henriques struct ceph_mds_session *session, 39fb18a575SLuis Henriques struct ceph_msg *msg) 40fb18a575SLuis Henriques { 41fb18a575SLuis Henriques struct super_block *sb = mdsc->fsc->sb; 42fb18a575SLuis Henriques struct ceph_mds_quota *h = msg->front.iov_base; 43fb18a575SLuis Henriques struct ceph_vino vino; 44fb18a575SLuis Henriques struct inode *inode; 45fb18a575SLuis Henriques struct ceph_inode_info *ci; 46fb18a575SLuis Henriques 470fcf6c02SYan, Zheng if (msg->front.iov_len < sizeof(*h)) { 48fb18a575SLuis Henriques pr_err("%s corrupt message mds%d len %d\n", __func__, 49fb18a575SLuis Henriques session->s_mds, (int)msg->front.iov_len); 50fb18a575SLuis Henriques ceph_msg_dump(msg); 51fb18a575SLuis Henriques return; 52fb18a575SLuis Henriques } 53fb18a575SLuis Henriques 54fb18a575SLuis Henriques /* increment msg sequence number */ 55fb18a575SLuis Henriques mutex_lock(&session->s_mutex); 56fb18a575SLuis Henriques session->s_seq++; 57fb18a575SLuis Henriques mutex_unlock(&session->s_mutex); 58fb18a575SLuis Henriques 59fb18a575SLuis Henriques /* lookup inode */ 60fb18a575SLuis Henriques vino.ino = le64_to_cpu(h->ino); 61fb18a575SLuis Henriques vino.snap = CEPH_NOSNAP; 62fb18a575SLuis Henriques inode = ceph_find_inode(sb, vino); 63fb18a575SLuis Henriques if (!inode) { 64fb18a575SLuis Henriques pr_warn("Failed to find inode %llu\n", vino.ino); 65fb18a575SLuis Henriques return; 66fb18a575SLuis Henriques } 67fb18a575SLuis Henriques ci = ceph_inode(inode); 68fb18a575SLuis Henriques 69fb18a575SLuis Henriques spin_lock(&ci->i_ceph_lock); 70fb18a575SLuis Henriques ci->i_rbytes = le64_to_cpu(h->rbytes); 71fb18a575SLuis Henriques ci->i_rfiles = le64_to_cpu(h->rfiles); 72fb18a575SLuis Henriques ci->i_rsubdirs = le64_to_cpu(h->rsubdirs); 73d557c48dSLuis Henriques __ceph_update_quota(ci, le64_to_cpu(h->max_bytes), 74d557c48dSLuis Henriques le64_to_cpu(h->max_files)); 75fb18a575SLuis Henriques spin_unlock(&ci->i_ceph_lock); 76fb18a575SLuis Henriques 77fb18a575SLuis Henriques iput(inode); 78fb18a575SLuis Henriques } 79b7a29217SLuis Henriques 800c44a8e0SLuis Henriques static struct ceph_quotarealm_inode * 810c44a8e0SLuis Henriques find_quotarealm_inode(struct ceph_mds_client *mdsc, u64 ino) 820c44a8e0SLuis Henriques { 830c44a8e0SLuis Henriques struct ceph_quotarealm_inode *qri = NULL; 840c44a8e0SLuis Henriques struct rb_node **node, *parent = NULL; 850c44a8e0SLuis Henriques 860c44a8e0SLuis Henriques mutex_lock(&mdsc->quotarealms_inodes_mutex); 870c44a8e0SLuis Henriques node = &(mdsc->quotarealms_inodes.rb_node); 880c44a8e0SLuis Henriques while (*node) { 890c44a8e0SLuis Henriques parent = *node; 900c44a8e0SLuis Henriques qri = container_of(*node, struct ceph_quotarealm_inode, node); 910c44a8e0SLuis Henriques 920c44a8e0SLuis Henriques if (ino < qri->ino) 930c44a8e0SLuis Henriques node = &((*node)->rb_left); 940c44a8e0SLuis Henriques else if (ino > qri->ino) 950c44a8e0SLuis Henriques node = &((*node)->rb_right); 960c44a8e0SLuis Henriques else 970c44a8e0SLuis Henriques break; 980c44a8e0SLuis Henriques } 990c44a8e0SLuis Henriques if (!qri || (qri->ino != ino)) { 1000c44a8e0SLuis Henriques /* Not found, create a new one and insert it */ 1010c44a8e0SLuis Henriques qri = kmalloc(sizeof(*qri), GFP_KERNEL); 1020c44a8e0SLuis Henriques if (qri) { 1030c44a8e0SLuis Henriques qri->ino = ino; 1040c44a8e0SLuis Henriques qri->inode = NULL; 1050c44a8e0SLuis Henriques qri->timeout = 0; 1060c44a8e0SLuis Henriques mutex_init(&qri->mutex); 1070c44a8e0SLuis Henriques rb_link_node(&qri->node, parent, node); 1080c44a8e0SLuis Henriques rb_insert_color(&qri->node, &mdsc->quotarealms_inodes); 1090c44a8e0SLuis Henriques } else 1100c44a8e0SLuis Henriques pr_warn("Failed to alloc quotarealms_inode\n"); 1110c44a8e0SLuis Henriques } 1120c44a8e0SLuis Henriques mutex_unlock(&mdsc->quotarealms_inodes_mutex); 1130c44a8e0SLuis Henriques 1140c44a8e0SLuis Henriques return qri; 1150c44a8e0SLuis Henriques } 1160c44a8e0SLuis Henriques 1170c44a8e0SLuis Henriques /* 1180c44a8e0SLuis Henriques * This function will try to lookup a realm inode which isn't visible in the 1190c44a8e0SLuis Henriques * filesystem mountpoint. A list of these kind of inodes (not visible) is 1200c44a8e0SLuis Henriques * maintained in the mdsc and freed only when the filesystem is umounted. 1210c44a8e0SLuis Henriques * 1220c44a8e0SLuis Henriques * Note that these inodes are kept in this list even if the lookup fails, which 1230c44a8e0SLuis Henriques * allows to prevent useless lookup requests. 1240c44a8e0SLuis Henriques */ 1250c44a8e0SLuis Henriques static struct inode *lookup_quotarealm_inode(struct ceph_mds_client *mdsc, 1260c44a8e0SLuis Henriques struct super_block *sb, 1270c44a8e0SLuis Henriques struct ceph_snap_realm *realm) 1280c44a8e0SLuis Henriques { 1290c44a8e0SLuis Henriques struct ceph_quotarealm_inode *qri; 1300c44a8e0SLuis Henriques struct inode *in; 1310c44a8e0SLuis Henriques 1320c44a8e0SLuis Henriques qri = find_quotarealm_inode(mdsc, realm->ino); 1330c44a8e0SLuis Henriques if (!qri) 1340c44a8e0SLuis Henriques return NULL; 1350c44a8e0SLuis Henriques 1360c44a8e0SLuis Henriques mutex_lock(&qri->mutex); 1370c44a8e0SLuis Henriques if (qri->inode) { 1380c44a8e0SLuis Henriques /* A request has already returned the inode */ 1390c44a8e0SLuis Henriques mutex_unlock(&qri->mutex); 1400c44a8e0SLuis Henriques return qri->inode; 1410c44a8e0SLuis Henriques } 1420c44a8e0SLuis Henriques /* Check if this inode lookup has failed recently */ 1430c44a8e0SLuis Henriques if (qri->timeout && 1440c44a8e0SLuis Henriques time_before_eq(jiffies, qri->timeout)) { 1450c44a8e0SLuis Henriques mutex_unlock(&qri->mutex); 1460c44a8e0SLuis Henriques return NULL; 1470c44a8e0SLuis Henriques } 1480c44a8e0SLuis Henriques in = ceph_lookup_inode(sb, realm->ino); 1490c44a8e0SLuis Henriques if (IS_ERR(in)) { 1500c44a8e0SLuis Henriques pr_warn("Can't lookup inode %llx (err: %ld)\n", 1510c44a8e0SLuis Henriques realm->ino, PTR_ERR(in)); 1520c44a8e0SLuis Henriques qri->timeout = jiffies + msecs_to_jiffies(60 * 1000); /* XXX */ 1530c44a8e0SLuis Henriques } else { 1540c44a8e0SLuis Henriques qri->timeout = 0; 1550c44a8e0SLuis Henriques qri->inode = in; 1560c44a8e0SLuis Henriques } 1570c44a8e0SLuis Henriques mutex_unlock(&qri->mutex); 1580c44a8e0SLuis Henriques 1590c44a8e0SLuis Henriques return in; 1600c44a8e0SLuis Henriques } 1610c44a8e0SLuis Henriques 1620c44a8e0SLuis Henriques void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc) 1630c44a8e0SLuis Henriques { 1640c44a8e0SLuis Henriques struct ceph_quotarealm_inode *qri; 1650c44a8e0SLuis Henriques struct rb_node *node; 1660c44a8e0SLuis Henriques 1670c44a8e0SLuis Henriques /* 1680c44a8e0SLuis Henriques * It should now be safe to clean quotarealms_inode tree without holding 1690c44a8e0SLuis Henriques * mdsc->quotarealms_inodes_mutex... 1700c44a8e0SLuis Henriques */ 1710c44a8e0SLuis Henriques mutex_lock(&mdsc->quotarealms_inodes_mutex); 1720c44a8e0SLuis Henriques while (!RB_EMPTY_ROOT(&mdsc->quotarealms_inodes)) { 1730c44a8e0SLuis Henriques node = rb_first(&mdsc->quotarealms_inodes); 1740c44a8e0SLuis Henriques qri = rb_entry(node, struct ceph_quotarealm_inode, node); 1750c44a8e0SLuis Henriques rb_erase(node, &mdsc->quotarealms_inodes); 1760c44a8e0SLuis Henriques iput(qri->inode); 1770c44a8e0SLuis Henriques kfree(qri); 1780c44a8e0SLuis Henriques } 1790c44a8e0SLuis Henriques mutex_unlock(&mdsc->quotarealms_inodes_mutex); 1800c44a8e0SLuis Henriques } 1810c44a8e0SLuis Henriques 182cafe21a4SLuis Henriques /* 183cafe21a4SLuis Henriques * This function walks through the snaprealm for an inode and returns the 184cafe21a4SLuis Henriques * ceph_snap_realm for the first snaprealm that has quotas set (either max_files 185cafe21a4SLuis Henriques * or max_bytes). If the root is reached, return the root ceph_snap_realm 186cafe21a4SLuis Henriques * instead. 187cafe21a4SLuis Henriques * 188cafe21a4SLuis Henriques * Note that the caller is responsible for calling ceph_put_snap_realm() on the 189cafe21a4SLuis Henriques * returned realm. 1900c44a8e0SLuis Henriques * 1910c44a8e0SLuis Henriques * Callers of this function need to hold mdsc->snap_rwsem. However, if there's 1920c44a8e0SLuis Henriques * a need to do an inode lookup, this rwsem will be temporarily dropped. Hence 1930c44a8e0SLuis Henriques * the 'retry' argument: if rwsem needs to be dropped and 'retry' is 'false' 1940c44a8e0SLuis Henriques * this function will return -EAGAIN; otherwise, the snaprealms walk-through 1950c44a8e0SLuis Henriques * will be restarted. 196cafe21a4SLuis Henriques */ 197cafe21a4SLuis Henriques static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc, 1980c44a8e0SLuis Henriques struct inode *inode, bool retry) 199cafe21a4SLuis Henriques { 200cafe21a4SLuis Henriques struct ceph_inode_info *ci = NULL; 201cafe21a4SLuis Henriques struct ceph_snap_realm *realm, *next; 202cafe21a4SLuis Henriques struct inode *in; 2030eb6bbe4SYan, Zheng bool has_quota; 204cafe21a4SLuis Henriques 20525963669SYan, Zheng if (ceph_snap(inode) != CEPH_NOSNAP) 20625963669SYan, Zheng return NULL; 20725963669SYan, Zheng 2080c44a8e0SLuis Henriques restart: 209cafe21a4SLuis Henriques realm = ceph_inode(inode)->i_snap_realm; 21025963669SYan, Zheng if (realm) 211cafe21a4SLuis Henriques ceph_get_snap_realm(mdsc, realm); 21225963669SYan, Zheng else 21325963669SYan, Zheng pr_err_ratelimited("get_quota_realm: ino (%llx.%llx) " 21425963669SYan, Zheng "null i_snap_realm\n", ceph_vinop(inode)); 215cafe21a4SLuis Henriques while (realm) { 2160c44a8e0SLuis Henriques bool has_inode; 2170c44a8e0SLuis Henriques 218e3161f17SLuis Henriques spin_lock(&realm->inodes_with_caps_lock); 2190c44a8e0SLuis Henriques has_inode = realm->inode; 2200c44a8e0SLuis Henriques in = has_inode ? igrab(realm->inode) : NULL; 221e3161f17SLuis Henriques spin_unlock(&realm->inodes_with_caps_lock); 2220c44a8e0SLuis Henriques if (has_inode && !in) 223cafe21a4SLuis Henriques break; 2240c44a8e0SLuis Henriques if (!in) { 2250c44a8e0SLuis Henriques up_read(&mdsc->snap_rwsem); 2260c44a8e0SLuis Henriques in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm); 2270c44a8e0SLuis Henriques down_read(&mdsc->snap_rwsem); 2280c44a8e0SLuis Henriques if (IS_ERR_OR_NULL(in)) 2290c44a8e0SLuis Henriques break; 2300c44a8e0SLuis Henriques ceph_put_snap_realm(mdsc, realm); 2310c44a8e0SLuis Henriques if (!retry) 2320c44a8e0SLuis Henriques return ERR_PTR(-EAGAIN); 2330c44a8e0SLuis Henriques goto restart; 2340c44a8e0SLuis Henriques } 235e3161f17SLuis Henriques 236cafe21a4SLuis Henriques ci = ceph_inode(in); 237d557c48dSLuis Henriques has_quota = __ceph_has_any_quota(ci); 238cafe21a4SLuis Henriques iput(in); 2390eb6bbe4SYan, Zheng 240cafe21a4SLuis Henriques next = realm->parent; 2410eb6bbe4SYan, Zheng if (has_quota || !next) 2420eb6bbe4SYan, Zheng return realm; 2430eb6bbe4SYan, Zheng 244cafe21a4SLuis Henriques ceph_get_snap_realm(mdsc, next); 245cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, realm); 246cafe21a4SLuis Henriques realm = next; 247cafe21a4SLuis Henriques } 248cafe21a4SLuis Henriques if (realm) 249cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, realm); 250cafe21a4SLuis Henriques 251cafe21a4SLuis Henriques return NULL; 252cafe21a4SLuis Henriques } 253cafe21a4SLuis Henriques 254cafe21a4SLuis Henriques bool ceph_quota_is_same_realm(struct inode *old, struct inode *new) 255cafe21a4SLuis Henriques { 256cafe21a4SLuis Henriques struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc; 257cafe21a4SLuis Henriques struct ceph_snap_realm *old_realm, *new_realm; 258cafe21a4SLuis Henriques bool is_same; 259cafe21a4SLuis Henriques 2600c44a8e0SLuis Henriques restart: 2610c44a8e0SLuis Henriques /* 2620c44a8e0SLuis Henriques * We need to lookup 2 quota realms atomically, i.e. with snap_rwsem. 2630c44a8e0SLuis Henriques * However, get_quota_realm may drop it temporarily. By setting the 2640c44a8e0SLuis Henriques * 'retry' parameter to 'false', we'll get -EAGAIN if the rwsem was 2650c44a8e0SLuis Henriques * dropped and we can then restart the whole operation. 2660c44a8e0SLuis Henriques */ 267cafe21a4SLuis Henriques down_read(&mdsc->snap_rwsem); 2680c44a8e0SLuis Henriques old_realm = get_quota_realm(mdsc, old, true); 2690c44a8e0SLuis Henriques new_realm = get_quota_realm(mdsc, new, false); 2700c44a8e0SLuis Henriques if (PTR_ERR(new_realm) == -EAGAIN) { 2710c44a8e0SLuis Henriques up_read(&mdsc->snap_rwsem); 2720c44a8e0SLuis Henriques if (old_realm) 2730c44a8e0SLuis Henriques ceph_put_snap_realm(mdsc, old_realm); 2740c44a8e0SLuis Henriques goto restart; 2750c44a8e0SLuis Henriques } 276cafe21a4SLuis Henriques is_same = (old_realm == new_realm); 277cafe21a4SLuis Henriques up_read(&mdsc->snap_rwsem); 278cafe21a4SLuis Henriques 279cafe21a4SLuis Henriques if (old_realm) 280cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, old_realm); 281cafe21a4SLuis Henriques if (new_realm) 282cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, new_realm); 283cafe21a4SLuis Henriques 284cafe21a4SLuis Henriques return is_same; 285cafe21a4SLuis Henriques } 286cafe21a4SLuis Henriques 287b7a29217SLuis Henriques enum quota_check_op { 2882b83845fSLuis Henriques QUOTA_CHECK_MAX_FILES_OP, /* check quota max_files limit */ 2891ab302a0SLuis Henriques QUOTA_CHECK_MAX_BYTES_OP, /* check quota max_files limit */ 2901ab302a0SLuis Henriques QUOTA_CHECK_MAX_BYTES_APPROACHING_OP /* check if quota max_files 2911ab302a0SLuis Henriques limit is approaching */ 292b7a29217SLuis Henriques }; 293b7a29217SLuis Henriques 294b7a29217SLuis Henriques /* 295b7a29217SLuis Henriques * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each 296b7a29217SLuis Henriques * realm, it will execute quota check operation defined by the 'op' parameter. 297b7a29217SLuis Henriques * The snaprealm walk is interrupted if the quota check detects that the quota 298b7a29217SLuis Henriques * is exceeded or if the root inode is reached. 299b7a29217SLuis Henriques */ 300b7a29217SLuis Henriques static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op, 301b7a29217SLuis Henriques loff_t delta) 302b7a29217SLuis Henriques { 303b7a29217SLuis Henriques struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; 304b7a29217SLuis Henriques struct ceph_inode_info *ci; 305b7a29217SLuis Henriques struct ceph_snap_realm *realm, *next; 306b7a29217SLuis Henriques struct inode *in; 307b7a29217SLuis Henriques u64 max, rvalue; 308b7a29217SLuis Henriques bool exceeded = false; 309b7a29217SLuis Henriques 31025963669SYan, Zheng if (ceph_snap(inode) != CEPH_NOSNAP) 31125963669SYan, Zheng return false; 31225963669SYan, Zheng 313b7a29217SLuis Henriques down_read(&mdsc->snap_rwsem); 3140c44a8e0SLuis Henriques restart: 315b7a29217SLuis Henriques realm = ceph_inode(inode)->i_snap_realm; 31625963669SYan, Zheng if (realm) 317b7a29217SLuis Henriques ceph_get_snap_realm(mdsc, realm); 31825963669SYan, Zheng else 31925963669SYan, Zheng pr_err_ratelimited("check_quota_exceeded: ino (%llx.%llx) " 32025963669SYan, Zheng "null i_snap_realm\n", ceph_vinop(inode)); 321b7a29217SLuis Henriques while (realm) { 3220c44a8e0SLuis Henriques bool has_inode; 323e3161f17SLuis Henriques 3240c44a8e0SLuis Henriques spin_lock(&realm->inodes_with_caps_lock); 3250c44a8e0SLuis Henriques has_inode = realm->inode; 3260c44a8e0SLuis Henriques in = has_inode ? igrab(realm->inode) : NULL; 3270c44a8e0SLuis Henriques spin_unlock(&realm->inodes_with_caps_lock); 3280c44a8e0SLuis Henriques if (has_inode && !in) 3290c44a8e0SLuis Henriques break; 3300c44a8e0SLuis Henriques if (!in) { 3310c44a8e0SLuis Henriques up_read(&mdsc->snap_rwsem); 3320c44a8e0SLuis Henriques in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm); 3330c44a8e0SLuis Henriques down_read(&mdsc->snap_rwsem); 3340c44a8e0SLuis Henriques if (IS_ERR_OR_NULL(in)) 3350c44a8e0SLuis Henriques break; 3360c44a8e0SLuis Henriques ceph_put_snap_realm(mdsc, realm); 3370c44a8e0SLuis Henriques goto restart; 3380c44a8e0SLuis Henriques } 339b7a29217SLuis Henriques ci = ceph_inode(in); 340b7a29217SLuis Henriques spin_lock(&ci->i_ceph_lock); 341b7a29217SLuis Henriques if (op == QUOTA_CHECK_MAX_FILES_OP) { 342b7a29217SLuis Henriques max = ci->i_max_files; 343b7a29217SLuis Henriques rvalue = ci->i_rfiles + ci->i_rsubdirs; 3442b83845fSLuis Henriques } else { 3452b83845fSLuis Henriques max = ci->i_max_bytes; 3462b83845fSLuis Henriques rvalue = ci->i_rbytes; 347b7a29217SLuis Henriques } 348b7a29217SLuis Henriques spin_unlock(&ci->i_ceph_lock); 349b7a29217SLuis Henriques switch (op) { 350b7a29217SLuis Henriques case QUOTA_CHECK_MAX_FILES_OP: 351b7a29217SLuis Henriques exceeded = (max && (rvalue >= max)); 352b7a29217SLuis Henriques break; 3532b83845fSLuis Henriques case QUOTA_CHECK_MAX_BYTES_OP: 3542b83845fSLuis Henriques exceeded = (max && (rvalue + delta > max)); 3552b83845fSLuis Henriques break; 3561ab302a0SLuis Henriques case QUOTA_CHECK_MAX_BYTES_APPROACHING_OP: 3571ab302a0SLuis Henriques if (max) { 3581ab302a0SLuis Henriques if (rvalue >= max) 3591ab302a0SLuis Henriques exceeded = true; 3601ab302a0SLuis Henriques else { 3611ab302a0SLuis Henriques /* 3621ab302a0SLuis Henriques * when we're writing more that 1/16th 3631ab302a0SLuis Henriques * of the available space 3641ab302a0SLuis Henriques */ 3651ab302a0SLuis Henriques exceeded = 3661ab302a0SLuis Henriques (((max - rvalue) >> 4) < delta); 3671ab302a0SLuis Henriques } 3681ab302a0SLuis Henriques } 3691ab302a0SLuis Henriques break; 370b7a29217SLuis Henriques default: 371b7a29217SLuis Henriques /* Shouldn't happen */ 372b7a29217SLuis Henriques pr_warn("Invalid quota check op (%d)\n", op); 373b7a29217SLuis Henriques exceeded = true; /* Just break the loop */ 374b7a29217SLuis Henriques } 375b7a29217SLuis Henriques iput(in); 376b7a29217SLuis Henriques 377b7a29217SLuis Henriques next = realm->parent; 3780eb6bbe4SYan, Zheng if (exceeded || !next) 3790eb6bbe4SYan, Zheng break; 380b7a29217SLuis Henriques ceph_get_snap_realm(mdsc, next); 381b7a29217SLuis Henriques ceph_put_snap_realm(mdsc, realm); 382b7a29217SLuis Henriques realm = next; 383b7a29217SLuis Henriques } 38471f2cc64SLuis Henriques if (realm) 385b7a29217SLuis Henriques ceph_put_snap_realm(mdsc, realm); 386b7a29217SLuis Henriques up_read(&mdsc->snap_rwsem); 387b7a29217SLuis Henriques 388b7a29217SLuis Henriques return exceeded; 389b7a29217SLuis Henriques } 390b7a29217SLuis Henriques 391b7a29217SLuis Henriques /* 392b7a29217SLuis Henriques * ceph_quota_is_max_files_exceeded - check if we can create a new file 393b7a29217SLuis Henriques * @inode: directory where a new file is being created 394b7a29217SLuis Henriques * 395b7a29217SLuis Henriques * This functions returns true is max_files quota allows a new file to be 396b7a29217SLuis Henriques * created. It is necessary to walk through the snaprealm hierarchy (until the 397b7a29217SLuis Henriques * FS root) to check all realms with quotas set. 398b7a29217SLuis Henriques */ 399b7a29217SLuis Henriques bool ceph_quota_is_max_files_exceeded(struct inode *inode) 400b7a29217SLuis Henriques { 401d557c48dSLuis Henriques if (!ceph_has_realms_with_quotas(inode)) 402d557c48dSLuis Henriques return false; 403d557c48dSLuis Henriques 404b7a29217SLuis Henriques WARN_ON(!S_ISDIR(inode->i_mode)); 405b7a29217SLuis Henriques 406b7a29217SLuis Henriques return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0); 407b7a29217SLuis Henriques } 4082b83845fSLuis Henriques 4092b83845fSLuis Henriques /* 4102b83845fSLuis Henriques * ceph_quota_is_max_bytes_exceeded - check if we can write to a file 4112b83845fSLuis Henriques * @inode: inode being written 4122b83845fSLuis Henriques * @newsize: new size if write succeeds 4132b83845fSLuis Henriques * 4142b83845fSLuis Henriques * This functions returns true is max_bytes quota allows a file size to reach 4152b83845fSLuis Henriques * @newsize; it returns false otherwise. 4162b83845fSLuis Henriques */ 4172b83845fSLuis Henriques bool ceph_quota_is_max_bytes_exceeded(struct inode *inode, loff_t newsize) 4182b83845fSLuis Henriques { 4192b83845fSLuis Henriques loff_t size = i_size_read(inode); 4202b83845fSLuis Henriques 421d557c48dSLuis Henriques if (!ceph_has_realms_with_quotas(inode)) 422d557c48dSLuis Henriques return false; 423d557c48dSLuis Henriques 4242b83845fSLuis Henriques /* return immediately if we're decreasing file size */ 4252b83845fSLuis Henriques if (newsize <= size) 4262b83845fSLuis Henriques return false; 4272b83845fSLuis Henriques 4282b83845fSLuis Henriques return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_OP, (newsize - size)); 4292b83845fSLuis Henriques } 4301ab302a0SLuis Henriques 4311ab302a0SLuis Henriques /* 4321ab302a0SLuis Henriques * ceph_quota_is_max_bytes_approaching - check if we're reaching max_bytes 4331ab302a0SLuis Henriques * @inode: inode being written 4341ab302a0SLuis Henriques * @newsize: new size if write succeeds 4351ab302a0SLuis Henriques * 4361ab302a0SLuis Henriques * This function returns true if the new file size @newsize will be consuming 4371ab302a0SLuis Henriques * more than 1/16th of the available quota space; it returns false otherwise. 4381ab302a0SLuis Henriques */ 4391ab302a0SLuis Henriques bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize) 4401ab302a0SLuis Henriques { 4411ab302a0SLuis Henriques loff_t size = ceph_inode(inode)->i_reported_size; 4421ab302a0SLuis Henriques 443d557c48dSLuis Henriques if (!ceph_has_realms_with_quotas(inode)) 444d557c48dSLuis Henriques return false; 445d557c48dSLuis Henriques 4461ab302a0SLuis Henriques /* return immediately if we're decreasing file size */ 4471ab302a0SLuis Henriques if (newsize <= size) 4481ab302a0SLuis Henriques return false; 4491ab302a0SLuis Henriques 4501ab302a0SLuis Henriques return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP, 4511ab302a0SLuis Henriques (newsize - size)); 4521ab302a0SLuis Henriques } 4539122eed5SLuis Henriques 4549122eed5SLuis Henriques /* 4559122eed5SLuis Henriques * ceph_quota_update_statfs - if root has quota update statfs with quota status 4569122eed5SLuis Henriques * @fsc: filesystem client instance 4579122eed5SLuis Henriques * @buf: statfs to update 4589122eed5SLuis Henriques * 4599122eed5SLuis Henriques * If the mounted filesystem root has max_bytes quota set, update the filesystem 4609122eed5SLuis Henriques * statistics with the quota status. 4619122eed5SLuis Henriques * 4629122eed5SLuis Henriques * This function returns true if the stats have been updated, false otherwise. 4639122eed5SLuis Henriques */ 4649122eed5SLuis Henriques bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf) 4659122eed5SLuis Henriques { 4669122eed5SLuis Henriques struct ceph_mds_client *mdsc = fsc->mdsc; 4679122eed5SLuis Henriques struct ceph_inode_info *ci; 4689122eed5SLuis Henriques struct ceph_snap_realm *realm; 4699122eed5SLuis Henriques struct inode *in; 4709122eed5SLuis Henriques u64 total = 0, used, free; 4719122eed5SLuis Henriques bool is_updated = false; 4729122eed5SLuis Henriques 4739122eed5SLuis Henriques down_read(&mdsc->snap_rwsem); 4740c44a8e0SLuis Henriques realm = get_quota_realm(mdsc, d_inode(fsc->sb->s_root), true); 4759122eed5SLuis Henriques up_read(&mdsc->snap_rwsem); 4769122eed5SLuis Henriques if (!realm) 4779122eed5SLuis Henriques return false; 4789122eed5SLuis Henriques 4799122eed5SLuis Henriques spin_lock(&realm->inodes_with_caps_lock); 4809122eed5SLuis Henriques in = realm->inode ? igrab(realm->inode) : NULL; 4819122eed5SLuis Henriques spin_unlock(&realm->inodes_with_caps_lock); 4829122eed5SLuis Henriques if (in) { 4839122eed5SLuis Henriques ci = ceph_inode(in); 4849122eed5SLuis Henriques spin_lock(&ci->i_ceph_lock); 4859122eed5SLuis Henriques if (ci->i_max_bytes) { 4869122eed5SLuis Henriques total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT; 4879122eed5SLuis Henriques used = ci->i_rbytes >> CEPH_BLOCK_SHIFT; 4889122eed5SLuis Henriques /* It is possible for a quota to be exceeded. 4899122eed5SLuis Henriques * Report 'zero' in that case 4909122eed5SLuis Henriques */ 4919122eed5SLuis Henriques free = total > used ? total - used : 0; 4929122eed5SLuis Henriques } 4939122eed5SLuis Henriques spin_unlock(&ci->i_ceph_lock); 4949122eed5SLuis Henriques if (total) { 4959122eed5SLuis Henriques buf->f_blocks = total; 4969122eed5SLuis Henriques buf->f_bfree = free; 4979122eed5SLuis Henriques buf->f_bavail = free; 4989122eed5SLuis Henriques is_updated = true; 4999122eed5SLuis Henriques } 5009122eed5SLuis Henriques iput(in); 5019122eed5SLuis Henriques } 5029122eed5SLuis Henriques ceph_put_snap_realm(mdsc, realm); 5039122eed5SLuis Henriques 5049122eed5SLuis Henriques return is_updated; 5059122eed5SLuis Henriques } 5069122eed5SLuis Henriques 507