Merge tag 'uuid-for-4.14' of git://git.infradead.org/users/hch/uuid
[platform/kernel/linux-exynos.git] / block / genhd.c
index 7f520fa..dd305c6 100644 (file)
@@ -45,6 +45,52 @@ static void disk_add_events(struct gendisk *disk);
 static void disk_del_events(struct gendisk *disk);
 static void disk_release_events(struct gendisk *disk);
 
+void part_inc_in_flight(struct request_queue *q, struct hd_struct *part, int rw)
+{
+       if (q->mq_ops)
+               return;
+
+       atomic_inc(&part->in_flight[rw]);
+       if (part->partno)
+               atomic_inc(&part_to_disk(part)->part0.in_flight[rw]);
+}
+
+void part_dec_in_flight(struct request_queue *q, struct hd_struct *part, int rw)
+{
+       if (q->mq_ops)
+               return;
+
+       atomic_dec(&part->in_flight[rw]);
+       if (part->partno)
+               atomic_dec(&part_to_disk(part)->part0.in_flight[rw]);
+}
+
+void part_in_flight(struct request_queue *q, struct hd_struct *part,
+                   unsigned int inflight[2])
+{
+       if (q->mq_ops) {
+               blk_mq_in_flight(q, part, inflight);
+               return;
+       }
+
+       inflight[0] = atomic_read(&part->in_flight[0]) +
+                       atomic_read(&part->in_flight[1]);
+       if (part->partno) {
+               part = &part_to_disk(part)->part0;
+               inflight[1] = atomic_read(&part->in_flight[0]) +
+                               atomic_read(&part->in_flight[1]);
+       }
+}
+
+struct hd_struct *__disk_get_part(struct gendisk *disk, int partno)
+{
+       struct disk_part_tbl *ptbl = rcu_dereference(disk->part_tbl);
+
+       if (unlikely(partno < 0 || partno >= ptbl->len))
+               return NULL;
+       return rcu_dereference(ptbl->part[partno]);
+}
+
 /**
  * disk_get_part - get partition
  * @disk: disk to look partition from
@@ -61,21 +107,12 @@ static void disk_release_events(struct gendisk *disk);
  */
 struct hd_struct *disk_get_part(struct gendisk *disk, int partno)
 {
-       struct hd_struct *part = NULL;
-       struct disk_part_tbl *ptbl;
-
-       if (unlikely(partno < 0))
-               return NULL;
+       struct hd_struct *part;
 
        rcu_read_lock();
-
-       ptbl = rcu_dereference(disk->part_tbl);
-       if (likely(partno < ptbl->len)) {
-               part = rcu_dereference(ptbl->part[partno]);
-               if (part)
-                       get_device(part_to_dev(part));
-       }
-
+       part = __disk_get_part(disk, partno);
+       if (part)
+               get_device(part_to_dev(part));
        rcu_read_unlock();
 
        return part;
@@ -242,6 +279,7 @@ EXPORT_SYMBOL_GPL(disk_map_sector_rcu);
  * Can be deleted altogether. Later.
  *
  */
+#define BLKDEV_MAJOR_HASH_SIZE 255
 static struct blk_major_name {
        struct blk_major_name *next;
        int major;
@@ -259,12 +297,11 @@ void blkdev_show(struct seq_file *seqf, off_t offset)
 {
        struct blk_major_name *dp;
 
-       if (offset < BLKDEV_MAJOR_HASH_SIZE) {
-               mutex_lock(&block_class_lock);
-               for (dp = major_names[offset]; dp; dp = dp->next)
+       mutex_lock(&block_class_lock);
+       for (dp = major_names[major_to_index(offset)]; dp; dp = dp->next)
+               if (dp->major == offset)
                        seq_printf(seqf, "%3d %s\n", dp->major, dp->name);
-               mutex_unlock(&block_class_lock);
-       }
+       mutex_unlock(&block_class_lock);
 }
 #endif /* CONFIG_PROC_FS */
 
@@ -309,6 +346,14 @@ int register_blkdev(unsigned int major, const char *name)
                ret = major;
        }
 
+       if (major >= BLKDEV_MAJOR_MAX) {
+               pr_err("register_blkdev: major requested (%d) is greater than the maximum (%d) for %s\n",
+                      major, BLKDEV_MAJOR_MAX, name);
+
+               ret = -EINVAL;
+               goto out;
+       }
+
        p = kmalloc(sizeof(struct blk_major_name), GFP_KERNEL);
        if (p == NULL) {
                ret = -ENOMEM;
@@ -1090,12 +1135,13 @@ static const struct attribute_group *disk_attr_groups[] = {
  * original ptbl is freed using RCU callback.
  *
  * LOCKING:
- * Matching bd_mutx locked.
+ * Matching bd_mutex locked or the caller is the only user of @disk.
  */
 static void disk_replace_part_tbl(struct gendisk *disk,
                                  struct disk_part_tbl *new_ptbl)
 {
-       struct disk_part_tbl *old_ptbl = disk->part_tbl;
+       struct disk_part_tbl *old_ptbl =
+               rcu_dereference_protected(disk->part_tbl, 1);
 
        rcu_assign_pointer(disk->part_tbl, new_ptbl);
 
@@ -1114,14 +1160,16 @@ static void disk_replace_part_tbl(struct gendisk *disk,
  * uses RCU to allow unlocked dereferencing for stats and other stuff.
  *
  * LOCKING:
- * Matching bd_mutex locked, might sleep.
+ * Matching bd_mutex locked or the caller is the only user of @disk.
+ * Might sleep.
  *
  * RETURNS:
  * 0 on success, -errno on failure.
  */
 int disk_expand_part_tbl(struct gendisk *disk, int partno)
 {
-       struct disk_part_tbl *old_ptbl = disk->part_tbl;
+       struct disk_part_tbl *old_ptbl =
+               rcu_dereference_protected(disk->part_tbl, 1);
        struct disk_part_tbl *new_ptbl;
        int len = old_ptbl ? old_ptbl->len : 0;
        int i, target;
@@ -1204,6 +1252,7 @@ static int diskstats_show(struct seq_file *seqf, void *v)
        struct disk_part_iter piter;
        struct hd_struct *hd;
        char buf[BDEVNAME_SIZE];
+       unsigned int inflight[2];
        int cpu;
 
        /*
@@ -1217,8 +1266,9 @@ static int diskstats_show(struct seq_file *seqf, void *v)
        disk_part_iter_init(&piter, gp, DISK_PITER_INCL_EMPTY_PART0);
        while ((hd = disk_part_iter_next(&piter))) {
                cpu = part_stat_lock();
-               part_round_stats(cpu, hd);
+               part_round_stats(gp->queue, cpu, hd);
                part_stat_unlock();
+               part_in_flight(gp->queue, hd, inflight);
                seq_printf(seqf, "%4d %7d %s %lu %lu %lu "
                           "%u %lu %lu %lu %u %u %u %u\n",
                           MAJOR(part_devt(hd)), MINOR(part_devt(hd)),
@@ -1231,7 +1281,7 @@ static int diskstats_show(struct seq_file *seqf, void *v)
                           part_stat_read(hd, merges[WRITE]),
                           part_stat_read(hd, sectors[WRITE]),
                           jiffies_to_msecs(part_stat_read(hd, ticks[WRITE])),
-                          part_in_flight(hd),
+                          inflight[0],
                           jiffies_to_msecs(part_stat_read(hd, io_ticks)),
                           jiffies_to_msecs(part_stat_read(hd, time_in_queue))
                        );
@@ -1313,6 +1363,14 @@ EXPORT_SYMBOL(alloc_disk);
 struct gendisk *alloc_disk_node(int minors, int node_id)
 {
        struct gendisk *disk;
+       struct disk_part_tbl *ptbl;
+
+       if (minors > DISK_MAX_PARTS) {
+               printk(KERN_ERR
+                       "block: can't allocated more than %d partitions\n",
+                       DISK_MAX_PARTS);
+               minors = DISK_MAX_PARTS;
+       }
 
        disk = kzalloc_node(sizeof(struct gendisk), GFP_KERNEL, node_id);
        if (disk) {
@@ -1326,7 +1384,8 @@ struct gendisk *alloc_disk_node(int minors, int node_id)
                        kfree(disk);
                        return NULL;
                }
-               disk->part_tbl->part[0] = &disk->part0;
+               ptbl = rcu_dereference_protected(disk->part_tbl, 1);
+               rcu_assign_pointer(ptbl->part[0], &disk->part0);
 
                /*
                 * set_capacity() and get_capacity() currently don't use