drivers: power: report battery voltage in AOSP compatible format
[GitHub/mt8127/android_kernel_alcatel_ttab.git] / drivers / md / dm.c
index d5370a94b2c1308ece53dd50ca5f9a5011b25fc7..975bb316a552704aa4fc1a78bf1c7f3f5d06d794 100644 (file)
@@ -184,8 +184,8 @@ struct mapped_device {
        /* forced geometry settings */
        struct hd_geometry geometry;
 
-       /* sysfs handle */
-       struct kobject kobj;
+       /* kobject and completion */
+       struct dm_kobject_holder kobj_holder;
 
        /* zero-length flush that will be cloned and submitted to targets */
        struct bio flush_bio;
@@ -386,10 +386,12 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
                        unsigned int cmd, unsigned long arg)
 {
        struct mapped_device *md = bdev->bd_disk->private_data;
-       struct dm_table *map = dm_get_live_table(md);
+       struct dm_table *map;
        struct dm_target *tgt;
        int r = -ENOTTY;
 
+retry:
+       map = dm_get_live_table(md);
        if (!map || !dm_table_get_size(map))
                goto out;
 
@@ -410,6 +412,11 @@ static int dm_blk_ioctl(struct block_device *bdev, fmode_t mode,
 out:
        dm_table_put(map);
 
+       if (r == -ENOTCONN) {
+               msleep(10);
+               goto retry;
+       }
+
        return r;
 }
 
@@ -969,11 +976,62 @@ int dm_set_target_max_io_len(struct dm_target *ti, sector_t len)
 }
 EXPORT_SYMBOL_GPL(dm_set_target_max_io_len);
 
+/*
+ * Flush current->bio_list when the target map method blocks.
+ * This fixes deadlocks in snapshot and possibly in other targets.
+ */
+struct dm_offload {
+       struct blk_plug plug;
+       struct blk_plug_cb cb;
+};
+
+static void flush_current_bio_list(struct blk_plug_cb *cb, bool from_schedule)
+{
+       struct dm_offload *o = container_of(cb, struct dm_offload, cb);
+       struct bio_list list;
+       struct bio *bio;
+
+       INIT_LIST_HEAD(&o->cb.list);
+
+       if (unlikely(!current->bio_list))
+               return;
+
+       list = *current->bio_list;
+       bio_list_init(current->bio_list);
+
+       while ((bio = bio_list_pop(&list))) {
+               struct bio_set *bs = bio->bi_pool;
+               if (unlikely(!bs) || bs == fs_bio_set) {
+                       bio_list_add(current->bio_list, bio);
+                       continue;
+               }
+
+               spin_lock(&bs->rescue_lock);
+               bio_list_add(&bs->rescue_list, bio);
+               queue_work(bs->rescue_workqueue, &bs->rescue_work);
+               spin_unlock(&bs->rescue_lock);
+       }
+}
+
+static void dm_offload_start(struct dm_offload *o)
+{
+       blk_start_plug(&o->plug);
+       o->cb.callback = flush_current_bio_list;
+       list_add(&o->cb.list, &current->plug->cb_list);
+}
+
+static void dm_offload_end(struct dm_offload *o)
+{
+       list_del(&o->cb.list);
+       blk_finish_plug(&o->plug);
+}
+
 static void __map_bio(struct dm_target_io *tio)
 {
        int r;
        sector_t sector;
        struct mapped_device *md;
+       struct dm_offload o;
        struct bio *clone = &tio->clone;
        struct dm_target *ti = tio->ti;
 
@@ -987,7 +1045,11 @@ static void __map_bio(struct dm_target_io *tio)
         */
        atomic_inc(&tio->io->io_count);
        sector = clone->bi_sector;
+
+       dm_offload_start(&o);
        r = ti->type->map(ti, clone);
+       dm_offload_end(&o);
+
        if (r == DM_MAPIO_REMAPPED) {
                /* the bio has been remapped so dispatch it */
 
@@ -1897,6 +1959,7 @@ static struct mapped_device *alloc_dev(int minor)
        init_waitqueue_head(&md->wait);
        INIT_WORK(&md->work, dm_wq_work);
        init_waitqueue_head(&md->eventq);
+       init_completion(&md->kobj_holder.completion);
 
        md->disk->major = _major;
        md->disk->first_minor = minor;
@@ -2211,6 +2274,17 @@ struct target_type *dm_get_immutable_target_type(struct mapped_device *md)
        return md->immutable_target_type;
 }
 
+/*
+ * The queue_limits are only valid as long as you have a reference
+ * count on 'md'.
+ */
+struct queue_limits *dm_get_queue_limits(struct mapped_device *md)
+{
+       BUG_ON(!atomic_read(&md->holders));
+       return &md->queue->limits;
+}
+EXPORT_SYMBOL_GPL(dm_get_queue_limits);
+
 /*
  * Fully initialize a request-based queue (->elevator, ->request_fn, etc).
  */
@@ -2251,7 +2325,7 @@ int dm_setup_md_queue(struct mapped_device *md)
        return 0;
 }
 
-static struct mapped_device *dm_find_md(dev_t dev)
+struct mapped_device *dm_get_md(dev_t dev)
 {
        struct mapped_device *md;
        unsigned minor = MINOR(dev);
@@ -2262,12 +2336,15 @@ static struct mapped_device *dm_find_md(dev_t dev)
        spin_lock(&_minor_lock);
 
        md = idr_find(&_minor_idr, minor);
-       if (md && (md == MINOR_ALLOCED ||
-                  (MINOR(disk_devt(dm_disk(md))) != minor) ||
-                  dm_deleting_md(md) ||
-                  test_bit(DMF_FREEING, &md->flags))) {
-               md = NULL;
-               goto out;
+       if (md) {
+               if ((md == MINOR_ALLOCED ||
+                    (MINOR(disk_devt(dm_disk(md))) != minor) ||
+                    dm_deleting_md(md) ||
+                    test_bit(DMF_FREEING, &md->flags))) {
+                       md = NULL;
+                       goto out;
+               }
+               dm_get(md);
        }
 
 out:
@@ -2275,16 +2352,6 @@ out:
 
        return md;
 }
-
-struct mapped_device *dm_get_md(dev_t dev)
-{
-       struct mapped_device *md = dm_find_md(dev);
-
-       if (md)
-               dm_get(md);
-
-       return md;
-}
 EXPORT_SYMBOL_GPL(dm_get_md);
 
 void *dm_get_mdptr(struct mapped_device *md)
@@ -2311,6 +2378,7 @@ EXPORT_SYMBOL_GPL(dm_device_name);
 
 static void __dm_destroy(struct mapped_device *md, bool wait)
 {
+       struct request_queue *q = md->queue;
        struct dm_table *map;
 
        might_sleep();
@@ -2321,10 +2389,20 @@ static void __dm_destroy(struct mapped_device *md, bool wait)
        set_bit(DMF_FREEING, &md->flags);
        spin_unlock(&_minor_lock);
 
+       spin_lock_irq(q->queue_lock);
+       queue_flag_set(QUEUE_FLAG_DYING, q);
+       spin_unlock_irq(q->queue_lock);
+
+       /*
+        * Take suspend_lock so that presuspend and postsuspend methods
+        * do not race with internal suspend.
+        */
+       mutex_lock(&md->suspend_lock);
        if (!dm_suspended_md(md)) {
                dm_table_presuspend_targets(map);
                dm_table_postsuspend_targets(map);
        }
+       mutex_unlock(&md->suspend_lock);
 
        /*
         * Rare, but there may be I/O requests still going to complete,
@@ -2717,20 +2795,14 @@ struct gendisk *dm_disk(struct mapped_device *md)
 
 struct kobject *dm_kobject(struct mapped_device *md)
 {
-       return &md->kobj;
+       return &md->kobj_holder.kobj;
 }
 
-/*
- * struct mapped_device should not be exported outside of dm.c
- * so use this check to verify that kobj is part of md structure
- */
 struct mapped_device *dm_get_from_kobject(struct kobject *kobj)
 {
        struct mapped_device *md;
 
-       md = container_of(kobj, struct mapped_device, kobj);
-       if (&md->kobj != kobj)
-               return NULL;
+       md = container_of(kobj, struct mapped_device, kobj_holder.kobj);
 
        if (test_bit(DMF_FREEING, &md->flags) ||
            dm_deleting_md(md))