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 773e1d0452SYan, Zheng /* avoid calling iput_final() in dispatch thread */ 783e1d0452SYan, Zheng ceph_async_iput(inode); 79fb18a575SLuis Henriques } 80b7a29217SLuis Henriques 810c44a8e0SLuis Henriques static struct ceph_quotarealm_inode * 820c44a8e0SLuis Henriques find_quotarealm_inode(struct ceph_mds_client *mdsc, u64 ino) 830c44a8e0SLuis Henriques { 840c44a8e0SLuis Henriques struct ceph_quotarealm_inode *qri = NULL; 850c44a8e0SLuis Henriques struct rb_node **node, *parent = NULL; 860c44a8e0SLuis Henriques 870c44a8e0SLuis Henriques mutex_lock(&mdsc->quotarealms_inodes_mutex); 880c44a8e0SLuis Henriques node = &(mdsc->quotarealms_inodes.rb_node); 890c44a8e0SLuis Henriques while (*node) { 900c44a8e0SLuis Henriques parent = *node; 910c44a8e0SLuis Henriques qri = container_of(*node, struct ceph_quotarealm_inode, node); 920c44a8e0SLuis Henriques 930c44a8e0SLuis Henriques if (ino < qri->ino) 940c44a8e0SLuis Henriques node = &((*node)->rb_left); 950c44a8e0SLuis Henriques else if (ino > qri->ino) 960c44a8e0SLuis Henriques node = &((*node)->rb_right); 970c44a8e0SLuis Henriques else 980c44a8e0SLuis Henriques break; 990c44a8e0SLuis Henriques } 1000c44a8e0SLuis Henriques if (!qri || (qri->ino != ino)) { 1010c44a8e0SLuis Henriques /* Not found, create a new one and insert it */ 1020c44a8e0SLuis Henriques qri = kmalloc(sizeof(*qri), GFP_KERNEL); 1030c44a8e0SLuis Henriques if (qri) { 1040c44a8e0SLuis Henriques qri->ino = ino; 1050c44a8e0SLuis Henriques qri->inode = NULL; 1060c44a8e0SLuis Henriques qri->timeout = 0; 1070c44a8e0SLuis Henriques mutex_init(&qri->mutex); 1080c44a8e0SLuis Henriques rb_link_node(&qri->node, parent, node); 1090c44a8e0SLuis Henriques rb_insert_color(&qri->node, &mdsc->quotarealms_inodes); 1100c44a8e0SLuis Henriques } else 1110c44a8e0SLuis Henriques pr_warn("Failed to alloc quotarealms_inode\n"); 1120c44a8e0SLuis Henriques } 1130c44a8e0SLuis Henriques mutex_unlock(&mdsc->quotarealms_inodes_mutex); 1140c44a8e0SLuis Henriques 1150c44a8e0SLuis Henriques return qri; 1160c44a8e0SLuis Henriques } 1170c44a8e0SLuis Henriques 1180c44a8e0SLuis Henriques /* 1190c44a8e0SLuis Henriques * This function will try to lookup a realm inode which isn't visible in the 1200c44a8e0SLuis Henriques * filesystem mountpoint. A list of these kind of inodes (not visible) is 1210c44a8e0SLuis Henriques * maintained in the mdsc and freed only when the filesystem is umounted. 1220c44a8e0SLuis Henriques * 1230c44a8e0SLuis Henriques * Note that these inodes are kept in this list even if the lookup fails, which 1240c44a8e0SLuis Henriques * allows to prevent useless lookup requests. 1250c44a8e0SLuis Henriques */ 1260c44a8e0SLuis Henriques static struct inode *lookup_quotarealm_inode(struct ceph_mds_client *mdsc, 1270c44a8e0SLuis Henriques struct super_block *sb, 1280c44a8e0SLuis Henriques struct ceph_snap_realm *realm) 1290c44a8e0SLuis Henriques { 1300c44a8e0SLuis Henriques struct ceph_quotarealm_inode *qri; 1310c44a8e0SLuis Henriques struct inode *in; 1320c44a8e0SLuis Henriques 1330c44a8e0SLuis Henriques qri = find_quotarealm_inode(mdsc, realm->ino); 1340c44a8e0SLuis Henriques if (!qri) 1350c44a8e0SLuis Henriques return NULL; 1360c44a8e0SLuis Henriques 1370c44a8e0SLuis Henriques mutex_lock(&qri->mutex); 1382ef5df1aSYan, Zheng if (qri->inode && ceph_is_any_caps(qri->inode)) { 1390c44a8e0SLuis Henriques /* A request has already returned the inode */ 1400c44a8e0SLuis Henriques mutex_unlock(&qri->mutex); 1410c44a8e0SLuis Henriques return qri->inode; 1420c44a8e0SLuis Henriques } 1430c44a8e0SLuis Henriques /* Check if this inode lookup has failed recently */ 1440c44a8e0SLuis Henriques if (qri->timeout && 1450c44a8e0SLuis Henriques time_before_eq(jiffies, qri->timeout)) { 1460c44a8e0SLuis Henriques mutex_unlock(&qri->mutex); 1470c44a8e0SLuis Henriques return NULL; 1480c44a8e0SLuis Henriques } 1492ef5df1aSYan, Zheng if (qri->inode) { 1502ef5df1aSYan, Zheng /* get caps */ 1512ef5df1aSYan, Zheng int ret = __ceph_do_getattr(qri->inode, NULL, 1522ef5df1aSYan, Zheng CEPH_STAT_CAP_INODE, true); 1532ef5df1aSYan, Zheng if (ret >= 0) 1542ef5df1aSYan, Zheng in = qri->inode; 1552ef5df1aSYan, Zheng else 1562ef5df1aSYan, Zheng in = ERR_PTR(ret); 1572ef5df1aSYan, Zheng } else { 1580c44a8e0SLuis Henriques in = ceph_lookup_inode(sb, realm->ino); 1592ef5df1aSYan, Zheng } 1602ef5df1aSYan, Zheng 1610c44a8e0SLuis Henriques if (IS_ERR(in)) { 1620c44a8e0SLuis Henriques pr_warn("Can't lookup inode %llx (err: %ld)\n", 1630c44a8e0SLuis Henriques realm->ino, PTR_ERR(in)); 1640c44a8e0SLuis Henriques qri->timeout = jiffies + msecs_to_jiffies(60 * 1000); /* XXX */ 1650c44a8e0SLuis Henriques } else { 1660c44a8e0SLuis Henriques qri->timeout = 0; 1670c44a8e0SLuis Henriques qri->inode = in; 1680c44a8e0SLuis Henriques } 1690c44a8e0SLuis Henriques mutex_unlock(&qri->mutex); 1700c44a8e0SLuis Henriques 1710c44a8e0SLuis Henriques return in; 1720c44a8e0SLuis Henriques } 1730c44a8e0SLuis Henriques 1740c44a8e0SLuis Henriques void ceph_cleanup_quotarealms_inodes(struct ceph_mds_client *mdsc) 1750c44a8e0SLuis Henriques { 1760c44a8e0SLuis Henriques struct ceph_quotarealm_inode *qri; 1770c44a8e0SLuis Henriques struct rb_node *node; 1780c44a8e0SLuis Henriques 1790c44a8e0SLuis Henriques /* 1800c44a8e0SLuis Henriques * It should now be safe to clean quotarealms_inode tree without holding 1810c44a8e0SLuis Henriques * mdsc->quotarealms_inodes_mutex... 1820c44a8e0SLuis Henriques */ 1830c44a8e0SLuis Henriques mutex_lock(&mdsc->quotarealms_inodes_mutex); 1840c44a8e0SLuis Henriques while (!RB_EMPTY_ROOT(&mdsc->quotarealms_inodes)) { 1850c44a8e0SLuis Henriques node = rb_first(&mdsc->quotarealms_inodes); 1860c44a8e0SLuis Henriques qri = rb_entry(node, struct ceph_quotarealm_inode, node); 1870c44a8e0SLuis Henriques rb_erase(node, &mdsc->quotarealms_inodes); 1880c44a8e0SLuis Henriques iput(qri->inode); 1890c44a8e0SLuis Henriques kfree(qri); 1900c44a8e0SLuis Henriques } 1910c44a8e0SLuis Henriques mutex_unlock(&mdsc->quotarealms_inodes_mutex); 1920c44a8e0SLuis Henriques } 1930c44a8e0SLuis Henriques 194cafe21a4SLuis Henriques /* 195cafe21a4SLuis Henriques * This function walks through the snaprealm for an inode and returns the 196cafe21a4SLuis Henriques * ceph_snap_realm for the first snaprealm that has quotas set (either max_files 197cafe21a4SLuis Henriques * or max_bytes). If the root is reached, return the root ceph_snap_realm 198cafe21a4SLuis Henriques * instead. 199cafe21a4SLuis Henriques * 200cafe21a4SLuis Henriques * Note that the caller is responsible for calling ceph_put_snap_realm() on the 201cafe21a4SLuis Henriques * returned realm. 2020c44a8e0SLuis Henriques * 2030c44a8e0SLuis Henriques * Callers of this function need to hold mdsc->snap_rwsem. However, if there's 2040c44a8e0SLuis Henriques * a need to do an inode lookup, this rwsem will be temporarily dropped. Hence 2050c44a8e0SLuis Henriques * the 'retry' argument: if rwsem needs to be dropped and 'retry' is 'false' 2060c44a8e0SLuis Henriques * this function will return -EAGAIN; otherwise, the snaprealms walk-through 2070c44a8e0SLuis Henriques * will be restarted. 208cafe21a4SLuis Henriques */ 209cafe21a4SLuis Henriques static struct ceph_snap_realm *get_quota_realm(struct ceph_mds_client *mdsc, 2100c44a8e0SLuis Henriques struct inode *inode, bool retry) 211cafe21a4SLuis Henriques { 212cafe21a4SLuis Henriques struct ceph_inode_info *ci = NULL; 213cafe21a4SLuis Henriques struct ceph_snap_realm *realm, *next; 214cafe21a4SLuis Henriques struct inode *in; 2150eb6bbe4SYan, Zheng bool has_quota; 216cafe21a4SLuis Henriques 21725963669SYan, Zheng if (ceph_snap(inode) != CEPH_NOSNAP) 21825963669SYan, Zheng return NULL; 21925963669SYan, Zheng 2200c44a8e0SLuis Henriques restart: 221cafe21a4SLuis Henriques realm = ceph_inode(inode)->i_snap_realm; 22225963669SYan, Zheng if (realm) 223cafe21a4SLuis Henriques ceph_get_snap_realm(mdsc, realm); 22425963669SYan, Zheng else 22525963669SYan, Zheng pr_err_ratelimited("get_quota_realm: ino (%llx.%llx) " 22625963669SYan, Zheng "null i_snap_realm\n", ceph_vinop(inode)); 227cafe21a4SLuis Henriques while (realm) { 2280c44a8e0SLuis Henriques bool has_inode; 2290c44a8e0SLuis Henriques 230e3161f17SLuis Henriques spin_lock(&realm->inodes_with_caps_lock); 2310c44a8e0SLuis Henriques has_inode = realm->inode; 2320c44a8e0SLuis Henriques in = has_inode ? igrab(realm->inode) : NULL; 233e3161f17SLuis Henriques spin_unlock(&realm->inodes_with_caps_lock); 2340c44a8e0SLuis Henriques if (has_inode && !in) 235cafe21a4SLuis Henriques break; 2360c44a8e0SLuis Henriques if (!in) { 2370c44a8e0SLuis Henriques up_read(&mdsc->snap_rwsem); 2380c44a8e0SLuis Henriques in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm); 2390c44a8e0SLuis Henriques down_read(&mdsc->snap_rwsem); 2400c44a8e0SLuis Henriques if (IS_ERR_OR_NULL(in)) 2410c44a8e0SLuis Henriques break; 2420c44a8e0SLuis Henriques ceph_put_snap_realm(mdsc, realm); 2430c44a8e0SLuis Henriques if (!retry) 2440c44a8e0SLuis Henriques return ERR_PTR(-EAGAIN); 2450c44a8e0SLuis Henriques goto restart; 2460c44a8e0SLuis Henriques } 247e3161f17SLuis Henriques 248cafe21a4SLuis Henriques ci = ceph_inode(in); 249d557c48dSLuis Henriques has_quota = __ceph_has_any_quota(ci); 2503e1d0452SYan, Zheng /* avoid calling iput_final() while holding mdsc->snap_rwsem */ 2513e1d0452SYan, Zheng ceph_async_iput(in); 2520eb6bbe4SYan, Zheng 253cafe21a4SLuis Henriques next = realm->parent; 2540eb6bbe4SYan, Zheng if (has_quota || !next) 2550eb6bbe4SYan, Zheng return realm; 2560eb6bbe4SYan, Zheng 257cafe21a4SLuis Henriques ceph_get_snap_realm(mdsc, next); 258cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, realm); 259cafe21a4SLuis Henriques realm = next; 260cafe21a4SLuis Henriques } 261cafe21a4SLuis Henriques if (realm) 262cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, realm); 263cafe21a4SLuis Henriques 264cafe21a4SLuis Henriques return NULL; 265cafe21a4SLuis Henriques } 266cafe21a4SLuis Henriques 267cafe21a4SLuis Henriques bool ceph_quota_is_same_realm(struct inode *old, struct inode *new) 268cafe21a4SLuis Henriques { 269cafe21a4SLuis Henriques struct ceph_mds_client *mdsc = ceph_inode_to_client(old)->mdsc; 270cafe21a4SLuis Henriques struct ceph_snap_realm *old_realm, *new_realm; 271cafe21a4SLuis Henriques bool is_same; 272cafe21a4SLuis Henriques 2730c44a8e0SLuis Henriques restart: 2740c44a8e0SLuis Henriques /* 2750c44a8e0SLuis Henriques * We need to lookup 2 quota realms atomically, i.e. with snap_rwsem. 2760c44a8e0SLuis Henriques * However, get_quota_realm may drop it temporarily. By setting the 2770c44a8e0SLuis Henriques * 'retry' parameter to 'false', we'll get -EAGAIN if the rwsem was 2780c44a8e0SLuis Henriques * dropped and we can then restart the whole operation. 2790c44a8e0SLuis Henriques */ 280cafe21a4SLuis Henriques down_read(&mdsc->snap_rwsem); 2810c44a8e0SLuis Henriques old_realm = get_quota_realm(mdsc, old, true); 2820c44a8e0SLuis Henriques new_realm = get_quota_realm(mdsc, new, false); 2830c44a8e0SLuis Henriques if (PTR_ERR(new_realm) == -EAGAIN) { 2840c44a8e0SLuis Henriques up_read(&mdsc->snap_rwsem); 2850c44a8e0SLuis Henriques if (old_realm) 2860c44a8e0SLuis Henriques ceph_put_snap_realm(mdsc, old_realm); 2870c44a8e0SLuis Henriques goto restart; 2880c44a8e0SLuis Henriques } 289cafe21a4SLuis Henriques is_same = (old_realm == new_realm); 290cafe21a4SLuis Henriques up_read(&mdsc->snap_rwsem); 291cafe21a4SLuis Henriques 292cafe21a4SLuis Henriques if (old_realm) 293cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, old_realm); 294cafe21a4SLuis Henriques if (new_realm) 295cafe21a4SLuis Henriques ceph_put_snap_realm(mdsc, new_realm); 296cafe21a4SLuis Henriques 297cafe21a4SLuis Henriques return is_same; 298cafe21a4SLuis Henriques } 299cafe21a4SLuis Henriques 300b7a29217SLuis Henriques enum quota_check_op { 3012b83845fSLuis Henriques QUOTA_CHECK_MAX_FILES_OP, /* check quota max_files limit */ 3021ab302a0SLuis Henriques QUOTA_CHECK_MAX_BYTES_OP, /* check quota max_files limit */ 3031ab302a0SLuis Henriques QUOTA_CHECK_MAX_BYTES_APPROACHING_OP /* check if quota max_files 3041ab302a0SLuis Henriques limit is approaching */ 305b7a29217SLuis Henriques }; 306b7a29217SLuis Henriques 307b7a29217SLuis Henriques /* 308b7a29217SLuis Henriques * check_quota_exceeded() will walk up the snaprealm hierarchy and, for each 309b7a29217SLuis Henriques * realm, it will execute quota check operation defined by the 'op' parameter. 310b7a29217SLuis Henriques * The snaprealm walk is interrupted if the quota check detects that the quota 311b7a29217SLuis Henriques * is exceeded or if the root inode is reached. 312b7a29217SLuis Henriques */ 313b7a29217SLuis Henriques static bool check_quota_exceeded(struct inode *inode, enum quota_check_op op, 314b7a29217SLuis Henriques loff_t delta) 315b7a29217SLuis Henriques { 316b7a29217SLuis Henriques struct ceph_mds_client *mdsc = ceph_inode_to_client(inode)->mdsc; 317b7a29217SLuis Henriques struct ceph_inode_info *ci; 318b7a29217SLuis Henriques struct ceph_snap_realm *realm, *next; 319b7a29217SLuis Henriques struct inode *in; 320b7a29217SLuis Henriques u64 max, rvalue; 321b7a29217SLuis Henriques bool exceeded = false; 322b7a29217SLuis Henriques 32325963669SYan, Zheng if (ceph_snap(inode) != CEPH_NOSNAP) 32425963669SYan, Zheng return false; 32525963669SYan, Zheng 326b7a29217SLuis Henriques down_read(&mdsc->snap_rwsem); 3270c44a8e0SLuis Henriques restart: 328b7a29217SLuis Henriques realm = ceph_inode(inode)->i_snap_realm; 32925963669SYan, Zheng if (realm) 330b7a29217SLuis Henriques ceph_get_snap_realm(mdsc, realm); 33125963669SYan, Zheng else 33225963669SYan, Zheng pr_err_ratelimited("check_quota_exceeded: ino (%llx.%llx) " 33325963669SYan, Zheng "null i_snap_realm\n", ceph_vinop(inode)); 334b7a29217SLuis Henriques while (realm) { 3350c44a8e0SLuis Henriques bool has_inode; 336e3161f17SLuis Henriques 3370c44a8e0SLuis Henriques spin_lock(&realm->inodes_with_caps_lock); 3380c44a8e0SLuis Henriques has_inode = realm->inode; 3390c44a8e0SLuis Henriques in = has_inode ? igrab(realm->inode) : NULL; 3400c44a8e0SLuis Henriques spin_unlock(&realm->inodes_with_caps_lock); 3410c44a8e0SLuis Henriques if (has_inode && !in) 3420c44a8e0SLuis Henriques break; 3430c44a8e0SLuis Henriques if (!in) { 3440c44a8e0SLuis Henriques up_read(&mdsc->snap_rwsem); 3450c44a8e0SLuis Henriques in = lookup_quotarealm_inode(mdsc, inode->i_sb, realm); 3460c44a8e0SLuis Henriques down_read(&mdsc->snap_rwsem); 3470c44a8e0SLuis Henriques if (IS_ERR_OR_NULL(in)) 3480c44a8e0SLuis Henriques break; 3490c44a8e0SLuis Henriques ceph_put_snap_realm(mdsc, realm); 3500c44a8e0SLuis Henriques goto restart; 3510c44a8e0SLuis Henriques } 352b7a29217SLuis Henriques ci = ceph_inode(in); 353b7a29217SLuis Henriques spin_lock(&ci->i_ceph_lock); 354b7a29217SLuis Henriques if (op == QUOTA_CHECK_MAX_FILES_OP) { 355b7a29217SLuis Henriques max = ci->i_max_files; 356b7a29217SLuis Henriques rvalue = ci->i_rfiles + ci->i_rsubdirs; 3572b83845fSLuis Henriques } else { 3582b83845fSLuis Henriques max = ci->i_max_bytes; 3592b83845fSLuis Henriques rvalue = ci->i_rbytes; 360b7a29217SLuis Henriques } 361b7a29217SLuis Henriques spin_unlock(&ci->i_ceph_lock); 362b7a29217SLuis Henriques switch (op) { 363b7a29217SLuis Henriques case QUOTA_CHECK_MAX_FILES_OP: 364b7a29217SLuis Henriques exceeded = (max && (rvalue >= max)); 365b7a29217SLuis Henriques break; 3662b83845fSLuis Henriques case QUOTA_CHECK_MAX_BYTES_OP: 3672b83845fSLuis Henriques exceeded = (max && (rvalue + delta > max)); 3682b83845fSLuis Henriques break; 3691ab302a0SLuis Henriques case QUOTA_CHECK_MAX_BYTES_APPROACHING_OP: 3701ab302a0SLuis Henriques if (max) { 3711ab302a0SLuis Henriques if (rvalue >= max) 3721ab302a0SLuis Henriques exceeded = true; 3731ab302a0SLuis Henriques else { 3741ab302a0SLuis Henriques /* 3751ab302a0SLuis Henriques * when we're writing more that 1/16th 3761ab302a0SLuis Henriques * of the available space 3771ab302a0SLuis Henriques */ 3781ab302a0SLuis Henriques exceeded = 3791ab302a0SLuis Henriques (((max - rvalue) >> 4) < delta); 3801ab302a0SLuis Henriques } 3811ab302a0SLuis Henriques } 3821ab302a0SLuis Henriques break; 383b7a29217SLuis Henriques default: 384b7a29217SLuis Henriques /* Shouldn't happen */ 385b7a29217SLuis Henriques pr_warn("Invalid quota check op (%d)\n", op); 386b7a29217SLuis Henriques exceeded = true; /* Just break the loop */ 387b7a29217SLuis Henriques } 3883e1d0452SYan, Zheng /* avoid calling iput_final() while holding mdsc->snap_rwsem */ 3893e1d0452SYan, Zheng ceph_async_iput(in); 390b7a29217SLuis Henriques 391b7a29217SLuis Henriques next = realm->parent; 3920eb6bbe4SYan, Zheng if (exceeded || !next) 3930eb6bbe4SYan, Zheng break; 394b7a29217SLuis Henriques ceph_get_snap_realm(mdsc, next); 395b7a29217SLuis Henriques ceph_put_snap_realm(mdsc, realm); 396b7a29217SLuis Henriques realm = next; 397b7a29217SLuis Henriques } 39871f2cc64SLuis Henriques if (realm) 399b7a29217SLuis Henriques ceph_put_snap_realm(mdsc, realm); 400b7a29217SLuis Henriques up_read(&mdsc->snap_rwsem); 401b7a29217SLuis Henriques 402b7a29217SLuis Henriques return exceeded; 403b7a29217SLuis Henriques } 404b7a29217SLuis Henriques 405b7a29217SLuis Henriques /* 406b7a29217SLuis Henriques * ceph_quota_is_max_files_exceeded - check if we can create a new file 407b7a29217SLuis Henriques * @inode: directory where a new file is being created 408b7a29217SLuis Henriques * 409b7a29217SLuis Henriques * This functions returns true is max_files quota allows a new file to be 410b7a29217SLuis Henriques * created. It is necessary to walk through the snaprealm hierarchy (until the 411b7a29217SLuis Henriques * FS root) to check all realms with quotas set. 412b7a29217SLuis Henriques */ 413b7a29217SLuis Henriques bool ceph_quota_is_max_files_exceeded(struct inode *inode) 414b7a29217SLuis Henriques { 415d557c48dSLuis Henriques if (!ceph_has_realms_with_quotas(inode)) 416d557c48dSLuis Henriques return false; 417d557c48dSLuis Henriques 418b7a29217SLuis Henriques WARN_ON(!S_ISDIR(inode->i_mode)); 419b7a29217SLuis Henriques 420b7a29217SLuis Henriques return check_quota_exceeded(inode, QUOTA_CHECK_MAX_FILES_OP, 0); 421b7a29217SLuis Henriques } 4222b83845fSLuis Henriques 4232b83845fSLuis Henriques /* 4242b83845fSLuis Henriques * ceph_quota_is_max_bytes_exceeded - check if we can write to a file 4252b83845fSLuis Henriques * @inode: inode being written 4262b83845fSLuis Henriques * @newsize: new size if write succeeds 4272b83845fSLuis Henriques * 4282b83845fSLuis Henriques * This functions returns true is max_bytes quota allows a file size to reach 4292b83845fSLuis Henriques * @newsize; it returns false otherwise. 4302b83845fSLuis Henriques */ 4312b83845fSLuis Henriques bool ceph_quota_is_max_bytes_exceeded(struct inode *inode, loff_t newsize) 4322b83845fSLuis Henriques { 4332b83845fSLuis Henriques loff_t size = i_size_read(inode); 4342b83845fSLuis Henriques 435d557c48dSLuis Henriques if (!ceph_has_realms_with_quotas(inode)) 436d557c48dSLuis Henriques return false; 437d557c48dSLuis Henriques 4382b83845fSLuis Henriques /* return immediately if we're decreasing file size */ 4392b83845fSLuis Henriques if (newsize <= size) 4402b83845fSLuis Henriques return false; 4412b83845fSLuis Henriques 4422b83845fSLuis Henriques return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_OP, (newsize - size)); 4432b83845fSLuis Henriques } 4441ab302a0SLuis Henriques 4451ab302a0SLuis Henriques /* 4461ab302a0SLuis Henriques * ceph_quota_is_max_bytes_approaching - check if we're reaching max_bytes 4471ab302a0SLuis Henriques * @inode: inode being written 4481ab302a0SLuis Henriques * @newsize: new size if write succeeds 4491ab302a0SLuis Henriques * 4501ab302a0SLuis Henriques * This function returns true if the new file size @newsize will be consuming 4511ab302a0SLuis Henriques * more than 1/16th of the available quota space; it returns false otherwise. 4521ab302a0SLuis Henriques */ 4531ab302a0SLuis Henriques bool ceph_quota_is_max_bytes_approaching(struct inode *inode, loff_t newsize) 4541ab302a0SLuis Henriques { 4551ab302a0SLuis Henriques loff_t size = ceph_inode(inode)->i_reported_size; 4561ab302a0SLuis Henriques 457d557c48dSLuis Henriques if (!ceph_has_realms_with_quotas(inode)) 458d557c48dSLuis Henriques return false; 459d557c48dSLuis Henriques 4601ab302a0SLuis Henriques /* return immediately if we're decreasing file size */ 4611ab302a0SLuis Henriques if (newsize <= size) 4621ab302a0SLuis Henriques return false; 4631ab302a0SLuis Henriques 4641ab302a0SLuis Henriques return check_quota_exceeded(inode, QUOTA_CHECK_MAX_BYTES_APPROACHING_OP, 4651ab302a0SLuis Henriques (newsize - size)); 4661ab302a0SLuis Henriques } 4679122eed5SLuis Henriques 4689122eed5SLuis Henriques /* 4699122eed5SLuis Henriques * ceph_quota_update_statfs - if root has quota update statfs with quota status 4709122eed5SLuis Henriques * @fsc: filesystem client instance 4719122eed5SLuis Henriques * @buf: statfs to update 4729122eed5SLuis Henriques * 4739122eed5SLuis Henriques * If the mounted filesystem root has max_bytes quota set, update the filesystem 4749122eed5SLuis Henriques * statistics with the quota status. 4759122eed5SLuis Henriques * 4769122eed5SLuis Henriques * This function returns true if the stats have been updated, false otherwise. 4779122eed5SLuis Henriques */ 4789122eed5SLuis Henriques bool ceph_quota_update_statfs(struct ceph_fs_client *fsc, struct kstatfs *buf) 4799122eed5SLuis Henriques { 4809122eed5SLuis Henriques struct ceph_mds_client *mdsc = fsc->mdsc; 4819122eed5SLuis Henriques struct ceph_inode_info *ci; 4829122eed5SLuis Henriques struct ceph_snap_realm *realm; 4839122eed5SLuis Henriques struct inode *in; 4849122eed5SLuis Henriques u64 total = 0, used, free; 4859122eed5SLuis Henriques bool is_updated = false; 4869122eed5SLuis Henriques 4879122eed5SLuis Henriques down_read(&mdsc->snap_rwsem); 4880c44a8e0SLuis Henriques realm = get_quota_realm(mdsc, d_inode(fsc->sb->s_root), true); 4899122eed5SLuis Henriques up_read(&mdsc->snap_rwsem); 4909122eed5SLuis Henriques if (!realm) 4919122eed5SLuis Henriques return false; 4929122eed5SLuis Henriques 4939122eed5SLuis Henriques spin_lock(&realm->inodes_with_caps_lock); 4949122eed5SLuis Henriques in = realm->inode ? igrab(realm->inode) : NULL; 4959122eed5SLuis Henriques spin_unlock(&realm->inodes_with_caps_lock); 4969122eed5SLuis Henriques if (in) { 4979122eed5SLuis Henriques ci = ceph_inode(in); 4989122eed5SLuis Henriques spin_lock(&ci->i_ceph_lock); 4999122eed5SLuis Henriques if (ci->i_max_bytes) { 5009122eed5SLuis Henriques total = ci->i_max_bytes >> CEPH_BLOCK_SHIFT; 5019122eed5SLuis Henriques used = ci->i_rbytes >> CEPH_BLOCK_SHIFT; 5029122eed5SLuis Henriques /* It is possible for a quota to be exceeded. 5039122eed5SLuis Henriques * Report 'zero' in that case 5049122eed5SLuis Henriques */ 5059122eed5SLuis Henriques free = total > used ? total - used : 0; 5069122eed5SLuis Henriques } 5079122eed5SLuis Henriques spin_unlock(&ci->i_ceph_lock); 5089122eed5SLuis Henriques if (total) { 5099122eed5SLuis Henriques buf->f_blocks = total; 5109122eed5SLuis Henriques buf->f_bfree = free; 5119122eed5SLuis Henriques buf->f_bavail = free; 5129122eed5SLuis Henriques is_updated = true; 5139122eed5SLuis Henriques } 5149122eed5SLuis Henriques iput(in); 5159122eed5SLuis Henriques } 5169122eed5SLuis Henriques ceph_put_snap_realm(mdsc, realm); 5179122eed5SLuis Henriques 5189122eed5SLuis Henriques return is_updated; 5199122eed5SLuis Henriques } 5209122eed5SLuis Henriques 521