Merge to Fedora kernel-2.6.17-1.2187_FC5 patched with stable patch-2.6.17.13-vs2...
[linux-2.6.git] / drivers / xen / blkfront / vbd.c
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  */
34
35 #include "block.h"
36 #include <linux/blkdev.h>
37 #include <linux/list.h>
38
39 #define BLKIF_MAJOR(dev) ((dev)>>8)
40 #define BLKIF_MINOR(dev) ((dev) & 0xff)
41
42 /*
43  * For convenience we distinguish between ide, scsi and 'other' (i.e.,
44  * potentially combinations of the two) in the naming scheme and in a few other
45  * places.
46  */
47
48 #define NUM_IDE_MAJORS 10
49 #define NUM_SCSI_MAJORS 9
50 #define NUM_VBD_MAJORS 1
51
52 static struct xlbd_type_info xlbd_ide_type = {
53         .partn_shift = 6,
54         .disks_per_major = 2,
55         .devname = "ide",
56         .diskname = "hd",
57 };
58
59 static struct xlbd_type_info xlbd_scsi_type = {
60         .partn_shift = 4,
61         .disks_per_major = 16,
62         .devname = "sd",
63         .diskname = "sd",
64 };
65
66 static struct xlbd_type_info xlbd_vbd_type = {
67         .partn_shift = 4,
68         .disks_per_major = 16,
69         .devname = "xvd",
70         .diskname = "xvd",
71 };
72
73 static struct xlbd_major_info *major_info[NUM_IDE_MAJORS + NUM_SCSI_MAJORS +
74                                          NUM_VBD_MAJORS];
75
76 #define XLBD_MAJOR_IDE_START    0
77 #define XLBD_MAJOR_SCSI_START   (NUM_IDE_MAJORS)
78 #define XLBD_MAJOR_VBD_START    (NUM_IDE_MAJORS + NUM_SCSI_MAJORS)
79
80 #define XLBD_MAJOR_IDE_RANGE    XLBD_MAJOR_IDE_START ... XLBD_MAJOR_SCSI_START - 1
81 #define XLBD_MAJOR_SCSI_RANGE   XLBD_MAJOR_SCSI_START ... XLBD_MAJOR_VBD_START - 1
82 #define XLBD_MAJOR_VBD_RANGE    XLBD_MAJOR_VBD_START ... XLBD_MAJOR_VBD_START + NUM_VBD_MAJORS - 1
83
84 /* Information about our VBDs. */
85 #define MAX_VBDS 64
86 static LIST_HEAD(vbds_list);
87
88 static struct block_device_operations xlvbd_block_fops =
89 {
90         .owner = THIS_MODULE,
91         .open = blkif_open,
92         .release = blkif_release,
93         .ioctl  = blkif_ioctl,
94         .getgeo = blkif_getgeo
95 };
96
97 DEFINE_SPINLOCK(blkif_io_lock);
98
99 static struct xlbd_major_info *
100 xlbd_alloc_major_info(int major, int minor, int index)
101 {
102         struct xlbd_major_info *ptr;
103
104         ptr = kzalloc(sizeof(struct xlbd_major_info), GFP_KERNEL);
105         if (ptr == NULL)
106                 return NULL;
107
108         ptr->major = major;
109
110         switch (index) {
111         case XLBD_MAJOR_IDE_RANGE:
112                 ptr->type = &xlbd_ide_type;
113                 ptr->index = index - XLBD_MAJOR_IDE_START;
114                 break;
115         case XLBD_MAJOR_SCSI_RANGE:
116                 ptr->type = &xlbd_scsi_type;
117                 ptr->index = index - XLBD_MAJOR_SCSI_START;
118                 break;
119         case XLBD_MAJOR_VBD_RANGE:
120                 ptr->type = &xlbd_vbd_type;
121                 ptr->index = index - XLBD_MAJOR_VBD_START;
122                 break;
123         }
124
125         printk("Registering block device major %i\n", ptr->major);
126         if (register_blkdev(ptr->major, ptr->type->devname)) {
127                 WPRINTK("can't get major %d with name %s\n",
128                         ptr->major, ptr->type->devname);
129                 kfree(ptr);
130                 return NULL;
131         }
132
133         devfs_mk_dir(ptr->type->devname);
134         major_info[index] = ptr;
135         return ptr;
136 }
137
138 static struct xlbd_major_info *
139 xlbd_get_major_info(int vdevice)
140 {
141         struct xlbd_major_info *mi;
142         int major, minor, index;
143
144         major = BLKIF_MAJOR(vdevice);
145         minor = BLKIF_MINOR(vdevice);
146
147         switch (major) {
148         case IDE0_MAJOR: index = 0; break;
149         case IDE1_MAJOR: index = 1; break;
150         case IDE2_MAJOR: index = 2; break;
151         case IDE3_MAJOR: index = 3; break;
152         case IDE4_MAJOR: index = 4; break;
153         case IDE5_MAJOR: index = 5; break;
154         case IDE6_MAJOR: index = 6; break;
155         case IDE7_MAJOR: index = 7; break;
156         case IDE8_MAJOR: index = 8; break;
157         case IDE9_MAJOR: index = 9; break;
158         case SCSI_DISK0_MAJOR: index = 10; break;
159         case SCSI_DISK1_MAJOR ... SCSI_DISK7_MAJOR:
160                 index = 11 + major - SCSI_DISK1_MAJOR;
161                 break;
162         case SCSI_CDROM_MAJOR: index = 18; break;
163         default: index = 19; break;
164         }
165
166         mi = ((major_info[index] != NULL) ? major_info[index] :
167               xlbd_alloc_major_info(major, minor, index));
168         if (mi)
169                 mi->usage++;
170         return mi;
171 }
172
173 static void
174 xlbd_put_major_info(struct xlbd_major_info *mi)
175 {
176         mi->usage--;
177         /* XXX: release major if 0 */
178 }
179
180 static int
181 xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
182 {
183         request_queue_t *rq;
184
185         rq = blk_init_queue(do_blkif_request, &blkif_io_lock);
186         if (rq == NULL)
187                 return -1;
188
189         elevator_init(rq, "noop");
190
191         /* Hard sector size and max sectors impersonate the equiv. hardware. */
192         blk_queue_hardsect_size(rq, sector_size);
193         blk_queue_max_sectors(rq, 512);
194
195         /* Each segment in a request is up to an aligned page in size. */
196         blk_queue_segment_boundary(rq, PAGE_SIZE - 1);
197         blk_queue_max_segment_size(rq, PAGE_SIZE);
198
199         /* Ensure a merged request will fit in a single I/O ring slot. */
200         blk_queue_max_phys_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
201         blk_queue_max_hw_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
202
203         /* Make sure buffer addresses are sector-aligned. */
204         blk_queue_dma_alignment(rq, 511);
205
206         gd->queue = rq;
207
208         return 0;
209 }
210
211 static int
212 xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity, int vdevice,
213                     u16 vdisk_info, u16 sector_size,
214                     struct blkfront_info *info)
215 {
216         struct gendisk *gd;
217         struct xlbd_major_info *mi;
218         int nr_minors = 1;
219         int err = -ENODEV;
220
221         BUG_ON(info->gd != NULL);
222         BUG_ON(info->mi != NULL);
223         BUG_ON(info->rq != NULL);
224
225         mi = xlbd_get_major_info(vdevice);
226         if (mi == NULL)
227                 goto out;
228         info->mi = mi;
229
230         if ((minor & ((1 << mi->type->partn_shift) - 1)) == 0)
231                 nr_minors = 1 << mi->type->partn_shift;
232
233         gd = alloc_disk(nr_minors);
234         if (gd == NULL)
235                 goto out;
236
237         if (nr_minors > 1)
238                 sprintf(gd->disk_name, "%s%c", mi->type->diskname,
239                         'a' + mi->index * mi->type->disks_per_major +
240                         (minor >> mi->type->partn_shift));
241         else
242                 sprintf(gd->disk_name, "%s%c%d", mi->type->diskname,
243                         'a' + mi->index * mi->type->disks_per_major +
244                         (minor >> mi->type->partn_shift),
245                         minor & ((1 << mi->type->partn_shift) - 1));
246
247         gd->major = mi->major;
248         gd->first_minor = minor;
249         gd->fops = &xlvbd_block_fops;
250         gd->private_data = info;
251         gd->driverfs_dev = &(info->xbdev->dev);
252         set_capacity(gd, capacity);
253
254         if (xlvbd_init_blk_queue(gd, sector_size)) {
255                 del_gendisk(gd);
256                 goto out;
257         }
258
259         info->rq = gd->queue;
260
261         if (vdisk_info & VDISK_READONLY)
262                 set_disk_ro(gd, 1);
263
264         if (vdisk_info & VDISK_REMOVABLE)
265                 gd->flags |= GENHD_FL_REMOVABLE;
266
267         if (vdisk_info & VDISK_CDROM)
268                 gd->flags |= GENHD_FL_CD;
269
270         info->gd = gd;
271
272         return 0;
273
274  out:
275         if (mi)
276                 xlbd_put_major_info(mi);
277         info->mi = NULL;
278         return err;
279 }
280
281 int
282 xlvbd_add(blkif_sector_t capacity, int vdevice, u16 vdisk_info,
283           u16 sector_size, struct blkfront_info *info)
284 {
285         struct block_device *bd;
286         int err = 0;
287
288         info->dev = MKDEV(BLKIF_MAJOR(vdevice), BLKIF_MINOR(vdevice));
289
290         bd = bdget(info->dev);
291         if (bd == NULL)
292                 return -ENODEV;
293
294         err = xlvbd_alloc_gendisk(BLKIF_MINOR(vdevice), capacity, vdevice,
295                                   vdisk_info, sector_size, info);
296
297         bdput(bd);
298         return err;
299 }
300
301 void
302 xlvbd_del(struct blkfront_info *info)
303 {
304         if (info->mi == NULL)
305                 return;
306
307         BUG_ON(info->gd == NULL);
308         del_gendisk(info->gd);
309         put_disk(info->gd);
310         info->gd = NULL;
311
312         xlbd_put_major_info(info->mi);
313         info->mi = NULL;
314
315         BUG_ON(info->rq == NULL);
316         blk_cleanup_queue(info->rq);
317         info->rq = NULL;
318 }