nilfs2: add nilfs_sufile_set_suinfo to update segment usage
authorAndreas Rohner <andreas.rohner@gmx.net>
Thu, 3 Apr 2014 21:50:27 +0000 (14:50 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 3 Apr 2014 23:21:25 +0000 (16:21 -0700)
Introduce nilfs_sufile_set_suinfo(), which expects an array of
nilfs_suinfo_update structures and updates the segment usage information
accordingly.

This is basically a helper function for the newly introduced
NILFS_IOCTL_SET_SUINFO ioctl.

[konishi.ryusuke@lab.ntt.co.jp: use put_bh() instead of brelse() because we know bh != NULL]
Signed-off-by: Andreas Rohner <andreas.rohner@gmx.net>
Signed-off-by: Ryusuke Konishi <konishi.ryusuke@lab.ntt.co.jp>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
fs/nilfs2/sufile.c
fs/nilfs2/sufile.h

index 3127e9f..5628b99 100644 (file)
@@ -870,6 +870,137 @@ ssize_t nilfs_sufile_get_suinfo(struct inode *sufile, __u64 segnum, void *buf,
 }
 
 /**
+ * nilfs_sufile_set_suinfo - sets segment usage info
+ * @sufile: inode of segment usage file
+ * @buf: array of suinfo_update
+ * @supsz: byte size of suinfo_update
+ * @nsup: size of suinfo_update array
+ *
+ * Description: Takes an array of nilfs_suinfo_update structs and updates
+ * segment usage accordingly. Only the fields indicated by the sup_flags
+ * are updated.
+ *
+ * Return Value: On success, 0 is returned. On error, one of the
+ * following negative error codes is returned.
+ *
+ * %-EIO - I/O error.
+ *
+ * %-ENOMEM - Insufficient amount of memory available.
+ *
+ * %-EINVAL - Invalid values in input (segment number, flags or nblocks)
+ */
+ssize_t nilfs_sufile_set_suinfo(struct inode *sufile, void *buf,
+                               unsigned supsz, size_t nsup)
+{
+       struct the_nilfs *nilfs = sufile->i_sb->s_fs_info;
+       struct buffer_head *header_bh, *bh;
+       struct nilfs_suinfo_update *sup, *supend = buf + supsz * nsup;
+       struct nilfs_segment_usage *su;
+       void *kaddr;
+       unsigned long blkoff, prev_blkoff;
+       int cleansi, cleansu, dirtysi, dirtysu;
+       long ncleaned = 0, ndirtied = 0;
+       int ret = 0;
+
+       if (unlikely(nsup == 0))
+               return ret;
+
+       for (sup = buf; sup < supend; sup = (void *)sup + supsz) {
+               if (sup->sup_segnum >= nilfs->ns_nsegments
+                       || (sup->sup_flags &
+                               (~0UL << __NR_NILFS_SUINFO_UPDATE_FIELDS))
+                       || (nilfs_suinfo_update_nblocks(sup) &&
+                               sup->sup_sui.sui_nblocks >
+                               nilfs->ns_blocks_per_segment))
+                       return -EINVAL;
+       }
+
+       down_write(&NILFS_MDT(sufile)->mi_sem);
+
+       ret = nilfs_sufile_get_header_block(sufile, &header_bh);
+       if (ret < 0)
+               goto out_sem;
+
+       sup = buf;
+       blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum);
+       ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh);
+       if (ret < 0)
+               goto out_header;
+
+       for (;;) {
+               kaddr = kmap_atomic(bh->b_page);
+               su = nilfs_sufile_block_get_segment_usage(
+                       sufile, sup->sup_segnum, bh, kaddr);
+
+               if (nilfs_suinfo_update_lastmod(sup))
+                       su->su_lastmod = cpu_to_le64(sup->sup_sui.sui_lastmod);
+
+               if (nilfs_suinfo_update_nblocks(sup))
+                       su->su_nblocks = cpu_to_le32(sup->sup_sui.sui_nblocks);
+
+               if (nilfs_suinfo_update_flags(sup)) {
+                       /*
+                        * Active flag is a virtual flag projected by running
+                        * nilfs kernel code - drop it not to write it to
+                        * disk.
+                        */
+                       sup->sup_sui.sui_flags &=
+                                       ~(1UL << NILFS_SEGMENT_USAGE_ACTIVE);
+
+                       cleansi = nilfs_suinfo_clean(&sup->sup_sui);
+                       cleansu = nilfs_segment_usage_clean(su);
+                       dirtysi = nilfs_suinfo_dirty(&sup->sup_sui);
+                       dirtysu = nilfs_segment_usage_dirty(su);
+
+                       if (cleansi && !cleansu)
+                               ++ncleaned;
+                       else if (!cleansi && cleansu)
+                               --ncleaned;
+
+                       if (dirtysi && !dirtysu)
+                               ++ndirtied;
+                       else if (!dirtysi && dirtysu)
+                               --ndirtied;
+
+                       su->su_flags = cpu_to_le32(sup->sup_sui.sui_flags);
+               }
+
+               kunmap_atomic(kaddr);
+
+               sup = (void *)sup + supsz;
+               if (sup >= supend)
+                       break;
+
+               prev_blkoff = blkoff;
+               blkoff = nilfs_sufile_get_blkoff(sufile, sup->sup_segnum);
+               if (blkoff == prev_blkoff)
+                       continue;
+
+               /* get different block */
+               mark_buffer_dirty(bh);
+               put_bh(bh);
+               ret = nilfs_mdt_get_block(sufile, blkoff, 1, NULL, &bh);
+               if (unlikely(ret < 0))
+                       goto out_mark;
+       }
+       mark_buffer_dirty(bh);
+       put_bh(bh);
+
+ out_mark:
+       if (ncleaned || ndirtied) {
+               nilfs_sufile_mod_counter(header_bh, (u64)ncleaned,
+                               (u64)ndirtied);
+               NILFS_SUI(sufile)->ncleansegs += ncleaned;
+       }
+       nilfs_mdt_mark_dirty(sufile);
+ out_header:
+       put_bh(header_bh);
+ out_sem:
+       up_write(&NILFS_MDT(sufile)->mi_sem);
+       return ret;
+}
+
+/**
  * nilfs_sufile_read - read or get sufile inode
  * @sb: super block instance
  * @susize: size of a segment usage entry
index e84bc5b..366003c 100644 (file)
@@ -44,6 +44,7 @@ int nilfs_sufile_set_segment_usage(struct inode *sufile, __u64 segnum,
 int nilfs_sufile_get_stat(struct inode *, struct nilfs_sustat *);
 ssize_t nilfs_sufile_get_suinfo(struct inode *, __u64, void *, unsigned,
                                size_t);
+ssize_t nilfs_sufile_set_suinfo(struct inode *, void *, unsigned , size_t);
 
 int nilfs_sufile_updatev(struct inode *, __u64 *, size_t, int, size_t *,
                         void (*dofunc)(struct inode *, __u64,