Whamcloud - gitweb
Branch HEAD
[fs/lustre-release.git] / lustre / kernel_patches / patches / quota-support-64-bit-quota-format.patch
diff --git a/lustre/kernel_patches/patches/quota-support-64-bit-quota-format.patch b/lustre/kernel_patches/patches/quota-support-64-bit-quota-format.patch
new file mode 100644 (file)
index 0000000..14fe9a8
--- /dev/null
@@ -0,0 +1,282 @@
+From: Jan Kara <jack@suse.cz>
+
+Implement conversion functions for new version (version 1) of quota format
+which supports 64-bit block and inode limits and 64-bit inode usage.  The
+original implementation has been written by Andrew Perepechko.
+
+Signed-off-by: Andrew Perepechko <andrew.perepechko@sun.com>
+Signed-off-by: Jan Kara <jack@suse.cz>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+---
+
+ fs/quota_v2.c   |  140 ++++++++++++++++++++++++++++++++++++----------
+ fs/quotaio_v2.h |   26 ++++++--
+ 2 files changed, 132 insertions(+), 34 deletions(-)
+
+diff -puN fs/quota_v2.c~quota-support-64-bit-quota-format fs/quota_v2.c
+--- a/fs/quota_v2.c~quota-support-64-bit-quota-format
++++ a/fs/quota_v2.c
+@@ -23,14 +23,24 @@ MODULE_LICENSE("GPL");
+ #define __QUOTA_V2_PARANOIA
+-static void v2_mem2diskdqb(void *dp, struct dquot *dquot);
+-static void v2_disk2memdqb(struct dquot *dquot, void *dp);
+-static int v2_is_id(void *dp, struct dquot *dquot);
+-
+-static struct qtree_fmt_operations v2_qtree_ops = {
+-      .mem2disk_dqblk = v2_mem2diskdqb,
+-      .disk2mem_dqblk = v2_disk2memdqb,
+-      .is_id = v2_is_id,
++static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot);
++static void v2r0_disk2memdqb(struct dquot *dquot, void *dp);
++static int v2r0_is_id(void *dp, struct dquot *dquot);
++
++static struct qtree_fmt_operations v2r0_qtree_ops = {
++      .mem2disk_dqblk = v2r0_mem2diskdqb,
++      .disk2mem_dqblk = v2r0_disk2memdqb,
++      .is_id = v2r0_is_id,
++};
++
++static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot);
++static void v2r1_disk2memdqb(struct dquot *dquot, void *dp);
++static int v2r1_is_id(void *dp, struct dquot *dquot);
++
++static struct qtree_fmt_operations v2r1_qtree_ops = {
++      .mem2disk_dqblk = v2r1_mem2diskdqb,
++      .disk2mem_dqblk = v2r1_disk2memdqb,
++      .is_id = v2r1_is_id,
+ };
+ #define QUOTABLOCK_BITS 10
+@@ -46,8 +56,7 @@ static inline qsize_t v2_qbtos(qsize_t b
+       return blocks << QUOTABLOCK_BITS;
+ }
+-/* Check whether given file is really vfsv0 quotafile */
+-static int v2_check_quota_file(struct super_block *sb, int type)
++static int v2_check_quota_file_header(struct super_block *sb, int type)
+ {
+       struct v2_disk_dqheader dqhead;
+       ssize_t size;
+@@ -58,12 +67,20 @@ static int v2_check_quota_file(struct su
+       if (size != sizeof(struct v2_disk_dqheader)) {
+               printk("quota_v2: failed read expected=%zd got=%zd\n",
+                       sizeof(struct v2_disk_dqheader), size);
+-              return 0;
++              return -EIO;
+       }
+-      if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
+-          le32_to_cpu(dqhead.dqh_version) != quota_versions[type])
+-              return 0;
+-      return 1;
++      if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type])
++              return -ENOENT;
++      if (le32_to_cpu(dqhead.dqh_version) > quota_versions[type])
++              return -EOPNOTSUPP;
++      return le32_to_cpu(dqhead.dqh_version);
++}
++
++
++/* Check whether given file is really vfsv0 quotafile */
++static int v2_check_quota_file(struct super_block *sb, int type)
++{
++      return v2_check_quota_file_header(sb, type) >= 0;
+ }
+ /* Read information header from quota file */
+@@ -73,7 +90,13 @@ static int v2_read_file_info(struct supe
+       struct mem_dqinfo *info = sb_dqinfo(sb, type);
+       struct qtree_mem_dqinfo *qinfo;
+       ssize_t size;
++      int version = v2_check_quota_file_header(sb, type);
++      if (version < 0) {
++              printk(KERN_WARNING "Cannot identify quota file version on "
++                     "device %s: %d\n", sb->s_id, version);
++              return -1;
++      }
+       size = sb->s_op->quota_read(sb, type, (char *)&dinfo,
+              sizeof(struct v2_disk_dqinfo), V2_DQINFOOFF);
+       if (size != sizeof(struct v2_disk_dqinfo)) {
+@@ -88,9 +111,14 @@ static int v2_read_file_info(struct supe
+               return -1;
+       }
+       qinfo = info->dqi_priv;
+-      /* limits are stored as unsigned 32-bit data */
+-      info->dqi_maxblimit = 0xffffffff;
+-      info->dqi_maxilimit = 0xffffffff;
++      if (version == 0) {
++              /* limits are stored as unsigned 32-bit data */
++              info->dqi_maxblimit = 0xffffffff;
++              info->dqi_maxilimit = 0xffffffff;
++      } else {
++              info->dqi_maxblimit = 0x7fffffffffffffffULL;
++              info->dqi_maxilimit = 0x7fffffffffffffffULL;
++      }
+       info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
+       info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
+       info->dqi_flags = le32_to_cpu(dinfo.dqi_flags);
+@@ -102,8 +130,13 @@ static int v2_read_file_info(struct supe
+       qinfo->dqi_blocksize_bits = V2_DQBLKSIZE_BITS;
+       qinfo->dqi_usable_bs = 1 << V2_DQBLKSIZE_BITS;
+       qinfo->dqi_qtree_depth = qtree_depth(qinfo);
+-      qinfo->dqi_entry_size = sizeof(struct v2_disk_dqblk);
+-      qinfo->dqi_ops = &v2_qtree_ops;
++      if (version == 0) {
++              qinfo->dqi_entry_size = sizeof(struct v2r0_disk_dqblk);
++              qinfo->dqi_ops = &v2r0_qtree_ops;
++      } else {
++              qinfo->dqi_entry_size = sizeof(struct v2r1_disk_dqblk);
++              qinfo->dqi_ops = &v2r1_qtree_ops;
++      }
+       return 0;
+ }
+@@ -134,9 +167,9 @@ static int v2_write_file_info(struct sup
+       return 0;
+ }
+-static void v2_disk2memdqb(struct dquot *dquot, void *dp)
++static void v2r0_disk2memdqb(struct dquot *dquot, void *dp)
+ {
+-      struct v2_disk_dqblk *d = dp, empty;
++      struct v2r0_disk_dqblk *d = dp, empty;
+       struct mem_dqblk *m = &dquot->dq_dqb;
+       m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
+@@ -148,15 +181,15 @@ static void v2_disk2memdqb(struct dquot 
+       m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
+       m->dqb_btime = le64_to_cpu(d->dqb_btime);
+       /* We need to escape back all-zero structure */
+-      memset(&empty, 0, sizeof(struct v2_disk_dqblk));
++      memset(&empty, 0, sizeof(struct v2r0_disk_dqblk));
+       empty.dqb_itime = cpu_to_le64(1);
+-      if (!memcmp(&empty, dp, sizeof(struct v2_disk_dqblk)))
++      if (!memcmp(&empty, dp, sizeof(struct v2r0_disk_dqblk)))
+               m->dqb_itime = 0;
+ }
+-static void v2_mem2diskdqb(void *dp, struct dquot *dquot)
++static void v2r0_mem2diskdqb(void *dp, struct dquot *dquot)
+ {
+-      struct v2_disk_dqblk *d = dp;
++      struct v2r0_disk_dqblk *d = dp;
+       struct mem_dqblk *m = &dquot->dq_dqb;
+       struct qtree_mem_dqinfo *info =
+                       sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+@@ -174,9 +207,60 @@ static void v2_mem2diskdqb(void *dp, str
+               d->dqb_itime = cpu_to_le64(1);
+ }
+-static int v2_is_id(void *dp, struct dquot *dquot)
++static int v2r0_is_id(void *dp, struct dquot *dquot)
++{
++      struct v2r0_disk_dqblk *d = dp;
++      struct qtree_mem_dqinfo *info =
++                      sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
++
++      if (qtree_entry_unused(info, dp))
++              return 0;
++      return le32_to_cpu(d->dqb_id) == dquot->dq_id;
++}
++
++static void v2r1_disk2memdqb(struct dquot *dquot, void *dp)
++{
++      struct v2r1_disk_dqblk *d = dp, empty;
++      struct mem_dqblk *m = &dquot->dq_dqb;
++
++      m->dqb_ihardlimit = le64_to_cpu(d->dqb_ihardlimit);
++      m->dqb_isoftlimit = le64_to_cpu(d->dqb_isoftlimit);
++      m->dqb_curinodes = le64_to_cpu(d->dqb_curinodes);
++      m->dqb_itime = le64_to_cpu(d->dqb_itime);
++      m->dqb_bhardlimit = v2_qbtos(le64_to_cpu(d->dqb_bhardlimit));
++      m->dqb_bsoftlimit = v2_qbtos(le64_to_cpu(d->dqb_bsoftlimit));
++      m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
++      m->dqb_btime = le64_to_cpu(d->dqb_btime);
++      /* We need to escape back all-zero structure */
++      memset(&empty, 0, sizeof(struct v2r1_disk_dqblk));
++      empty.dqb_itime = cpu_to_le64(1);
++      if (!memcmp(&empty, dp, sizeof(struct v2r1_disk_dqblk)))
++              m->dqb_itime = 0;
++}
++
++static void v2r1_mem2diskdqb(void *dp, struct dquot *dquot)
++{
++      struct v2r1_disk_dqblk *d = dp;
++      struct mem_dqblk *m = &dquot->dq_dqb;
++      struct qtree_mem_dqinfo *info =
++                      sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
++
++      d->dqb_ihardlimit = cpu_to_le64(m->dqb_ihardlimit);
++      d->dqb_isoftlimit = cpu_to_le64(m->dqb_isoftlimit);
++      d->dqb_curinodes = cpu_to_le64(m->dqb_curinodes);
++      d->dqb_itime = cpu_to_le64(m->dqb_itime);
++      d->dqb_bhardlimit = cpu_to_le64(v2_stoqb(m->dqb_bhardlimit));
++      d->dqb_bsoftlimit = cpu_to_le64(v2_stoqb(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(dquot->dq_id);
++      if (qtree_entry_unused(info, dp))
++              d->dqb_itime = cpu_to_le64(1);
++}
++
++static int v2r1_is_id(void *dp, struct dquot *dquot)
+ {
+-      struct v2_disk_dqblk *d = dp;
++      struct v2r1_disk_dqblk *d = dp;
+       struct qtree_mem_dqinfo *info =
+                       sb_dqinfo(dquot->dq_sb, dquot->dq_type)->dqi_priv;
+diff -puN fs/quotaio_v2.h~quota-support-64-bit-quota-format fs/quotaio_v2.h
+--- a/fs/quotaio_v2.h~quota-support-64-bit-quota-format
++++ a/fs/quotaio_v2.h
+@@ -17,8 +17,8 @@
+ }
+ #define V2_INITQVERSIONS {\
+-      0,              /* USRQUOTA */\
+-      0               /* GRPQUOTA */\
++      1,              /* USRQUOTA */\
++      1               /* GRPQUOTA */\
+ }
+ /* First generic header */
+@@ -28,11 +28,11 @@ struct v2_disk_dqheader {
+ };
+ /*
+- * 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.
++ * The following structure defines the format of the disk quota file in version
++ * 0 - the file is a radix tree whose leaves point to blocks of these
++ * structures.
+  */
+-struct v2_disk_dqblk {
++struct v2r0_disk_dqblk {
+       __le32 dqb_id;          /* id this quota applies to */
+       __le32 dqb_ihardlimit;  /* absolute limit on allocated inodes */
+       __le32 dqb_isoftlimit;  /* preferred inode limit */
+@@ -44,6 +44,20 @@ struct v2_disk_dqblk {
+       __le64 dqb_itime;       /* time limit for excessive inode use */
+ };
++/* The same structure in quota file version 1 */
++struct v2r1_disk_dqblk {
++      __le32 dqb_id;          /* id this quota applies to */
++      __le32 dqb_padding;     /* padding field */
++      __le64 dqb_ihardlimit;  /* absolute limit on allocated inodes */
++      __le64 dqb_isoftlimit;  /* preferred inode limit */
++      __le64 dqb_curinodes;   /* current # allocated inodes */
++      __le64 dqb_bhardlimit;  /* absolute limit on disk space */
++      __le64 dqb_bsoftlimit;  /* preferred limit on disk space */
++      __le64 dqb_curspace;    /* current space occupied (in bytes) */
++      __le64 dqb_btime;       /* time limit for excessive disk use */
++      __le64 dqb_itime;       /* time limit for excessive inode use */
++};
++
+ /* Header with type and version specific information */
+ struct v2_disk_dqinfo {
+       __le32 dqi_bgrace;      /* Time before block soft limit becomes hard limit */
+_