diff options
author | Jan Beulich <jbeulich@novell.com> | 2010-08-07 12:28:55 -0400 |
---|---|---|
committer | Jens Axboe <jaxboe@fusionio.com> | 2010-08-07 12:28:55 -0400 |
commit | 0e34582699392d67910bd3919bc8fd9bedce115e (patch) | |
tree | 8726f57eead44ecce90112e969e12a33e119acd9 /drivers/block | |
parent | 203fd61f42fec81f43bc5abbf2d3755e04e000af (diff) |
blkfront: fixes for 'xm block-detach ... --force'
Prevent prematurely freeing 'struct blkfront_info' instances (when the
xenbus data structures are gone, but the Linux ones are still needed).
Prevent adding a disk with the same (major, minor) [and hence the same
name and sysfs entries, which leads to oopses] when the previous
instance wasn't fully de-allocated yet.
This still doesn't address all issues resulting from forced detach:
I/O submitted after the detach still blocks forever, likely preventing
subsequent un-mounting from completing. It's not clear to me (not
knowing much about the block layer) how this can be avoided.
Signed-off-by: Jan Beulich <jbeulich@novell.com>
Signed-off-by: Jeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
Signed-off-by: Jens Axboe <jaxboe@fusionio.com>
Diffstat (limited to 'drivers/block')
-rw-r--r-- | drivers/block/xen-blkfront.c | 88 |
1 files changed, 81 insertions, 7 deletions
diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 304009e77c73..22091e4e401f 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c | |||
@@ -105,6 +105,10 @@ struct blkfront_info | |||
105 | 105 | ||
106 | static DEFINE_SPINLOCK(blkif_io_lock); | 106 | static DEFINE_SPINLOCK(blkif_io_lock); |
107 | 107 | ||
108 | static unsigned int nr_minors; | ||
109 | static unsigned long *minors; | ||
110 | static DEFINE_SPINLOCK(minor_lock); | ||
111 | |||
108 | #define MAXIMUM_OUTSTANDING_BLOCK_REQS \ | 112 | #define MAXIMUM_OUTSTANDING_BLOCK_REQS \ |
109 | (BLKIF_MAX_SEGMENTS_PER_REQUEST * BLK_RING_SIZE) | 113 | (BLKIF_MAX_SEGMENTS_PER_REQUEST * BLK_RING_SIZE) |
110 | #define GRANT_INVALID_REF 0 | 114 | #define GRANT_INVALID_REF 0 |
@@ -139,6 +143,55 @@ static void add_id_to_freelist(struct blkfront_info *info, | |||
139 | info->shadow_free = id; | 143 | info->shadow_free = id; |
140 | } | 144 | } |
141 | 145 | ||
146 | static int xlbd_reserve_minors(unsigned int minor, unsigned int nr) | ||
147 | { | ||
148 | unsigned int end = minor + nr; | ||
149 | int rc; | ||
150 | |||
151 | if (end > nr_minors) { | ||
152 | unsigned long *bitmap, *old; | ||
153 | |||
154 | bitmap = kzalloc(BITS_TO_LONGS(end) * sizeof(*bitmap), | ||
155 | GFP_KERNEL); | ||
156 | if (bitmap == NULL) | ||
157 | return -ENOMEM; | ||
158 | |||
159 | spin_lock(&minor_lock); | ||
160 | if (end > nr_minors) { | ||
161 | old = minors; | ||
162 | memcpy(bitmap, minors, | ||
163 | BITS_TO_LONGS(nr_minors) * sizeof(*bitmap)); | ||
164 | minors = bitmap; | ||
165 | nr_minors = BITS_TO_LONGS(end) * BITS_PER_LONG; | ||
166 | } else | ||
167 | old = bitmap; | ||
168 | spin_unlock(&minor_lock); | ||
169 | kfree(old); | ||
170 | } | ||
171 | |||
172 | spin_lock(&minor_lock); | ||
173 | if (find_next_bit(minors, end, minor) >= end) { | ||
174 | for (; minor < end; ++minor) | ||
175 | __set_bit(minor, minors); | ||
176 | rc = 0; | ||
177 | } else | ||
178 | rc = -EBUSY; | ||
179 | spin_unlock(&minor_lock); | ||
180 | |||
181 | return rc; | ||
182 | } | ||
183 | |||
184 | static void xlbd_release_minors(unsigned int minor, unsigned int nr) | ||
185 | { | ||
186 | unsigned int end = minor + nr; | ||
187 | |||
188 | BUG_ON(end > nr_minors); | ||
189 | spin_lock(&minor_lock); | ||
190 | for (; minor < end; ++minor) | ||
191 | __clear_bit(minor, minors); | ||
192 | spin_unlock(&minor_lock); | ||
193 | } | ||
194 | |||
142 | static void blkif_restart_queue_callback(void *arg) | 195 | static void blkif_restart_queue_callback(void *arg) |
143 | { | 196 | { |
144 | struct blkfront_info *info = (struct blkfront_info *)arg; | 197 | struct blkfront_info *info = (struct blkfront_info *)arg; |
@@ -417,9 +470,14 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity, | |||
417 | if ((minor % nr_parts) == 0) | 470 | if ((minor % nr_parts) == 0) |
418 | nr_minors = nr_parts; | 471 | nr_minors = nr_parts; |
419 | 472 | ||
473 | err = xlbd_reserve_minors(minor, nr_minors); | ||
474 | if (err) | ||
475 | goto out; | ||
476 | err = -ENODEV; | ||
477 | |||
420 | gd = alloc_disk(nr_minors); | 478 | gd = alloc_disk(nr_minors); |
421 | if (gd == NULL) | 479 | if (gd == NULL) |
422 | goto out; | 480 | goto release; |
423 | 481 | ||
424 | offset = minor / nr_parts; | 482 | offset = minor / nr_parts; |
425 | 483 | ||
@@ -450,7 +508,7 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity, | |||
450 | 508 | ||
451 | if (xlvbd_init_blk_queue(gd, sector_size)) { | 509 | if (xlvbd_init_blk_queue(gd, sector_size)) { |
452 | del_gendisk(gd); | 510 | del_gendisk(gd); |
453 | goto out; | 511 | goto release; |
454 | } | 512 | } |
455 | 513 | ||
456 | info->rq = gd->queue; | 514 | info->rq = gd->queue; |
@@ -470,6 +528,8 @@ static int xlvbd_alloc_gendisk(blkif_sector_t capacity, | |||
470 | 528 | ||
471 | return 0; | 529 | return 0; |
472 | 530 | ||
531 | release: | ||
532 | xlbd_release_minors(minor, nr_minors); | ||
473 | out: | 533 | out: |
474 | return err; | 534 | return err; |
475 | } | 535 | } |
@@ -924,6 +984,7 @@ static void blkfront_connect(struct blkfront_info *info) | |||
924 | static void blkfront_closing(struct xenbus_device *dev) | 984 | static void blkfront_closing(struct xenbus_device *dev) |
925 | { | 985 | { |
926 | struct blkfront_info *info = dev_get_drvdata(&dev->dev); | 986 | struct blkfront_info *info = dev_get_drvdata(&dev->dev); |
987 | unsigned int minor, nr_minors; | ||
927 | unsigned long flags; | 988 | unsigned long flags; |
928 | 989 | ||
929 | dev_dbg(&dev->dev, "blkfront_closing: %s removed\n", dev->nodename); | 990 | dev_dbg(&dev->dev, "blkfront_closing: %s removed\n", dev->nodename); |
@@ -946,7 +1007,10 @@ static void blkfront_closing(struct xenbus_device *dev) | |||
946 | blk_cleanup_queue(info->rq); | 1007 | blk_cleanup_queue(info->rq); |
947 | info->rq = NULL; | 1008 | info->rq = NULL; |
948 | 1009 | ||
1010 | minor = info->gd->first_minor; | ||
1011 | nr_minors = info->gd->minors; | ||
949 | del_gendisk(info->gd); | 1012 | del_gendisk(info->gd); |
1013 | xlbd_release_minors(minor, nr_minors); | ||
950 | 1014 | ||
951 | out: | 1015 | out: |
952 | xenbus_frontend_closed(dev); | 1016 | xenbus_frontend_closed(dev); |
@@ -1004,7 +1068,10 @@ static int blkfront_remove(struct xenbus_device *dev) | |||
1004 | 1068 | ||
1005 | blkif_free(info, 0); | 1069 | blkif_free(info, 0); |
1006 | 1070 | ||
1007 | kfree(info); | 1071 | if(info->users == 0) |
1072 | kfree(info); | ||
1073 | else | ||
1074 | info->is_ready = -1; | ||
1008 | 1075 | ||
1009 | return 0; | 1076 | return 0; |
1010 | } | 1077 | } |
@@ -1013,18 +1080,22 @@ static int blkfront_is_ready(struct xenbus_device *dev) | |||
1013 | { | 1080 | { |
1014 | struct blkfront_info *info = dev_get_drvdata(&dev->dev); | 1081 | struct blkfront_info *info = dev_get_drvdata(&dev->dev); |
1015 | 1082 | ||
1016 | return info->is_ready; | 1083 | return info->is_ready > 0; |
1017 | } | 1084 | } |
1018 | 1085 | ||
1019 | static int blkif_open(struct block_device *bdev, fmode_t mode) | 1086 | static int blkif_open(struct block_device *bdev, fmode_t mode) |
1020 | { | 1087 | { |
1021 | struct blkfront_info *info = bdev->bd_disk->private_data; | 1088 | struct blkfront_info *info = bdev->bd_disk->private_data; |
1089 | int ret = 0; | ||
1022 | 1090 | ||
1023 | lock_kernel(); | 1091 | lock_kernel(); |
1024 | info->users++; | 1092 | if (info->is_ready < 0) |
1093 | ret = -ENODEV; | ||
1094 | else | ||
1095 | info->users++; | ||
1025 | unlock_kernel(); | 1096 | unlock_kernel(); |
1026 | 1097 | ||
1027 | return 0; | 1098 | return ret; |
1028 | } | 1099 | } |
1029 | 1100 | ||
1030 | static int blkif_release(struct gendisk *disk, fmode_t mode) | 1101 | static int blkif_release(struct gendisk *disk, fmode_t mode) |
@@ -1039,7 +1110,10 @@ static int blkif_release(struct gendisk *disk, fmode_t mode) | |||
1039 | struct xenbus_device *dev = info->xbdev; | 1110 | struct xenbus_device *dev = info->xbdev; |
1040 | enum xenbus_state state = xenbus_read_driver_state(dev->otherend); | 1111 | enum xenbus_state state = xenbus_read_driver_state(dev->otherend); |
1041 | 1112 | ||
1042 | if (state == XenbusStateClosing && info->is_ready) | 1113 | if(info->is_ready < 0) { |
1114 | blkfront_closing(dev); | ||
1115 | kfree(info); | ||
1116 | } else if (state == XenbusStateClosing && info->is_ready) | ||
1043 | blkfront_closing(dev); | 1117 | blkfront_closing(dev); |
1044 | } | 1118 | } |
1045 | unlock_kernel(); | 1119 | unlock_kernel(); |