Whamcloud - gitweb
LU-16415 quota: enforce project quota for root 60/49460/7
authorSergey Cheremencev <scherementsev@ddn.com>
Sat, 17 Dec 2022 21:42:10 +0000 (01:42 +0400)
committerOleg Drokin <green@whamcloud.com>
Fri, 27 Jan 2023 00:35:01 +0000 (00:35 +0000)
Patch adds an option to enforce project quotas for root.
It is disabled by default, to enable set
osd-ldiskfs.*.quota_slave.root_prj_enable to 1
at each target where you need this option.

Patch also adds sanity-quota_1j to test new feature.

Signed-off-by: Sergey Cheremencev <scherementsev@ddn.com>
Change-Id: I978dc8442235149794f85110309f63bc560defdc
Reviewed-on: https://review.whamcloud.com/c/fs/lustre-release/+/49460
Reviewed-by: Hongchao Zhang <hongchao@whamcloud.com>
Reviewed-by: Sebastien Buisson <sbuisson@ddn.com>
Reviewed-by: Oleg Drokin <green@whamcloud.com>
Reviewed-by: Andreas Dilger <adilger@whamcloud.com>
Tested-by: jenkins <devops@whamcloud.com>
Tested-by: Shuichi Ihara <sihara@ddn.com>
Tested-by: Maloo <maloo@whamcloud.com>
15 files changed:
lustre/include/lustre_quota.h
lustre/include/obd.h
lustre/include/uapi/linux/lustre/lustre_idl.h
lustre/mdt/mdt_io.c
lustre/ofd/ofd_io.c
lustre/osc/osc_cache.c
lustre/osc/osc_quota.c
lustre/osd-ldiskfs/osd_io.c
lustre/osd-ldiskfs/osd_quota.c
lustre/osd-zfs/osd_io.c
lustre/osd-zfs/osd_quota.c
lustre/quota/qsd_handler.c
lustre/quota/qsd_internal.h
lustre/quota/qsd_lib.c
lustre/tests/sanity-quota.sh

index 58d3b88..edb8fd6 100644 (file)
@@ -181,6 +181,7 @@ enum osd_quota_local_flags {
        QUOTA_FL_OVER_GRPQUOTA  = BIT(1),
        QUOTA_FL_SYNC           = BIT(2),
        QUOTA_FL_OVER_PRJQUOTA  = BIT(3),
+       QUOTA_FL_ROOT_PRJQUOTA  = BIT(4),
 };
 
 struct qsd_instance *qsd_init(const struct lu_env *, char *, struct dt_device *,
index 47a6df7..5c899dd 100644 (file)
@@ -209,7 +209,10 @@ struct client_obd {
        unsigned int             cl_checksum:1, /* 0 = disabled, 1 = enabled */
                                 cl_checksum_dump:1, /* same */
                                 cl_ocd_grant_param:1,
-                                cl_lsom_update:1; /* send LSOM updates */
+                                cl_lsom_update:1, /* send LSOM updates */
+                                cl_root_squash:1, /* if root squash enabled*/
+                                /* check prj quota for root */
+                                cl_root_prjquota:1;
        enum lustre_sec_part     cl_sp_me;
        enum lustre_sec_part     cl_sp_to;
        struct sptlrpc_flavor    cl_flvr_mgc; /* fixed flavor of mgc->mgs */
@@ -232,8 +235,6 @@ struct client_obd {
        struct list_head        cl_grant_chain;
        time64_t                cl_grant_shrink_interval; /* seconds */
 
-       int                     cl_root_squash; /* if root squash enabled*/
-
        /* A chunk is an optimal size used by osc_extent to determine
         * the extent size. A chunk is max(PAGE_SIZE, OST block size) */
        int                     cl_chunkbits;
index 18da355..24f0da9 100644 (file)
@@ -1102,6 +1102,7 @@ enum obdo_flags {
        OBD_FL_FLUSH        = 0x00200000, /* flush pages on the OST */
        OBD_FL_SHORT_IO     = 0x00400000, /* short io request */
        OBD_FL_ROOT_SQUASH  = 0x00800000, /* root squash */
+       OBD_FL_ROOT_PRJQUOTA  = 0x01000000, /* check prj quota for root */
        /* OBD_FL_LOCAL_MASK = 0xF0000000, was local-only flags until 2.10 */
 
        /*
@@ -1397,6 +1398,7 @@ struct hsm_state_set {
                                      * space for unstable pages; asking
                                      * it to sync quickly */
 #define OBD_BRW_OVER_PRJQUOTA 0x8000 /* Running out of project quota */
+#define OBD_BRW_ROOT_PRJQUOTA 0x10000 /* check project quota for root */
 #define OBD_BRW_RDMA_ONLY    0x20000 /* RPC contains RDMA-only pages*/
 #define OBD_BRW_SYS_RESOURCE 0x40000 /* page has CAP_SYS_RESOURCE */
 
index f766315..fa72341 100644 (file)
@@ -842,6 +842,9 @@ int mdt_obd_commitrw(const struct lu_env *env, int cmd, struct obd_export *exp,
                                        oa->o_flags = OBD_FL_NO_PRJQUOTA;
                        }
 
+                       if (lnb[0].lnb_flags & OBD_BRW_ROOT_PRJQUOTA)
+                               oa->o_flags |= OBD_FL_ROOT_PRJQUOTA;
+
                        if (root_squash)
                                oa->o_flags |= OBD_FL_ROOT_SQUASH;
 
index dbf6a22..9b6981f 100644 (file)
@@ -1545,6 +1545,9 @@ int ofd_commitrw(const struct lu_env *env, int cmd, struct obd_export *exp,
                                        oa->o_flags = OBD_FL_NO_PRJQUOTA;
                        }
 
+                       if (lnb[0].lnb_flags & OBD_BRW_ROOT_PRJQUOTA)
+                               oa->o_flags |= OBD_FL_ROOT_PRJQUOTA;
+
                        if (root_squash)
                                oa->o_flags |= OBD_FL_ROOT_SQUASH;
 
index 663e749..20765d7 100644 (file)
@@ -2302,7 +2302,7 @@ int osc_queue_async_io(const struct lu_env *env, struct cl_io *io,
         * we should bypass quota
         */
        if ((!oio->oi_cap_sys_resource ||
-            cli->cl_root_squash) &&
+            cli->cl_root_squash || cli->cl_root_prjquota) &&
            !io->ci_noquota) {
                struct cl_object *obj;
                struct cl_attr   *attr;
index 0f07952..e1606c5 100644 (file)
@@ -109,6 +109,7 @@ int osc_quota_setdq(struct client_obd *cli, __u64 xid, const unsigned int qid[],
 
        mutex_lock(&cli->cl_quota_mutex);
        cli->cl_root_squash = !!(flags & OBD_FL_ROOT_SQUASH);
+       cli->cl_root_prjquota = !!(flags & OBD_FL_ROOT_PRJQUOTA);
        /* still mark the quots is running out for the old request, because it
         * could be processed after the new request at OST, the side effect is
         * the following request will be processed synchronously, but it will
index 366312b..dbaee9e 100644 (file)
@@ -1655,6 +1655,8 @@ out_declare:
                lnb[0].lnb_flags |= OBD_BRW_OVER_GRPQUOTA;
        if (local_flags & QUOTA_FL_OVER_PRJQUOTA)
                lnb[0].lnb_flags |= OBD_BRW_OVER_PRJQUOTA;
+       if (local_flags & QUOTA_FL_ROOT_PRJQUOTA)
+               lnb[0].lnb_flags |= OBD_BRW_ROOT_PRJQUOTA;
 
        if (rc == 0)
                rc = osd_trunc_lock(osd_dt_obj(dt), oh, true);
index b28338c..75a26ac 100644 (file)
@@ -696,6 +696,8 @@ int osd_declare_inode_qid(const struct lu_env *env, qid_t uid, qid_t gid,
        qi->lqi_type = PRJQUOTA;
        rcp = osd_declare_qid(env, oh, qi, obj, true, local_flags);
 
+       if (local_flags && *local_flags & QUOTA_FL_ROOT_PRJQUOTA)
+               force = th->th_ignore_quota;
        if (force && (rcp == -EDQUOT || rcp == -EINPROGRESS)) {
                CDEBUG(D_QUOTA, "forced to ignore quota flags = %#x\n",
                       local_flags ? *local_flags : -1);
index 74644c9..018911b 100644 (file)
@@ -742,6 +742,8 @@ retry:
 #ifdef ZFS_PROJINHERIT
        if (local_flags & QUOTA_FL_OVER_PRJQUOTA)
                lnb[0].lnb_flags |= OBD_BRW_OVER_PRJQUOTA;
+       if (local_flags & QUOTA_FL_ROOT_PRJQUOTA)
+               lnb[0].lnb_flags |= OBD_BRW_ROOT_PRJQUOTA;
 #endif
 
        RETURN(rc);
index 7296f3e..708a4b6 100644 (file)
@@ -579,6 +579,8 @@ int osd_declare_quota(const struct lu_env *env, struct osd_device *osd,
                qi->lqi_type = PRJQUOTA;
                rcp = qsd_op_begin(env, qsd, &oh->ot_quota_trans, qi,
                                   local_flags);
+               if (local_flags && *local_flags & QUOTA_FL_ROOT_PRJQUOTA)
+                       force = th->th_ignore_quota;
                if (force && (rcp == -EDQUOT || rcp == -EINPROGRESS))
                        rcp = 0;
        }
index 01433b6..1602e59 100644 (file)
@@ -888,6 +888,9 @@ int qsd_op_begin(const struct lu_env *env, struct qsd_instance *qsd,
            (qsd->qsd_type_array[qi->lqi_type])->qqi_acct_failed)
                RETURN(0);
 
+       if (local_flags && qi->lqi_id.qid_projid && qsd->qsd_root_prj_enable)
+               *local_flags |= QUOTA_FL_ROOT_PRJQUOTA;
+
        LASSERTF(trans->lqt_id_cnt <= QUOTA_MAX_TRANSIDS, "id_cnt=%d\n",
                 trans->lqt_id_cnt);
        /* check whether we already allocated a slot for this id */
index b590b84..b530943 100644 (file)
@@ -113,7 +113,8 @@ struct qsd_instance {
                                qsd_exp_valid:1,/* qsd_exp is now valid */
                                qsd_stopping:1, /* qsd_instance is stopping */
                                qsd_updating:1, /* qsd is updating record */
-                               qsd_exclusive:1; /* upd exclusive with reint */
+                               qsd_exclusive:1, /* upd exclusive with reint */
+                               qsd_root_prj_enable:1;
 
 };
 
index 6b827cb..e29a5a7 100644 (file)
@@ -267,6 +267,34 @@ qsd_timeout_seq_write(struct file *file, const char __user *buffer,
 }
 LPROC_SEQ_FOPS(qsd_timeout);
 
+static int qsd_root_prj_enable_seq_show(struct seq_file *m, void *data)
+{
+       struct qsd_instance *qsd = m->private;
+
+       LASSERT(qsd != NULL);
+       seq_printf(m, "%d\n", qsd->qsd_root_prj_enable);
+       return 0;
+}
+
+static ssize_t
+qsd_root_prj_enable_seq_write(struct file *file, const char __user *buffer,
+                       size_t count, loff_t *off)
+{
+       struct seq_file *m = file->private_data;
+       struct qsd_instance *qsd = m->private;
+       bool enable;
+       int rc;
+
+       LASSERT(qsd != NULL);
+       rc = kstrtobool_from_user(buffer, count, &enable);
+       if (rc)
+               return rc;
+
+       qsd->qsd_root_prj_enable = enable;
+       return count;
+}
+LPROC_SEQ_FOPS(qsd_root_prj_enable);
+
 static struct lprocfs_vars lprocfs_quota_qsd_vars[] = {
        { .name =       "info",
          .fops =       &qsd_state_fops         },
@@ -276,6 +304,8 @@ static struct lprocfs_vars lprocfs_quota_qsd_vars[] = {
          .fops =       &qsd_force_reint_fops   },
        { .name =       "timeout",
          .fops =       &qsd_timeout_fops       },
+       { .name =       "root_prj_enable",
+         .fops =       &qsd_root_prj_enable_fops       },
        { NULL }
 };
 
index e6955c3..7431d66 100755 (executable)
@@ -559,7 +559,7 @@ test_1_check_write() {
                quota_error $short_qtype $TSTUSR \
                        "$qtype write failure, but expect success"
        log "Write out of block quota ..."
-       # this time maybe cache write,  ignore it's failure
+       # this time maybe cache write, ignore it's failure
        $RUNAS $DD of=$testfile count=$((limit/2)) seek=$((limit/2)) || true
        # flush cache, ensure noquota flag is set on client
        cancel_lru_locks osc
@@ -1127,7 +1127,6 @@ test_1i() {
 
        mds_supports_qp
        setup_quota_test || error "setup quota failed with $?"
-       stack_trap cleanup_quota_test EXIT
 
        # enable ost quota
        set_ost_qtype $QTYPE || error "enable ost quota failed"
@@ -1186,6 +1185,54 @@ test_1i() {
 }
 run_test 1i "Quota pools: different limit and usage relations"
 
+test_1j() {
+       local limit=20 # MB
+       local testfile="$DIR/$tdir/$tfile-0"
+
+       (( $OST1_VERSION >= $(version_code 2.15.52.206) )) ||
+               skip "need OST at least 2.15.52.206"
+
+       is_project_quota_supported ||
+               skip "skip project quota unsupported"
+
+       setup_quota_test || error "setup quota failed with $?"
+
+       # enable ost quota
+       set_ost_qtype $QTYPE || error "enable ost quota failed"
+
+       # test for Project
+       log "--------------------------------------"
+       log "Project quota (block hardlimit:$limit mb)"
+       $LFS setquota -p $TSTPRJID -b 0 -B ${limit}M -i 0 -I 0 $DIR ||
+               error "set project quota failed"
+
+       $LFS setstripe $testfile -c 1 -i 0 || error "setstripe $testfile failed"
+       change_project -p $TSTPRJID $testfile
+
+       local procf=osd-$ost1_FSTYPE.$FSNAME-OST0000.quota_slave.root_prj_enable
+       do_facet ost1 $LCTL set_param $procf=1 ||
+               error "enable root quotas for project failed"
+       stack_trap "do_facet ost1 $LCTL set_param $procf=0"
+
+       runas -u 0 -g 0 $DD of=$testfile count=$limit oflag=direct || true
+       runas -u 0 -g 0 $DD of=$testfile count=$((limit/2)) seek=$limit oflag=direct &&
+               quota_error "project" $TSTPRJID "root write to project success"
+
+       do_facet ost1 $LCTL set_param $procf=0 ||
+               error "disable root quotas for project failed"
+
+       runas -u 0 -g 0 $DD of=$testfile count=$limit seek=$limit oflag=direct ||
+               quota_error "project" $TSTPRJID "root write to project failed"
+
+       # cleanup
+       cleanup_quota_test
+
+       used=$(getquota -p $TSTPRJID global curspace)
+       (( $used == 0 )) || quota_error p $TSTPRJID \
+               "project quota isn't released after deletion"
+}
+run_test 1j "Enable project quota enforcement for root"
+
 # test inode hardlimit
 test_2() {
        local testfile="$DIR/$tdir/$tfile-0"
@@ -2642,7 +2689,6 @@ test_16b()
        mount_client $MOUNT || error "Unable to mount client"
 
        setup_quota_test || error "setup quota failed with $?"
-       stack_trap cleanup_quota_test EXIT
 
        $LFS setquota -u $TSTUSR -B 100M -I 10K $MOUNT ||
                error "failed to set quota for user $TSTUSR"
@@ -5532,7 +5578,6 @@ test_82()
                skip "skip project quota unsupported"
 
        setup_quota_test || error "setup quota failed with $?"
-       stack_trap cleanup_quota_test
        quota_init
 
        local parent_dir="$DIR/$tdir.parent"