diff options
Diffstat (limited to 'drivers/block/virtio_blk.c')
-rw-r--r-- | drivers/block/virtio_blk.c | 106 |
1 files changed, 65 insertions, 41 deletions
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c index 924ddd8bccd2..3b1a68d6eddb 100644 --- a/drivers/block/virtio_blk.c +++ b/drivers/block/virtio_blk.c | |||
@@ -7,8 +7,10 @@ | |||
7 | #include <linux/scatterlist.h> | 7 | #include <linux/scatterlist.h> |
8 | 8 | ||
9 | #define VIRTIO_MAX_SG (3+MAX_PHYS_SEGMENTS) | 9 | #define VIRTIO_MAX_SG (3+MAX_PHYS_SEGMENTS) |
10 | #define PART_BITS 4 | ||
11 | |||
12 | static int major, index; | ||
10 | 13 | ||
11 | static unsigned char virtblk_index = 'a'; | ||
12 | struct virtio_blk | 14 | struct virtio_blk |
13 | { | 15 | { |
14 | spinlock_t lock; | 16 | spinlock_t lock; |
@@ -36,7 +38,7 @@ struct virtblk_req | |||
36 | struct virtio_blk_inhdr in_hdr; | 38 | struct virtio_blk_inhdr in_hdr; |
37 | }; | 39 | }; |
38 | 40 | ||
39 | static bool blk_done(struct virtqueue *vq) | 41 | static void blk_done(struct virtqueue *vq) |
40 | { | 42 | { |
41 | struct virtio_blk *vblk = vq->vdev->priv; | 43 | struct virtio_blk *vblk = vq->vdev->priv; |
42 | struct virtblk_req *vbr; | 44 | struct virtblk_req *vbr; |
@@ -65,7 +67,6 @@ static bool blk_done(struct virtqueue *vq) | |||
65 | /* In case queue is stopped waiting for more buffers. */ | 67 | /* In case queue is stopped waiting for more buffers. */ |
66 | blk_start_queue(vblk->disk->queue); | 68 | blk_start_queue(vblk->disk->queue); |
67 | spin_unlock_irqrestore(&vblk->lock, flags); | 69 | spin_unlock_irqrestore(&vblk->lock, flags); |
68 | return true; | ||
69 | } | 70 | } |
70 | 71 | ||
71 | static bool do_req(struct request_queue *q, struct virtio_blk *vblk, | 72 | static bool do_req(struct request_queue *q, struct virtio_blk *vblk, |
@@ -153,20 +154,37 @@ static int virtblk_ioctl(struct inode *inode, struct file *filp, | |||
153 | (void __user *)data); | 154 | (void __user *)data); |
154 | } | 155 | } |
155 | 156 | ||
157 | /* We provide getgeo only to please some old bootloader/partitioning tools */ | ||
158 | static int virtblk_getgeo(struct block_device *bd, struct hd_geometry *geo) | ||
159 | { | ||
160 | /* some standard values, similar to sd */ | ||
161 | geo->heads = 1 << 6; | ||
162 | geo->sectors = 1 << 5; | ||
163 | geo->cylinders = get_capacity(bd->bd_disk) >> 11; | ||
164 | return 0; | ||
165 | } | ||
166 | |||
156 | static struct block_device_operations virtblk_fops = { | 167 | static struct block_device_operations virtblk_fops = { |
157 | .ioctl = virtblk_ioctl, | 168 | .ioctl = virtblk_ioctl, |
158 | .owner = THIS_MODULE, | 169 | .owner = THIS_MODULE, |
170 | .getgeo = virtblk_getgeo, | ||
159 | }; | 171 | }; |
160 | 172 | ||
173 | static int index_to_minor(int index) | ||
174 | { | ||
175 | return index << PART_BITS; | ||
176 | } | ||
177 | |||
161 | static int virtblk_probe(struct virtio_device *vdev) | 178 | static int virtblk_probe(struct virtio_device *vdev) |
162 | { | 179 | { |
163 | struct virtio_blk *vblk; | 180 | struct virtio_blk *vblk; |
164 | int err, major; | 181 | int err; |
165 | void *token; | ||
166 | unsigned int len; | ||
167 | u64 cap; | 182 | u64 cap; |
168 | u32 v; | 183 | u32 v; |
169 | 184 | ||
185 | if (index_to_minor(index) >= 1 << MINORBITS) | ||
186 | return -ENOSPC; | ||
187 | |||
170 | vdev->priv = vblk = kmalloc(sizeof(*vblk), GFP_KERNEL); | 188 | vdev->priv = vblk = kmalloc(sizeof(*vblk), GFP_KERNEL); |
171 | if (!vblk) { | 189 | if (!vblk) { |
172 | err = -ENOMEM; | 190 | err = -ENOMEM; |
@@ -178,7 +196,7 @@ static int virtblk_probe(struct virtio_device *vdev) | |||
178 | vblk->vdev = vdev; | 196 | vblk->vdev = vdev; |
179 | 197 | ||
180 | /* We expect one virtqueue, for output. */ | 198 | /* We expect one virtqueue, for output. */ |
181 | vblk->vq = vdev->config->find_vq(vdev, blk_done); | 199 | vblk->vq = vdev->config->find_vq(vdev, 0, blk_done); |
182 | if (IS_ERR(vblk->vq)) { | 200 | if (IS_ERR(vblk->vq)) { |
183 | err = PTR_ERR(vblk->vq); | 201 | err = PTR_ERR(vblk->vq); |
184 | goto out_free_vblk; | 202 | goto out_free_vblk; |
@@ -190,17 +208,11 @@ static int virtblk_probe(struct virtio_device *vdev) | |||
190 | goto out_free_vq; | 208 | goto out_free_vq; |
191 | } | 209 | } |
192 | 210 | ||
193 | major = register_blkdev(0, "virtblk"); | ||
194 | if (major < 0) { | ||
195 | err = major; | ||
196 | goto out_mempool; | ||
197 | } | ||
198 | |||
199 | /* FIXME: How many partitions? How long is a piece of string? */ | 211 | /* FIXME: How many partitions? How long is a piece of string? */ |
200 | vblk->disk = alloc_disk(1 << 4); | 212 | vblk->disk = alloc_disk(1 << PART_BITS); |
201 | if (!vblk->disk) { | 213 | if (!vblk->disk) { |
202 | err = -ENOMEM; | 214 | err = -ENOMEM; |
203 | goto out_unregister_blkdev; | 215 | goto out_mempool; |
204 | } | 216 | } |
205 | 217 | ||
206 | vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock); | 218 | vblk->disk->queue = blk_init_queue(do_virtblk_request, &vblk->lock); |
@@ -209,22 +221,32 @@ static int virtblk_probe(struct virtio_device *vdev) | |||
209 | goto out_put_disk; | 221 | goto out_put_disk; |
210 | } | 222 | } |
211 | 223 | ||
212 | sprintf(vblk->disk->disk_name, "vd%c", virtblk_index++); | 224 | if (index < 26) { |
225 | sprintf(vblk->disk->disk_name, "vd%c", 'a' + index % 26); | ||
226 | } else if (index < (26 + 1) * 26) { | ||
227 | sprintf(vblk->disk->disk_name, "vd%c%c", | ||
228 | 'a' + index / 26 - 1, 'a' + index % 26); | ||
229 | } else { | ||
230 | const unsigned int m1 = (index / 26 - 1) / 26 - 1; | ||
231 | const unsigned int m2 = (index / 26 - 1) % 26; | ||
232 | const unsigned int m3 = index % 26; | ||
233 | sprintf(vblk->disk->disk_name, "vd%c%c%c", | ||
234 | 'a' + m1, 'a' + m2, 'a' + m3); | ||
235 | } | ||
236 | |||
213 | vblk->disk->major = major; | 237 | vblk->disk->major = major; |
214 | vblk->disk->first_minor = 0; | 238 | vblk->disk->first_minor = index_to_minor(index); |
215 | vblk->disk->private_data = vblk; | 239 | vblk->disk->private_data = vblk; |
216 | vblk->disk->fops = &virtblk_fops; | 240 | vblk->disk->fops = &virtblk_fops; |
241 | index++; | ||
217 | 242 | ||
218 | /* If barriers are supported, tell block layer that queue is ordered */ | 243 | /* If barriers are supported, tell block layer that queue is ordered */ |
219 | token = vdev->config->find(vdev, VIRTIO_CONFIG_BLK_F, &len); | 244 | if (vdev->config->feature(vdev, VIRTIO_BLK_F_BARRIER)) |
220 | if (virtio_use_bit(vdev, token, len, VIRTIO_BLK_F_BARRIER)) | ||
221 | blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); | 245 | blk_queue_ordered(vblk->disk->queue, QUEUE_ORDERED_TAG, NULL); |
222 | 246 | ||
223 | err = virtio_config_val(vdev, VIRTIO_CONFIG_BLK_F_CAPACITY, &cap); | 247 | /* Host must always specify the capacity. */ |
224 | if (err) { | 248 | __virtio_config_val(vdev, offsetof(struct virtio_blk_config, capacity), |
225 | dev_err(&vdev->dev, "Bad/missing capacity in config\n"); | 249 | &cap); |
226 | goto out_cleanup_queue; | ||
227 | } | ||
228 | 250 | ||
229 | /* If capacity is too big, truncate with warning. */ | 251 | /* If capacity is too big, truncate with warning. */ |
230 | if ((sector_t)cap != cap) { | 252 | if ((sector_t)cap != cap) { |
@@ -234,31 +256,25 @@ static int virtblk_probe(struct virtio_device *vdev) | |||
234 | } | 256 | } |
235 | set_capacity(vblk->disk, cap); | 257 | set_capacity(vblk->disk, cap); |
236 | 258 | ||
237 | err = virtio_config_val(vdev, VIRTIO_CONFIG_BLK_F_SIZE_MAX, &v); | 259 | /* Host can optionally specify maximum segment size and number of |
260 | * segments. */ | ||
261 | err = virtio_config_val(vdev, VIRTIO_BLK_F_SIZE_MAX, | ||
262 | offsetof(struct virtio_blk_config, size_max), | ||
263 | &v); | ||
238 | if (!err) | 264 | if (!err) |
239 | blk_queue_max_segment_size(vblk->disk->queue, v); | 265 | blk_queue_max_segment_size(vblk->disk->queue, v); |
240 | else if (err != -ENOENT) { | ||
241 | dev_err(&vdev->dev, "Bad SIZE_MAX in config\n"); | ||
242 | goto out_cleanup_queue; | ||
243 | } | ||
244 | 266 | ||
245 | err = virtio_config_val(vdev, VIRTIO_CONFIG_BLK_F_SEG_MAX, &v); | 267 | err = virtio_config_val(vdev, VIRTIO_BLK_F_SEG_MAX, |
268 | offsetof(struct virtio_blk_config, seg_max), | ||
269 | &v); | ||
246 | if (!err) | 270 | if (!err) |
247 | blk_queue_max_hw_segments(vblk->disk->queue, v); | 271 | blk_queue_max_hw_segments(vblk->disk->queue, v); |
248 | else if (err != -ENOENT) { | ||
249 | dev_err(&vdev->dev, "Bad SEG_MAX in config\n"); | ||
250 | goto out_cleanup_queue; | ||
251 | } | ||
252 | 272 | ||
253 | add_disk(vblk->disk); | 273 | add_disk(vblk->disk); |
254 | return 0; | 274 | return 0; |
255 | 275 | ||
256 | out_cleanup_queue: | ||
257 | blk_cleanup_queue(vblk->disk->queue); | ||
258 | out_put_disk: | 276 | out_put_disk: |
259 | put_disk(vblk->disk); | 277 | put_disk(vblk->disk); |
260 | out_unregister_blkdev: | ||
261 | unregister_blkdev(major, "virtblk"); | ||
262 | out_mempool: | 278 | out_mempool: |
263 | mempool_destroy(vblk->pool); | 279 | mempool_destroy(vblk->pool); |
264 | out_free_vq: | 280 | out_free_vq: |
@@ -274,12 +290,16 @@ static void virtblk_remove(struct virtio_device *vdev) | |||
274 | struct virtio_blk *vblk = vdev->priv; | 290 | struct virtio_blk *vblk = vdev->priv; |
275 | int major = vblk->disk->major; | 291 | int major = vblk->disk->major; |
276 | 292 | ||
293 | /* Nothing should be pending. */ | ||
277 | BUG_ON(!list_empty(&vblk->reqs)); | 294 | BUG_ON(!list_empty(&vblk->reqs)); |
295 | |||
296 | /* Stop all the virtqueues. */ | ||
297 | vdev->config->reset(vdev); | ||
298 | |||
278 | blk_cleanup_queue(vblk->disk->queue); | 299 | blk_cleanup_queue(vblk->disk->queue); |
279 | put_disk(vblk->disk); | 300 | put_disk(vblk->disk); |
280 | unregister_blkdev(major, "virtblk"); | 301 | unregister_blkdev(major, "virtblk"); |
281 | mempool_destroy(vblk->pool); | 302 | mempool_destroy(vblk->pool); |
282 | /* There should be nothing in the queue now, so no need to shutdown */ | ||
283 | vdev->config->del_vq(vblk->vq); | 303 | vdev->config->del_vq(vblk->vq); |
284 | kfree(vblk); | 304 | kfree(vblk); |
285 | } | 305 | } |
@@ -299,11 +319,15 @@ static struct virtio_driver virtio_blk = { | |||
299 | 319 | ||
300 | static int __init init(void) | 320 | static int __init init(void) |
301 | { | 321 | { |
322 | major = register_blkdev(0, "virtblk"); | ||
323 | if (major < 0) | ||
324 | return major; | ||
302 | return register_virtio_driver(&virtio_blk); | 325 | return register_virtio_driver(&virtio_blk); |
303 | } | 326 | } |
304 | 327 | ||
305 | static void __exit fini(void) | 328 | static void __exit fini(void) |
306 | { | 329 | { |
330 | unregister_blkdev(major, "virtblk"); | ||
307 | unregister_virtio_driver(&virtio_blk); | 331 | unregister_virtio_driver(&virtio_blk); |
308 | } | 332 | } |
309 | module_init(init); | 333 | module_init(init); |