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>
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 *,
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 */
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;
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 */
/*
* 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 */
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;
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;
* 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;
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
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);
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);
#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);
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;
}
(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 */
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;
};
}
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 },
.fops = &qsd_force_reint_fops },
{ .name = "timeout",
.fops = &qsd_timeout_fops },
+ { .name = "root_prj_enable",
+ .fops = &qsd_root_prj_enable_fops },
{ NULL }
};
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
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"
}
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"
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"
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"