f5ee589ee31c4fce99a82bd96e7cb8b2610651dc
[linux-2.6.git] / drivers / xen / blkback / vbd.c
1 /******************************************************************************
2  * blkback/vbd.c
3  * 
4  * Routines for managing virtual block devices (VBDs).
5  * 
6  * NOTE: vbd_lock protects updates to the rb_tree against concurrent lookups 
7  * in vbd_translate.  All other lookups are implicitly protected because the 
8  * only caller (the control message dispatch routine) serializes the calls.
9  * 
10  * Copyright (c) 2003-2005, Keir Fraser & Steve Hand
11  */
12
13 #include "common.h"
14
15 struct vbd { 
16     blkif_vdev_t   vdevice;     /* what the domain refers to this vbd as */
17     unsigned char  readonly;    /* Non-zero -> read-only */
18     unsigned char  type;        /* VDISK_TYPE_xxx */
19     blkif_pdev_t   pdevice;     /* phys device that this vbd maps to */
20     struct block_device *bdev;
21     rb_node_t      rb;          /* for linking into R-B tree lookup struct */
22 }; 
23
24 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
25 static inline dev_t vbd_map_devnum(blkif_pdev_t cookie)
26 { return MKDEV(cookie>>8, cookie&0xff); }
27 #define vbd_sz(_v)   ((_v)->bdev->bd_part ? \
28     (_v)->bdev->bd_part->nr_sects : (_v)->bdev->bd_disk->capacity)
29 #define bdev_put(_b) blkdev_put(_b)
30 #else
31 #define vbd_sz(_v)   (blk_size[MAJOR((_v)->pdevice)][MINOR((_v)->pdevice)]*2)
32 #define bdev_put(_b) ((void)0)
33 #endif
34
35 void vbd_create(blkif_be_vbd_create_t *create) 
36 {
37     struct vbd  *vbd; 
38     rb_node_t  **rb_p, *rb_parent = NULL;
39     blkif_t     *blkif;
40     blkif_vdev_t vdevice = create->vdevice;
41
42     blkif = blkif_find_by_handle(create->domid, create->blkif_handle);
43     if ( unlikely(blkif == NULL) )
44     {
45         DPRINTK("vbd_create attempted for non-existent blkif (%u,%u)\n", 
46                 create->domid, create->blkif_handle); 
47         create->status = BLKIF_BE_STATUS_INTERFACE_NOT_FOUND;
48         return;
49     }
50
51     rb_p = &blkif->vbd_rb.rb_node;
52     while ( *rb_p != NULL )
53     {
54         rb_parent = *rb_p;
55         vbd = rb_entry(rb_parent, struct vbd, rb);
56         if ( vdevice < vbd->vdevice )
57         {
58             rb_p = &rb_parent->rb_left;
59         }
60         else if ( vdevice > vbd->vdevice )
61         {
62             rb_p = &rb_parent->rb_right;
63         }
64         else
65         {
66             DPRINTK("vbd_create attempted for already existing vbd\n");
67             create->status = BLKIF_BE_STATUS_VBD_EXISTS;
68             return;
69         }
70     }
71
72     if ( unlikely((vbd = kmalloc(sizeof(struct vbd), GFP_KERNEL)) == NULL) )
73     {
74         DPRINTK("vbd_create: out of memory\n");
75         create->status = BLKIF_BE_STATUS_OUT_OF_MEMORY;
76         return;
77     }
78
79     vbd->vdevice  = vdevice; 
80     vbd->readonly = create->readonly;
81     vbd->type     = VDISK_TYPE_DISK | VDISK_FLAG_VIRT;
82
83     /* Mask to 16-bit for compatibility with old tools */
84     vbd->pdevice  = create->pdevice & 0xffff;
85
86 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
87     vbd->bdev = open_by_devnum(
88         vbd_map_devnum(vbd->pdevice),
89         vbd->readonly ? FMODE_READ : FMODE_WRITE);
90     if ( IS_ERR(vbd->bdev) )
91     {
92         DPRINTK("vbd_creat: device %08x doesn't exist.\n", vbd->pdevice);
93         create->status = BLKIF_BE_STATUS_PHYSDEV_NOT_FOUND;
94         return;
95     }
96
97     if ( (vbd->bdev->bd_disk == NULL) )
98     {
99         DPRINTK("vbd_creat: device %08x doesn't exist.\n", vbd->pdevice);
100         create->status = BLKIF_BE_STATUS_PHYSDEV_NOT_FOUND;
101         bdev_put(vbd->bdev);
102         return;
103     }
104 #else
105     if ( (blk_size[MAJOR(vbd->pdevice)] == NULL) || (vbd_sz(vbd) == 0) )
106     {
107         DPRINTK("vbd_creat: device %08x doesn't exist.\n", vbd->pdevice);
108         create->status = BLKIF_BE_STATUS_PHYSDEV_NOT_FOUND;
109         return;
110     }
111 #endif
112
113     spin_lock(&blkif->vbd_lock);
114     rb_link_node(&vbd->rb, rb_parent, rb_p);
115     rb_insert_color(&vbd->rb, &blkif->vbd_rb);
116     spin_unlock(&blkif->vbd_lock);
117
118     DPRINTK("Successful creation of vdev=%04x (dom=%u)\n",
119             vdevice, create->domid);
120     create->status = BLKIF_BE_STATUS_OKAY;
121 }
122
123
124 void vbd_destroy(blkif_be_vbd_destroy_t *destroy) 
125 {
126     blkif_t           *blkif;
127     struct vbd        *vbd;
128     rb_node_t         *rb;
129     blkif_vdev_t       vdevice = destroy->vdevice;
130
131     blkif = blkif_find_by_handle(destroy->domid, destroy->blkif_handle);
132     if ( unlikely(blkif == NULL) )
133     {
134         DPRINTK("vbd_destroy attempted for non-existent blkif (%u,%u)\n", 
135                 destroy->domid, destroy->blkif_handle); 
136         destroy->status = BLKIF_BE_STATUS_INTERFACE_NOT_FOUND;
137         return;
138     }
139
140     rb = blkif->vbd_rb.rb_node;
141     while ( rb != NULL )
142     {
143         vbd = rb_entry(rb, struct vbd, rb);
144         if ( vdevice < vbd->vdevice )
145             rb = rb->rb_left;
146         else if ( vdevice > vbd->vdevice )
147             rb = rb->rb_right;
148         else
149             goto found;
150     }
151
152     destroy->status = BLKIF_BE_STATUS_VBD_NOT_FOUND;
153     return;
154
155  found:
156     spin_lock(&blkif->vbd_lock);
157     rb_erase(rb, &blkif->vbd_rb);
158     spin_unlock(&blkif->vbd_lock);
159     bdev_put(vbd->bdev);
160     kfree(vbd);
161 }
162
163
164 void destroy_all_vbds(blkif_t *blkif)
165 {
166     struct vbd *vbd;
167     rb_node_t  *rb;
168
169     spin_lock(&blkif->vbd_lock);
170
171     while ( (rb = blkif->vbd_rb.rb_node) != NULL )
172     {
173         vbd = rb_entry(rb, struct vbd, rb);
174         rb_erase(rb, &blkif->vbd_rb);
175         spin_unlock(&blkif->vbd_lock);
176         bdev_put(vbd->bdev);
177         kfree(vbd);
178         spin_lock(&blkif->vbd_lock);
179     }
180
181     spin_unlock(&blkif->vbd_lock);
182 }
183
184
185 static void vbd_probe_single(
186     blkif_t *blkif, vdisk_t *vbd_info, struct vbd *vbd)
187 {
188     vbd_info->device   = vbd->vdevice; 
189     vbd_info->info     = vbd->type | (vbd->readonly ? VDISK_FLAG_RO : 0);
190     vbd_info->capacity = vbd_sz(vbd);
191 }
192
193
194 int vbd_probe(blkif_t *blkif, vdisk_t *vbd_info, int max_vbds)
195 {
196     int        rc = 0, nr_vbds = 0;
197     rb_node_t *rb;
198
199     spin_lock(&blkif->vbd_lock);
200
201     if ( (rb = blkif->vbd_rb.rb_node) == NULL )
202         goto out;
203
204  new_subtree:
205     /* STEP 1. Find least node (it'll be left-most). */
206     while ( rb->rb_left != NULL )
207         rb = rb->rb_left;
208
209     for ( ; ; )
210     {
211         /* STEP 2. Dealt with left subtree. Now process current node. */
212         vbd_probe_single(blkif, &vbd_info[nr_vbds],
213                          rb_entry(rb, struct vbd, rb));
214         if ( ++nr_vbds == max_vbds )
215             goto out;
216
217         /* STEP 3. Process right subtree, if any. */
218         if ( rb->rb_right != NULL )
219         {
220             rb = rb->rb_right;
221             goto new_subtree;
222         }
223
224         /* STEP 4. Done both subtrees. Head back through ancesstors. */
225         for ( ; ; ) 
226         {
227             /* We're done when we get back to the root node. */
228             if ( rb->rb_parent == NULL )
229                 goto out;
230             /* If we are left of parent, then parent is next to process. */
231             if ( rb->rb_parent->rb_left == rb )
232                 break;
233             /* If we are right of parent, then we climb to grandparent. */
234             rb = rb->rb_parent;
235         }
236
237         rb = rb->rb_parent;
238     }
239
240  out:
241     spin_unlock(&blkif->vbd_lock);
242     return (rc == 0) ? nr_vbds : rc;  
243 }
244
245
246 int vbd_translate(struct phys_req *req, blkif_t *blkif, int operation)
247 {
248     struct vbd *vbd;
249     rb_node_t  *rb;
250     int         rc = -EACCES;
251
252     /* Take the vbd_lock because another thread could be updating the tree. */
253     spin_lock(&blkif->vbd_lock);
254
255     rb = blkif->vbd_rb.rb_node;
256     while ( rb != NULL )
257     {
258         vbd = rb_entry(rb, struct vbd, rb);
259         if ( req->dev < vbd->vdevice )
260             rb = rb->rb_left;
261         else if ( req->dev > vbd->vdevice )
262             rb = rb->rb_right;
263         else
264             goto found;
265     }
266
267     DPRINTK("vbd_translate; domain %u attempted to access "
268             "non-existent VBD.\n", blkif->domid);
269     rc = -ENODEV;
270     goto out;
271
272  found:
273
274     if ( (operation == WRITE) && vbd->readonly )
275         goto out;
276
277     if ( unlikely((req->sector_number + req->nr_sects) > vbd_sz(vbd)) )
278         goto out;
279
280     req->dev  = vbd->pdevice;
281     req->bdev = vbd->bdev;
282     rc = 0;
283
284  out:
285     spin_unlock(&blkif->vbd_lock);
286     return rc;
287 }