linux/fs/ceph/quota.c
<<
>>
Prefs
   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
 362