summaryrefslogtreecommitdiffstats
path: root/drivers/md/dm.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/md/dm.c')
-rw-r--r--drivers/md/dm.c79
1 files changed, 59 insertions, 20 deletions
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index d837d37f6209..07cbbb8eb3e0 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -7,6 +7,7 @@
#include "dm.h"
#include "dm-bio-list.h"
+#include "dm-uevent.h"
#include <linux/init.h>
#include <linux/module.h>
@@ -112,6 +113,9 @@ struct mapped_device {
*/
atomic_t event_nr;
wait_queue_head_t eventq;
+ atomic_t uevent_seq;
+ struct list_head uevent_list;
+ spinlock_t uevent_lock; /* Protect access to uevent_list */
/*
* freeze/thaw support require holding onto a super block
@@ -143,11 +147,19 @@ static int __init local_init(void)
return -ENOMEM;
}
+ r = dm_uevent_init();
+ if (r) {
+ kmem_cache_destroy(_tio_cache);
+ kmem_cache_destroy(_io_cache);
+ return r;
+ }
+
_major = major;
r = register_blkdev(_major, _name);
if (r < 0) {
kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache);
+ dm_uevent_exit();
return r;
}
@@ -162,6 +174,7 @@ static void local_exit(void)
kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache);
unregister_blkdev(_major, _name);
+ dm_uevent_exit();
_major = 0;
@@ -751,15 +764,13 @@ static void __clone_and_map(struct clone_info *ci)
/*
* Split the bio into several clones.
*/
-static void __split_bio(struct mapped_device *md, struct bio *bio)
+static int __split_bio(struct mapped_device *md, struct bio *bio)
{
struct clone_info ci;
ci.map = dm_get_table(md);
- if (!ci.map) {
- bio_io_error(bio);
- return;
- }
+ if (unlikely(!ci.map))
+ return -EIO;
ci.md = md;
ci.bio = bio;
@@ -779,6 +790,8 @@ static void __split_bio(struct mapped_device *md, struct bio *bio)
/* drop the extra reference count */
dec_pending(ci.io, 0);
dm_table_put(ci.map);
+
+ return 0;
}
/*-----------------------------------------------------------------
* CRUD END
@@ -790,7 +803,7 @@ static void __split_bio(struct mapped_device *md, struct bio *bio)
*/
static int dm_request(struct request_queue *q, struct bio *bio)
{
- int r;
+ int r = -EIO;
int rw = bio_data_dir(bio);
struct mapped_device *md = q->queuedata;
@@ -815,18 +828,11 @@ static int dm_request(struct request_queue *q, struct bio *bio)
while (test_bit(DMF_BLOCK_IO, &md->flags)) {
up_read(&md->io_lock);
- if (bio_rw(bio) == READA) {
- bio_io_error(bio);
- return 0;
- }
-
- r = queue_io(md, bio);
- if (r < 0) {
- bio_io_error(bio);
- return 0;
+ if (bio_rw(bio) != READA)
+ r = queue_io(md, bio);
- } else if (r == 0)
- return 0; /* deferred successfully */
+ if (r <= 0)
+ goto out_req;
/*
* We're in a while loop, because someone could suspend
@@ -835,8 +841,13 @@ static int dm_request(struct request_queue *q, struct bio *bio)
down_read(&md->io_lock);
}
- __split_bio(md, bio);
+ r = __split_bio(md, bio);
up_read(&md->io_lock);
+
+out_req:
+ if (r < 0)
+ bio_io_error(bio);
+
return 0;
}
@@ -977,6 +988,9 @@ static struct mapped_device *alloc_dev(int minor)
atomic_set(&md->holders, 1);
atomic_set(&md->open_count, 0);
atomic_set(&md->event_nr, 0);
+ atomic_set(&md->uevent_seq, 0);
+ INIT_LIST_HEAD(&md->uevent_list);
+ spin_lock_init(&md->uevent_lock);
md->queue = blk_alloc_queue(GFP_KERNEL);
if (!md->queue)
@@ -1044,12 +1058,14 @@ static struct mapped_device *alloc_dev(int minor)
return NULL;
}
+static void unlock_fs(struct mapped_device *md);
+
static void free_dev(struct mapped_device *md)
{
int minor = md->disk->first_minor;
if (md->suspended_bdev) {
- thaw_bdev(md->suspended_bdev, NULL);
+ unlock_fs(md);
bdput(md->suspended_bdev);
}
mempool_destroy(md->tio_pool);
@@ -1073,8 +1089,16 @@ static void free_dev(struct mapped_device *md)
*/
static void event_callback(void *context)
{
+ unsigned long flags;
+ LIST_HEAD(uevents);
struct mapped_device *md = (struct mapped_device *) context;
+ spin_lock_irqsave(&md->uevent_lock, flags);
+ list_splice_init(&md->uevent_list, &uevents);
+ spin_unlock_irqrestore(&md->uevent_lock, flags);
+
+ dm_send_uevents(&uevents, &md->disk->kobj);
+
atomic_inc(&md->event_nr);
wake_up(&md->eventq);
}
@@ -1233,7 +1257,8 @@ static void __flush_deferred_io(struct mapped_device *md, struct bio *c)
while (c) {
n = c->bi_next;
c->bi_next = NULL;
- __split_bio(md, c);
+ if (__split_bio(md, c))
+ bio_io_error(c);
c = n;
}
}
@@ -1491,6 +1516,11 @@ out:
/*-----------------------------------------------------------------
* Event notification.
*---------------------------------------------------------------*/
+uint32_t dm_next_uevent_seq(struct mapped_device *md)
+{
+ return atomic_add_return(1, &md->uevent_seq);
+}
+
uint32_t dm_get_event_nr(struct mapped_device *md)
{
return atomic_read(&md->event_nr);
@@ -1502,6 +1532,15 @@ int dm_wait_event(struct mapped_device *md, int event_nr)
(event_nr != atomic_read(&md->event_nr)));
}
+void dm_uevent_add(struct mapped_device *md, struct list_head *elist)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&md->uevent_lock, flags);
+ list_add(elist, &md->uevent_list);
+ spin_unlock_irqrestore(&md->uevent_lock, flags);
+}
+
/*
* The gendisk is only valid as long as you have a reference
* count on 'md'.
OpenPOWER on IntegriCloud