tools headers UAPI: Sync drm/i915_drm.h with the kernel sources
[linux-2.6-microblaze.git] / block / genhd.c
index 9e741a4..36ff45b 100644 (file)
@@ -162,15 +162,6 @@ static void part_in_flight_rw(struct block_device *part,
                inflight[1] = 0;
 }
 
-struct block_device *__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_part_iter_init - initialize partition iterator
  * @piter: iterator to initialize
@@ -185,26 +176,14 @@ struct block_device *__disk_get_part(struct gendisk *disk, int partno)
 void disk_part_iter_init(struct disk_part_iter *piter, struct gendisk *disk,
                          unsigned int flags)
 {
-       struct disk_part_tbl *ptbl;
-
-       rcu_read_lock();
-       ptbl = rcu_dereference(disk->part_tbl);
-
        piter->disk = disk;
        piter->part = NULL;
-
-       if (flags & DISK_PITER_REVERSE)
-               piter->idx = ptbl->len - 1;
-       else if (flags & (DISK_PITER_INCL_PART0 | DISK_PITER_INCL_EMPTY_PART0))
+       if (flags & (DISK_PITER_INCL_PART0 | DISK_PITER_INCL_EMPTY_PART0))
                piter->idx = 0;
        else
                piter->idx = 1;
-
        piter->flags = flags;
-
-       rcu_read_unlock();
 }
-EXPORT_SYMBOL_GPL(disk_part_iter_init);
 
 /**
  * disk_part_iter_next - proceed iterator to the next partition and return it
@@ -217,57 +196,30 @@ EXPORT_SYMBOL_GPL(disk_part_iter_init);
  */
 struct block_device *disk_part_iter_next(struct disk_part_iter *piter)
 {
-       struct disk_part_tbl *ptbl;
-       int inc, end;
+       struct block_device *part;
+       unsigned long idx;
 
        /* put the last partition */
        disk_part_iter_exit(piter);
 
-       /* get part_tbl */
        rcu_read_lock();
-       ptbl = rcu_dereference(piter->disk->part_tbl);
-
-       /* determine iteration parameters */
-       if (piter->flags & DISK_PITER_REVERSE) {
-               inc = -1;
-               if (piter->flags & (DISK_PITER_INCL_PART0 |
-                                   DISK_PITER_INCL_EMPTY_PART0))
-                       end = -1;
-               else
-                       end = 0;
-       } else {
-               inc = 1;
-               end = ptbl->len;
-       }
-
-       /* iterate to the next partition */
-       for (; piter->idx != end; piter->idx += inc) {
-               struct block_device *part;
-
-               part = rcu_dereference(ptbl->part[piter->idx]);
-               if (!part)
-                       continue;
-               piter->part = bdgrab(part);
-               if (!piter->part)
-                       continue;
+       xa_for_each_start(&piter->disk->part_tbl, idx, part, piter->idx) {
                if (!bdev_nr_sectors(part) &&
                    !(piter->flags & DISK_PITER_INCL_EMPTY) &&
                    !(piter->flags & DISK_PITER_INCL_EMPTY_PART0 &&
-                     piter->idx == 0)) {
-                       bdput(piter->part);
-                       piter->part = NULL;
+                     piter->idx == 0))
                        continue;
-               }
 
-               piter->idx += inc;
+               piter->part = bdgrab(part);
+               if (!piter->part)
+                       continue;
+               piter->idx = idx + 1;
                break;
        }
-
        rcu_read_unlock();
 
        return piter->part;
 }
-EXPORT_SYMBOL_GPL(disk_part_iter_next);
 
 /**
  * disk_part_iter_exit - finish up partition iteration
@@ -284,91 +236,6 @@ void disk_part_iter_exit(struct disk_part_iter *piter)
                bdput(piter->part);
        piter->part = NULL;
 }
-EXPORT_SYMBOL_GPL(disk_part_iter_exit);
-
-static inline int sector_in_part(struct block_device *part, sector_t sector)
-{
-       return part->bd_start_sect <= sector &&
-               sector < part->bd_start_sect + bdev_nr_sectors(part);
-}
-
-/**
- * disk_map_sector_rcu - map sector to partition
- * @disk: gendisk of interest
- * @sector: sector to map
- *
- * Find out which partition @sector maps to on @disk.  This is
- * primarily used for stats accounting.
- *
- * CONTEXT:
- * RCU read locked.
- *
- * RETURNS:
- * Found partition on success, part0 is returned if no partition matches
- * or the matched partition is being deleted.
- */
-struct block_device *disk_map_sector_rcu(struct gendisk *disk, sector_t sector)
-{
-       struct disk_part_tbl *ptbl;
-       struct block_device *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))
-               goto out_unlock;
-
-       for (i = 1; i < ptbl->len; i++) {
-               part = rcu_dereference(ptbl->part[i]);
-               if (part && sector_in_part(part, sector)) {
-                       rcu_assign_pointer(ptbl->last_lookup, part);
-                       goto out_unlock;
-               }
-       }
-
-       part = disk->part0;
-out_unlock:
-       rcu_read_unlock();
-       return part;
-}
-
-/**
- * disk_has_partitions
- * @disk: gendisk of interest
- *
- * Walk through the partition table and check if valid partition exists.
- *
- * CONTEXT:
- * Don't care.
- *
- * RETURNS:
- * True if the gendisk has at least one valid non-zero size partition.
- * Otherwise false.
- */
-bool disk_has_partitions(struct gendisk *disk)
-{
-       struct disk_part_tbl *ptbl;
-       int i;
-       bool ret = false;
-
-       rcu_read_lock();
-       ptbl = rcu_dereference(disk->part_tbl);
-
-       /* Iterate partitions skipping the whole device at index 0 */
-       for (i = 1; i < ptbl->len; i++) {
-               if (rcu_dereference(ptbl->part[i])) {
-                       ret = true;
-                       break;
-               }
-       }
-
-       rcu_read_unlock();
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(disk_has_partitions);
 
 /*
  * Can be deleted altogether. Later.
@@ -604,6 +471,18 @@ static char *bdevt_str(dev_t devt, char *buf)
        return buf;
 }
 
+void disk_uevent(struct gendisk *disk, enum kobject_action action)
+{
+       struct disk_part_iter piter;
+       struct block_device *part;
+
+       disk_part_iter_init(&piter, disk, DISK_PITER_INCL_PART0);
+       while ((part = disk_part_iter_next(&piter)))
+               kobject_uevent(bdev_kobj(part), action);
+       disk_part_iter_exit(&piter);
+}
+EXPORT_SYMBOL_GPL(disk_uevent);
+
 static void disk_scan_partitions(struct gendisk *disk)
 {
        struct block_device *bdev;
@@ -621,8 +500,6 @@ static void register_disk(struct device *parent, struct gendisk *disk,
                          const struct attribute_group **groups)
 {
        struct device *ddev = disk_to_dev(disk);
-       struct disk_part_iter piter;
-       struct block_device *part;
        int err;
 
        ddev->parent = parent;
@@ -665,15 +542,9 @@ static void register_disk(struct device *parent, struct gendisk *disk,
 
        disk_scan_partitions(disk);
 
-       /* announce disk after possible partitions are created */
+       /* announce the disk and partitions after all partitions are created */
        dev_set_uevent_suppress(ddev, 0);
-       kobject_uevent(&ddev->kobj, KOBJ_ADD);
-
-       /* announce possible partitions */
-       disk_part_iter_init(&piter, disk, 0);
-       while ((part = disk_part_iter_next(&piter)))
-               kobject_uevent(bdev_kobj(part), KOBJ_ADD);
-       disk_part_iter_exit(&piter);
+       disk_uevent(disk, KOBJ_ADD);
 
        if (disk->queue->backing_dev_info->dev) {
                err = sysfs_create_link(&ddev->kobj,
@@ -829,8 +700,7 @@ void del_gendisk(struct gendisk *disk)
        down_write(&bdev_lookup_sem);
 
        /* invalidate stuff */
-       disk_part_iter_init(&piter, disk,
-                            DISK_PITER_INCL_EMPTY | DISK_PITER_REVERSE);
+       disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
        while ((part = disk_part_iter_next(&piter))) {
                invalidate_partition(part);
                delete_partition(part);
@@ -929,7 +799,7 @@ struct block_device *bdget_disk(struct gendisk *disk, int partno)
        struct block_device *bdev = NULL;
 
        rcu_read_lock();
-       bdev = __disk_get_part(disk, partno);
+       bdev = xa_load(&disk->part_tbl, partno);
        if (bdev && !bdgrab(bdev))
                bdev = NULL;
        rcu_read_unlock();
@@ -1319,83 +1189,6 @@ static const struct attribute_group *disk_attr_groups[] = {
        NULL
 };
 
-/**
- * disk_replace_part_tbl - replace disk->part_tbl in RCU-safe way
- * @disk: disk to replace part_tbl for
- * @new_ptbl: new part_tbl to install
- *
- * Replace disk->part_tbl with @new_ptbl in RCU-safe way.  The
- * original ptbl is freed using RCU callback.
- *
- * LOCKING:
- * 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 =
-               rcu_dereference_protected(disk->part_tbl, 1);
-
-       rcu_assign_pointer(disk->part_tbl, new_ptbl);
-
-       if (old_ptbl) {
-               rcu_assign_pointer(old_ptbl->last_lookup, NULL);
-               kfree_rcu(old_ptbl, rcu_head);
-       }
-}
-
-/**
- * disk_expand_part_tbl - expand disk->part_tbl
- * @disk: disk to expand part_tbl for
- * @partno: expand such that this partno can fit in
- *
- * Expand disk->part_tbl such that @partno can fit in.  disk->part_tbl
- * uses RCU to allow unlocked dereferencing for stats and other stuff.
- *
- * LOCKING:
- * 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 =
-               rcu_dereference_protected(disk->part_tbl, 1);
-       struct disk_part_tbl *new_ptbl;
-       int len = old_ptbl ? old_ptbl->len : 0;
-       int i, target;
-
-       /*
-        * check for int overflow, since we can get here from blkpg_ioctl()
-        * with a user passed 'partno'.
-        */
-       target = partno + 1;
-       if (target < 0)
-               return -EINVAL;
-
-       /* disk_max_parts() is zero during initialization, ignore if so */
-       if (disk_max_parts(disk) && target > disk_max_parts(disk))
-               return -EINVAL;
-
-       if (target <= len)
-               return 0;
-
-       new_ptbl = kzalloc_node(struct_size(new_ptbl, part, target), GFP_KERNEL,
-                               disk->node_id);
-       if (!new_ptbl)
-               return -ENOMEM;
-
-       new_ptbl->len = target;
-
-       for (i = 0; i < len; i++)
-               rcu_assign_pointer(new_ptbl->part[i], old_ptbl->part[i]);
-
-       disk_replace_part_tbl(disk, new_ptbl);
-       return 0;
-}
-
 /**
  * disk_release - releases all allocated resources of the gendisk
  * @dev: the device representing this disk
@@ -1419,7 +1212,7 @@ static void disk_release(struct device *dev)
        blk_free_devt(dev->devt);
        disk_release_events(disk);
        kfree(disk->random);
-       disk_replace_part_tbl(disk, NULL);
+       xa_destroy(&disk->part_tbl);
        bdput(disk->part0);
        if (disk->queue)
                blk_put_queue(disk->queue);
@@ -1572,7 +1365,6 @@ dev_t blk_lookup_devt(const char *name, int partno)
 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
@@ -1590,11 +1382,9 @@ struct gendisk *__alloc_disk_node(int minors, int node_id)
                goto out_free_disk;
 
        disk->node_id = node_id;
-       if (disk_expand_part_tbl(disk, 0))
-               goto out_bdput;
-
-       ptbl = rcu_dereference_protected(disk->part_tbl, 1);
-       rcu_assign_pointer(ptbl->part[0], disk->part0);
+       xa_init(&disk->part_tbl);
+       if (xa_insert(&disk->part_tbl, 0, disk->part0, GFP_KERNEL))
+               goto out_destroy_part_tbl;
 
        disk->minors = minors;
        rand_initialize_disk(disk);
@@ -1603,7 +1393,8 @@ struct gendisk *__alloc_disk_node(int minors, int node_id)
        device_initialize(disk_to_dev(disk));
        return disk;
 
-out_bdput:
+out_destroy_part_tbl:
+       xa_destroy(&disk->part_tbl);
        bdput(disk->part0);
 out_free_disk:
        kfree(disk);
@@ -1638,31 +1429,32 @@ static void set_disk_ro_uevent(struct gendisk *gd, int ro)
        kobject_uevent_env(&disk_to_dev(gd)->kobj, KOBJ_CHANGE, envp);
 }
 
-void set_disk_ro(struct gendisk *disk, int flag)
+/**
+ * set_disk_ro - set a gendisk read-only
+ * @disk:      gendisk to operate on
+ * @read_only: %true to set the disk read-only, %false set the disk read/write
+ *
+ * This function is used to indicate whether a given disk device should have its
+ * read-only flag set. set_disk_ro() is typically used by device drivers to
+ * indicate whether the underlying physical device is write-protected.
+ */
+void set_disk_ro(struct gendisk *disk, bool read_only)
 {
-       struct disk_part_iter piter;
-       struct block_device *part;
-
-       if (disk->part0->bd_read_only != flag) {
-               set_disk_ro_uevent(disk, flag);
-               disk->part0->bd_read_only = flag;
+       if (read_only) {
+               if (test_and_set_bit(GD_READ_ONLY, &disk->state))
+                       return;
+       } else {
+               if (!test_and_clear_bit(GD_READ_ONLY, &disk->state))
+                       return;
        }
-
-       disk_part_iter_init(&piter, disk, DISK_PITER_INCL_EMPTY);
-       while ((part = disk_part_iter_next(&piter)))
-               part->bd_read_only = flag;
-       disk_part_iter_exit(&piter);
+       set_disk_ro_uevent(disk, read_only);
 }
-
 EXPORT_SYMBOL(set_disk_ro);
 
 int bdev_read_only(struct block_device *bdev)
 {
-       if (!bdev)
-               return 0;
-       return bdev->bd_read_only;
+       return bdev->bd_read_only || get_disk_ro(bdev->bd_disk);
 }
-
 EXPORT_SYMBOL(bdev_read_only);
 
 /*