diff --git a/lustre/doc/lfs.1 b/lustre/doc/lfs.1 index 662bf0641b76a793859d0c5ed6348406a0cb35f4..5192239cfaf60d50d51e43abfe6593fd46e28055 100644 --- a/lustre/doc/lfs.1 +++ b/lustre/doc/lfs.1 @@ -26,6 +26,8 @@ lfs \- Lustre utility to create a file with specific striping pattern, find the .br .B lfs quotaoff [-ug] <filesystem> .br +.B lfs quotainv [-ug] <filesystem> +.br .B lfs setquota [-u|-g] <name> <block-softlimit> <block-hardlimit> \fB<inode-softlimit> <inode-hardlimit> <filesystem>\fR .br @@ -68,6 +70,9 @@ To turn filesystem quotas on. Options specify quota for users (-u) groups (-g) a .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. +.TP .B setquota [-u|-g] <name> <block-softlimit> <block-hardlimit> <inode-softlimit> <inode-hardlimit> <filesystem> To set filesystem quotas for users or groups. Limits are specific as blocks and inodes, see EXAMPLES .TP diff --git a/lustre/include/linux/lustre_fsfilt.h b/lustre/include/linux/lustre_fsfilt.h index cee0c21411cc0dbd4dd2f70ad83198516a8c0180..e6c767c957f5d64e71105cb395840e118b647e23 100644 --- a/lustre/include/linux/lustre_fsfilt.h +++ b/lustre/include/linux/lustre_fsfilt.h @@ -404,7 +404,7 @@ static inline int fsfilt_quotainfo(struct obd_device *obd, } static inline int fsfilt_qids(struct obd_device *obd, struct file *file, - struct inode *inode, int type, + struct inode *inode, int type, struct list_head *list) { if (obd->obd_fsops->fs_qids) diff --git a/lustre/include/lustre/lustre_idl.h b/lustre/include/lustre/lustre_idl.h index 3f941442e35e5b6554d79de0abfb9da432a8fac9..a3ba3889ea9c4415cdd3b2687e6c9645afbd2bc9 100644 --- a/lustre/include/lustre/lustre_idl.h +++ b/lustre/include/lustre/lustre_idl.h @@ -1493,4 +1493,5 @@ typedef enum { #define QUOTA_RET_NOQUOTA 1 /* not support quota */ #define QUOTA_RET_NOLIMIT 2 /* quota limit isn't set */ #define QUOTA_RET_ACQUOTA 3 /* need to acquire extra quota */ + #endif diff --git a/lustre/include/lustre/lustre_user.h b/lustre/include/lustre/lustre_user.h index e046c947da656a5835f00608e06bd4bd6c883e33..57e7c6465463ac1a672e9be18a6206b9cbac0c77 100644 --- a/lustre/include/lustre/lustre_user.h +++ b/lustre/include/lustre/lustre_user.h @@ -163,12 +163,15 @@ static inline char *obd_uuid2str(struct obd_uuid *uuid) return (char *)(uuid->uuid); } -#define LUSTRE_Q_QUOTAON 0x800002 /* turn quotas on */ -#define LUSTRE_Q_QUOTAOFF 0x800003 /* turn quotas off */ -#define LUSTRE_Q_GETINFO 0x800005 /* get information about quota files */ -#define LUSTRE_Q_SETINFO 0x800006 /* set information about quota files */ -#define LUSTRE_Q_GETQUOTA 0x800007 /* get user quota structure */ -#define LUSTRE_Q_SETQUOTA 0x800008 /* set user quota structure */ +/* these must be explicitly translated into linux Q_* in ll_dir_ioctl */ +#define LUSTRE_Q_QUOTAON 0x800002 /* turn quotas on */ +#define LUSTRE_Q_QUOTAOFF 0x800003 /* turn quotas off */ +#define LUSTRE_Q_GETINFO 0x800005 /* get information about quota files */ +#define LUSTRE_Q_SETINFO 0x800006 /* set information about quota files */ +#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 UGQUOTA 2 /* set both USRQUOTA and GRPQUOTA */ @@ -218,6 +221,11 @@ struct mds_grp_downcall_data { #endif /* !__KERNEL__ */ +typedef enum lustre_quota_version { + LUSTRE_QUOTA_V1 = 0, + LUSTRE_QUOTA_V2 = 1 +} lustre_quota_version_t; + /* XXX: same as if_dqinfo struct in kernel */ struct obd_dqinfo { __u64 dqi_bgrace; diff --git a/lustre/include/lustre_quota.h b/lustre/include/lustre_quota.h index c7e97c1b095ffac30ca549e7a62654b6b5d70d40..2e0acb8c9eb07a0d2a7f92f19f006880533b7d5a 100644 --- a/lustre/include/lustre_quota.h +++ b/lustre/include/lustre_quota.h @@ -42,12 +42,24 @@ struct lustre_mem_dqinfo { struct lustre_quota_info { struct file *qi_files[MAXQUOTAS]; struct lustre_mem_dqinfo qi_info[MAXQUOTAS]; + lustre_quota_version_t qi_version; }; #define DQ_STATUS_AVAIL 0x0 /* Available dquot */ #define DQ_STATUS_SET 0x01 /* Sombody is setting dquot */ #define DQ_STATUS_RECOVERY 0x02 /* dquot is in recovery */ +struct lustre_mem_dqblk { + __u64 dqb_bhardlimit; /* absolute limit on disk blks alloc */ + __u64 dqb_bsoftlimit; /* preferred limit on disk blks */ + __u64 dqb_curspace; /* current used space */ + __u64 dqb_ihardlimit; /* absolute limit on allocated inodes */ + __u64 dqb_isoftlimit; /* preferred inode limit */ + __u64 dqb_curinodes; /* current # allocated inodes */ + time_t dqb_btime; /* time limit for excessive disk use */ + time_t dqb_itime; /* time limit for excessive inode use */ +}; + struct lustre_dquot { /* Hash list in memory, protect by dquot_hash_lock */ struct list_head dq_hash; @@ -63,7 +75,7 @@ struct lustre_dquot { int dq_type; /* Type fo quota (USRQUOTA, GRPQUOUTA) */ unsigned short dq_status; /* See DQ_STATUS_ */ unsigned long dq_flags; /* See DQ_ in quota.h */ - struct mem_dqblk dq_dqb; /* Diskquota usage */ + struct lustre_mem_dqblk dq_dqb; /* Diskquota usage */ }; struct dquot_id { @@ -77,6 +89,7 @@ struct dquot_id { #define QFILE_INIT_INFO 4 #define QFILE_RD_DQUOT 5 #define QFILE_WR_DQUOT 6 +#define QFILE_CONVERT 7 /* admin quotafile operations */ #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) @@ -88,6 +101,7 @@ int lustre_commit_dquot(struct lustre_dquot *dquot); int lustre_init_quota_info(struct lustre_quota_info *lqi, int type); int lustre_get_qids(struct file *file, struct inode *inode, int type, struct list_head *list); +int lustre_quota_convert(struct lustre_quota_info *lqi, int type); #else #ifndef DQ_FAKE_B @@ -122,6 +136,11 @@ static inline int lustre_init_quota_info(struct lustre_quota_info *lqi, { return 0; } +static inline int lustre_quota_convert(struct lustre_quota_info *lqi, + int type) +{ + return 0; +} #endif /* KERNEL_VERSION(2,5,0) */ #define LL_DQUOT_OFF(sb) DQUOT_OFF(sb) @@ -461,4 +480,14 @@ extern quota_interface_t mdc_quota_interface; extern quota_interface_t lov_quota_interface; #endif +#define LUSTRE_ADMIN_QUOTAFILES_V1 {\ + "admin_quotafile.usr", /* user admin quotafile */\ + "admin_quotafile.grp" /* group admin quotafile */\ +} + +#define LUSTRE_ADMIN_QUOTAFILES_V2 {\ + "admin_quotafile_v2.usr", /* user admin quotafile */\ + "admin_quotafile_v2.grp" /* group admin quotafile */\ +} + #endif /* _LUSTRE_QUOTA_H */ diff --git a/lustre/llite/dir.c b/lustre/llite/dir.c index 6e95fa3565f16b59550363d766b64cb55db45443..813351d78e9b035c88b78346f3cb15197b10233c 100644 --- a/lustre/llite/dir.c +++ b/lustre/llite/dir.c @@ -967,6 +967,7 @@ static int ll_dir_ioctl(struct inode *inode, struct file *file, type = qctl->qc_type; id = qctl->qc_id; switch (cmd) { + case LUSTRE_Q_INVALIDATE: case Q_QUOTAON: case Q_QUOTAOFF: case Q_SETQUOTA: diff --git a/lustre/lvfs/Makefile.in b/lustre/lvfs/Makefile.in index f103b623db3aaf9625c24cdf2ebf323567e14c31..de72dbdf108450556a51cd7fa4c237834e91d806 100644 --- a/lustre/lvfs/Makefile.in +++ b/lustre/lvfs/Makefile.in @@ -3,7 +3,7 @@ MODULES := lvfs @QUOTA_TRUE@MODULES += quotafmt_test lvfs-objs := lvfs_common.o lvfs_linux.o fsfilt.o upcall_cache.o prng.o lvfs_lib.o -@QUOTA_TRUE@lvfs-objs += lustre_quota_fmt.o +@QUOTA_TRUE@lvfs-objs += lustre_quota_fmt.o lustre_quota_fmt_convert.o @QUOTA_TRUE@quotafmt-objs := quotafmt_test.o diff --git a/lustre/lvfs/autoMakefile.am b/lustre/lvfs/autoMakefile.am index f5cb1650da22b7cb3668c6bd362ad9502937b9d7..b7b3a484e25b3210b33d00173b986af25c71c209 100644 --- a/lustre/lvfs/autoMakefile.am +++ b/lustre/lvfs/autoMakefile.am @@ -76,6 +76,7 @@ DIST_SOURCES = fsfilt.c fsfilt_ext3.c fsfilt_reiserfs.c lvfs_common.c \ lvfs_internal.h lvfs_linux.c lvfs_userfs.c \ upcall_cache.c prng.c lvfs_lib.c\ lustre_quota_fmt.c lustre_quota_fmt.h quotafmt_test.c \ + lustre_quota_fmt_convert.c \ # quotacheck_test.c quotactl_test.c fsfilt_ext3_quota.h MOSTLYCLEANFILES := @MOSTLYCLEANFILES@ diff --git a/lustre/lvfs/fsfilt_ext3.c b/lustre/lvfs/fsfilt_ext3.c index e2f5e10d2b60b71cb6600f83f689ba6d2633e090..693de9eae53029af81c94a42677413d0525c9b05 100644 --- a/lustre/lvfs/fsfilt_ext3.c +++ b/lustre/lvfs/fsfilt_ext3.c @@ -2063,6 +2063,9 @@ static int fsfilt_ext3_quotainfo(struct lustre_quota_info *lqi, int type, case QFILE_INIT_INFO: rc = lustre_init_quota_info(lqi, type); break; + case QFILE_CONVERT: + rc = lustre_quota_convert(lqi, type); + break; default: CERROR("Unsupported admin quota file cmd %d\n", cmd); LBUG(); diff --git a/lustre/lvfs/lustre_quota_fmt.c b/lustre/lvfs/lustre_quota_fmt.c index 96d98337936ee3c77ab51991abb71adb565b9752..e910a0c40b4421f81e7b799115b17f74fb8a8703 100644 --- a/lustre/lvfs/lustre_quota_fmt.c +++ b/lustre/lvfs/lustre_quota_fmt.c @@ -24,21 +24,33 @@ #include <asm/uaccess.h> #include <lustre_quota.h> +#include <obd_support.h> #include "lustre_quota_fmt.h" -typedef char *dqbuf_t; +static const uint lustre_initqversions[][MAXQUOTAS] = { + [LUSTRE_QUOTA_V1] = LUSTRE_INITQVERSIONS, + [LUSTRE_QUOTA_V2] = LUSTRE_INITQVERSIONS_V2 +}; -#define GETIDINDEX(id, depth) (((id) >> ((LUSTRE_DQTREEDEPTH-(depth)-1)*8)) & 0xff) -#define GETENTRIES(buf) ((struct lustre_disk_dqblk *)(((char *)buf)+sizeof(struct lustre_disk_dqdbheader))) +static const int lustre_dqstrinblk[] = { + [LUSTRE_QUOTA_V1] = LUSTRE_DQSTRINBLK, + [LUSTRE_QUOTA_V2] = LUSTRE_DQSTRINBLK_V2 +}; -static int check_quota_file(struct file *f, struct inode *inode, int type) +static const int lustre_disk_dqblk_sz[] = { + [LUSTRE_QUOTA_V1] = sizeof(struct lustre_disk_dqblk), + [LUSTRE_QUOTA_V2] = sizeof(struct lustre_disk_dqblk_v2) +}; + +int check_quota_file(struct file *f, struct inode *inode, int type, + lustre_quota_version_t version) { struct lustre_disk_dqheader dqhead; mm_segment_t fs; ssize_t size; loff_t offset = 0; static const uint quota_magics[] = LUSTRE_INITQMAGICS; - static const uint quota_versions[] = LUSTRE_INITQVERSIONS; + const uint *quota_versions = lustre_initqversions[version]; if (f) { fs = get_fs(); @@ -57,27 +69,24 @@ static int check_quota_file(struct file *f, struct inode *inode, int type) #endif } if (size != sizeof(struct lustre_disk_dqheader)) - return 0; + return -EINVAL; if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] || le32_to_cpu(dqhead.dqh_version) != quota_versions[type]) - return 0; - return 1; + return -EINVAL; + return 0; } /* Check whether given file is really lustre admin quotafile */ int lustre_check_quota_file(struct lustre_quota_info *lqi, int type) { struct file *f = lqi->qi_files[type]; - return check_quota_file(f, NULL, type); + return check_quota_file(f, NULL, type, lqi->qi_version); } -/* Read information header from quota file */ -int lustre_read_quota_info(struct lustre_quota_info *lqi, int type) +int lustre_read_quota_file_info(struct file* f, struct lustre_mem_dqinfo* info) { mm_segment_t fs; struct lustre_disk_dqinfo dinfo; - struct lustre_mem_dqinfo *info = &lqi->qi_info[type]; - struct file *f = lqi->qi_files[type]; ssize_t size; loff_t offset = LUSTRE_DQINFOOFF; @@ -87,9 +96,9 @@ int lustre_read_quota_info(struct lustre_quota_info *lqi, int type) sizeof(struct lustre_disk_dqinfo), &offset); set_fs(fs); if (size != sizeof(struct lustre_disk_dqinfo)) { - printk(KERN_WARNING "Can't read info structure on device %s.\n", + CERROR("Can't read info structure on device %s.\n", f->f_vfsmnt->mnt_sb->s_id); - return -1; + return -EINVAL; } info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace); info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace); @@ -100,6 +109,12 @@ int lustre_read_quota_info(struct lustre_quota_info *lqi, int type) return 0; } +/* Read information header from quota file */ +int lustre_read_quota_info(struct lustre_quota_info *lqi, int type) +{ + return lustre_read_quota_file_info(lqi->qi_files[type], &lqi->qi_info[type]); +} + /* Write information header to quota file */ int lustre_write_quota_info(struct lustre_quota_info *lqi, int type) { @@ -131,33 +146,65 @@ int lustre_write_quota_info(struct lustre_quota_info *lqi, int type) return 0; } -static void disk2memdqb(struct mem_dqblk *m, struct lustre_disk_dqblk *d) +#define DQ2MQ(v) ((sizeof(v) == sizeof(__u64)) ? \ + le64_to_cpu(v) : le32_to_cpu(v)) + +#define MQ2DQ(v,newv) ((sizeof(v) == sizeof(__u64)) ? \ + (v = cpu_to_le64((__u64)newv)) : (v = cpu_to_le32((__u32)newv))) + +#define DQF_GET(var,ver,field) ((ver == LUSTRE_QUOTA_V1)?\ + DQ2MQ(((struct lustre_disk_dqblk*)(var))->field):\ + DQ2MQ(((struct lustre_disk_dqblk_v2*)(var))->field)) + +#define DQF_PUT(var,ver,field,val) ((ver == LUSTRE_QUOTA_V1)?\ + MQ2DQ(((struct lustre_disk_dqblk*)(var))->field, val):\ + MQ2DQ(((struct lustre_disk_dqblk_v2*)(var))->field, val)) + +void disk2memdqb(struct lustre_mem_dqblk *m, void *d, + lustre_quota_version_t version) { - m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit); - m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit); - m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes); - m->dqb_itime = le64_to_cpu(d->dqb_itime); - m->dqb_bhardlimit = le32_to_cpu(d->dqb_bhardlimit); - m->dqb_bsoftlimit = le32_to_cpu(d->dqb_bsoftlimit); - m->dqb_curspace = le64_to_cpu(d->dqb_curspace); - m->dqb_btime = le64_to_cpu(d->dqb_btime); + m->dqb_ihardlimit = DQF_GET(d, version, dqb_ihardlimit); + m->dqb_isoftlimit = DQF_GET(d, version, dqb_isoftlimit); + m->dqb_curinodes = DQF_GET(d, version, dqb_curinodes); + m->dqb_itime = DQF_GET(d, version, dqb_itime); + m->dqb_bhardlimit = DQF_GET(d, version, dqb_bhardlimit); + m->dqb_bsoftlimit = DQF_GET(d, version, dqb_bsoftlimit); + m->dqb_curspace = DQF_GET(d, version, dqb_curspace); + m->dqb_btime = DQF_GET(d, version, dqb_btime); } -static void mem2diskdqb(struct lustre_disk_dqblk *d, struct mem_dqblk *m, - qid_t id) +static int check_quota_bounds(struct lustre_mem_dqblk *m, + lustre_quota_version_t version) { - d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit); - d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit); - d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes); - d->dqb_itime = cpu_to_le64(m->dqb_itime); - d->dqb_bhardlimit = cpu_to_le32(m->dqb_bhardlimit); - d->dqb_bsoftlimit = cpu_to_le32(m->dqb_bsoftlimit); - d->dqb_curspace = cpu_to_le64(m->dqb_curspace); - d->dqb_btime = cpu_to_le64(m->dqb_btime); - d->dqb_id = cpu_to_le32(id); + return (version == LUSTRE_QUOTA_V1 && + m->dqb_ihardlimit <= MAX_UL && + m->dqb_isoftlimit <= MAX_UL && + m->dqb_curinodes <= MAX_UL && + m->dqb_bhardlimit <= MAX_UL && + m->dqb_bsoftlimit <= MAX_UL) || + version != LUSTRE_QUOTA_V1; } -static dqbuf_t getdqbuf(void) +static int mem2diskdqb(void *d, struct lustre_mem_dqblk *m, + qid_t id, lustre_quota_version_t version) +{ + if (!check_quota_bounds(m, version)) + return -EINVAL; + + DQF_PUT(d, version, dqb_ihardlimit, m->dqb_ihardlimit); + DQF_PUT(d, version, dqb_isoftlimit, m->dqb_isoftlimit); + DQF_PUT(d, version, dqb_curinodes, m->dqb_curinodes); + DQF_PUT(d, version, dqb_itime, m->dqb_itime); + DQF_PUT(d, version, dqb_bhardlimit, m->dqb_bhardlimit); + DQF_PUT(d, version, dqb_bsoftlimit, m->dqb_bsoftlimit); + DQF_PUT(d, version, dqb_curspace, m->dqb_curspace); + DQF_PUT(d, version, dqb_btime, m->dqb_btime); + DQF_PUT(d, version, dqb_id, id); + + return 0; +} + +dqbuf_t getdqbuf(void) { dqbuf_t buf = kmalloc(LUSTRE_DQBLKSIZE, GFP_NOFS); if (!buf) @@ -166,12 +213,12 @@ static dqbuf_t getdqbuf(void) return buf; } -static inline void freedqbuf(dqbuf_t buf) +void freedqbuf(dqbuf_t buf) { kfree(buf); } -static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf) +ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf) { mm_segment_t fs; ssize_t ret; @@ -185,7 +232,7 @@ static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf) return ret; } -static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf) +ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf) { mm_segment_t fs; ssize_t ret; @@ -196,18 +243,15 @@ static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf) ret = filp->f_op->write(filp, (char *)buf, LUSTRE_DQBLKSIZE, &offset); set_fs(fs); return ret; - } -static void lustre_mark_info_dirty(struct lustre_mem_dqinfo *info) +void lustre_mark_info_dirty(struct lustre_mem_dqinfo *info) { set_bit(DQF_INFO_DIRTY_B, &info->dqi_flags); } -#define lustre_info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) - /* Remove empty block from list and return it */ -static int get_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info) +int get_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info) { dqbuf_t buf = getdqbuf(); struct lustre_disk_dqdbheader *dh = @@ -223,7 +267,8 @@ static int get_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info) info->dqi_free_blk = le32_to_cpu(dh->dqdh_next_free); } else { memset(buf, 0, LUSTRE_DQBLKSIZE); - if ((ret = write_blk(filp, info->dqi_blocks, buf)) < 0) /* Assure block allocation... */ + /* Assure block allocation... */ + if ((ret = write_blk(filp, info->dqi_blocks, buf)) < 0) goto out_buf; blk = info->dqi_blocks++; } @@ -235,8 +280,8 @@ out_buf: } /* Insert empty block to the list */ -static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info, - dqbuf_t buf, uint blk) +int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info, + dqbuf_t buf, uint blk) { struct lustre_disk_dqdbheader *dh = (struct lustre_disk_dqdbheader *)buf; @@ -254,9 +299,9 @@ static int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info, } /* Remove given block from the list of blocks with free entries */ -static int remove_free_dqentry(struct file *filp, - struct lustre_mem_dqinfo *info, dqbuf_t buf, - uint blk) +int remove_free_dqentry(struct file *filp, + struct lustre_mem_dqinfo *info, dqbuf_t buf, + uint blk) { dqbuf_t tmpbuf = getdqbuf(); struct lustre_disk_dqdbheader *dh = @@ -288,9 +333,10 @@ static int remove_free_dqentry(struct file *filp, } freedqbuf(tmpbuf); dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0); - if (write_blk(filp, blk, buf) < 0) /* No matter whether write succeeds block is out of list */ - printk(KERN_ERR - "VFS: Can't write block (%u) with free entries.\n", blk); + err = write_blk(filp, blk, buf); + if (err < 0) /* No matter whether write succeeds block is out of list */ + CERROR("VFS: Can't write block (%u) with " + "free entries (rc=%d).\n", blk, err); return 0; out_buf: freedqbuf(tmpbuf); @@ -298,9 +344,9 @@ out_buf: } /* Insert given block to the beginning of list with free entries */ -static int insert_free_dqentry(struct file *filp, - struct lustre_mem_dqinfo *info, dqbuf_t buf, - uint blk) +int insert_free_dqentry(struct file *filp, + struct lustre_mem_dqinfo *info, dqbuf_t buf, + uint blk) { dqbuf_t tmpbuf = getdqbuf(); struct lustre_disk_dqdbheader *dh = @@ -330,16 +376,21 @@ out_buf: return err; } + + /* Find space for dquot */ -static uint find_free_dqentry(struct lustre_dquot *dquot, int *err) +static uint find_free_dqentry(struct lustre_dquot *dquot, int *err, + lustre_quota_version_t version) { struct lustre_quota_info *lqi = dquot->dq_info; struct file *filp = lqi->qi_files[dquot->dq_type]; struct lustre_mem_dqinfo *info = &lqi->qi_info[dquot->dq_type]; uint blk, i; struct lustre_disk_dqdbheader *dh; - struct lustre_disk_dqblk *ddquot; - struct lustre_disk_dqblk fakedquot; + void *ddquot; + int dqblk_sz = lustre_disk_dqblk_sz[version]; + int dqstrinblk = lustre_dqstrinblk[version]; + char fakedquot[dqblk_sz]; dqbuf_t buf; *err = 0; @@ -348,7 +399,7 @@ static uint find_free_dqentry(struct lustre_dquot *dquot, int *err) return 0; } dh = (struct lustre_disk_dqdbheader *)buf; - ddquot = GETENTRIES(buf); + ddquot = GETENTRIES(buf, version); if (info->dqi_free_entry) { blk = info->dqi_free_entry; if ((*err = read_blk(filp, blk, buf)) < 0) @@ -361,39 +412,40 @@ static uint find_free_dqentry(struct lustre_dquot *dquot, int *err) return 0; } memset(buf, 0, LUSTRE_DQBLKSIZE); - info->dqi_free_entry = blk; /* This is enough as block is already zeroed and entry list is empty... */ + info->dqi_free_entry = blk; /* This is enough as block is + already zeroed and entry list + is empty... */ lustre_mark_info_dirty(info); } - if (le16_to_cpu(dh->dqdh_entries) + 1 >= LUSTRE_DQSTRINBLK) /* Block will be full? */ + + /* Will block be full */ + if (le16_to_cpu(dh->dqdh_entries) + 1 >= dqstrinblk) if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) { - printk(KERN_ERR - "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", - blk); + CERROR("VFS: Can't remove block %u" + " from entry free list.\n", blk); goto out_buf; } dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries) + 1); - memset(&fakedquot, 0, sizeof(struct lustre_disk_dqblk)); + memset(fakedquot, 0, dqblk_sz); /* Find free structure in block */ - for (i = 0; i < LUSTRE_DQSTRINBLK && - memcmp(&fakedquot, ddquot + i, sizeof(fakedquot)); i++) ; + for (i = 0; i < dqstrinblk && + memcmp(fakedquot, (char*)ddquot + i * dqblk_sz, + sizeof(fakedquot)); i++); - if (i == LUSTRE_DQSTRINBLK) { - printk(KERN_ERR - "VFS: find_free_dqentry(): Data block full but it shouldn't.\n"); + if (i == dqstrinblk) { + CERROR("VFS: Data block full but it shouldn't.\n"); *err = -EIO; goto out_buf; } if ((*err = write_blk(filp, blk, buf)) < 0) { - printk(KERN_ERR - "VFS: find_free_dqentry(): Can't write quota data block %u.\n", - blk); + CERROR("VFS: Can't write quota data block %u.\n", blk); goto out_buf; } dquot->dq_off = (blk << LUSTRE_DQBLKSIZE_BITS) + sizeof(struct lustre_disk_dqdbheader) + - i * sizeof(struct lustre_disk_dqblk); + i * dqblk_sz; freedqbuf(buf); return blk; out_buf: @@ -402,7 +454,8 @@ out_buf: } /* Insert reference to structure into the trie */ -static int do_insert_tree(struct lustre_dquot *dquot, uint * treeblk, int depth) +static int do_insert_tree(struct lustre_dquot *dquot, uint * treeblk, int depth, + lustre_quota_version_t version) { struct lustre_quota_info *lqi = dquot->dq_info; struct file *filp = lqi->qi_files[dquot->dq_type]; @@ -423,8 +476,7 @@ static int do_insert_tree(struct lustre_dquot *dquot, uint * treeblk, int depth) newact = 1; } else { if ((ret = read_blk(filp, *treeblk, buf)) < 0) { - printk(KERN_ERR - "VFS: Can't read tree quota block %u.\n", + CERROR("VFS: Can't read tree quota block %u.\n", *treeblk); goto out_buf; } @@ -436,16 +488,16 @@ static int do_insert_tree(struct lustre_dquot *dquot, uint * treeblk, int depth) if (depth == LUSTRE_DQTREEDEPTH - 1) { if (newblk) { - printk(KERN_ERR - "VFS: Inserting already present quota entry (block %u).\n", + CERROR("VFS: Inserting already present quota entry " + "(block %u).\n", ref[GETIDINDEX(dquot->dq_id, depth)]); ret = -EIO; goto out_buf; } - newblk = find_free_dqentry(dquot, &ret); + newblk = find_free_dqentry(dquot, &ret, version); } else - ret = do_insert_tree(dquot, &newblk, depth + 1); + ret = do_insert_tree(dquot, &newblk, depth + 1, version); if (newson && ret >= 0) { ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk); ret = write_blk(filp, *treeblk, buf); @@ -457,46 +509,51 @@ out_buf: } /* Wrapper for inserting quota structure into tree */ -static inline int dq_insert_tree(struct lustre_dquot *dquot) +static inline int dq_insert_tree(struct lustre_dquot *dquot, + lustre_quota_version_t version) { int tmp = LUSTRE_DQTREEOFF; - return do_insert_tree(dquot, &tmp, 0); + return do_insert_tree(dquot, &tmp, 0, version); } /* - * We don't have to be afraid of deadlocks as we never have quotas on quota files... + * We don't have to be afraid of deadlocks as we never have quotas on quota files... */ -static int lustre_write_dquot(struct lustre_dquot *dquot) +static int lustre_write_dquot(struct lustre_dquot *dquot, + lustre_quota_version_t version) { int type = dquot->dq_type; struct file *filp; mm_segment_t fs; loff_t offset; ssize_t ret; - struct lustre_disk_dqblk ddquot, empty; + int dqblk_sz = lustre_disk_dqblk_sz[version]; + char ddquot[dqblk_sz], empty[dqblk_sz]; + + ret = mem2diskdqb(ddquot, &dquot->dq_dqb, dquot->dq_id, version); + if (ret < 0) + return ret; if (!dquot->dq_off) - if ((ret = dq_insert_tree(dquot)) < 0) { - printk(KERN_ERR - "VFS: Error %Zd occurred while creating quota.\n", + if ((ret = dq_insert_tree(dquot, version)) < 0) { + CERROR("VFS: Error %Zd occurred while creating quota.\n", ret); return ret; } filp = dquot->dq_info->qi_files[type]; offset = dquot->dq_off; - mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id); /* Argh... We may need to write structure full of zeroes but that would be * treated as an empty place by the rest of the code. Format change would * be definitely cleaner but the problems probably are not worth it */ - memset(&empty, 0, sizeof(struct lustre_disk_dqblk)); - if (!memcmp(&empty, &ddquot, sizeof(struct lustre_disk_dqblk))) - ddquot.dqb_itime = cpu_to_le64(1); + memset(empty, 0, dqblk_sz); + if (!memcmp(empty, ddquot, dqblk_sz)) + DQF_PUT(ddquot, version, dqb_itime, 1); fs = get_fs(); set_fs(KERNEL_DS); - ret = filp->f_op->write(filp, (char *)&ddquot, - sizeof(struct lustre_disk_dqblk), &offset); + ret = filp->f_op->write(filp, ddquot, + dqblk_sz, &offset); set_fs(fs); - if (ret != sizeof(struct lustre_disk_dqblk)) { + if (ret != dqblk_sz) { printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", filp->f_dentry->d_sb->s_id); if (ret >= 0) @@ -508,25 +565,27 @@ static int lustre_write_dquot(struct lustre_dquot *dquot) } /* Free dquot entry in data block */ -static int free_dqentry(struct lustre_dquot *dquot, uint blk) +static int free_dqentry(struct lustre_dquot *dquot, uint blk, + lustre_quota_version_t version) { struct file *filp = dquot->dq_info->qi_files[dquot->dq_type]; struct lustre_mem_dqinfo *info = &dquot->dq_info->qi_info[dquot->dq_type]; struct lustre_disk_dqdbheader *dh; dqbuf_t buf = getdqbuf(); + int dqstrinblk = lustre_dqstrinblk[version]; int ret = 0; if (!buf) return -ENOMEM; if (dquot->dq_off >> LUSTRE_DQBLKSIZE_BITS != blk) { - printk(KERN_ERR - "VFS: Quota structure has offset to other block (%u) than it should (%u).\n", - blk, (uint) (dquot->dq_off >> LUSTRE_DQBLKSIZE_BITS)); + CERROR("VFS: Quota structure has offset to other block (%u) " + "than it should (%u).\n", blk, + (uint)(dquot->dq_off >> LUSTRE_DQBLKSIZE_BITS)); goto out_buf; } if ((ret = read_blk(filp, blk, buf)) < 0) { - printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk); + CERROR("VFS: Can't read quota data block %u\n", blk); goto out_buf; } dh = (struct lustre_disk_dqdbheader *)buf; @@ -534,27 +593,23 @@ static int free_dqentry(struct lustre_dquot *dquot, uint blk) if (!le16_to_cpu(dh->dqdh_entries)) { /* Block got free? */ if ((ret = remove_free_dqentry(filp, info, buf, blk)) < 0 || (ret = put_free_dqblk(filp, info, buf, blk)) < 0) { - printk(KERN_ERR - "VFS: Can't move quota data block (%u) to free list.\n", - blk); + CERROR("VFS: Can't move quota data block (%u) " + "to free list.\n", blk); goto out_buf; } } else { - memset(buf + - (dquot->dq_off & ((1 << LUSTRE_DQBLKSIZE_BITS) - 1)), 0, - sizeof(struct lustre_disk_dqblk)); - if (le16_to_cpu(dh->dqdh_entries) == LUSTRE_DQSTRINBLK - 1) { + memset(buf + (dquot->dq_off & ((1<<LUSTRE_DQBLKSIZE_BITS) - 1)), + 0, lustre_disk_dqblk_sz[version]); + if (le16_to_cpu(dh->dqdh_entries) == dqstrinblk - 1) { /* Insert will write block itself */ if ((ret = insert_free_dqentry(filp, info, buf, blk)) < 0) { - printk(KERN_ERR - "VFS: Can't insert quota data block (%u) to free entry list.\n", - blk); + CERROR("VFS: Can't insert quota data block (%u) " + "to free entry list.\n", blk); goto out_buf; } } else if ((ret = write_blk(filp, blk, buf)) < 0) { - printk(KERN_ERR - "VFS: Can't write quota data block %u\n", blk); + CERROR("VFS: Can't write quota data block %u\n", blk); goto out_buf; } } @@ -565,7 +620,8 @@ out_buf: } /* Remove reference to dquot from tree */ -static int remove_tree(struct lustre_dquot *dquot, uint * blk, int depth) +static int remove_tree(struct lustre_dquot *dquot, uint * blk, int depth, + lustre_quota_version_t version) { struct file *filp = dquot->dq_info->qi_files[dquot->dq_type]; struct lustre_mem_dqinfo *info = @@ -578,26 +634,26 @@ static int remove_tree(struct lustre_dquot *dquot, uint * blk, int depth) if (!buf) return -ENOMEM; if ((ret = read_blk(filp, *blk, buf)) < 0) { - printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk); + CERROR("VFS: Can't read quota data block %u\n", *blk); goto out_buf; } newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]); if (depth == LUSTRE_DQTREEDEPTH - 1) { - ret = free_dqentry(dquot, newblk); + ret = free_dqentry(dquot, newblk, version); newblk = 0; } else - ret = remove_tree(dquot, &newblk, depth + 1); + ret = remove_tree(dquot, &newblk, depth + 1, version); if (ret >= 0 && !newblk) { int i; ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0); - for (i = 0; i < LUSTRE_DQBLKSIZE && !buf[i]; i++) ; /* Block got empty? */ + for (i = 0; i < LUSTRE_DQBLKSIZE && !buf[i]; i++) + /* Block got empty? */ ; /* don't put the root block into free blk list! */ if (i == LUSTRE_DQBLKSIZE && *blk != LUSTRE_DQTREEOFF) { put_free_dqblk(filp, info, buf, *blk); *blk = 0; } else if ((ret = write_blk(filp, *blk, buf)) < 0) - printk(KERN_ERR - "VFS: Can't write quota tree block %u.\n", *blk); + CERROR("VFS: Can't write quota tree block %u.\n", *blk); } out_buf: freedqbuf(buf); @@ -605,47 +661,50 @@ out_buf: } /* Delete dquot from tree */ -static int lustre_delete_dquot(struct lustre_dquot *dquot) +static int lustre_delete_dquot(struct lustre_dquot *dquot, + lustre_quota_version_t version) { uint tmp = LUSTRE_DQTREEOFF; if (!dquot->dq_off) /* Even not allocated? */ return 0; - return remove_tree(dquot, &tmp, 0); + return remove_tree(dquot, &tmp, 0, version); } /* Find entry in block */ -static loff_t find_block_dqentry(struct lustre_dquot *dquot, uint blk) +static loff_t find_block_dqentry(struct lustre_dquot *dquot, uint blk, + lustre_quota_version_t version) { struct file *filp = dquot->dq_info->qi_files[dquot->dq_type]; dqbuf_t buf = getdqbuf(); loff_t ret = 0; int i; - struct lustre_disk_dqblk *ddquot = GETENTRIES(buf); + char *ddquot = GETENTRIES(buf, version); + int dqblk_sz = lustre_disk_dqblk_sz[version]; + int dqstrinblk = lustre_dqstrinblk[version]; if (!buf) return -ENOMEM; if ((ret = read_blk(filp, blk, buf)) < 0) { - printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); + CERROR("VFS: Can't read quota tree block %u.\n", blk); goto out_buf; } if (dquot->dq_id) - for (i = 0; - i < LUSTRE_DQSTRINBLK - && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++) ; + for (i = 0; i < dqstrinblk && + DQF_GET(ddquot+i*dqblk_sz, version, dqb_id) != dquot->dq_id; + i++) ; else { /* ID 0 as a bit more complicated searching... */ - struct lustre_disk_dqblk fakedquot; + char fakedquot[dqblk_sz]; - memset(&fakedquot, 0, sizeof(struct lustre_disk_dqblk)); - for (i = 0; i < LUSTRE_DQSTRINBLK; i++) - if (!le32_to_cpu(ddquot[i].dqb_id) - && memcmp(&fakedquot, ddquot + i, - sizeof(struct lustre_disk_dqblk))) + memset(fakedquot, 0, sizeof(fakedquot)); + for (i = 0; i < dqstrinblk; i++) + if (!DQF_GET(ddquot + i*dqblk_sz, version, dqb_id) + && memcmp(fakedquot, ddquot + i*dqblk_sz, + dqblk_sz)) break; } - if (i == LUSTRE_DQSTRINBLK) { - printk(KERN_ERR - "VFS: Quota for id %u referenced but not present.\n", + if (i == dqstrinblk) { + CERROR("VFS: Quota for id %u referenced but not present.\n", dquot->dq_id); ret = -EIO; goto out_buf; @@ -653,14 +712,15 @@ static loff_t find_block_dqentry(struct lustre_dquot *dquot, uint blk) ret = (blk << LUSTRE_DQBLKSIZE_BITS) + sizeof(struct lustre_disk_dqdbheader) + - i * sizeof(struct lustre_disk_dqblk); + i * dqblk_sz; out_buf: freedqbuf(buf); return ret; } /* Find entry for given id in the tree */ -static loff_t find_tree_dqentry(struct lustre_dquot *dquot, uint blk, int depth) +static loff_t find_tree_dqentry(struct lustre_dquot *dquot, uint blk, int depth, + lustre_quota_version_t version) { struct file *filp = dquot->dq_info->qi_files[dquot->dq_type]; dqbuf_t buf = getdqbuf(); @@ -670,7 +730,7 @@ static loff_t find_tree_dqentry(struct lustre_dquot *dquot, uint blk, int depth) if (!buf) return -ENOMEM; if ((ret = read_blk(filp, blk, buf)) < 0) { - printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); + CERROR("VFS: Can't read quota tree block %u.\n", blk); goto out_buf; } ret = 0; @@ -678,18 +738,19 @@ static loff_t find_tree_dqentry(struct lustre_dquot *dquot, uint blk, int depth) if (!blk) /* No reference? */ goto out_buf; if (depth < LUSTRE_DQTREEDEPTH - 1) - ret = find_tree_dqentry(dquot, blk, depth + 1); + ret = find_tree_dqentry(dquot, blk, depth + 1, version); else - ret = find_block_dqentry(dquot, blk); + ret = find_block_dqentry(dquot, blk, version); out_buf: freedqbuf(buf); return ret; } /* Find entry for given id in the tree - wrapper function */ -static inline loff_t find_dqentry(struct lustre_dquot *dquot) +static inline loff_t find_dqentry(struct lustre_dquot *dquot, + lustre_quota_version_t version) { - return find_tree_dqentry(dquot, LUSTRE_DQTREEOFF, 0); + return find_tree_dqentry(dquot, LUSTRE_DQTREEOFF, 0, version); } int lustre_read_dquot(struct lustre_dquot *dquot) @@ -698,50 +759,50 @@ int lustre_read_dquot(struct lustre_dquot *dquot) struct file *filp; mm_segment_t fs; loff_t offset; - struct lustre_disk_dqblk ddquot, empty; - int ret = 0; + int ret = 0, dqblk_sz; + lustre_quota_version_t version; /* Invalidated quota? */ if (!dquot->dq_info || !(filp = dquot->dq_info->qi_files[type])) { - printk(KERN_ERR "VFS: Quota invalidated while reading!\n"); + CERROR("VFS: Quota invalidated while reading!\n"); return -EIO; } - offset = find_dqentry(dquot); + version = dquot->dq_info->qi_version; + dqblk_sz = lustre_disk_dqblk_sz[version]; + + offset = find_dqentry(dquot, version); if (offset <= 0) { /* Entry not present? */ if (offset < 0) - printk(KERN_ERR - "VFS: Can't read quota structure for id %u.\n", + CERROR("VFS: Can't read quota structure for id %u.\n", dquot->dq_id); dquot->dq_off = 0; set_bit(DQ_FAKE_B, &dquot->dq_flags); - memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk)); + memset(&dquot->dq_dqb, 0, sizeof(struct lustre_mem_dqblk)); ret = offset; } else { + char ddquot[dqblk_sz], empty[dqblk_sz]; + dquot->dq_off = offset; fs = get_fs(); set_fs(KERNEL_DS); - if ((ret = filp->f_op->read(filp, (char *)&ddquot, - sizeof(struct lustre_disk_dqblk), - &offset)) != - sizeof(struct lustre_disk_dqblk)) { + if ((ret = filp->f_op->read(filp, ddquot, dqblk_sz, &offset)) != + dqblk_sz) { if (ret >= 0) ret = -EIO; - printk(KERN_ERR - "VFS: Error while reading quota structure for id %u.\n", - dquot->dq_id); - memset(&ddquot, 0, sizeof(struct lustre_disk_dqblk)); + CERROR("VFS: Error while reading quota structure " + "for id %u.\n", dquot->dq_id); + memset(ddquot, 0, dqblk_sz); } else { ret = 0; /* We need to escape back all-zero structure */ - memset(&empty, 0, sizeof(struct lustre_disk_dqblk)); - empty.dqb_itime = cpu_to_le64(1); - if (!memcmp(&empty, &ddquot, - sizeof(struct lustre_disk_dqblk))) - ddquot.dqb_itime = 0; + memset(empty, 0, dqblk_sz); + DQF_PUT(empty, version, dqb_itime, 1); + if (!memcmp(empty, ddquot, dqblk_sz)) + DQF_PUT(ddquot, version, dqb_itime, 0); } set_fs(fs); - disk2memdqb(&dquot->dq_dqb, &ddquot); + disk2memdqb(&dquot->dq_dqb, ddquot, version); } return ret; @@ -751,6 +812,8 @@ int lustre_read_dquot(struct lustre_dquot *dquot) int lustre_commit_dquot(struct lustre_dquot *dquot) { int rc = 0; + lustre_quota_version_t version = dquot->dq_info->qi_version; + /* always clear the flag so we don't loop on an IO error... */ clear_bit(DQ_MOD_B, &dquot->dq_flags); @@ -758,9 +821,9 @@ int lustre_commit_dquot(struct lustre_dquot *dquot) * over all cluster, so keep the fake dquot entry on disk is * meaningless, just remove it */ if (test_bit(DQ_FAKE_B, &dquot->dq_flags)) - rc = lustre_delete_dquot(dquot); + rc = lustre_delete_dquot(dquot, version); else - rc = lustre_write_dquot(dquot); + rc = lustre_write_dquot(dquot, version); if (rc < 0) return rc; @@ -771,29 +834,41 @@ int lustre_commit_dquot(struct lustre_dquot *dquot) return rc; } -/* We need to export this function to initialize quotafile, because we haven't - * user level check utility */ -int lustre_init_quota_info(struct lustre_quota_info *lqi, int type) +int lustre_init_quota_header(struct lustre_quota_info *lqi, int type, int fakemagics) { - struct lustre_mem_dqinfo *dqinfo = &lqi->qi_info[type]; + static const uint quota_magics[] = LUSTRE_INITQMAGICS; + static const uint fake_magics[] = LUSTRE_BADQMAGICS; + const uint* quota_versions = lustre_initqversions[lqi->qi_version]; struct lustre_disk_dqheader dqhead; - struct file *fp = lqi->qi_files[type]; ssize_t size; loff_t offset = 0; + struct file *fp = lqi->qi_files[type]; int rc = 0; - static const uint quota_magics[] = LUSTRE_INITQMAGICS; - static const uint quota_versions[] = LUSTRE_INITQVERSIONS; /* write quotafile header */ - dqhead.dqh_magic = cpu_to_le32(quota_magics[type]); + dqhead.dqh_magic = cpu_to_le32(fakemagics ? + fake_magics[type] : quota_magics[type]); dqhead.dqh_version = cpu_to_le32(quota_versions[type]); size = fp->f_op->write(fp, (char *)&dqhead, sizeof(struct lustre_disk_dqheader), &offset); if (size != sizeof(struct lustre_disk_dqheader)) { - printk(KERN_ERR "error writing quoafile header (rc:%d)\n", rc); + CERROR("error writing quotafile header (rc:%d)\n", rc); rc = size; } + + return rc; +} + +/* We need to export this function to initialize quotafile, because we haven't + * user level check utility */ +int lustre_init_quota_info_generic(struct lustre_quota_info *lqi, int type, + int fakemagics) +{ + struct lustre_mem_dqinfo *dqinfo = &lqi->qi_info[type]; + int rc; + + rc = lustre_init_quota_header(lqi, type, fakemagics); if (rc) return rc; @@ -806,13 +881,13 @@ int lustre_init_quota_info(struct lustre_quota_info *lqi, int type) return lustre_write_quota_info(lqi, type); } -struct dqblk { - struct list_head link; - uint blk; -}; +int lustre_init_quota_info(struct lustre_quota_info *lqi, int type) +{ + return lustre_init_quota_info_generic(lqi, type, 0); +} -static ssize_t quota_read(struct file *file, struct inode *inode, int type, - uint blk, dqbuf_t buf) +ssize_t quota_read(struct file *file, struct inode *inode, int type, + uint blk, dqbuf_t buf) { if (file) { return read_blk(file, blk, buf); @@ -843,7 +918,7 @@ static int walk_block_dqentry(struct file *filp, struct inode *inode, int type, if (!buf) return -ENOMEM; if ((ret = quota_read(filp, inode, type, blk, buf)) < 0) { - printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); + CERROR("VFS: Can't read quota tree block %u.\n", blk); goto out_buf; } ret = 0; @@ -880,8 +955,8 @@ out_buf: return ret; } -static int walk_tree_dqentry(struct file *filp, struct inode *inode, int type, - uint blk, int depth, struct list_head *list) +int walk_tree_dqentry(struct file *filp, struct inode *inode, int type, + uint blk, int depth, struct list_head *list) { dqbuf_t buf = getdqbuf(); loff_t ret = 0; @@ -891,7 +966,7 @@ static int walk_tree_dqentry(struct file *filp, struct inode *inode, int type, if (!buf) return -ENOMEM; if ((ret = quota_read(filp, inode, type, blk, buf)) < 0) { - printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk); + CERROR("VFS: Can't read quota tree block %u.\n", blk); goto out_buf; } ret = 0; @@ -902,7 +977,7 @@ static int walk_tree_dqentry(struct file *filp, struct inode *inode, int type, continue; if (depth < LUSTRE_DQTREEDEPTH - 1) - ret = walk_tree_dqentry(filp, inode, type, blk, + ret = walk_tree_dqentry(filp, inode, type, blk, depth + 1, list); else ret = walk_block_dqentry(filp, inode, type, blk, list); @@ -919,61 +994,65 @@ int lustre_get_qids(struct file *fp, struct inode *inode, int type, struct list_head blk_list; struct dqblk *blk_item, *tmp; dqbuf_t buf = NULL; - struct lustre_disk_dqblk *ddquot; + char *ddquot; int rc; + lustre_quota_version_t version; - if (!check_quota_file(fp, inode, type)) { - printk(KERN_ERR "unknown quota file format!\n"); - return -EINVAL; + ENTRY; + + if (check_quota_file(fp, inode, type, LUSTRE_QUOTA_V1) == 0) + version = LUSTRE_QUOTA_V1; + else if (check_quota_file(fp, inode, type, LUSTRE_QUOTA_V2) == 0) + version = LUSTRE_QUOTA_V2; + else { + CERROR("unknown quota file format!\n"); + RETURN(-EINVAL); } + if (!list_empty(list)) { - printk(KERN_ERR "not empty list\n"); - return -EINVAL; + CERROR("not empty list\n"); + RETURN(-EINVAL); } INIT_LIST_HEAD(&blk_list); rc = walk_tree_dqentry(fp, inode, type, LUSTRE_DQTREEOFF, 0, &blk_list); if (rc) { - printk(KERN_ERR "walk through quota file failed!(%d)\n", rc); - goto out_free; + CERROR("walk through quota file failed!(%d)\n", rc); + GOTO(out_free, rc); } if (list_empty(&blk_list)) - return 0; + RETURN(0); buf = getdqbuf(); if (!buf) - return -ENOMEM; - ddquot = GETENTRIES(buf); + RETURN(-ENOMEM); + ddquot = GETENTRIES(buf, version); list_for_each_entry(blk_item, &blk_list, link) { loff_t ret = 0; - int i; - struct lustre_disk_dqblk fakedquot; + int i, dqblk_sz = lustre_disk_dqblk_sz[version]; + char fakedquot[dqblk_sz]; memset(buf, 0, LUSTRE_DQBLKSIZE); if ((ret = quota_read(fp, inode, type, blk_item->blk, buf))<0) { - printk(KERN_ERR - "VFS: Can't read quota tree block %u.\n", + CERROR("VFS: Can't read quota tree block %u.\n", blk_item->blk); - rc = ret; - goto out_free; + GOTO(out_free, rc = ret); } - memset(&fakedquot, 0, sizeof(struct lustre_disk_dqblk)); - for (i = 0; i < LUSTRE_DQSTRINBLK; i++) { + memset(fakedquot, 0, dqblk_sz); + for (i = 0; i < lustre_dqstrinblk[version]; i++) { struct dquot_id *dqid; /* skip empty entry */ - if (!memcmp - (&fakedquot, ddquot + i, - sizeof(struct lustre_disk_dqblk))) + if (!memcmp(fakedquot, ddquot + i*dqblk_sz, dqblk_sz)) continue; dqid = kmalloc(sizeof(*dqid), GFP_NOFS); - if (!dqid) { - rc = -ENOMEM; - goto out_free; - } - dqid->di_id = le32_to_cpu(ddquot[i].dqb_id); + if (!dqid) + GOTO(out_free, rc = -ENOMEM); + + dqid->di_id = DQF_GET(ddquot + i * dqblk_sz, + version, dqb_id); INIT_LIST_HEAD(&dqid->di_link); list_add(&dqid->di_link, list); } @@ -986,12 +1065,14 @@ out_free: } if (buf) freedqbuf(buf); - return rc; + + RETURN(rc); } -EXPORT_SYMBOL(lustre_check_quota_file); + EXPORT_SYMBOL(lustre_read_quota_info); EXPORT_SYMBOL(lustre_write_quota_info); +EXPORT_SYMBOL(lustre_check_quota_file); EXPORT_SYMBOL(lustre_read_dquot); EXPORT_SYMBOL(lustre_commit_dquot); EXPORT_SYMBOL(lustre_init_quota_info); diff --git a/lustre/lvfs/lustre_quota_fmt.h b/lustre/lvfs/lustre_quota_fmt.h index 28f5c15cb6f382f86d9bdd5e187a91662a88b8a8..78a22f7685a5d02957707faf13d57f08f7ef63ad 100644 --- a/lustre/lvfs/lustre_quota_fmt.h +++ b/lustre/lvfs/lustre_quota_fmt.h @@ -17,19 +17,26 @@ * Same with quota v2's magic */ #define LUSTRE_INITQMAGICS {\ - 0xd9c01f11, /* USRQUOTA */\ - 0xd9c01927 /* GRPQUOTA */\ + 0xd9c01f11, /* USRQUOTA */\ + 0xd9c01927 /* GRPQUOTA */\ } +/* Invalid magics that mark quota file as inconsistent */ +#define LUSTRE_BADQMAGICS {\ + 0xbadbadba, /* USRQUOTA */\ + 0xbadbadba /* GRPQUOTA */\ +} + +/* for the verson 1 of lustre_disk_dqblk*/ #define LUSTRE_INITQVERSIONS {\ - 0, /* USRQUOTA */\ - 0 /* GRPQUOTA */\ + 0, /* USRQUOTA */\ + 0 /* GRPQUOTA */\ } /* * The following structure defines the format of the disk quota file * (as it appears on disk) - the file is a radix tree whose leaves point - * to blocks of these structures. + * to blocks of these structures. for the version 1. */ struct lustre_disk_dqblk { __u32 dqb_id; /* id this quota applies to */ @@ -43,6 +50,45 @@ struct lustre_disk_dqblk { __u64 dqb_itime; /* time limit for excessive inode use */ }; +/* Number of entries in one blocks(21 entries) */ +#define LUSTRE_DQSTRINBLK \ + ((LUSTRE_DQBLKSIZE - sizeof(struct lustre_disk_dqdbheader)) \ + / sizeof(struct lustre_disk_dqblk)) +#define GETENTRIES_V1(buf) (((char *)buf)+sizeof(struct lustre_disk_dqdbheader)) + +/* for the verson 2 of lustre_disk_dqblk*/ +#define LUSTRE_INITQVERSIONS_V2 {\ + 1, /* USRQUOTA */\ + 1 /* GRPQUOTA */\ +} + +/* + * The following structure defines the format of the disk quota file + * (as it appears on disk) - the file is a radix tree whose leaves point + * to blocks of these structures. for the version 2. + */ +struct lustre_disk_dqblk_v2 { + __u32 dqb_id; /* id this quota applies to */ + __u32 padding; + __u64 dqb_ihardlimit; /* absolute limit on allocated inodes */ + __u64 dqb_isoftlimit; /* preferred inode limit */ + __u64 dqb_curinodes; /* current # allocated inodes */ + __u64 dqb_bhardlimit; /* absolute limit on disk space (in QUOTABLOCK_SIZE) */ + __u64 dqb_bsoftlimit; /* preferred limit on disk space (in QUOTABLOCK_SIZE) */ + __u64 dqb_curspace; /* current space occupied (in bytes) */ + __u64 dqb_btime; /* time limit for excessive disk use */ + __u64 dqb_itime; /* time limit for excessive inode use */ +}; + +/* Number of entries in one blocks(14 entries) */ +#define LUSTRE_DQSTRINBLK_V2 \ + ((LUSTRE_DQBLKSIZE - sizeof(struct lustre_disk_dqdbheader)) \ + / sizeof(struct lustre_disk_dqblk_v2)) +#define GETENTRIES_V2(buf) (((char *)buf)+sizeof(struct lustre_disk_dqdbheader)) + +#define GETENTRIES(buf,version) ((version == LUSTRE_QUOTA_V1) ? \ + GETENTRIES_V1(buf) : GETENTRIES_V2(buf)) + /* * Here are header structures as written on disk and their in-memory copies */ @@ -79,6 +125,59 @@ struct lustre_disk_dqdbheader { #define LUSTRE_DQBLKSIZE (1 << LUSTRE_DQBLKSIZE_BITS) /* Size of block with quota structures */ #define LUSTRE_DQTREEOFF 1 /* Offset of tree in file in blocks */ #define LUSTRE_DQTREEDEPTH 4 /* Depth of quota tree */ -#define LUSTRE_DQSTRINBLK ((LUSTRE_DQBLKSIZE - sizeof(struct lustre_disk_dqdbheader)) / sizeof(struct lustre_disk_dqblk)) /* Number of entries in one blocks */ + +typedef char *dqbuf_t; + +#define GETIDINDEX(id, depth) (((id) >> ((LUSTRE_DQTREEDEPTH-(depth)-1)*8)) & 0xff) + +#define MAX_UL (0xffffffffUL) + +#define lustre_info_dirty(info) test_bit(DQF_INFO_DIRTY_B, &(info)->dqi_flags) + +struct dqblk { + struct list_head link; + uint blk; +}; + +/* come from lustre_fmt_common.c */ +dqbuf_t getdqbuf(void); +void freedqbuf(dqbuf_t buf); +void disk2memdqb(struct lustre_mem_dqblk *m, void *d, + enum lustre_quota_version version); +void lustre_mark_info_dirty(struct lustre_mem_dqinfo *info); +int lustre_init_quota_header(struct lustre_quota_info *lqi, int type, + int fakemagics); +int lustre_init_quota_info_generic(struct lustre_quota_info *lqi, int type, + int fakemagics); +int lustre_read_quota_info(struct lustre_quota_info *lqi, int type); +int lustre_read_quota_file_info(struct file* f, struct lustre_mem_dqinfo* info); +int lustre_write_quota_info(struct lustre_quota_info *lqi, int type); +ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf); +ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf); +int get_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info); +int put_free_dqblk(struct file *filp, struct lustre_mem_dqinfo *info, + dqbuf_t buf, uint blk); +int remove_free_dqentry(struct file *filp, + struct lustre_mem_dqinfo *info, dqbuf_t buf, + uint blk); +int insert_free_dqentry(struct file *filp, + struct lustre_mem_dqinfo *info, dqbuf_t buf, + uint blk); +ssize_t quota_read(struct file *file, struct inode *inode, int type, + uint blk, dqbuf_t buf); +int walk_tree_dqentry(struct file *filp, struct inode *inode, int type, + uint blk, int depth, struct list_head *list); +int check_quota_file(struct file *f, struct inode *inode, int type, + lustre_quota_version_t version); +int lustre_check_quota_file(struct lustre_quota_info *lqi, int type); +int lustre_read_dquot(struct lustre_dquot *dquot); +int lustre_commit_dquot(struct lustre_dquot *dquot); +int lustre_init_quota_info(struct lustre_quota_info *lqi, int type); +int lustre_get_qids(struct file *fp, struct inode *inode, int type, + struct list_head *list); + + +/* come from lustre_quota_fmt_conver.c */ +int lustre_quota_convert(struct lustre_quota_info *lqi, int type); #endif /* lustre_quota_fmt.h */ diff --git a/lustre/lvfs/lustre_quota_fmt_convert.c b/lustre/lvfs/lustre_quota_fmt_convert.c new file mode 100644 index 0000000000000000000000000000000000000000..70350ed8f2f8f584bfbbe0f179d166a29a95727b --- /dev/null +++ b/lustre/lvfs/lustre_quota_fmt_convert.c @@ -0,0 +1,185 @@ +/* -*- mode: c; c-basic-offset: 8; indent-tabs-mode: nil; -*- + * vim:expandtab:shiftwidth=8:tabstop=8: + * + * convert quota format. + * + * from + * linux/fs/quota_v2.c + */ + +#ifndef EXPORT_SYMTAB +# define EXPORT_SYMTAB +#endif + +#include <linux/errno.h> +#include <linux/fs.h> +#include <linux/mount.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/quotaio_v1.h> + +#include <asm/byteorder.h> +#include <asm/uaccess.h> + +#include <lustre_quota.h> +#include <obd_support.h> +#include "lustre_quota_fmt.h" + +static int admin_convert_dqinfo(struct file *fp_v1, struct file *fp_v2, + struct lustre_quota_info *lqi, int type) +{ + struct lustre_mem_dqinfo *info_old, *info_new = &lqi->qi_info[type]; + int rc; + + OBD_ALLOC_PTR(info_old); + if (info_old == NULL) + return -ENOMEM; + + rc = lustre_read_quota_file_info(fp_v1, info_old); + if (!rc) { + /* save essential fields: bgrace, igrace, flags */ + info_new->dqi_bgrace = info_old->dqi_bgrace; + info_new->dqi_igrace = info_old->dqi_igrace; + info_new->dqi_flags = info_old->dqi_flags; + rc = lustre_write_quota_info(lqi, type); + } + + OBD_FREE_PTR(info_old); + + return rc; +} + +static int admin_convert_v1_to_v2(struct file *fp_v1, struct file *fp_v2, + struct lustre_quota_info *lqi, int type) +{ + struct list_head blk_list; + struct dqblk *blk_item, *tmp; + dqbuf_t buf = NULL; + struct lustre_disk_dqblk *ddquot; + struct lustre_dquot *dquot = NULL; + int rc; + + ENTRY; + + INIT_LIST_HEAD(&blk_list); + + rc = admin_convert_dqinfo(fp_v1, fp_v2, lqi, type); + if (rc) { + CERROR("could not copy dqinfo!(%d)\n", rc); + GOTO(out_free, rc); + } + + rc = walk_tree_dqentry(fp_v1, NULL, type, LUSTRE_DQTREEOFF, 0, &blk_list); + if (rc) { + CERROR("walk through quota file failed!(%d)\n", rc); + GOTO(out_free, rc); + } + if (list_empty(&blk_list)) + RETURN(0); + + buf = getdqbuf(); + if (!buf) + GOTO(out_free, rc = -ENOMEM); + + ddquot = (struct lustre_disk_dqblk*)GETENTRIES(buf, LUSTRE_QUOTA_V1); + + OBD_ALLOC_PTR(dquot); + if (dquot == NULL) + GOTO(out_free, rc = -ENOMEM); + + list_for_each_entry(blk_item, &blk_list, link) { + loff_t ret = 0; + int i; + struct lustre_disk_dqblk fakedquot; + + memset(buf, 0, LUSTRE_DQBLKSIZE); + if ((ret = quota_read(fp_v1, NULL, type, blk_item->blk, buf))<0) { + CERROR("VFS: Can't read quota tree block %u.\n", + blk_item->blk); + GOTO(out_free, rc = ret); + } + + memset(&fakedquot, 0, sizeof(struct lustre_disk_dqblk)); + for (i = 0; i < LUSTRE_DQSTRINBLK; i++) { + /* skip empty entry */ + if (!memcmp + (&fakedquot, ddquot + i, + sizeof(struct lustre_disk_dqblk))) + continue; + + memset(dquot, 0, sizeof(*dquot)); + + dquot->dq_id = le32_to_cpu(ddquot[i].dqb_id); + dquot->dq_type = type; + dquot->dq_info = lqi; + + disk2memdqb(&dquot->dq_dqb, &ddquot[i], LUSTRE_QUOTA_V1); + rc = lustre_commit_dquot(dquot); + if (rc < 0) + GOTO(out_free, rc); + } + } + + EXIT; + +out_free: + list_for_each_entry_safe(blk_item, tmp, &blk_list, link) { + list_del_init(&blk_item->link); + kfree(blk_item); + } + if (buf) + freedqbuf(buf); + if (dquot) + OBD_FREE_PTR(dquot); + return rc; +} + +int lustre_quota_convert(struct lustre_quota_info *lqi, int type) +{ + struct file *f_v2 = lqi->qi_files[type]; + const char *qf_v1[] = LUSTRE_ADMIN_QUOTAFILES_V1; + char name[64]; + struct file *f_v1; + int rc = 0; + ENTRY; + + LASSERT(f_v2); + + rc = lustre_init_quota_info_generic(lqi, type, 1); + if (rc) { + CERROR("could not initialize new quota file(%d)\n", rc); + RETURN(rc); + } + + /* Open old quota file and copy to the new one */ + sprintf(name, "OBJECTS/%s", qf_v1[type]); + 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); + if (rc) + CERROR("failed to convert v1 quota file" + " to v2 quota file.\n"); + else + CDEBUG(D_INFO, "Found v1 quota file, " + "successfully converted to v2.\n"); + } + else + CERROR("old quota file is broken, " + "new quota file will be empty\n"); + + filp_close(f_v1, 0); + } else if (PTR_ERR(f_v1) != -ENOENT) /* No quota file is ok */ + CERROR("old quota file can not be open, " + "new quota file will be empty (%ld)\n", PTR_ERR(f_v1)); + + /* mark corresponding quota file as correct */ + if (!rc) + lustre_init_quota_header(lqi, type, 0); + + RETURN(rc); +} + +EXPORT_SYMBOL(lustre_quota_convert); diff --git a/lustre/lvfs/quotafmt_test.c b/lustre/lvfs/quotafmt_test.c index 09e10affb887be11db00d72144652c09b076940b..3af91f443225cfc41a7658de7dde6aa3bdf993af 100644 --- a/lustre/lvfs/quotafmt_test.c +++ b/lustre/lvfs/quotafmt_test.c @@ -70,7 +70,7 @@ static int quotfmt_initialize(struct lustre_quota_info *lqi, sizeof(struct lustre_disk_dqheader), &offset); if (size != sizeof(struct lustre_disk_dqheader)) { - CERROR("error writing quoafile header %s (rc = %d)\n", + CERROR("error writing quotafile header %s (rc = %d)\n", name, rc); rc = size; break; @@ -129,7 +129,7 @@ static int quotfmt_test_1(struct lustre_quota_info *lqi) ENTRY; for (i = 0; i < MAXQUOTAS; i++) { - if (!lustre_check_quota_file(lqi, i)) + if (lustre_check_quota_file(lqi, i)) RETURN(-EINVAL); } RETURN(0); @@ -220,7 +220,7 @@ static void put_rand_dquot(struct lustre_dquot *dquot) static int write_check_dquot(struct lustre_quota_info *lqi) { struct lustre_dquot *dquot; - struct mem_dqblk dqblk; + struct lustre_mem_dqblk dqblk; int rc = 0; ENTRY; diff --git a/lustre/quota/quota_ctl.c b/lustre/quota/quota_ctl.c index 8876603058cd1f1ebdbc4a6c9c61a916dedb36b1..40804f790354f49c28f47f2430d1579043bafeeb 100644 --- a/lustre/quota/quota_ctl.c +++ b/lustre/quota/quota_ctl.c @@ -74,6 +74,9 @@ int mds_quota_ctl(struct obd_export *exp, struct obd_quotactl *oqctl) case Q_GETOQUOTA: rc = mds_get_obd_quota(obd, oqctl); break; + case LUSTRE_Q_INVALIDATE: + rc = mds_quota_invalidate(obd, oqctl); + break; default: CERROR("%s: unsupported mds_quotactl command: %d\n", obd->obd_name, oqctl->qc_cmd); diff --git a/lustre/quota/quota_interface.c b/lustre/quota/quota_interface.c index 8acd72767ae1799afb68d93c559ddce1d7dd7e14..ab3b619fc43aba24a611ef047049e430c252e4cd 100644 --- a/lustre/quota/quota_interface.c +++ b/lustre/quota/quota_interface.c @@ -175,7 +175,7 @@ EXPORT_SYMBOL(lprocfs_wr_itune); #define USER_QUOTA 1 #define GROUP_QUOTA 2 -#define MAX_STYPE_SIZE 4 +#define MAX_STYPE_SIZE 5 int lprocfs_rd_type(char *page, char **start, off_t off, int count, int *eof, void *data) { @@ -192,7 +192,28 @@ int lprocfs_rd_type(char *page, char **start, off_t off, int count, if (type & GROUP_QUOTA) strcat(stype, "g"); } - + + /* append with quota version on MDS */ + if (!strcmp(obd->obd_type->typ_name, LUSTRE_MDS_NAME)) { + int rc; + lustre_quota_version_t version; + + rc = mds_quota_get_version(obd, &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 snprintf(page, count, "%s\n", stype); } EXPORT_SYMBOL(lprocfs_rd_type); @@ -255,17 +276,52 @@ int lprocfs_wr_type(struct file *file, const char *buffer, struct obd_device *obd = (struct obd_device *)data; struct obd_device_target *obt = &obd->u.obt; int type = 0; + unsigned long i; char stype[MAX_STYPE_SIZE + 1] = ""; LASSERT(obd != NULL); - if (copy_from_user(stype, buffer, MAX_STYPE_SIZE)) + if (count > MAX_STYPE_SIZE) + return -EINVAL; + + if (copy_from_user(stype, buffer, count)) return -EFAULT; - if (strchr(stype, 'u')) - type |= USER_QUOTA; - if (strchr(stype, 'g')) - type |= GROUP_QUOTA; - + for (i = 0 ; i < count ; i++) { + int rc; + + switch (stype[i]) { + case 'u' : + type |= USER_QUOTA; + break; + case 'g' : + type |= GROUP_QUOTA; + 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); + if (rc) { + CDEBUG(D_QUOTA, "could not set quota v2! %d\n", rc); + return rc; + } + break; + default : /* just skip stray symbols like \n */ + break; + } + } + obt->obt_qctxt.lqc_atype = type; if (type == 0) @@ -496,6 +552,7 @@ static int mds_quota_setup(struct obd_device *obd) int rc; ENTRY; + mds->mds_quota_info.qi_version = LUSTRE_QUOTA_V2; atomic_set(&obt->obt_quotachecking, 1); /* initialize quota master and quota context */ sema_init(&mds->mds_qonoff_sem, 1); diff --git a/lustre/quota/quota_internal.h b/lustre/quota/quota_internal.h index fcfee5533b74b3c5e5df2cf487e73720347bcdcd..7fa3629cdb876acadc5f6b2e620c13aaa28c674f 100644 --- a/lustre/quota/quota_internal.h +++ b/lustre/quota/quota_internal.h @@ -27,8 +27,8 @@ #define DQUOT_DEBUG(dquot, fmt, arg...) \ CDEBUG(D_QUOTA, "refcnt(%u) id(%u) type(%u) off(%llu) flags(%lu) " \ - "bhardlimit(%u) curspace("LPX64") ihardlimit(%u) " \ - "curinodes(%u): " fmt, dquot->dq_refcnt, \ + "bhardlimit("LPU64") curspace("LPU64") ihardlimit("LPU64")" \ + "curinodes("LPU64"): " fmt, dquot->dq_refcnt, \ dquot->dq_id, dquot->dq_type, dquot->dq_off, dquot->dq_flags, \ dquot->dq_dqb.dqb_bhardlimit, dquot->dq_dqb.dqb_curspace, \ dquot->dq_dqb.dqb_ihardlimit, dquot->dq_dqb.dqb_curinodes, \ @@ -74,6 +74,10 @@ int mds_quota_adjust(struct obd_device *obd, unsigned int qcids[], int filter_quota_adjust(struct obd_device *obd, unsigned int qcids[], unsigned int qpids[], int rc, int opc); int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl); +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_admin_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl); int mds_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl); int mds_quota_off(struct obd_device *obd, struct obd_quotactl *oqctl); diff --git a/lustre/quota/quota_master.c b/lustre/quota/quota_master.c index 0a916a670e0dda6b74a5f44e9586501955a3fc57..c112bdd41ea99a71b48381293565a21adddf99db 100644 --- a/lustre/quota/quota_master.c +++ b/lustre/quota/quota_master.c @@ -361,31 +361,122 @@ int filter_quota_adjust(struct obd_device *obd, unsigned int qcids[], RETURN(0); } -#define LUSTRE_ADMIN_QUOTAFILES {\ - "admin_quotafile.usr", /* user admin quotafile */\ - "admin_quotafile.grp" /* group admin quotafile */\ -} static const char prefix[] = "OBJECTS/"; +int mds_quota_get_version(struct obd_device *obd, + lustre_quota_version_t *version) +{ + struct mds_obd *mds = &obd->u.mds; + struct lustre_quota_info *qinfo = &mds->mds_quota_info; + + *version = qinfo->qi_version; + + return 0; +} + +int mds_quota_set_version(struct obd_device *obd, lustre_quota_version_t version) +{ + struct mds_obd *mds = &obd->u.mds; + struct lustre_quota_info *qinfo = &mds->mds_quota_info; + int rc = 0, i; + + if (version != LUSTRE_QUOTA_V1 && + version != LUSTRE_QUOTA_V2) + return -EINVAL; + + down(&mds->mds_qonoff_sem); + + /* no need to change version? nothing to do then */ + if (qinfo->qi_version == version) + goto out; + + for (i = 0; i < MAXQUOTAS; i++) { + /* quota file has been opened ? */ + if (qinfo->qi_files[i]) { + rc = -EBUSY; + goto out; + } + } + + CDEBUG(D_INFO, "changing quota version %d -> %d\n", qinfo->qi_version, + version); + + qinfo->qi_version = version; + +out: + up(&mds->mds_qonoff_sem); + + return rc; +} + +int mds_quota_invalidate(struct obd_device *obd, struct obd_quotactl *oqctl) +{ + struct mds_obd *mds = &obd->u.mds; + struct lustre_quota_info *qinfo = &mds->mds_quota_info; + int rc = 0, i; + char *quotafiles_v1[] = LUSTRE_ADMIN_QUOTAFILES_V1; + char *quotafiles_v2[] = LUSTRE_ADMIN_QUOTAFILES_V2; + char name[64]; + struct lvfs_run_ctxt saved; + + push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); + + down(&mds->mds_qonoff_sem); + + for (i = 0; i < MAXQUOTAS; i++) { + struct file *fp; + char* quotafile = (qinfo->qi_version == LUSTRE_QUOTA_V1)? + quotafiles_v1[i]:quotafiles_v2[i]; + + if (!Q_TYPESET(oqctl, i)) + continue; + + /* quota file has been opened ? */ + if (qinfo->qi_files[i]) { + rc = -EBUSY; + goto out; + } + + LASSERT(strlen(quotafile) + sizeof(prefix) <= sizeof(name)); + sprintf(name, "%s%s", prefix, quotafile); + + fp = filp_open(name, O_CREAT | O_TRUNC, 0644); + if (IS_ERR(fp)) { + rc = PTR_ERR(fp); + CERROR("error invalidating admin quotafile %s (rc:%d)\n", + name, rc); + } + else + filp_close(fp, 0); + } + +out: + 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; struct lustre_quota_info *qinfo = &mds->mds_quota_info; - const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES; + char *quotafiles_v1[] = LUSTRE_ADMIN_QUOTAFILES_V1; + char *quotafiles_v2[] = LUSTRE_ADMIN_QUOTAFILES_V2; struct lvfs_run_ctxt saved; char name[64]; int i, rc = 0; - struct dentry *dparent = mds->mds_objects_dir; - struct inode *iparent = dparent->d_inode; ENTRY; - LASSERT(iparent); push_ctxt(&saved, &obd->obd_lvfs_ctxt, NULL); down(&mds->mds_qonoff_sem); - for (i = 0; i < MAXQUOTAS; i++) { - struct dentry *de; + + for (i = 0; i < MAXQUOTAS && !rc; i++) { struct file *fp; + char* quotafile = (qinfo->qi_version == LUSTRE_QUOTA_V1)? + quotafiles_v1[i]:quotafiles_v2[i]; if (!Q_TYPESET(oqctl, i)) continue; @@ -397,33 +488,44 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl) continue; } - /* lookup quota file */ - rc = 0; - LOCK_INODE_MUTEX(iparent); - de = lookup_one_len(quotafiles[i], dparent, - strlen(quotafiles[i])); - UNLOCK_INODE_MUTEX(iparent); - if (IS_ERR(de) || de->d_inode == NULL || - !S_ISREG(de->d_inode->i_mode)) - rc = IS_ERR(de) ? PTR_ERR(de) : -ENOENT; - if (!IS_ERR(de)) - dput(de); - - if (rc && rc != -ENOENT) { - CERROR("error lookup quotafile %s! (rc:%d)\n", + LASSERT(strlen(quotafile) + sizeof(prefix) <= sizeof(name)); + sprintf(name, "%s%s", prefix, quotafile); + + /* check if quota file exists and is correct */ + fp = filp_open(name, O_RDONLY, 0); + if (!IS_ERR(fp)) { + /* irregular file is not the right place for quota */ + if (!S_ISREG(fp->f_dentry->d_inode->i_mode)) { + CERROR("admin quota file %s is not " + "regular!", quotafile); + filp_close(fp, 0); + rc = -EINVAL; + break; + } + qinfo->qi_files[i] = fp; + rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_CHK); + qinfo->qi_files[i] = 0; + filp_close(fp, 0); + } + else + rc = PTR_ERR(fp); + + if (!rc) + continue; + + /* -EINVAL may be returned by quotainfo for bad quota file */ + if (rc != -ENOENT && rc != -EINVAL) { + CERROR("error opening old quota file %s (%d)\n", name, rc); break; - } else if (!rc) { - continue; } - LASSERT(strlen(quotafiles[i]) + sizeof(prefix) <= sizeof(name)); - sprintf(name, "%s%s", prefix, quotafiles[i]); + CDEBUG(D_INFO, "%s new quota file %s\n", name, + rc == -ENOENT ? "creating" : "overwriting"); - LASSERT(rc == -ENOENT); - /* create quota file */ - fp = filp_open(name, O_CREAT | O_EXCL, 0644); - if (IS_ERR(fp) || !S_ISREG(fp->f_dentry->d_inode->i_mode)) { + /* create quota file overwriting old if needed */ + fp = filp_open(name, O_CREAT | O_TRUNC, 0644); + if (IS_ERR(fp)) { rc = PTR_ERR(fp); CERROR("error creating admin quotafile %s (rc:%d)\n", name, rc); @@ -431,15 +533,26 @@ int init_admin_quotafiles(struct obd_device *obd, struct obd_quotactl *oqctl) } qinfo->qi_files[i] = fp; - rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_INIT_INFO); - filp_close(fp, 0); - qinfo->qi_files[i] = NULL; - if (rc) { - CERROR("error init %s admin quotafile! (rc:%d)\n", - i == USRQUOTA ? "user" : "group", rc); + switch (qinfo->qi_version) { + case LUSTRE_QUOTA_V1: + rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_INIT_INFO); + if (rc) + CERROR("error init %s admin quotafile! (rc:%d)\n", + i == USRQUOTA ? "user" : "group", rc); + break; + case LUSTRE_QUOTA_V2: + rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_CONVERT); + if (rc) + CERROR("error convert %s admin quotafile! (rc:%d)\n", + i == USRQUOTA ? "user" : "group", rc); break; + default: + LBUG(); } + + filp_close(fp, 0); + qinfo->qi_files[i] = NULL; } up(&mds->mds_qonoff_sem); @@ -470,38 +583,61 @@ int mds_admin_quota_on(struct obd_device *obd, struct obd_quotactl *oqctl) { struct mds_obd *mds = &obd->u.mds; struct lustre_quota_info *qinfo = &mds->mds_quota_info; - const char *quotafiles[] = LUSTRE_ADMIN_QUOTAFILES; + const char *quotafiles_v1[] = LUSTRE_ADMIN_QUOTAFILES_V1; + const char *quotafiles_v2[] = LUSTRE_ADMIN_QUOTAFILES_V2; char name[64]; int i, rc = 0; - struct inode *iparent = mds->mds_objects_dir->d_inode; ENTRY; - LASSERT(iparent); - /* open admin quota files and read quotafile info */ for (i = 0; i < MAXQUOTAS; i++) { struct file *fp; + const char* quotafile = qinfo->qi_version == LUSTRE_QUOTA_V1? + quotafiles_v1[i] : quotafiles_v2[i]; if (!Q_TYPESET(oqctl, i)) continue; - LASSERT(strlen(quotafiles[i]) + sizeof(prefix) <= sizeof(name)); - sprintf(name, "%s%s", prefix, quotafiles[i]); + LASSERT(strlen(quotafile) + + sizeof(prefix) <= sizeof(name)); + sprintf(name, "%s%s", prefix, quotafile); if (qinfo->qi_files[i] != NULL) { rc = -EBUSY; break; } - fp = filp_open(name, O_RDWR | O_EXCL, 0644); + fp = filp_open(name, O_RDWR, 0); + /* handle transparent migration to 64 bit quota file */ + if (IS_ERR(fp) && PTR_ERR(fp) == -ENOENT && + 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); + if (!IS_ERR(fp)) { + qinfo->qi_files[i] = fp; + rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_CONVERT); + if (rc) { + CERROR("error convert %s admin quotafile! (rc:%d)\n", + i == USRQUOTA ? "user" : "group", rc); + break; + } + } + } + if (IS_ERR(fp) || !S_ISREG(fp->f_dentry->d_inode->i_mode)) { - rc = PTR_ERR(fp); - CDEBUG(rc == -ENOENT ? D_QUOTA : D_ERROR, - "open %s failed! (rc:%d)\n", name, rc); + rc = IS_ERR(fp) ? PTR_ERR(fp) : -EINVAL; + CERROR("error open/create %s! (rc:%d)\n", name, rc); break; } qinfo->qi_files[i] = fp; + rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_CHK); + if (rc) { + CERROR("invalid quota file %s! (rc:%d)\n", + name, rc); + break; + } + rc = fsfilt_quotainfo(obd, qinfo, i, QFILE_RD_INFO); if (rc) { CERROR("error read quotainfo of %s! (rc:%d)\n", @@ -748,7 +884,7 @@ int mds_set_dqblk(struct obd_device *obd, struct obd_quotactl *oqctl) { struct mds_obd *mds = &obd->u.mds; struct lustre_quota_info *qinfo = &mds->mds_quota_info; - __u32 ihardlimit, isoftlimit, bhardlimit, bsoftlimit; + __u64 ihardlimit, isoftlimit, bhardlimit, bsoftlimit; time_t btime, itime; struct lustre_dquot *dquot; struct obd_dqblk *dqblk = &oqctl->qc_dqblk; diff --git a/lustre/tests/sanity-quota.sh b/lustre/tests/sanity-quota.sh index 973c53d47238a00d71c06c971a905044c7107c4b..bbe846390abc2e6f829de8d79f95fa144f30ae18 100644 --- a/lustre/tests/sanity-quota.sh +++ b/lustre/tests/sanity-quota.sh @@ -1029,6 +1029,79 @@ test_14(){ # b=12223 -- setting quota on root } run_test 14 "test setting quota on root ===" +quota_set_version() { + do_facet mds "for i in /proc/fs/lustre/mds/${FSNAME}-MDT*/quota_type; do + echo $1 >> \\\$i; + done" +} + +test_14a(){ + # 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 + # 4. restart quota in new mode forcing conversion and check the entries + + MISSING_USERS="" + for i in `seq 1 30`; do + check_runas_id_ret quota15_$i + if [ "$?" != "0" ]; then + MISSING_USERS="$MISSING_USERS quota15_$i" + fi + done + + if [ -n "$MISSING_USERS" ]; then + echo "following users are missing: $MISSING_USERS, test skipped" + return + fi + + $LFS quotaoff -ug $DIR + quota_set_version 1 + $LFS quotacheck -ug $DIR + + for i in `seq 1 30`; do + $LFS setquota -u quota15_$i $i $i $i $i $DIR || error "lfs setquota failed" + done + + $LFS quotaoff -ug $DIR + quota_set_version 2 + $LFS quotainv -ug $DIR + $LFS quotacheck -ug $DIR + + for i in `seq 1 30`; do + # 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 0 0 0 0 $DIR || error "ifs setquota clear failed" + done +} +run_test 14a "setting 30 quota entries in quota v1 file before conversion ===" + +test_15(){ + LIMIT=$((24 * 1024 * 1024 * 1024 * 1024)) # 24 TB + PATTERN="`echo $DIR | sed 's/\//\\\\\//g'`" + + # test for user + $LFS setquota -u $TSTUSR 0 $LIMIT 0 0 $DIR || error "failed setting user quota limit $LIMIT" + TOTAL_LIMIT="`$LFS quota -u $TSTUSR $DIR | awk '/^.*'$PATTERN'.*[[:digit:]+][[:space:]+]/ { print $4 }'`" + [ $TOTAL_LIMIT -eq $LIMIT ] || error " (user)total limits = $TOTAL_LIMIT; limit = $LIMIT, failed!" + echo " (user)total limits = $TOTAL_LIMIT; limit = $LIMIT, successful!" + $LFS setquota -u $TSTUSR 0 0 0 0 $DIR || error "failed removing user quota limit" + + # test for group + $LFS setquota -g $TSTUSR 0 $LIMIT 0 0 $DIR || error "failed setting group quota limit $LIMIT" + TOTAL_LIMIT="`$LFS quota -g $TSTUSR $DIR | awk '/^.*'$PATTERN'.*[[:digit:]+][[:space:]+]/ { print $4 }'`" + [ $TOTAL_LIMIT -eq $LIMIT ] || error " (group)total limits = $TOTAL_LIMIT; limit = $LIMIT, failed!" + echo " (group)total limits = $TOTAL_LIMIT; limit = $LIMIT, successful!" + $LFS setquota -g $TSTUSR 0 0 0 0 $DIR || error "failed removing group quota limit" + $LFS quotaoff -ug $DIR + quota_set_version 1 + $LFS quotacheck -ug $DIR || error "quotacheck failed" + + echo "Testing that >4GB quota limits fail on volume with quota v1" + ! $LFS setquota -u $TSTUSR 0 $LIMIT 0 0 $DIR +} +run_test 15 "set block quota more than 4T ===" + # turn off quota test_99() { diff --git a/lustre/tests/test-framework.sh b/lustre/tests/test-framework.sh index 30578b62510be1b566fdb8b34648fa5793f4adf9..3fe9d3949d4e42e4cfb9b639bbc6c8df3435b50d 100644 --- a/lustre/tests/test-framework.sh +++ b/lustre/tests/test-framework.sh @@ -1246,3 +1246,15 @@ check_runas_id() { add user $myRUNAS_ID:$myRUNAS_ID on these nodes." rm -rf $DIR/d0_runas_test } + +check_runas_id_ret() { + local myRUNAS_ID=$1 + shift + local myRUNAS=$@ + mkdir $DIR/d0_runas_test + chmod 0755 $DIR + chown $myRUNAS_ID:$myRUNAS_ID $DIR/d0_runas_test + $myRUNAS touch $DIR/d0_runas_test/f$$ || return 1 + rm -rf $DIR/d0_runas_test + return 0 +} diff --git a/lustre/utils/lfs.c b/lustre/utils/lfs.c index fae0aadbf41beeaeca7132c546a17cc046f5a02a..8bb138e8c2f88a64508b9736e451a5c3e24783e0 100644 --- a/lustre/utils/lfs.c +++ b/lustre/utils/lfs.c @@ -66,6 +66,7 @@ static int lfs_quotaon(int argc, char **argv); static int lfs_quotaoff(int argc, char **argv); static int lfs_setquota(int argc, char **argv); static int lfs_quota(int argc, char **argv); +static int lfs_quotainv(int argc, char **argv); #endif static int lfs_join(int argc, char **argv); @@ -135,6 +136,8 @@ command_t cmdlist[] = { " setquota -t [ -u | -g ] <block-grace> <inode-grace> <filesystem>"}, {"quota", lfs_quota, 0, "Display disk usage and limits.\n" "usage: quota [ -o obd_uuid ] [{-u|-g <name>}|-t] <filesystem>"}, + {"quotainv", lfs_quotainv, 0, "Invalidate quota data.\n" + "usage: quotainv [-u|-g] <filesystem>"}, #endif {"help", Parser_help, 0, "help"}, {"exit", Parser_quit, 0, "quit"}, @@ -1257,6 +1260,52 @@ static int lfs_quotaoff(int argc, char **argv) return 0; } +static int lfs_quotainv(int argc, char **argv) +{ + int c; + char *mnt; + struct if_quotactl qctl; + char *obd_type = (char *)qctl.obd_type; + int rc; + + memset(&qctl, 0, sizeof(qctl)); + qctl.qc_cmd = LUSTRE_Q_INVALIDATE; + + optind = 0; + while ((c = getopt(argc, argv, "ug")) != -1) { + switch (c) { + case 'u': + qctl.qc_type |= 0x01; + break; + case 'g': + qctl.qc_type |= 0x02; + break; + default: + fprintf(stderr, "error: %s: option '-%c' " + "unrecognized\n", argv[0], c); + return CMD_HELP; + } + } + + if (qctl.qc_type) + qctl.qc_type--; + else /* by default, invalidate quota for both user & group */ + qctl.qc_type = 0x02; + + if (argc == optind) + return CMD_HELP; + + mnt = argv[optind]; + + rc = llapi_quotactl(mnt, &qctl); + if (rc) { + fprintf(stderr, "quotainv failed: %s\n", strerror(errno)); + return rc; + } + + return 0; +} + static int name2id(unsigned int *id, char *name, int type) { if (type == USRQUOTA) { @@ -1380,6 +1429,17 @@ error: return ULONG_MAX; } +#define ARG2ULL(nr, str, msg) \ +do { \ + char *endp; \ + nr = strtoull(str, &endp, 0); \ + if (*endp) { \ + fprintf(stderr, "error: bad %s: %s\n", msg, str); \ + return CMD_HELP; \ + } \ +} while (0) + + int lfs_setquota(int argc, char **argv) { int c; @@ -1432,10 +1492,10 @@ int lfs_setquota(int argc, char **argv) return CMD_HELP; } - ARG2INT(dqb->dqb_bsoftlimit, argv[optind++], "block-softlimit"); - ARG2INT(dqb->dqb_bhardlimit, argv[optind++], "block-hardlimit"); - ARG2INT(dqb->dqb_isoftlimit, argv[optind++], "inode-softlimit"); - ARG2INT(dqb->dqb_ihardlimit, argv[optind++], "inode-hardlimit"); + ARG2ULL(dqb->dqb_bsoftlimit, argv[optind++], "block-softlimit"); + ARG2ULL(dqb->dqb_bhardlimit, argv[optind++], "block-hardlimit"); + ARG2ULL(dqb->dqb_isoftlimit, argv[optind++], "inode-softlimit"); + ARG2ULL(dqb->dqb_ihardlimit, argv[optind++], "inode-hardlimit"); dqb->dqb_valid = QIF_LIMITS; } else {