smb311: Add support for lookup with posix extensions query info
[linux-2.6-microblaze.git] / block / genhd.c
index 06b642b..1a76593 100644 (file)
@@ -92,7 +92,6 @@ const char *bdevname(struct block_device *bdev, char *buf)
 }
 EXPORT_SYMBOL(bdevname);
 
-#ifdef CONFIG_SMP
 static void part_stat_read_all(struct hd_struct *part, struct disk_stats *stat)
 {
        int cpu;
@@ -112,44 +111,13 @@ static void part_stat_read_all(struct hd_struct *part, struct disk_stats *stat)
                stat->io_ticks += ptr->io_ticks;
        }
 }
-#else /* CONFIG_SMP */
-static void part_stat_read_all(struct hd_struct *part, struct disk_stats *stat)
-{
-       memcpy(stat, &part->dkstats, sizeof(struct disk_stats));
-}
-#endif /* CONFIG_SMP */
-
-void part_inc_in_flight(struct request_queue *q, struct hd_struct *part, int rw)
-{
-       if (queue_is_mq(q))
-               return;
-
-       part_stat_local_inc(part, in_flight[rw]);
-       if (part->partno)
-               part_stat_local_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 (queue_is_mq(q))
-               return;
-
-       part_stat_local_dec(part, in_flight[rw]);
-       if (part->partno)
-               part_stat_local_dec(&part_to_disk(part)->part0, in_flight[rw]);
-}
 
 static unsigned int part_in_flight(struct request_queue *q,
                struct hd_struct *part)
 {
+       unsigned int inflight = 0;
        int cpu;
-       unsigned int inflight;
 
-       if (queue_is_mq(q)) {
-               return blk_mq_in_flight(q, part);
-       }
-
-       inflight = 0;
        for_each_possible_cpu(cpu) {
                inflight += part_stat_local_read_cpu(part, in_flight[0], cpu) +
                            part_stat_local_read_cpu(part, in_flight[1], cpu);
@@ -165,11 +133,6 @@ static void part_in_flight_rw(struct request_queue *q, struct hd_struct *part,
 {
        int cpu;
 
-       if (queue_is_mq(q)) {
-               blk_mq_in_flight_rw(q, part, inflight);
-               return;
-       }
-
        inflight[0] = 0;
        inflight[1] = 0;
        for_each_possible_cpu(cpu) {
@@ -344,11 +307,13 @@ static inline int sector_in_part(struct hd_struct *part, sector_t sector)
  * primarily used for stats accounting.
  *
  * CONTEXT:
- * RCU read locked.  The returned partition pointer is valid only
- * while preemption is disabled.
+ * RCU read locked.  The returned partition pointer is always valid
+ * because its refcount is grabbed except for part0, which lifetime
+ * is same with the disk.
  *
  * RETURNS:
  * Found partition on success, part0 is returned if no partition matches
+ * or the matched partition is being deleted.
  */
 struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector)
 {
@@ -356,21 +321,33 @@ struct hd_struct *disk_map_sector_rcu(struct gendisk *disk, sector_t sector)
        struct hd_struct *part;
        int i;
 
+       rcu_read_lock();
        ptbl = rcu_dereference(disk->part_tbl);
 
        part = rcu_dereference(ptbl->last_lookup);
-       if (part && sector_in_part(part, sector))
-               return part;
+       if (part && sector_in_part(part, sector) && hd_struct_try_get(part))
+               goto out_unlock;
 
        for (i = 1; i < ptbl->len; i++) {
                part = rcu_dereference(ptbl->part[i]);
 
                if (part && sector_in_part(part, sector)) {
+                       /*
+                        * only live partition can be cached for lookup,
+                        * so use-after-free on cached & deleting partition
+                        * can be avoided
+                        */
+                       if (!hd_struct_try_get(part))
+                               break;
                        rcu_assign_pointer(ptbl->last_lookup, part);
-                       return part;
+                       goto out_unlock;
                }
        }
-       return &disk->part0;
+
+       part = &disk->part0;
+out_unlock:
+       rcu_read_unlock();
+       return part;
 }
 
 /**
@@ -840,13 +817,15 @@ static void __device_add_disk(struct device *parent, struct gendisk *disk,
                disk->flags |= GENHD_FL_SUPPRESS_PARTITION_INFO;
                disk->flags |= GENHD_FL_NO_PART_SCAN;
        } else {
+               struct backing_dev_info *bdi = disk->queue->backing_dev_info;
+               struct device *dev = disk_to_dev(disk);
                int ret;
 
                /* Register BDI before referencing it from bdev */
-               disk_to_dev(disk)->devt = devt;
-               ret = bdi_register_owner(disk->queue->backing_dev_info,
-                                               disk_to_dev(disk));
+               dev->devt = devt;
+               ret = bdi_register(bdi, "%u:%u", MAJOR(devt), MINOR(devt));
                WARN_ON(ret);
+               bdi_set_owner(bdi, dev);
                blk_register_region(disk_devt(disk), disk->minors, NULL,
                                    exact_match, exact_lock, disk);
        }
@@ -878,6 +857,25 @@ void device_add_disk_no_queue_reg(struct device *parent, struct gendisk *disk)
 }
 EXPORT_SYMBOL(device_add_disk_no_queue_reg);
 
+static void invalidate_partition(struct gendisk *disk, int partno)
+{
+       struct block_device *bdev;
+
+       bdev = bdget_disk(disk, partno);
+       if (!bdev)
+               return;
+
+       fsync_bdev(bdev);
+       __invalidate_device(bdev, true);
+
+       /*
+        * Unhash the bdev inode for this device so that it gets evicted as soon
+        * as last inode reference is dropped.
+        */
+       remove_inode_hash(bdev->bd_inode);
+       bdput(bdev);
+}
+
 void del_gendisk(struct gendisk *disk)
 {
        struct disk_part_iter piter;
@@ -896,13 +894,11 @@ void del_gendisk(struct gendisk *disk)
                             DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE);
        while ((part = disk_part_iter_next(&piter))) {
                invalidate_partition(disk, part->partno);
-               bdev_unhash_inode(part_devt(part));
-               delete_partition(disk, part->partno);
+               delete_partition(disk, part);
        }
        disk_part_iter_exit(&piter);
 
        invalidate_partition(disk, 0);
-       bdev_unhash_inode(disk_devt(disk));
        set_capacity(disk, 0);
        disk->flags &= ~GENHD_FL_UP;
        up_write(&disk->lookup_sem);
@@ -1279,7 +1275,10 @@ ssize_t part_stat_show(struct device *dev,
        unsigned int inflight;
 
        part_stat_read_all(p, &stat);
-       inflight = part_in_flight(q, p);
+       if (queue_is_mq(q))
+               inflight = blk_mq_in_flight(q, p);
+       else
+               inflight = part_in_flight(q, p);
 
        return sprintf(buf,
                "%8lu %8lu %8llu %8u "
@@ -1318,7 +1317,11 @@ ssize_t part_inflight_show(struct device *dev, struct device_attribute *attr,
        struct request_queue *q = part_to_disk(p)->queue;
        unsigned int inflight[2];
 
-       part_in_flight_rw(q, p, inflight);
+       if (queue_is_mq(q))
+               blk_mq_in_flight_rw(q, p, inflight);
+       else
+               part_in_flight_rw(q, p, inflight);
+
        return sprintf(buf, "%8u %8u\n", inflight[0], inflight[1]);
 }
 
@@ -1573,7 +1576,10 @@ 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))) {
                part_stat_read_all(hd, &stat);
-               inflight = part_in_flight(gp->queue, hd);
+               if (queue_is_mq(gp->queue))
+                       inflight = blk_mq_in_flight(gp->queue, hd);
+               else
+                       inflight = part_in_flight(gp->queue, hd);
 
                seq_printf(seqf, "%4d %7d %s "
                           "%lu %lu %lu %u "
@@ -1680,14 +1686,15 @@ struct gendisk *__alloc_disk_node(int minors, int node_id)
 
        disk = kzalloc_node(sizeof(struct gendisk), GFP_KERNEL, node_id);
        if (disk) {
-               if (!init_part_stats(&disk->part0)) {
+               disk->part0.dkstats = alloc_percpu(struct disk_stats);
+               if (!disk->part0.dkstats) {
                        kfree(disk);
                        return NULL;
                }
                init_rwsem(&disk->lookup_sem);
                disk->node_id = node_id;
                if (disk_expand_part_tbl(disk, 0)) {
-                       free_part_stats(&disk->part0);
+                       free_percpu(disk->part0.dkstats);
                        kfree(disk);
                        return NULL;
                }
@@ -1703,7 +1710,7 @@ struct gendisk *__alloc_disk_node(int minors, int node_id)
                 * TODO: Ideally set_capacity() and get_capacity() should be
                 * converted to make use of bd_mutex and sequence counters.
                 */
-               seqcount_init(&disk->part0.nr_sects_seq);
+               hd_sects_seq_init(&disk->part0);
                if (hd_ref_init(&disk->part0)) {
                        hd_free_part(&disk->part0);
                        kfree(disk);
@@ -1806,20 +1813,6 @@ int bdev_read_only(struct block_device *bdev)
 
 EXPORT_SYMBOL(bdev_read_only);
 
-int invalidate_partition(struct gendisk *disk, int partno)
-{
-       int res = 0;
-       struct block_device *bdev = bdget_disk(disk, partno);
-       if (bdev) {
-               fsync_bdev(bdev);
-               res = __invalidate_device(bdev, true);
-               bdput(bdev);
-       }
-       return res;
-}
-
-EXPORT_SYMBOL(invalidate_partition);
-
 /*
  * Disk events - monitor disk events like media change and eject request.
  */