- 2.6.17 port work build breaks, but the patch set is relativly stable
[linux-flexiantxendom0-3.2.10.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 };
95
96 spinlock_t blkif_io_lock = SPIN_LOCK_UNLOCKED;
97
98 static struct xlbd_major_info *
99 xlbd_alloc_major_info(int major, int minor, int index)
100 {
101         struct xlbd_major_info *ptr;
102
103         ptr = kzalloc(sizeof(struct xlbd_major_info), GFP_KERNEL);
104         if (ptr == NULL)
105                 return NULL;
106
107         ptr->major = major;
108
109         switch (index) {
110         case XLBD_MAJOR_IDE_RANGE:
111                 ptr->type = &xlbd_ide_type;
112                 ptr->index = index - XLBD_MAJOR_IDE_START;
113                 break;
114         case XLBD_MAJOR_SCSI_RANGE:
115                 ptr->type = &xlbd_scsi_type;
116                 ptr->index = index - XLBD_MAJOR_SCSI_START;
117                 break;
118         case XLBD_MAJOR_VBD_RANGE:
119                 ptr->type = &xlbd_vbd_type;
120                 ptr->index = index - XLBD_MAJOR_VBD_START;
121                 break;
122         }
123
124         printk("Registering block device major %i\n", ptr->major);
125         if (register_blkdev(ptr->major, ptr->type->devname)) {
126                 WPRINTK("can't get major %d with name %s\n",
127                         ptr->major, ptr->type->devname);
128                 kfree(ptr);
129                 return NULL;
130         }
131
132         devfs_mk_dir(ptr->type->devname);
133         major_info[index] = ptr;
134         return ptr;
135 }
136
137 static struct xlbd_major_info *
138 xlbd_get_major_info(int vdevice)
139 {
140         struct xlbd_major_info *mi;
141         int major, minor, index;
142
143         major = BLKIF_MAJOR(vdevice);
144         minor = BLKIF_MINOR(vdevice);
145
146         switch (major) {
147         case IDE0_MAJOR: index = 0; break;
148         case IDE1_MAJOR: index = 1; break;
149         case IDE2_MAJOR: index = 2; break;
150         case IDE3_MAJOR: index = 3; break;
151         case IDE4_MAJOR: index = 4; break;
152         case IDE5_MAJOR: index = 5; break;
153         case IDE6_MAJOR: index = 6; break;
154         case IDE7_MAJOR: index = 7; break;
155         case IDE8_MAJOR: index = 8; break;
156         case IDE9_MAJOR: index = 9; break;
157         case SCSI_DISK0_MAJOR: index = 10; break;
158         case SCSI_DISK1_MAJOR ... SCSI_DISK7_MAJOR:
159                 index = 11 + major - SCSI_DISK1_MAJOR;
160                 break;
161         case SCSI_CDROM_MAJOR: index = 18; break;
162         default: index = 19; break;
163         }
164
165         mi = ((major_info[index] != NULL) ? major_info[index] :
166               xlbd_alloc_major_info(major, minor, index));
167         if (mi)
168                 mi->usage++;
169         return mi;
170 }
171
172 static void
173 xlbd_put_major_info(struct xlbd_major_info *mi)
174 {
175         mi->usage--;
176         /* XXX: release major if 0 */
177 }
178
179 static int
180 xlvbd_init_blk_queue(struct gendisk *gd, u16 sector_size)
181 {
182         request_queue_t *rq;
183
184         rq = blk_init_queue(do_blkif_request, &blkif_io_lock);
185         if (rq == NULL)
186                 return -1;
187
188         elevator_init(rq, "noop");
189
190         /* Hard sector size and max sectors impersonate the equiv. hardware. */
191         blk_queue_hardsect_size(rq, sector_size);
192         blk_queue_max_sectors(rq, 512);
193
194         /* Each segment in a request is up to an aligned page in size. */
195         blk_queue_segment_boundary(rq, PAGE_SIZE - 1);
196         blk_queue_max_segment_size(rq, PAGE_SIZE);
197
198         /* Ensure a merged request will fit in a single I/O ring slot. */
199         blk_queue_max_phys_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
200         blk_queue_max_hw_segments(rq, BLKIF_MAX_SEGMENTS_PER_REQUEST);
201
202         /* Make sure buffer addresses are sector-aligned. */
203         blk_queue_dma_alignment(rq, 511);
204
205         gd->queue = rq;
206
207         return 0;
208 }
209
210 static int
211 xlvbd_alloc_gendisk(int minor, blkif_sector_t capacity, int vdevice,
212                     u16 vdisk_info, u16 sector_size,
213                     struct blkfront_info *info)
214 {
215         struct gendisk *gd;
216         struct xlbd_major_info *mi;
217         int nr_minors = 1;
218         int err = -ENODEV;
219
220         BUG_ON(info->gd != NULL);
221         BUG_ON(info->mi != NULL);
222         BUG_ON(info->rq != NULL);
223
224         mi = xlbd_get_major_info(vdevice);
225         if (mi == NULL)
226                 goto out;
227         info->mi = mi;
228
229         if ((minor & ((1 << mi->type->partn_shift) - 1)) == 0)
230                 nr_minors = 1 << mi->type->partn_shift;
231
232         gd = alloc_disk(nr_minors);
233         if (gd == NULL)
234                 goto out;
235
236         if (nr_minors > 1)
237                 sprintf(gd->disk_name, "%s%c", mi->type->diskname,
238                         'a' + mi->index * mi->type->disks_per_major +
239                         (minor >> mi->type->partn_shift));
240         else
241                 sprintf(gd->disk_name, "%s%c%d", mi->type->diskname,
242                         'a' + mi->index * mi->type->disks_per_major +
243                         (minor >> mi->type->partn_shift),
244                         minor & ((1 << mi->type->partn_shift) - 1));
245
246         gd->major = mi->major;
247         gd->first_minor = minor;
248         gd->fops = &xlvbd_block_fops;
249         gd->private_data = info;
250         gd->driverfs_dev = &(info->xbdev->dev);
251         set_capacity(gd, capacity);
252
253         if (xlvbd_init_blk_queue(gd, sector_size)) {
254                 del_gendisk(gd);
255                 goto out;
256         }
257
258         info->rq = gd->queue;
259
260         if (vdisk_info & VDISK_READONLY)
261                 set_disk_ro(gd, 1);
262
263         if (vdisk_info & VDISK_REMOVABLE)
264                 gd->flags |= GENHD_FL_REMOVABLE;
265
266         if (vdisk_info & VDISK_CDROM)
267                 gd->flags |= GENHD_FL_CD;
268
269         info->gd = gd;
270
271         return 0;
272
273  out:
274         if (mi)
275                 xlbd_put_major_info(mi);
276         info->mi = NULL;
277         return err;
278 }
279
280 int
281 xlvbd_add(blkif_sector_t capacity, int vdevice, u16 vdisk_info,
282           u16 sector_size, struct blkfront_info *info)
283 {
284         struct block_device *bd;
285         int err = 0;
286
287         info->dev = MKDEV(BLKIF_MAJOR(vdevice), BLKIF_MINOR(vdevice));
288
289         bd = bdget(info->dev);
290         if (bd == NULL)
291                 return -ENODEV;
292
293         err = xlvbd_alloc_gendisk(BLKIF_MINOR(vdevice), capacity, vdevice,
294                                   vdisk_info, sector_size, info);
295
296         bdput(bd);
297         return err;
298 }
299
300 void
301 xlvbd_del(struct blkfront_info *info)
302 {
303         if (info->mi == NULL)
304                 return;
305
306         BUG_ON(info->gd == NULL);
307         del_gendisk(info->gd);
308         put_disk(info->gd);
309         info->gd = NULL;
310
311         xlbd_put_major_info(info->mi);
312         info->mi = NULL;
313
314         BUG_ON(info->rq == NULL);
315         blk_cleanup_queue(info->rq);
316         info->rq = NULL;
317 }
318
319 /*
320  * Local variables:
321  *  c-file-style: "linux"
322  *  indent-tabs-mode: t
323  *  c-indent-level: 8
324  *  c-basic-offset: 8
325  *  tab-width: 8
326  * End:
327  */