direct-io.hg

view linux-2.6-xen-sparse/drivers/xen/blkfront/vbd.c @ 12352:60a4af3d243f

PV-on-HVM: Do not enable barriers in blkfront before 2.6.16

Barrier support was rewritten after 2.6.15. Perhaps it would be
possible to implement support for old kernels as well but for now it
is easier to just disable them.

Signed-off-by: Ian Campbell <ian.campbell@xensource.com>
author Ian Campbell <ian.campbell@xensource.com>
date Fri Nov 10 12:34:26 2006 +0000 (2006-11-10)
parents 4eaadb2ae198
children 3adf00179a63
line source
1 /******************************************************************************
2 * vbd.c
3 *
4 * XenLinux virtual block-device driver (xvd).
5 *
6 * Copyright (c) 2003-2004, Keir Fraser & Steve Hand
7 * Modifications by Mark A. Williamson are (c) Intel Research Cambridge
8 * Copyright (c) 2004-2005, Christian Limpach
9 *
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License version 2
12 * as published by the Free Software Foundation; or, when distributed
13 * separately from the Linux kernel or incorporated into other
14 * software packages, subject to the following license:
15 *
16 * Permission is hereby granted, free of charge, to any person obtaining a copy
17 * of this source file (the "Software"), to deal in the Software without
18 * restriction, including without limitation the rights to use, copy, modify,
19 * merge, publish, distribute, sublicense, and/or sell copies of the Software,
20 * and to permit persons to whom the Software is furnished to do so, subject to
21 * the following conditions:
22 *
23 * The above copyright notice and this permission notice shall be included in
24 * all copies or substantial portions of the Software.
25 *
26 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
31 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
32 * IN THE SOFTWARE.
33 */
35 #include "block.h"
36 #include <linux/blkdev.h>
37 #include <linux/list.h>
39 #ifdef HAVE_XEN_PLATFORM_COMPAT_H
40 #include <xen/platform-compat.h>
41 #endif
43 #define BLKIF_MAJOR(dev) ((dev)>>8)
44 #define BLKIF_MINOR(dev) ((dev) & 0xff)
46 /*
47 * For convenience we distinguish between ide, scsi and 'other' (i.e.,
48 * potentially combinations of the two) in the naming scheme and in a few other
49 * places.
50 */
52 #define NUM_IDE_MAJORS 10
53 #define NUM_SCSI_MAJORS 17
54 #define NUM_VBD_MAJORS 1
56 static struct xlbd_type_info xlbd_ide_type = {
57 .partn_shift = 6,
58 .disks_per_major = 2,
59 .devname = "ide",
60 .diskname = "hd",
61 };
63 static struct xlbd_type_info xlbd_scsi_type = {
64 .partn_shift = 4,
65 .disks_per_major = 16,
66 .devname = "sd",
67 .diskname = "sd",
68 };
70 static struct xlbd_type_info xlbd_vbd_type = {
71 .partn_shift = 4,
72 .disks_per_major = 16,
73 .devname = "xvd",
74 .diskname = "xvd",
75 };
77 static struct xlbd_major_info *major_info[NUM_IDE_MAJORS + NUM_SCSI_MAJORS +
78 NUM_VBD_MAJORS];
80 #define XLBD_MAJOR_IDE_START 0
81 #define XLBD_MAJOR_SCSI_START (NUM_IDE_MAJORS)
82 #define XLBD_MAJOR_VBD_START (NUM_IDE_MAJORS + NUM_SCSI_MAJORS)
84 #define XLBD_MAJOR_IDE_RANGE XLBD_MAJOR_IDE_START ... XLBD_MAJOR_SCSI_START - 1
85 #define XLBD_MAJOR_SCSI_RANGE XLBD_MAJOR_SCSI_START ... XLBD_MAJOR_VBD_START - 1
86 #define XLBD_MAJOR_VBD_RANGE XLBD_MAJOR_VBD_START ... XLBD_MAJOR_VBD_START + NUM_VBD_MAJORS - 1
88 /* Information about our VBDs. */
89 #define MAX_VBDS 64
90 static LIST_HEAD(vbds_list);
92 static struct block_device_operations xlvbd_block_fops =
93 {
94 .owner = THIS_MODULE,
95 .open = blkif_open,
96 .release = blkif_release,
97 .ioctl = blkif_ioctl,
98 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
99 .getgeo = blkif_getgeo
100 #endif
101 };
103 DEFINE_SPINLOCK(blkif_io_lock);
105 static struct xlbd_major_info *
106 xlbd_alloc_major_info(int major, int minor, int index)
107 {
108 struct xlbd_major_info *ptr;
110 ptr = kzalloc(sizeof(struct xlbd_major_info), GFP_KERNEL);
111 if (ptr == NULL)
112 return NULL;
114 ptr->major = major;
116 switch (index) {
117 case XLBD_MAJOR_IDE_RANGE:
118 ptr->type = &xlbd_ide_type;
119 ptr->index = index - XLBD_MAJOR_IDE_START;
120 break;
121 case XLBD_MAJOR_SCSI_RANGE:
122 ptr->type = &xlbd_scsi_type;
123 ptr->index = index - XLBD_MAJOR_SCSI_START;
124 break;
125 case XLBD_MAJOR_VBD_RANGE:
126 ptr->type = &xlbd_vbd_type;
127 ptr->index = index - XLBD_MAJOR_VBD_START;
128 break;
129 }
131 printk("Registering block device major %i\n", ptr->major);
132 if (register_blkdev(ptr->major, ptr->type->devname)) {
133 WPRINTK("can't get major %d with name %s\n",
134 ptr->major, ptr->type->devname);
135 kfree(ptr);
136 return NULL;
137 }
139 devfs_mk_dir(ptr->type->devname);
140 major_info[index] = ptr;
141 return ptr;
142 }
144 static struct xlbd_major_info *
145 xlbd_get_major_info(int vdevice)
146 {
147 struct xlbd_major_info *mi;
148 int major, minor, index;
150 major = BLKIF_MAJOR(vdevice);
151 minor = BLKIF_MINOR(vdevice);
153 switch (major) {
154 case IDE0_MAJOR: index = 0; break;
155 case IDE1_MAJOR: index = 1; break;
156 case IDE2_MAJOR: index = 2; break;
157 case IDE3_MAJOR: index = 3; break;
158 case IDE4_MAJOR: index = 4; break;
159 case IDE5_MAJOR: index = 5; break;
160 case IDE6_MAJOR: index = 6; break;
161 case IDE7_MAJOR: index = 7; break;
162 case IDE8_MAJOR: index = 8; break;
163 case IDE9_MAJOR: index = 9; break;
164 case SCSI_DISK0_MAJOR: index = 10; break;
165 case SCSI_DISK1_MAJOR ... SCSI_DISK7_MAJOR:
166 index = 11 + major - SCSI_DISK1_MAJOR;
167 break;
168 case SCSI_DISK8_MAJOR ... SCSI_DISK15_MAJOR:
169 index = 18 + major - SCSI_DISK8_MAJOR;
170 break;
171 case SCSI_CDROM_MAJOR: index = 26; break;
172 default: index = 27; break;
173 }
175 mi = ((major_info[index] != NULL) ? major_info[index] :
176 xlbd_alloc_major_info(major, minor, index));
177 if (mi)
178 mi->usage++;
179 return mi;
180 }
182 static void
183 xlbd_put_major_info(struct xlbd_major_info *mi)
184 {
185 mi->usage--;
186 /* XXX: release major if 0 */
187 }
189 static int
190 xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
191 {
192 request_queue_t *rq;
194 rq = blk_init_queue(do_blkif_request, &blkif_io_lock);
195 if (rq == NULL)
196 return -1;
198 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10)
199 elevator_init(rq, "noop");
200 #else
201 elevator_init(rq, &elevator_noop);
202 #endif
204 /* Hard sector size and max sectors impersonate the equiv. hardware. */
205 blk_queue_hardsect_size(rq, sector_size);
206 blk_queue_max_sectors(rq, 512);
208 /* Each segment in a request is up to an aligned page in size. */
209 blk_queue_segment_boundary(rq, PAGE_SIZE - 1);
210 blk_queue_max_segment_size(rq, PAGE_SIZE);
212 /* Ensure a merged request will fit in a single I/O ring slot. */
213 blk_queue_max_phys_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
214 blk_queue_max_hw_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
216 /* Make sure buffer addresses are sector-aligned. */
217 blk_queue_dma_alignment(rq, 511);
219 gd->queue = rq;
221 return 0;
222 }
224 static int
225 xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity, int vdevice,
226 u16 vdisk_info, u16 sector_size,
227 struct blkfront_info *info)
228 {
229 struct gendisk *gd;
230 struct xlbd_major_info *mi;
231 int nr_minors = 1;
232 int err = -ENODEV;
233 unsigned int offset;
235 BUG_ON(info->gd != NULL);
236 BUG_ON(info->mi != NULL);
237 BUG_ON(info->rq != NULL);
239 mi = xlbd_get_major_info(vdevice);
240 if (mi == NULL)
241 goto out;
242 info->mi = mi;
244 if ((minor & ((1 << mi->type->partn_shift) - 1)) == 0)
245 nr_minors = 1 << mi->type->partn_shift;
247 gd = alloc_disk(nr_minors);
248 if (gd == NULL)
249 goto out;
251 offset = mi->index * mi->type->disks_per_major +
252 (minor >> mi->type->partn_shift);
253 if (nr_minors > 1) {
254 if (offset < 26) {
255 sprintf(gd->disk_name, "%s%c",
256 mi->type->diskname, 'a' + offset );
257 }
258 else {
259 sprintf(gd->disk_name, "%s%c%c",
260 mi->type->diskname,
261 'a' + ((offset/26)-1), 'a' + (offset%26) );
262 }
263 }
264 else {
265 if (offset < 26) {
266 sprintf(gd->disk_name, "%s%c%d",
267 mi->type->diskname,
268 'a' + offset,
269 minor & ((1 << mi->type->partn_shift) - 1));
270 }
271 else {
272 sprintf(gd->disk_name, "%s%c%c%d",
273 mi->type->diskname,
274 'a' + ((offset/26)-1), 'a' + (offset%26),
275 minor & ((1 << mi->type->partn_shift) - 1));
276 }
277 }
279 gd->major = mi->major;
280 gd->first_minor = minor;
281 gd->fops = &xlvbd_block_fops;
282 gd->private_data = info;
283 gd->driverfs_dev = &(info->xbdev->dev);
284 set_capacity(gd, capacity);
286 if (xlvbd_init_blk_queue(gd, sector_size)) {
287 del_gendisk(gd);
288 goto out;
289 }
291 info->rq = gd->queue;
292 info->gd = gd;
294 if (info->feature_barrier)
295 xlvbd_barrier(info);
297 if (vdisk_info & VDISK_READONLY)
298 set_disk_ro(gd, 1);
300 if (vdisk_info & VDISK_REMOVABLE)
301 gd->flags |= GENHD_FL_REMOVABLE;
303 if (vdisk_info & VDISK_CDROM)
304 gd->flags |= GENHD_FL_CD;
306 return 0;
308 out:
309 if (mi)
310 xlbd_put_major_info(mi);
311 info->mi = NULL;
312 return err;
313 }
315 int
316 xlvbd_add(blkif_sector_t capacity, int vdevice, u16 vdisk_info,
317 u16 sector_size, struct blkfront_info *info)
318 {
319 struct block_device *bd;
320 int err = 0;
322 info->dev = MKDEV(BLKIF_MAJOR(vdevice), BLKIF_MINOR(vdevice));
324 bd = bdget(info->dev);
325 if (bd == NULL)
326 return -ENODEV;
328 err = xlvbd_alloc_gendisk(BLKIF_MINOR(vdevice), capacity, vdevice,
329 vdisk_info, sector_size, info);
331 bdput(bd);
332 return err;
333 }
335 void
336 xlvbd_del(struct blkfront_info *info)
337 {
338 if (info->mi == NULL)
339 return;
341 BUG_ON(info->gd == NULL);
342 del_gendisk(info->gd);
343 put_disk(info->gd);
344 info->gd = NULL;
346 xlbd_put_major_info(info->mi);
347 info->mi = NULL;
349 BUG_ON(info->rq == NULL);
350 blk_cleanup_queue(info->rq);
351 info->rq = NULL;
352 }
354 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,16)
355 int
356 xlvbd_barrier(struct blkfront_info *info)
357 {
358 int err;
360 err = blk_queue_ordered(info->rq,
361 info->feature_barrier ? QUEUE_ORDERED_DRAIN : QUEUE_ORDERED_NONE, NULL);
362 if (err)
363 return err;
364 printk("blkfront: %s: barriers %s\n",
365 info->gd->disk_name, info->feature_barrier ? "enabled" : "disabled");
366 return 0;
367 }
368 #else
369 int
370 xlvbd_barrier(struct blkfront_info *info)
371 {
372 printk("blkfront: %s: barriers disabled\n", info->gd->disk_name);
373 return -ENOSYS;
374 }
375 #endif