#include "dm.h"
#include "dm-bio-list.h"
+#include "dm-uevent.h"
#include <linux/init.h>
#include <linux/module.h>
*/
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
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;
}
kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache);
unregister_blkdev(_major, _name);
+ dm_uevent_exit();
_major = 0;
/*
* 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;
/* drop the extra reference count */
dec_pending(ci.io, 0);
dm_table_put(ci.map);
+
+ return 0;
}
/*-----------------------------------------------------------------
* CRUD END
*/
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;
while (test_bit(DMF_BLOCK_IO, &md->flags)) {
up_read(&md->io_lock);
- if (bio_rw(bio) == READA) {
- bio_io_error(bio);
- return 0;
- }
+ if (bio_rw(bio) != READA)
+ r = queue_io(md, bio);
- r = queue_io(md, bio);
- if (r < 0) {
- bio_io_error(bio);
- return 0;
-
- } else if (r == 0)
- return 0; /* deferred successfully */
+ if (r <= 0)
+ goto out_req;
/*
* We're in a while loop, because someone could suspend
down_read(&md->io_lock);
}
- __split_bio(md, bio);
+ r = __split_bio(md, bio);
up_read(&md->io_lock);
- return 0;
-}
-
-static int dm_flush_all(struct request_queue *q, struct gendisk *disk,
- sector_t *error_sector)
-{
- struct mapped_device *md = q->queuedata;
- struct dm_table *map = dm_get_table(md);
- int ret = -ENXIO;
- if (map) {
- ret = dm_table_flush_all(map);
- dm_table_put(map);
- }
+out_req:
+ if (r < 0)
+ bio_io_error(bio);
- return ret;
+ return 0;
}
static void dm_unplug_all(struct request_queue *q)
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)
blk_queue_make_request(md->queue, dm_request);
blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
md->queue->unplug_fn = dm_unplug_all;
- md->queue->issue_flush_fn = dm_flush_all;
md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache);
if (!md->io_pool)
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);
*/
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);
}
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;
}
}
/*-----------------------------------------------------------------
* 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);
(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'.