[PATCH] md: allow md/raid_disks to be settable
[firefly-linux-kernel-4.4.55.git] / drivers / md / md.c
index 594d8c312e6aa54db6f1aa4cbc6145bd66cee7b7..86e9f2efae5c7b95c69074f73f92e66a706d48c2 100644 (file)
@@ -1000,6 +1000,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
        }
        rdev->preferred_minor = 0xffff;
        rdev->data_offset = le64_to_cpu(sb->data_offset);
+       atomic_set(&rdev->corrected_errors, le32_to_cpu(sb->cnt_corrected_read));
 
        rdev->sb_size = le32_to_cpu(sb->max_dev) * 2 + 256;
        bmask = queue_hardsect_size(rdev->bdev->bd_disk->queue)-1;
@@ -1139,6 +1140,8 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
        else
                sb->resync_offset = cpu_to_le64(0);
 
+       sb->cnt_corrected_read = atomic_read(&rdev->corrected_errors);
+
        if (mddev->bitmap && mddev->bitmap_file == NULL) {
                sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset);
                sb->feature_map = cpu_to_le32(MD_FEATURE_BITMAP_OFFSET);
@@ -1592,9 +1595,30 @@ super_show(mdk_rdev_t *rdev, char *page)
 }
 static struct rdev_sysfs_entry rdev_super = __ATTR_RO(super);
 
+static ssize_t
+errors_show(mdk_rdev_t *rdev, char *page)
+{
+       return sprintf(page, "%d\n", atomic_read(&rdev->corrected_errors));
+}
+
+static ssize_t
+errors_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+{
+       char *e;
+       unsigned long n = simple_strtoul(buf, &e, 10);
+       if (*buf && (*e == 0 || *e == '\n')) {
+               atomic_set(&rdev->corrected_errors, n);
+               return len;
+       }
+       return -EINVAL;
+}
+static struct rdev_sysfs_entry rdev_errors =
+__ATTR(errors, 0644, errors_show, errors_store);
+
 static struct attribute *rdev_default_attrs[] = {
        &rdev_state.attr,
        &rdev_super.attr,
+       &rdev_errors.attr,
        NULL,
 };
 static ssize_t
@@ -1674,6 +1698,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
        rdev->data_offset = 0;
        atomic_set(&rdev->nr_pending, 0);
        atomic_set(&rdev->read_errors, 0);
+       atomic_set(&rdev->corrected_errors, 0);
 
        size = rdev->bdev->bd_inode->i_size >> BLOCK_SIZE_BITS;
        if (!size) {
@@ -1818,7 +1843,27 @@ raid_disks_show(mddev_t *mddev, char *page)
        return sprintf(page, "%d\n", mddev->raid_disks);
 }
 
-static struct md_sysfs_entry md_raid_disks = __ATTR_RO(raid_disks);
+static int update_raid_disks(mddev_t *mddev, int raid_disks);
+
+static ssize_t
+raid_disks_store(mddev_t *mddev, const char *buf, size_t len)
+{
+       /* can only set raid_disks if array is not yet active */
+       char *e;
+       int rv = 0;
+       unsigned long n = simple_strtoul(buf, &e, 10);
+
+       if (!*buf || (*e && *e != '\n'))
+               return -EINVAL;
+
+       if (mddev->pers)
+               rv = update_raid_disks(mddev, n);
+       else
+               mddev->raid_disks = n;
+       return rv ? rv : len;
+}
+static struct md_sysfs_entry md_raid_disks =
+__ATTR(raid_disks, 0644, raid_disks_show, raid_disks_store);
 
 static ssize_t
 chunk_size_show(mddev_t *mddev, char *page)
@@ -3176,6 +3221,33 @@ static int update_size(mddev_t *mddev, unsigned long size)
        return rv;
 }
 
+static int update_raid_disks(mddev_t *mddev, int raid_disks)
+{
+       int rv;
+       /* change the number of raid disks */
+       if (mddev->pers->reshape == NULL)
+               return -EINVAL;
+       if (raid_disks <= 0 ||
+           raid_disks >= mddev->max_disks)
+               return -EINVAL;
+       if (mddev->sync_thread)
+               return -EBUSY;
+       rv = mddev->pers->reshape(mddev, raid_disks);
+       if (!rv) {
+               struct block_device *bdev;
+
+               bdev = bdget_disk(mddev->gendisk, 0);
+               if (bdev) {
+                       down(&bdev->bd_inode->i_sem);
+                       i_size_write(bdev->bd_inode, mddev->array_size << 10);
+                       up(&bdev->bd_inode->i_sem);
+                       bdput(bdev);
+               }
+       }
+       return rv;
+}
+
+
 /*
  * update_array_info is used to change the configuration of an
  * on-line array.
@@ -3227,28 +3299,9 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
        if (mddev->size != info->size)
                rv = update_size(mddev, info->size);
 
-       if (mddev->raid_disks    != info->raid_disks) {
-               /* change the number of raid disks */
-               if (mddev->pers->reshape == NULL)
-                       return -EINVAL;
-               if (info->raid_disks <= 0 ||
-                   info->raid_disks >= mddev->max_disks)
-                       return -EINVAL;
-               if (mddev->sync_thread)
-                       return -EBUSY;
-               rv = mddev->pers->reshape(mddev, info->raid_disks);
-               if (!rv) {
-                       struct block_device *bdev;
-
-                       bdev = bdget_disk(mddev->gendisk, 0);
-                       if (bdev) {
-                               down(&bdev->bd_inode->i_sem);
-                               i_size_write(bdev->bd_inode, mddev->array_size << 10);
-                               up(&bdev->bd_inode->i_sem);
-                               bdput(bdev);
-                       }
-               }
-       }
+       if (mddev->raid_disks    != info->raid_disks)
+               rv = update_raid_disks(mddev, info->raid_disks);
+
        if ((state ^ info->state) & (1<<MD_SB_BITMAP_PRESENT)) {
                if (mddev->pers->quiesce == NULL)
                        return -EINVAL;
@@ -4729,7 +4782,7 @@ static int set_ro(const char *val, struct kernel_param *kp)
        int num = simple_strtoul(val, &e, 10);
        if (*val && (*e == '\0' || *e == '\n')) {
                start_readonly = num;
-               return 0;;
+               return 0;
        }
        return -EINVAL;
 }