- Update Xen patches to 3.3-rc5 and c/s 1157.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / blkback / vbd.c
1 /******************************************************************************
2  * blkback/vbd.c
3  * 
4  * Routines for managing virtual block devices (VBDs).
5  * 
6  * Copyright (c) 2003-2005, Keir Fraser & Steve Hand
7  * 
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License version 2
10  * as published by the Free Software Foundation; or, when distributed
11  * separately from the Linux kernel or incorporated into other
12  * software packages, subject to the following license:
13  * 
14  * Permission is hereby granted, free of charge, to any person obtaining a copy
15  * of this source file (the "Software"), to deal in the Software without
16  * restriction, including without limitation the rights to use, copy, modify,
17  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
18  * and to permit persons to whom the Software is furnished to do so, subject to
19  * the following conditions:
20  * 
21  * The above copyright notice and this permission notice shall be included in
22  * all copies or substantial portions of the Software.
23  * 
24  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
25  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
26  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
27  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
28  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
29  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
30  * IN THE SOFTWARE.
31  */
32
33 #include "common.h"
34
35 #define vbd_sz(_v)   ((_v)->bdev->bd_part ?                             \
36         (_v)->bdev->bd_part->nr_sects : get_capacity((_v)->bdev->bd_disk))
37
38 unsigned long long vbd_size(struct vbd *vbd)
39 {
40         return vbd->bdev ? vbd_sz(vbd) : 0;
41 }
42
43 unsigned long vbd_secsize(struct vbd *vbd)
44 {
45         return vbd->bdev ? bdev_logical_block_size(vbd->bdev) : 0;
46 }
47
48 int vbd_create(blkif_t *blkif, blkif_vdev_t handle, unsigned major,
49                unsigned minor, fmode_t mode, bool cdrom)
50 {
51         struct vbd *vbd;
52         struct block_device *bdev;
53         struct request_queue *q;
54
55         vbd = &blkif->vbd;
56         vbd->handle   = handle; 
57         vbd->size     = 0;
58         vbd->type     = cdrom ? VDISK_CDROM : 0;
59
60         if (!(mode & FMODE_WRITE)) {
61                 mode &= ~FMODE_EXCL; /* xend doesn't even allow mode="r!" */
62                 vbd->type |= VDISK_READONLY;
63         }
64         vbd->mode = mode;
65
66         vbd->pdevice  = MKDEV(major, minor);
67
68         bdev = blkdev_get_by_dev(vbd->pdevice, mode, blkif);
69
70         if (IS_ERR(bdev)) {
71                 if (PTR_ERR(bdev) != -ENOMEDIUM) {
72                         DPRINTK("vbd_creat: device %08x could not be opened\n",
73                                 vbd->pdevice);
74                         return -ENOENT;
75                 }
76
77                 DPRINTK("vbd_creat: device %08x has no medium\n",
78                         vbd->pdevice);
79                 if (cdrom)
80                         return -ENOMEDIUM;
81
82                 bdev = blkdev_get_by_dev(vbd->pdevice, mode | FMODE_NDELAY,
83                                          blkif);
84                 if (IS_ERR(bdev))
85                         return -ENOMEDIUM;
86
87                 if (bdev->bd_disk) {
88                         if (bdev->bd_disk->flags & GENHD_FL_CD)
89                                 vbd->type |= VDISK_CDROM;
90                         if (bdev->bd_disk->flags & GENHD_FL_REMOVABLE)
91                                 vbd->type |= VDISK_REMOVABLE;
92                 }
93
94                 blkdev_put(bdev, mode);
95                 return -ENOMEDIUM;
96         }
97
98         vbd->bdev = bdev;
99
100         if (vbd->bdev->bd_disk == NULL) {
101                 DPRINTK("vbd_creat: device %08x doesn't exist.\n",
102                         vbd->pdevice);
103                 vbd_free(vbd);
104                 return -ENOENT;
105         }
106
107         vbd->size = vbd_size(vbd);
108
109         if (bdev->bd_disk->flags & GENHD_FL_CD)
110                 vbd->type |= VDISK_CDROM;
111         if (vbd->bdev->bd_disk->flags & GENHD_FL_REMOVABLE)
112                 vbd->type |= VDISK_REMOVABLE;
113
114         q = bdev_get_queue(bdev);
115         if (q && q->flush_flags)
116                 vbd->flush_support = true;
117
118         if (q && blk_queue_secdiscard(q))
119                 vbd->discard_secure = true;
120
121         DPRINTK("Successful creation of handle=%04x (dom=%u)\n",
122                 handle, blkif->domid);
123         return 0;
124 }
125
126 void vbd_free(struct vbd *vbd)
127 {
128         if (vbd->bdev)
129                 blkdev_put(vbd->bdev, vbd->mode);
130         vbd->bdev = NULL;
131 }
132
133 int vbd_translate(struct phys_req *req, blkif_t *blkif, int operation)
134 {
135         struct vbd *vbd = &blkif->vbd;
136         int rc = -EACCES;
137
138         if ((operation != READ) && !(vbd->mode & FMODE_WRITE))
139                 goto out;
140
141         if (vbd->bdev == NULL) {
142                 rc = -ENOMEDIUM;
143                 goto out;
144         }
145
146         if (likely(req->nr_sects)) {
147                 blkif_sector_t end = req->sector_number + req->nr_sects;
148
149                 if (unlikely(end < req->sector_number))
150                         goto out;
151                 if (unlikely(end > vbd_sz(vbd)))
152                         goto out;
153         }
154
155         req->dev  = vbd->pdevice;
156         req->bdev = vbd->bdev;
157         rc = 0;
158
159  out:
160         return rc;
161 }
162
163 void vbd_resize(blkif_t *blkif)
164 {
165         struct vbd *vbd = &blkif->vbd;
166         struct xenbus_transaction xbt;
167         int err;
168         struct xenbus_device *dev = blkif->be->dev;
169         unsigned long long new_size = vbd_size(vbd);
170
171         pr_info("VBD Resize: new size %Lu\n", new_size);
172         vbd->size = new_size;
173 again:
174         err = xenbus_transaction_start(&xbt);
175         if (err) {
176                 pr_warning("Error %d starting transaction", err);
177                 return;
178         }
179         err = xenbus_printf(xbt, dev->nodename, "sectors", "%Lu",
180                             vbd_size(vbd));
181         if (err) {
182                 pr_warning("Error %d writing new size", err);
183                 goto abort;
184         }
185
186         err = xenbus_printf(xbt, dev->nodename, "sector-size", "%lu",
187                             vbd_secsize(vbd));
188         if (err) {
189                 pr_warning("Error writing new sector size");
190                 goto abort;
191         }
192
193         /*
194          * Write the current state; we will use this to synchronize
195          * the front-end. If the current state is "connected" the
196          * front-end will get the new size information online.
197          */
198         err = xenbus_printf(xbt, dev->nodename, "state", "%d", dev->state);
199         if (err) {
200                 pr_warning("Error %d writing the state", err);
201                 goto abort;
202         }
203
204         err = xenbus_transaction_end(xbt, 0);
205         if (err == -EAGAIN)
206                 goto again;
207         if (err)
208                 pr_warning("Error %d ending transaction", err);
209         return;
210 abort:
211         xenbus_transaction_end(xbt, 1);
212 }