md/bitmap: add new 'space' attribute for bitmaps.
authorNeilBrown <neilb@suse.de>
Tue, 22 May 2012 03:55:07 +0000 (13:55 +1000)
committerNeilBrown <neilb@suse.de>
Tue, 22 May 2012 03:55:07 +0000 (13:55 +1000)
If we are to allow bitmaps to be resized when the array is resized,
we need to know how much space there is.

So create an attribute to store this information and set appropriate
defaults.

It can be set more precisely via sysfs, or future metadata extensions
may allow it to be recorded.

Signed-off-by: NeilBrown <neilb@suse.de>
drivers/md/bitmap.c
drivers/md/md.c
drivers/md/md.h

index c7784a9856764b6bd2fd3e4244ec4fa2f6fc05d3..ac688fb54e1d535892074a2445cd7aacc7eedb1c 100644 (file)
@@ -1934,6 +1934,44 @@ location_store(struct mddev *mddev, const char *buf, size_t len)
 static struct md_sysfs_entry bitmap_location =
 __ATTR(location, S_IRUGO|S_IWUSR, location_show, location_store);
 
+/* 'bitmap/space' is the space available at 'location' for the
+ * bitmap.  This allows the kernel to know when it is safe to
+ * resize the bitmap to match a resized array.
+ */
+static ssize_t
+space_show(struct mddev *mddev, char *page)
+{
+       return sprintf(page, "%lu\n", mddev->bitmap_info.space);
+}
+
+static ssize_t
+space_store(struct mddev *mddev, const char *buf, size_t len)
+{
+       unsigned long sectors;
+       int rv;
+
+       rv = kstrtoul(buf, 10, &sectors);
+       if (rv)
+               return rv;
+
+       if (sectors == 0)
+               return -EINVAL;
+
+       if (mddev->bitmap &&
+           sectors  < ((mddev->bitmap->file_pages - 1) * PAGE_SIZE
+                       + mddev->bitmap->last_page_size + 511) >> 9)
+               return -EFBIG; /* Bitmap is too big for this small space */
+
+       /* could make sure it isn't too big, but that isn't really
+        * needed - user-space should be careful.
+        */
+       mddev->bitmap_info.space = sectors;
+       return len;
+}
+
+static struct md_sysfs_entry bitmap_space =
+__ATTR(space, S_IRUGO|S_IWUSR, space_show, space_store);
+
 static ssize_t
 timeout_show(struct mddev *mddev, char *page)
 {
@@ -2109,6 +2147,7 @@ __ATTR(max_backlog_used, S_IRUGO | S_IWUSR,
 
 static struct attribute *md_bitmap_attrs[] = {
        &bitmap_location.attr,
+       &bitmap_space.attr,
        &bitmap_timeout.attr,
        &bitmap_backlog.attr,
        &bitmap_chunksize.attr,
index ac99616f48d400e171fd3b4c57f968ee9ba44943..9a677f2078a75258251807798e119631a6954769 100644 (file)
@@ -1197,7 +1197,10 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev)
                mddev->dev_sectors = ((sector_t)sb->size) * 2;
                mddev->events = ev1;
                mddev->bitmap_info.offset = 0;
+               mddev->bitmap_info.space = 0;
+               /* bitmap can use 60 K after the 4K superblocks */
                mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
+               mddev->bitmap_info.default_space = 64*2 - (MD_SB_BYTES >> 9);
                mddev->reshape_backwards = 0;
 
                if (mddev->minor_version >= 91) {
@@ -1234,9 +1237,12 @@ static int super_90_validate(struct mddev *mddev, struct md_rdev *rdev)
                mddev->max_disks = MD_SB_DISKS;
 
                if (sb->state & (1<<MD_SB_BITMAP_PRESENT) &&
-                   mddev->bitmap_info.file == NULL)
+                   mddev->bitmap_info.file == NULL) {
                        mddev->bitmap_info.offset =
                                mddev->bitmap_info.default_offset;
+                       mddev->bitmap_info.space =
+                               mddev->bitmap_info.space;
+               }
 
        } else if (mddev->pers == NULL) {
                /* Insist on good event counter while assembling, except
@@ -1677,7 +1683,12 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
                mddev->dev_sectors = le64_to_cpu(sb->size);
                mddev->events = ev1;
                mddev->bitmap_info.offset = 0;
+               mddev->bitmap_info.space = 0;
+               /* Default location for bitmap is 1K after superblock
+                * using 3K - total of 4K
+                */
                mddev->bitmap_info.default_offset = 1024 >> 9;
+               mddev->bitmap_info.default_space = (4096-1024) >> 9;
                mddev->reshape_backwards = 0;
 
                mddev->recovery_cp = le64_to_cpu(sb->resync_offset);
@@ -1686,9 +1697,23 @@ static int super_1_validate(struct mddev *mddev, struct md_rdev *rdev)
                mddev->max_disks =  (4096-256)/2;
 
                if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_BITMAP_OFFSET) &&
-                   mddev->bitmap_info.file == NULL )
+                   mddev->bitmap_info.file == NULL) {
                        mddev->bitmap_info.offset =
                                (__s32)le32_to_cpu(sb->bitmap_offset);
+                       /* Metadata doesn't record how much space is available.
+                        * For 1.0, we assume we can use up to the superblock
+                        * if before, else to 4K beyond superblock.
+                        * For others, assume no change is possible.
+                        */
+                       if (mddev->minor_version > 0)
+                               mddev->bitmap_info.space = 0;
+                       else if (mddev->bitmap_info.offset > 0)
+                               mddev->bitmap_info.space =
+                                       8 - mddev->bitmap_info.offset;
+                       else
+                               mddev->bitmap_info.space =
+                                       -mddev->bitmap_info.offset;
+               }
 
                if ((le32_to_cpu(sb->feature_map) & MD_FEATURE_RESHAPE_ACTIVE)) {
                        mddev->reshape_position = le64_to_cpu(sb->reshape_position);
@@ -5280,6 +5305,7 @@ static void md_clean(struct mddev *mddev)
        mddev->merge_check_needed = 0;
        mddev->bitmap_info.offset = 0;
        mddev->bitmap_info.default_offset = 0;
+       mddev->bitmap_info.default_space = 0;
        mddev->bitmap_info.chunksize = 0;
        mddev->bitmap_info.daemon_sleep = 0;
        mddev->bitmap_info.max_write_behind = 0;
@@ -6076,6 +6102,7 @@ static int set_array_info(struct mddev * mddev, mdu_array_info_t *info)
        set_bit(MD_CHANGE_DEVS, &mddev->flags);
 
        mddev->bitmap_info.default_offset = MD_SB_BYTES >> 9;
+       mddev->bitmap_info.default_space = 64*2 - (MD_SB_BYTES >> 9);
        mddev->bitmap_info.offset = 0;
 
        mddev->reshape_position = MaxSector;
@@ -6258,6 +6285,8 @@ static int update_array_info(struct mddev *mddev, mdu_array_info_t *info)
                                return -EINVAL;
                        mddev->bitmap_info.offset =
                                mddev->bitmap_info.default_offset;
+                       mddev->bitmap_info.space =
+                               mddev->bitmap_info.default_space;
                        mddev->pers->quiesce(mddev, 1);
                        rv = bitmap_create(mddev);
                        if (!rv)
index 360937389e640644254532a60e35ef68bb5a2437..7b4a3c318cae437ecb2041c33b2bdcee2c66bf15 100644 (file)
@@ -393,10 +393,13 @@ struct mddev {
                                                 * For external metadata, offset
                                                 * from start of device. 
                                                 */
+               unsigned long           space; /* space available at this offset */
                loff_t                  default_offset; /* this is the offset to use when
                                                         * hot-adding a bitmap.  It should
                                                         * eventually be settable by sysfs.
                                                         */
+               unsigned long           default_space; /* space available at
+                                                       * default offset */
                struct mutex            mutex;
                unsigned long           chunksize;
                unsigned long           daemon_sleep; /* how many jiffies between updates? */