LC_QUOTA_READ
LC_COOKIE_FOLLOW_LINK
LC_FUNC_RCU
+ LC_QUOTA64
# does the kernel have VFS intent patches?
LC_VFS_INTENT_PATCHES
])
#
+# LC_QUOTA64
+# linux kernel may have 64-bit limits support
+#
+AC_DEFUN([LC_QUOTA64],
+[AC_MSG_CHECKING([if kernel has 64-bit quota limits support])
+LB_LINUX_TRY_COMPILE([
+ #include <linux/kernel.h>
+ #include <linux/fs.h>
+ #include <linux/quotaio_v2.h>
+ int versions[] = V2_INITQVERSIONS_R1;
+ struct v2_disk_dqblk_r1 dqblk_r1;
+],[],[
+ AC_DEFINE(HAVE_QUOTA64, 1, [have quota64])
+ AC_MSG_RESULT([yes])
+
+],[
+ AC_MSG_WARN([You have got no 64-bit kernel quota support.])
+ AC_MSG_WARN([Continuing with limited quota support.])
+ AC_MSG_WARN([quotacheck is needed for filesystems with recent quota versions.])
+ AC_MSG_RESULT([no])
+])
+])
+
+#
# LC_CONFIGURE
#
# other configure checks
.br
.B lfs quotaoff [-ug] <filesystem>
.br
-.B lfs quotainv [-ug] <filesystem>
+.B lfs quotainv [-ug] [-f] <filesystem>
.br
.B lfs setquota [-u|--user|-g|--group] <username|groupname>
\fB[--block-softlimit <block-softlimit>]
.B quotaoff [-ugf] <filesystem>
To turn filesystem quotas off. Options specify quota for users (-u) groups (-g) and force (-f)
.TP
-.B quotainv [-ug] <filesystem>
-Clear quota files, all of their quota entries, for (-u) users or (-g) groups; after quotainv one must use quotacheck before using quotas. USE THIS COMMAND WITH EXTREME CARE, ITS RESULTS CANNOT BE UNDONE.
+.B quotainv [-ug] [-f] <filesystem>
+Clear quota files (administrative quota files if used without -f, operational quota files otherwise), all of their quota entries, for (-u) users or (-g) groups; after quotainv one must use quotacheck before using quotas. DO NOT USE THIS COMMAND UNLESS YOU REALLY KNOW WHAT IT DOES. IT IS MAINLY FOR INTERNAL PURPOSES.
.TP
.B setquota [-u|-g] <name> [--block-softlimit <block-softlimit>] [--block-hardlimit <block-hardlimit>] [--inode-softlimit <inode-softlimit>] [--inode-hardlimit <inode-hardlimit>] <filesystem>
To set filesystem quotas for users or groups. Limits can be specified with -b, -k, -m, -g, -t, -p suffixes which specify units of 1, 2^10, 2^20, 2^30, 2^40 and 2^50 accordingly. Block limits unit is kilobyte (1024) by default and block limits are always kilobyte-grained (even if specified in bytes), see EXAMPLES
#define Q_INITQUOTA 0x800101 /* init slave limits */
#define Q_GETOINFO 0x800102 /* get obd quota info */
#define Q_GETOQUOTA 0x800103 /* get obd quotas */
+#define Q_FINVALIDATE 0x800104 /* invalidate operational quotas */
-#define Q_TYPESET(oqc, type) \
- ((oqc)->qc_type == type || (oqc)->qc_type == UGQUOTA)
+#define Q_TYPEMATCH(id, type) \
+ ((id) == (type) || (id) == UGQUOTA)
+
+#define Q_TYPESET(oqc, type) Q_TYPEMATCH((oqc)->qc_type, type)
#define Q_GETOCMD(oqc) \
((oqc)->qc_cmd == Q_GETOINFO || (oqc)->qc_cmd == Q_GETOQUOTA)
#define LUSTRE_Q_GETQUOTA 0x800007 /* get user quota structure */
#define LUSTRE_Q_SETQUOTA 0x800008 /* set user quota structure */
/* lustre-specific control commands */
-#define LUSTRE_Q_INVALIDATE 0x80000b /* invalidate quota data */
+#define LUSTRE_Q_INVALIDATE 0x80000b /* invalidate quota data */
+#define LUSTRE_Q_FINVALIDATE 0x80000c /* invalidate filter quota data */
#define UGQUOTA 2 /* set both USRQUOTA and GRPQUOTA */
-#define QFMT_LDISKFS 2 /* QFMT_VFS_V0(2), quota format for ldiskfs */
-
struct if_quotacheck {
char obd_type[16];
struct obd_uuid obd_uuid;
typedef int (*dqacq_handler_t) (struct obd_device * obd, struct qunit_data * qd,
int opc);
+
+/* user quota is turned on on filter */
+#define LQC_USRQUOTA_FLAG (1 << 0)
+/* group quota is turned on on filter */
+#define LQC_GRPQUOTA_FLAG (1 << 1)
+
+#define UGQUOTA2LQC(id) ((Q_TYPEMATCH(id, USRQUOTA) ? LQC_USRQUOTA_FLAG : 0) | \
+ (Q_TYPEMATCH(id, GRPQUOTA) ? LQC_GRPQUOTA_FLAG : 0))
+
struct lustre_quota_ctxt {
struct super_block *lqc_sb; /* superblock this applies to */
struct obd_import *lqc_import; /* import used to send dqacq/dqrel RPC */
dqacq_handler_t lqc_handler; /* dqacq/dqrel RPC handler, only for quota master */
+ unsigned long lqc_flags; /* quota flags */
unsigned long lqc_recovery:1, /* Doing recovery */
- lqc_atype:2, /* Turn on user/group quota at setup automatically,
- * 0: none, 1: user quota, 2: group quota, 3: both */
- lqc_status:1, /* Quota status. 0:Off, 1:On */
lqc_switch_qs:1; /* the function of change qunit size
* 0:Off, 1:On */
unsigned long lqc_iunit_sz; /* original unit size of file quota and
struct super_block *obt_sb;
atomic_t obt_quotachecking;
struct lustre_quota_ctxt obt_qctxt;
+ lustre_quota_version_t obt_qfmt;
};
typedef void (*obd_pin_extent_cb)(void *data);
id = qctl->qc_id;
switch (cmd) {
case LUSTRE_Q_INVALIDATE:
+ case LUSTRE_Q_FINVALIDATE:
case Q_QUOTAON:
case Q_QUOTAOFF:
case Q_SETQUOTA:
#include <linux/ext3_extents.h>
#endif
+#include "lustre_quota_fmt.h"
+
#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,15)
#define FSFILT_DATA_TRANS_BLOCKS(sb) EXT3_DATA_TRANS_BLOCKS
#define FSFILT_DELETE_TRANS_BLOCKS(sb) EXT3_DELETE_TRANS_BLOCKS
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,6)) && HAVE_QUOTA_SUPPORT
/* enable journaled quota support */
/* kfreed in ext3_put_super() */
- sbi->s_qf_names[USRQUOTA] = kstrdup("lquota.user", GFP_KERNEL);
+ sbi->s_qf_names[USRQUOTA] = kstrdup("lquota.user.reserved", GFP_KERNEL);
if (!sbi->s_qf_names[USRQUOTA])
return -ENOMEM;
- sbi->s_qf_names[GRPQUOTA] = kstrdup("lquota.group", GFP_KERNEL);
+ sbi->s_qf_names[GRPQUOTA] = kstrdup("lquota.group.reserved", GFP_KERNEL);
if (!sbi->s_qf_names[GRPQUOTA]) {
kfree(sbi->s_qf_names[USRQUOTA]);
sbi->s_qf_names[USRQUOTA] = NULL;
return 0;
}
-static const char *op_quotafile[] = { "lquota.user", "lquota.group" };
-
#define DQINFO_COPY(out, in) \
do { \
Q_COPY(out, in, dqi_bgrace); \
continue;
if (oqc->qc_cmd == Q_QUOTAON) {
+ lustre_quota_version_t qfmt = oqc->qc_id;
+ char *name[][MAXQUOTAS] = LUSTRE_OPQFILES_NAMES;
+
if (!qcop->quota_on)
GOTO(out, rc = -ENOSYS);
- rc = qcop->quota_on(sb, i, oqc->qc_id,
- (char *)op_quotafile[i]);
+
+ rc = qcop->quota_on(sb, i, QFMT_VFS_V0,
+ name[qfmt][i]);
+#ifdef HAVE_QUOTA64
+ if (rc == -ENOENT || rc == -EINVAL) {
+ /* see bug 13904 */
+ rc = lustre_slave_quota_convert(qfmt, i);
+ if (!rc)
+ rc = qcop->quota_on(sb, i,
+ QFMT_VFS_V0,
+ name[qfmt][i]);
+ else if (rc == -ESTALE)
+ rc = -ENOENT;
+ }
+#endif
} else if (oqc->qc_cmd == Q_QUOTAOFF) {
if (!qcop->quota_off)
GOTO(out, rc = -ENOSYS);
GOTO(out, rc = -ENOSYS);
qcop->quota_sync(sb, oqc->qc_type);
break;
+ case Q_FINVALIDATE:
+ CDEBUG(D_WARNING, "invalidating operational quota files\n");
+ for (i = 0; i < MAXQUOTAS; i++) {
+ struct file *fp;
+ lustre_quota_version_t qfmt = oqc->qc_id;
+ char *name[][MAXQUOTAS] = LUSTRE_OPQFILES_NAMES;
+
+ if (!Q_TYPESET(oqc, i))
+ continue;
+
+ fp = filp_open(name[qfmt][i], O_CREAT | O_TRUNC | O_RDWR, 0644);
+ if (IS_ERR(fp)) {
+ rc = PTR_ERR(fp);
+ CERROR("error invalidating operational quota file"
+ " %s (rc:%d)\n", name[qfmt][i], rc);
+ } else {
+ filp_close(fp, 0);
+ }
+
+ }
+ break;
default:
- CERROR("unsupported quotactl command: %d", oqc->qc_cmd);
+ CERROR("unsupported quotactl command: %d\n", oqc->qc_cmd);
LBUG();
}
out:
return cdqb;
}
-static inline int quota_onoff(struct super_block *sb, int cmd, int type)
+static inline int quota_onoff(struct super_block *sb, int cmd, int type, int qfmt)
{
struct obd_quotactl *oqctl;
int rc;
RETURN(-ENOMEM);
oqctl->qc_cmd = cmd;
- oqctl->qc_id = QFMT_LDISKFS;
+ oqctl->qc_id = qfmt;
oqctl->qc_type = type;
rc = fsfilt_ext3_quotactl(sb, oqctl);
static int v2_write_dqheader(struct file *f, int type)
{
static const __u32 quota_magics[] = V2_INITQMAGICS;
+#ifdef HAVE_QUOTA64
+ static const __u32 quota_versions[] = V2_INITQVERSIONS_R0;
+#else
static const __u32 quota_versions[] = V2_INITQVERSIONS;
+#endif
struct v2_disk_dqheader dqhead;
loff_t offset = 0;
return cfs_user_write(f, (char *)&dqinfo, sizeof(dqinfo), &offset);
}
+#ifdef HAVE_QUOTA64
+static int v3_write_dqheader(struct file *f, int type)
+{
+ static const __u32 quota_magics[] = V2_INITQMAGICS;
+ static const __u32 quota_versions[] = V2_INITQVERSIONS_R1;
+ struct v2_disk_dqheader dqhead;
+ loff_t offset = 0;
+
+ CLASSERT(ARRAY_SIZE(quota_magics) == ARRAY_SIZE(quota_versions));
+ LASSERT(0 <= type && type < ARRAY_SIZE(quota_magics));
+
+ dqhead.dqh_magic = cpu_to_le32(quota_magics[type]);
+ dqhead.dqh_version = cpu_to_le32(quota_versions[type]);
+
+ return cfs_user_write(f, (char *)&dqhead, sizeof(dqhead), &offset);
+}
+
+/* write dqinfo struct in a new quota file */
+static int v3_write_dqinfo(struct file *f, int type, struct if_dqinfo *info)
+{
+ return v2_write_dqinfo(f, type, info);
+}
+#endif
+
static int create_new_quota_files(struct qchk_ctxt *qctxt,
struct obd_quotactl *oqc)
{
struct if_dqinfo *info = qctxt->qckt_first_check[i]?
NULL : &qctxt->qckt_dqinfo[i];
struct file *file;
+ const char *name[][MAXQUOTAS] = LUSTRE_OPQFILES_NAMES;
+ int (*write_dqheader)(struct file *, int);
+ int (*write_dqinfo)(struct file *, int, struct if_dqinfo *);
if (!Q_TYPESET(oqc, i))
continue;
- file = filp_open(op_quotafile[i], O_RDWR | O_CREAT | O_TRUNC,
- 0644);
+ file = filp_open(name[oqc->qc_id][i],
+ O_RDWR | O_CREAT | O_TRUNC, 0644);
if (IS_ERR(file)) {
rc = PTR_ERR(file);
CERROR("can't create %s file: rc = %d\n",
- op_quotafile[i], rc);
+ name[oqc->qc_id][i], rc);
GOTO(out, rc);
}
if (!S_ISREG(file->f_dentry->d_inode->i_mode)) {
- CERROR("file %s is not regular", op_quotafile[i]);
+ CERROR("file %s is not regular", name[oqc->qc_id][i]);
filp_close(file, 0);
GOTO(out, rc = -EINVAL);
}
DQUOT_DROP(file->f_dentry->d_inode);
- rc = v2_write_dqheader(file, i);
+ switch (oqc->qc_id) {
+ case LUSTRE_QUOTA_V1 : write_dqheader = v2_write_dqheader;
+ write_dqinfo = v2_write_dqinfo;
+ break;
+#ifdef HAVE_QUOTA64
+ case LUSTRE_QUOTA_V2 : write_dqheader = v3_write_dqheader;
+ write_dqinfo = v3_write_dqinfo;
+ break;
+#endif
+ default : CERROR("unknown quota format!\n");
+ LBUG();
+ }
+
+ rc = (*write_dqheader)(file, i);
if (rc) {
filp_close(file, 0);
GOTO(out, rc);
}
- rc = v2_write_dqinfo(file, i, info);
+ rc = (*write_dqinfo)(file, i, info);
filp_close(file, 0);
if (rc)
GOTO(out, rc);
if (!Q_TYPESET(oqc, i))
continue;
- rc = quota_onoff(sb, Q_QUOTAON, i);
+ rc = quota_onoff(sb, Q_QUOTAON, i, oqc->qc_id);
if (!rc || rc == -EBUSY) {
rc = read_old_dqinfo(sb, i, qctxt->qckt_dqinfo);
if (rc)
GOTO(out, rc);
- } else if (rc == -ENOENT) {
+ } else if (rc == -ENOENT || rc == -EINVAL || rc == -EEXIST) {
qctxt->qckt_first_check[i] = 1;
} else if (rc) {
GOTO(out, rc);
}
#endif
/* turn off quota cause we are to dump chk_dqblk to files */
- quota_onoff(sb, Q_QUOTAOFF, oqc->qc_type);
+ quota_onoff(sb, Q_QUOTAOFF, oqc->qc_type, oqc->qc_id);
rc = create_new_quota_files(qctxt, oqc);
if (rc)
GOTO(out, rc);
/* we use vfs functions to set dqblk, so turn quota on */
- rc = quota_onoff(sb, Q_QUOTAON, oqc->qc_type);
+ rc = quota_onoff(sb, Q_QUOTAON, oqc->qc_type, oqc->qc_id);
out:
/* dump and free chk_dqblk */
rc = prune_chkquots(sb, qctxt, rc);
/* turn off quota, `lfs quotacheck` will turn on when all
* nodes quotacheck finish. */
- quota_onoff(sb, Q_QUOTAOFF, oqc->qc_type);
+ quota_onoff(sb, Q_QUOTAOFF, oqc->qc_type, oqc->qc_id);
oqc->qc_stat = rc;
if (rc)
struct list_head *list);
-/* come from lustre_quota_fmt_conver.c */
+/* comes from lustre_quota_fmt_convert.c */
+int lustre_slave_quota_convert(lustre_quota_version_t qfmt, int type);
int lustre_quota_convert(struct lustre_quota_info *lqi, int type);
+#define LUSTRE_OPQFILES_NAMES { { "lquota.user", "lquota.group" }, \
+ { "lquota_v2.user", "lquota_v2.group" } }
#endif /* lustre_quota_fmt.h */
return rc;
}
-static int admin_convert_v1_to_v2(struct file *fp_v1, struct file *fp_v2,
+static int quota_convert_v1_to_v2(struct file *fp_v1, struct file *fp_v2,
struct lustre_quota_info *lqi, int type)
{
struct list_head blk_list;
f_v1 = filp_open(name, O_RDONLY, 0);
if (!IS_ERR(f_v1)) {
if (!check_quota_file(f_v1, NULL, type, LUSTRE_QUOTA_V1)) {
- rc = admin_convert_v1_to_v2(f_v1, f_v2, lqi, type);
+ rc = quota_convert_v1_to_v2(f_v1, f_v2, lqi, type);
if (rc)
CERROR("failed to convert v1 quota file"
" to v2 quota file.\n");
RETURN(rc);
}
-
EXPORT_SYMBOL(lustre_quota_convert);
+
+#ifdef HAVE_QUOTA64
+/*
+ * convert operational quota files to the requested version
+ * returns: -ESTALE if upgrading to qfmt version is not supported
+ * -ENOMEM if memory was not allocated for conv. structures
+ *
+ * other error codes can be returned by VFS and have the
+ * appropriate meaning
+ */
+int lustre_slave_quota_convert(lustre_quota_version_t qfmt, int type)
+{
+ struct lustre_quota_info *lqi;
+ struct file *f_v1, *f_v2;
+ const char *name[][MAXQUOTAS] = LUSTRE_OPQFILES_NAMES;
+ int rc;
+
+ ENTRY;
+
+ /* we convert only to v2 version */
+ if (qfmt != LUSTRE_QUOTA_V2)
+ GOTO(out, rc = -ESTALE);
+
+ OBD_ALLOC_PTR(lqi);
+ if (lqi == NULL)
+ GOTO(out, rc = -ENOMEM);
+
+ /* now that we support only v1 and v2 formats,
+ * only upgrade from v1 is possible,
+ * let's check if v1 file exists so that we convert it to v2 */
+ f_v1 = filp_open(name[LUSTRE_QUOTA_V1][type], O_RDONLY, 0);
+ if (IS_ERR(f_v1))
+ GOTO(out_free, rc = PTR_ERR(f_v1));
+
+ /* make sure it is really a v1 file */
+ if (check_quota_file(f_v1, NULL, type, LUSTRE_QUOTA_V1))
+ GOTO(out_f_v1, rc = -EINVAL);
+
+ /* create new quota file for v2 version, follow the same rationale as
+ * mds_admin_quota_on: if the file already exists, then do not try to
+ * overwrite it, user has to fix the quotaon issue manually,
+ * e.g. through running quotacheck */
+ f_v2 = filp_open(name[LUSTRE_QUOTA_V2][type],
+ O_CREAT | O_EXCL | O_TRUNC | O_RDWR, 0644);
+ if (IS_ERR(f_v2))
+ GOTO(out_f_v1, rc = PTR_ERR(f_v2));
+
+ lqi->qi_version = LUSTRE_QUOTA_V2;
+ lqi->qi_files[type] = f_v2;
+
+ /* initialize quota file with defaults, marking it invalid,
+ * this will help us not to get confused with partially converted
+ * operational quota files if we crash during conversion */
+ rc = lustre_init_quota_info_generic(lqi, type, 1);
+ if (rc)
+ GOTO(out_f_v2, rc);
+
+ rc = quota_convert_v1_to_v2(f_v1, f_v2, lqi, type);
+ if (!rc) {
+ /* we dont want good magic to store before the quota data,
+ * just to be safe if ldiskfs is running in writeback mode */
+ LOCK_INODE_MUTEX(f_v2->f_dentry->d_inode);
+ rc = lustre_fsync(f_v2);
+ if (rc)
+ CERROR("error from fsync, rc=%d\n", rc);
+ UNLOCK_INODE_MUTEX(f_v2->f_dentry->d_inode);
+
+ /* now that conversion successfully finished we mark
+ * this operational quota file with the correct magic,
+ * since this moment quotaon will treat it as a correct
+ * quota file */
+ rc = lustre_init_quota_header(lqi, type, 0);
+ }
+
+ EXIT;
+
+out_f_v2:
+ filp_close(f_v2, 0);
+out_f_v1:
+ filp_close(f_v1, 0);
+out_free:
+ OBD_FREE_PTR(lqi);
+out:
+ return rc;
+}
+EXPORT_SYMBOL(lustre_slave_quota_convert);
+#endif
qta->qta_exp = exp;
qta->qta_oqctl = *oqctl;
+ qta->qta_oqctl.qc_id = obt->obt_qfmt; /* override qfmt version */
qta->qta_sb = obt->obt_sb;
qta->qta_sem = &obt->obt_quotachecking;
qctxt->lqc_cqs_least_bunit = PTLRPC_MAX_BRW_SIZE;
qctxt->lqc_cqs_least_iunit = 2;
qctxt->lqc_cqs_qs_factor = 2;
- qctxt->lqc_atype = 0;
- qctxt->lqc_status= 0;
+ qctxt->lqc_flags = 0;
qctxt->lqc_bunit_sz = default_bunit_sz;
qctxt->lqc_btune_sz = default_bunit_sz / 100 * default_btune_ratio;
qctxt->lqc_iunit_sz = default_iunit_sz;
case LUSTRE_Q_INVALIDATE:
rc = mds_quota_invalidate(obd, oqctl);
break;
+ case LUSTRE_Q_FINVALIDATE:
+ rc = mds_quota_finvalidate(obd, oqctl);
+ break;
default:
CERROR("%s: unsupported mds_quotactl command: %d\n",
obd->obd_name, oqctl->qc_cmd);
ENTRY;
switch (oqctl->qc_cmd) {
+ case Q_FINVALIDATE:
case Q_QUOTAON:
case Q_QUOTAOFF:
if (!atomic_dec_and_test(&obt->obt_quotachecking)) {
rc = -EBUSY;
break;
}
+ if (oqctl->qc_cmd == Q_FINVALIDATE &&
+ (obt->obt_qctxt.lqc_flags & UGQUOTA2LQC(oqctl->qc_type))) {
+ rc = -EBUSY;
+ break;
+ }
+ oqctl->qc_id = obt->obt_qfmt; /* override qfmt version */
case Q_GETOINFO:
case Q_GETOQUOTA:
case Q_GETQUOTA:
1);
push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
- rc = fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl);
+ rc = fsfilt_quotactl(obd, obt->obt_sb, oqctl);
pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
- if (oqctl->qc_cmd == Q_QUOTAON || oqctl->qc_cmd == Q_QUOTAOFF) {
- if (!rc)
- obt->obt_qctxt.lqc_status =
- (oqctl->qc_cmd == Q_QUOTAON) ? 1 : 0;
+ if (oqctl->qc_cmd == Q_QUOTAON || oqctl->qc_cmd == Q_QUOTAOFF ||
+ oqctl->qc_cmd == Q_FINVALIDATE) {
+ if (!rc && oqctl->qc_cmd == Q_QUOTAON)
+ obt->obt_qctxt.lqc_flags |= UGQUOTA2LQC(oqctl->qc_type);
+ if (!rc && oqctl->qc_cmd == Q_QUOTAOFF)
+ obt->obt_qctxt.lqc_flags &= ~UGQUOTA2LQC(oqctl->qc_type);
atomic_inc(&obt->obt_quotachecking);
}
break;
if (oqctl->qc_cmd != Q_QUOTAON && oqctl->qc_cmd != Q_QUOTAOFF &&
oqctl->qc_cmd != Q_GETOQUOTA && oqctl->qc_cmd != Q_INITQUOTA &&
- oqctl->qc_cmd != Q_SETQUOTA) {
+ oqctl->qc_cmd != Q_SETQUOTA && oqctl->qc_cmd != Q_FINVALIDATE) {
CERROR("bad quota opc %x for lov obd", oqctl->qc_cmd);
RETURN(-EFAULT);
}
#define GROUP_QUOTA 2
#define MAX_STYPE_SIZE 5
+
+/* The following information about CURRENT quotas is expected on the output:
+ * MDS: u for user quotas (administrative+operational) turned on,
+ * g for group quotas (administrative+operational) turned on,
+ * 1 for 32-bit operational quotas and 32-bit administrative quotas,
+ * 2 for 32-bit operational quotas and 64-bit administrative quotas,
+ * 3 for 64-bit operational quotas and 64-bit administrative quotas
+ * OST: u for user quotas (operational) turned on,
+ * g for group quotas (operational) turned on,
+ * 1 for 32-bit local operational quotas,
+ * 3 for 64-bit local operational quotas,
+ * Permanent parameters can be read with lctl (?)
+ */
int lprocfs_quota_rd_type(char *page, char **start, off_t off, int count,
int *eof, void *data)
{
struct obd_device *obd = (struct obd_device *)data;
char stype[MAX_STYPE_SIZE + 1] = "";
- int type = obd->u.obt.obt_qctxt.lqc_atype;
- LASSERT(obd != NULL);
+ int oq_type, rc, is_mds;
+ lustre_quota_version_t aq_version, oq_version;
+ struct obd_device_target *obt;
- if (type == 0) {
- strcpy(stype, "off");
- } else {
- if (type & USER_QUOTA)
- strcat(stype, "u");
- if (type & GROUP_QUOTA)
- strcat(stype, "g");
- }
+ LASSERT(obd != NULL);
- /* append with quota version on MDS */
- if (!strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME)) {
- int rc;
- lustre_quota_version_t version;
+ obt = &obd->u.obt;
+ is_mds = !strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME);
- rc = mds_quota_get_version(obd, &version);
+ /* Collect the needed information */
+ oq_type = obd->u.obt.obt_qctxt.lqc_flags;
+ oq_version = obt->obt_qfmt;
+ if (is_mds) {
+ rc = mds_quota_get_version(obd, &aq_version);
if (rc)
- return rc;
-
- switch (version) {
- case LUSTRE_QUOTA_V1:
- strcat(stype, "1");
- break;
- case LUSTRE_QUOTA_V2:
- strcat(stype, "2");
- break;
- default:
- return -ENOSYS;
- }
+ return -EPROTO;
+ /* Here we can also assert that aq_type == oq_type
+ * except for quota startup/shutdown states */
}
+ /* Transform the collected data into a user-readable string */
+ if (oq_type & LQC_USRQUOTA_FLAG)
+ strcat(stype, "u");
+ if (oq_type & LQC_GRPQUOTA_FLAG)
+ strcat(stype, "g");
+
+ if ((!is_mds || aq_version == LUSTRE_QUOTA_V1) &&
+ oq_version == LUSTRE_QUOTA_V1)
+ strcat(stype, "1");
+#ifdef HAVE_QUOTA64
+ else if ((!is_mds || aq_version == LUSTRE_QUOTA_V2) &&
+ oq_version == LUSTRE_QUOTA_V2)
+ strcat(stype, "3");
+#endif
+ else if (is_mds && aq_version == LUSTRE_QUOTA_V2 &&
+ oq_version == LUSTRE_QUOTA_V1)
+ strcat(stype, "2");
+ else
+ return -EPROTO;
+
return snprintf(page, count, "%s\n", stype);
}
EXPORT_SYMBOL(lprocfs_quota_rd_type);
{
struct obd_quotactl *oqctl;
struct lvfs_run_ctxt saved;
- int rc;
+ int rc = 0, id;
+ struct obd_device_target *obt;
ENTRY;
LASSERT(type == USRQUOTA || type == GRPQUOTA || type == UGQUOTA);
- /* quota already turned on */
- if (obd->u.obt.obt_qctxt.lqc_status)
- RETURN(0);
+ obt = &obd->u.obt;
OBD_ALLOC_PTR(oqctl);
if (!oqctl)
RETURN(-ENOMEM);
+ if (!atomic_dec_and_test(&obt->obt_quotachecking)) {
+ CDEBUG(D_INFO, "other people are doing quotacheck\n");
+ atomic_inc(&obt->obt_quotachecking);
+ RETURN(-EBUSY);
+ }
+
+ id = UGQUOTA2LQC(type);
+ /* quota already turned on */
+ if ((obt->obt_qctxt.lqc_flags & id) == id) {
+ rc = 0;
+ goto out;
+ }
+
oqctl->qc_type = type;
oqctl->qc_cmd = Q_QUOTAON;
- oqctl->qc_id = QFMT_LDISKFS;
+ oqctl->qc_id = obt->obt_qfmt;
push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
+ if (is_master) {
+ struct mds_obd *mds = &obd->u.mds;
- if (!is_master)
- goto local_quota;
+ down(&mds->mds_qonoff_sem);
+ /* turn on cluster wide quota */
+ rc = mds_admin_quota_on(obd, oqctl);
+ if (rc)
+ CDEBUG(rc == -ENOENT ? D_QUOTA : D_ERROR,
+ "auto-enable admin quota failed. rc=%d\n", rc);
+ up(&mds->mds_qonoff_sem);
- /* turn on cluster wide quota */
- rc = mds_admin_quota_on(obd, oqctl);
- if (rc) {
- CDEBUG(rc == -ENOENT ? D_QUOTA : D_ERROR,
- "auto-enable admin quota failed. rc=%d\n", rc);
- GOTO(out_pop, rc);
}
-local_quota:
- /* turn on local quota */
- rc = fsfilt_quotactl(obd, sb, oqctl);
- if (rc) {
- CDEBUG(rc == -ENOENT ? D_QUOTA : D_ERROR,
- "auto-enable local quota failed. rc=%d\n", rc);
- if (is_master)
- mds_quota_off(obd, oqctl);
- } else {
- obd->u.obt.obt_qctxt.lqc_status = 1;
+ if (!rc) {
+ /* turn on local quota */
+ rc = fsfilt_quotactl(obd, sb, oqctl);
+ if (rc)
+ CDEBUG(rc == -ENOENT ? D_QUOTA : D_ERROR,
+ "auto-enable local quota failed. rc=%d\n", rc);
+ else
+ obt->obt_qctxt.lqc_flags |= UGQUOTA2LQC(type);
}
-out_pop:
+
pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
+out:
+ atomic_inc(&obt->obt_quotachecking);
+
OBD_FREE_PTR(oqctl);
RETURN(rc);
}
+static int filter_quota_set_version(struct obd_device *obd,
+ lustre_quota_version_t version)
+{
+ struct obd_device_target *obt = &obd->u.obt;
+
+ if (version != LUSTRE_QUOTA_V1) {
+#ifdef HAVE_QUOTA64
+ if (version != LUSTRE_QUOTA_V2)
+#endif
+ return -EINVAL;
+ }
+
+ if (!atomic_dec_and_test(&obt->obt_quotachecking)) {
+ CDEBUG(D_INFO, "other people are doing quotacheck\n");
+ atomic_inc(&obt->obt_quotachecking);
+ return -EBUSY;
+ }
+
+ if (obt->obt_qctxt.lqc_flags & (LQC_USRQUOTA_FLAG | LQC_GRPQUOTA_FLAG)) {
+ atomic_inc(&obt->obt_quotachecking);
+ return -EBUSY;
+ }
+
+ obt->obt_qfmt = version;
+
+ atomic_inc(&obt->obt_quotachecking);
+
+ return 0;
+}
+
+/* The following settings of CURRENT quotas is expected on the input:
+ * MDS: u for user quotas (administrative+operational) turned on,
+ * g for group quotas (administrative+operational) turned on,
+ * 1 for 32-bit operational quotas and 32-bit administrative quotas,
+ * 2 for 32-bit operational quotas and 64-bit administrative quotas,
+ * 3 for 64-bit operational quotas and 64-bit administrative quotas
+ * OST: u for user quotas (operational) turned on,
+ * g for group quotas (operational) turned on,
+ * 1 for 32-bit local operational quotas,
+ * 2 for 32-bit local operational quotas,
+ * 3 for 64-bit local operational quotas,
+ * Permanent parameters can be set with lctl/tunefs
+ */
int lprocfs_quota_wr_type(struct file *file, const char *buffer,
unsigned long count, void *data)
{
struct obd_device *obd = (struct obd_device *)data;
- struct obd_device_target *obt = &obd->u.obt;
- int type = 0;
+ struct obd_device_target *obt;
+ int type = 0, is_mds, idx;
unsigned long i;
char stype[MAX_STYPE_SIZE + 1] = "";
+ static const lustre_quota_version_t s2av[3] = {LUSTRE_QUOTA_V1,
+ LUSTRE_QUOTA_V2,
+ LUSTRE_QUOTA_V2},
+ s2ov[3] = {LUSTRE_QUOTA_V1,
+ LUSTRE_QUOTA_V1,
+ LUSTRE_QUOTA_V2};
LASSERT(obd != NULL);
+ obt = &obd->u.obt;
+
+ is_mds = !strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME);
+
if (count > MAX_STYPE_SIZE)
return -EINVAL;
break;
/* quota version specifiers */
case '1' :
- if (strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME))
- break;
-
- rc = mds_quota_set_version(obd, LUSTRE_QUOTA_V1);
- if (rc) {
- CDEBUG(D_QUOTA, "failed to set quota v1! %d\n", rc);
- return rc;
- }
- break;
case '2' :
- if (strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME))
- break;
-
- rc = mds_quota_set_version(obd, LUSTRE_QUOTA_V2);
+ case '3' :
+ idx = stype[i] - '1';
+#ifndef HAVE_QUOTA64
+ if (s2ov[idx] == LUSTRE_QUOTA_V2)
+ return -EINVAL;
+#endif
+ if (is_mds) {
+ rc = mds_quota_set_version(obd, s2av[idx]);
+ if (rc) {
+ CDEBUG(D_QUOTA, "failed to set admin "
+ "quota to spec %c! %d\n",
+ stype[i], rc);
+ return rc;
+ }
+ }
+ rc = filter_quota_set_version(obd, s2ov[idx]);
if (rc) {
- CDEBUG(D_QUOTA, "could not set quota v2! %d\n", rc);
+ CDEBUG(D_QUOTA, "failed to set operational quota"
+ " to spec %c! %d\n", stype[i], rc);
return rc;
}
break;
}
}
- obt->obt_qctxt.lqc_atype = type;
-
- if (type == 0)
- return count;
-
- if (!strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME))
- auto_quota_on(obd, type - 1, obt->obt_sb, 1);
- else if (!strcmp(obd->obd_type->typ_name, LUSTRE_OST_NAME))
- auto_quota_on(obd, type - 1, obt->obt_sb, 0);
- else
- return -EFAULT;
+ if (type != 0)
+ auto_quota_on(obd, type - 1, obt->obt_sb, is_mds);
return count;
}
struct obd_device_target *obt = &obd->u.obt;
ENTRY;
+#ifdef HAVE_QUOTA64
+ obt->obt_qfmt = LUSTRE_QUOTA_V2;
+#else
+ obt->obt_qfmt = LUSTRE_QUOTA_V1;
+#endif
atomic_set(&obt->obt_quotachecking, 1);
rc = qctxt_init(&obt->obt_qctxt, obt->obt_sb, NULL);
- if (rc) {
+ if (rc)
CERROR("initialize quota context failed! (rc:%d)\n", rc);
- RETURN(rc);
- }
+
RETURN(rc);
}
int rc;
ENTRY;
+#ifdef HAVE_QUOTA64
+ obt->obt_qfmt = LUSTRE_QUOTA_V2;
+#else
+ obt->obt_qfmt = LUSTRE_QUOTA_V1;
+#endif
mds->mds_quota_info.qi_version = LUSTRE_QUOTA_V2;
atomic_set(&obt->obt_quotachecking, 1);
/* initialize quota master and quota context */
int mds_quota_get_version(struct obd_device *obd, lustre_quota_version_t *ver);
int mds_quota_set_version(struct obd_device *obd, lustre_quota_version_t ver);
int mds_quota_invalidate(struct obd_device *obd, struct obd_quotactl *oqctl);
+int mds_quota_finvalidate(struct obd_device *obd, struct obd_quotactl *oqctl);
int mds_admin_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl);
int mds_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl);
struct lustre_quota_info *qinfo = &mds->mds_quota_info;
int rc = 0, i;
- if (version != LUSTRE_QUOTA_V1 &&
- version != LUSTRE_QUOTA_V2)
+ if (version != LUSTRE_QUOTA_V1 && version != LUSTRE_QUOTA_V2)
return -EINVAL;
down(&mds->mds_qonoff_sem);
LASSERT(strlen(quotafile) + sizeof(prefix) <= sizeof(name));
sprintf(name, "%s%s", prefix, quotafile);
- fp = filp_open(name, O_CREAT | O_TRUNC, 0644);
+ fp = filp_open(name, O_CREAT | O_TRUNC | O_RDWR, 0644);
if (IS_ERR(fp)) {
rc = PTR_ERR(fp);
CERROR("error invalidating admin quotafile %s (rc:%d)\n",
return rc;
}
+int mds_quota_finvalidate(struct obd_device *obd, struct obd_quotactl *oqctl)
+{
+ struct mds_obd *mds = &obd->u.mds;
+ int rc;
+ struct lvfs_run_ctxt saved;
+
+ push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
+ down(&mds->mds_qonoff_sem);
+
+ oqctl->qc_cmd = Q_FINVALIDATE;
+ oqctl->qc_id = obd->u.obt.obt_qfmt;
+ rc = fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl);
+ if (!rc)
+ rc = obd_quotactl(mds->mds_osc_exp, oqctl);
+
+ up(&mds->mds_qonoff_sem);
+ pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
+
+ return rc;
+}
+
int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl)
{
struct mds_obd *mds = &obd->u.mds;
rc == -ENOENT ? "creating" : "overwriting");
/* create quota file overwriting old if needed */
- fp = filp_open(name, O_CREAT | O_TRUNC, 0644);
+ fp = filp_open(name, O_CREAT | O_TRUNC | O_RDWR, 0644);
if (IS_ERR(fp)) {
rc = PTR_ERR(fp);
CERROR("error creating admin quotafile %s (rc:%d)\n",
qinfo->qi_version == LUSTRE_QUOTA_V2) {
CDEBUG(D_INFO, "attempting to convert V1 quota file to"
" V2 format\n");
- fp = filp_open(name, O_CREAT | O_TRUNC, 0644);
+ fp = filp_open(name, O_CREAT | O_TRUNC | O_RDWR, 0644);
if (!IS_ERR(fp)) {
qinfo->qi_files[i] = fp;
rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_CONVERT);
rc = fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl);
if (!rc)
- obt->obt_qctxt.lqc_status = 1;
+ obt->obt_qctxt.lqc_flags |= UGQUOTA2LQC(oqctl->qc_type);
out:
pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
up(&mds->mds_qonoff_sem);
rc = obd_quotactl(mds->mds_osc_exp, oqctl);
rc2 = fsfilt_quotactl(obd, obd->u.obt.obt_sb, oqctl);
if (!rc2)
- obt->obt_qctxt.lqc_status = 0;
+ obt->obt_qctxt.lqc_flags &= ~UGQUOTA2LQC(oqctl->qc_type);
pop_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL);
up(&mds->mds_qonoff_sem);
ENTRY;
oqctl.qc_cmd = Q_QUOTAON;
- oqctl.qc_id = QFMT_LDISKFS;
+ oqctl.qc_id = obd->u.obt.obt_qfmt;
oqctl.qc_type = UGQUOTA;
rc = fsfilt_quotactl(obd, sb, &oqctl);
if (rc)
}
run_test 14a "test setting quota on root ==="
+# set quota version (both administrative and operational quotas)
quota_set_version() {
- local qver=$1
- do_facet mds "lctl set_param mds.${FSNAME}-MDT*.quota_type=$qver"
+ do_facet mds "lctl set_param mds.${FSNAME}-MDT*.quota_type=$1"
+ for j in `seq $OSTCOUNT`; do
+ do_facet ost$j "lctl set_param obdfilter.*.quota_type=$1"
+ done
+}
+
+# save quota version (both administrative and operational quotas)
+quota_save_version() {
+ do_facet mgs "lctl conf_param ${FSNAME}-MDT*.mdt.quota_type=$1"
+ do_facet mgs "lctl conf_param ${FSNAME}-OST*.ost.quota_type=$1"
}
-test_14b() { # was test_14a
+test_14b(){
+ local l
+ local CURSPACE
+
# 1. check that required users exist
# 2. ensure that switch to new mode will start conversion
# 3. start quota in old mode and put some entries
MISSING_USERS=""
for i in `seq 1 30`; do
- check_runas_id_ret quota15_$i "runas -u quota15_$i"
+ check_runas_id_ret quota15_$i "runas -u quota15_$i" >/dev/null 2>/dev/null
if [ "$?" != "0" ]; then
MISSING_USERS="$MISSING_USERS quota15_$i"
fi
fi
$LFS quotaoff -ug $DIR
+ echo "setting quota version 1"
quota_set_version 1
+ echo "running quotacheck"
$LFS quotacheck -ug $DIR
+ chmod 0777 $DIR/$tdir
+ for i in `seq 1 30`; do
+ l=$[$i*1024*128] # set limits in 128 Mb units
+ $LFS setquota -u quota15_$i $l $l $l $l $DIR || error "lfs setquota failed"
+ runas -u quota15_$i dd if=/dev/zero of="$DIR/$tdir/quota15_$i" \
+ bs=1048576 count=$[($i+1)/2] || error "dd failed"
+ done
+ cancel_lru_locks osc
+
+ echo "saving quota data"
for i in `seq 1 30`; do
- $LFS setquota -u quota15_$i -b $i -B $i -i $i -I $i $DIR
+ CURSPACE[$i]=`$LFS quota -u quota15_$i $MOUNT | awk '{if(start) {start=0; sum += $1} if(($1 ~ /OST/) && (NF==1)) {start=1;}
+ if(($1 ~ /OST/) && (NF != 1)) {sum += $2}; } END { print sum }'`
done
$LFS quotaoff -ug $DIR
- quota_set_version 2
+ echo "setting version 3 or 2 (dependent on the kernel support)"
+ quota_set_version 3 2>&1 | grep "Invalid argument" && quota_set_version 2
+
+ echo "invalidating quota files"
$LFS quotainv -ug $DIR
+ $LFS quotainv -ugf $DIR
$LFS quotacheck -ug $DIR
for i in `seq 1 30`; do
+ l=$[$i*1024*128]
# the format is "mntpnt curspace[*] bsoftlimit bhardlimit [time] curinodes[*] isoftlimit ihardlimit"
- ($LFS quota -u quota15_$i $DIR | grep -E '^ *'$DIR' *[0-9]+\** *'$i' *'$i' *[0-9]+\** *'$i' *'$i) \
- || error "lfs quota output is unexpected"
- $LFS setquota -u quota15_$i -b 0 -B 0 -i 0 -I 0 $DIR
+ echo "checking administrative quota migration results for user quota15_$i"
+ $LFS quota -u quota15_$i $DIR | grep -E '^ *'$MOUNT' *[0-9]+\** *'$l' *'$l' *[0-9]+\** *'$l' *'$l \
+ || error "lfs quota output is unexpected"
+ echo "checking operational quota migration results for user quota15_$i, curspace should be ${CURSPACE[$i]}"
+ l=`$LFS quota -u quota15_$i $MOUNT | awk '{if(start) {start=0; sum += $1} if(($1 ~ /OST/) && (NF==1)) {start=1;}
+ if(($1 ~ /OST/) && (NF != 1)) {sum += $2}; } END { print sum }'`
+ echo "...real is $l"
+ [ "$l" -eq "${CURSPACE[$i]}" ] || error "curspace mismatch"
+ rm $DIR/$tdir/quota15_$i || error "could not remove quota15_$i"
+ $LFS setquota -u quota15_$i 0 0 0 0 $DIR || error "ifs setquota clear failed"
done
}
run_test 14b "setting 30 quota entries in quota v1 file before conversion ==="
}
run_test 21 "run for fixing bug16053 ==========="
+test_22() {
+ local SAVEREFORMAT
+
+ SAVEREFORMAT=$REFORMAT
+ $LFS quotaoff -ug $DIR || error "could not turn quotas off"
+ quota_set_version "1"
+ $LFS quotacheck -ug $DIR || error "quotacheck failed"
+
+ quota_save_version "ug1"
+
+ REFORMAT="reformat"
+ stopall
+ mount
+ setupall
+ REFORMAT=$SAVEREFORMAT
+
+ echo "checking parameters"
+
+ do_facet mds "lctl get_param mds.${FSNAME}-MDT*.quota_type" | grep "ug1" || error "admin failure"
+ do_facet ost1 "lctl get_param obdfilter.*.quota_type" | grep "ug1" || error "op failure"
+
+ run_test 0 "reboot lustre"
+}
+run_test 22 "test if quota_type saved as permanent parameter ===="
+
# turn off quota
test_99()
{
memset(&qctl, 0, sizeof(qctl));
qctl.qc_cmd = LUSTRE_Q_QUOTAOFF;
- qctl.qc_id = QFMT_LDISKFS;
qctl.qc_type = check_type;
rc = llapi_quotactl(mnt, &qctl);
if (rc) {
memset(&qctl, 0, sizeof(qctl));
qctl.qc_cmd = LUSTRE_Q_QUOTAON;
- qctl.qc_id = QFMT_LDISKFS;
qctl.qc_type = check_type;
rc = llapi_quotactl(mnt, &qctl);
if (rc) {
memset(&qctl, 0, sizeof(qctl));
qctl.qc_cmd = LUSTRE_Q_QUOTAON;
- qctl.qc_id = QFMT_LDISKFS;
optind = 0;
while ((c = getopt(argc, argv, "ugf")) != -1) {
qctl.qc_cmd = LUSTRE_Q_INVALIDATE;
optind = 0;
- while ((c = getopt(argc, argv, "ug")) != -1) {
+ while ((c = getopt(argc, argv, "ugf")) != -1) {
switch (c) {
case 'u':
qctl.qc_type |= 0x01;
case 'g':
qctl.qc_type |= 0x02;
break;
+ case 'f':
+ qctl.qc_cmd = LUSTRE_Q_FINVALIDATE;
+ break;
default:
fprintf(stderr, "error: %s: option '-%c' "
"unrecognized\n", argv[0], c);
rc = write(fp, value, strlen(value));
if (rc < 0)
fprintf(stderr,
- "error writing to file %s\n",
- glob_info.gl_pathv[i]);
+ "error writing to file %s (%s)\n",
+ glob_info.gl_pathv[i], strerror(errno));
else
rc = 0;
close(fp);