ia64/xen-unstable

view linux-2.6.11-xen-sparse/drivers/xen/blkback/interface.c @ 5688:d231efdaa66d

manual merge
author iap10@freefall.cl.cam.ac.uk
date Wed Jul 06 18:55:16 2005 +0000 (2005-07-06)
parents 287583627544 43e8e30cbea7
children 32fb371cc283 707fcf42a5ae
line source
1 /******************************************************************************
2 * arch/xen/drivers/blkif/backend/interface.c
3 *
4 * Block-device interface management.
5 *
6 * Copyright (c) 2004, Keir Fraser
7 */
9 #include "common.h"
11 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
12 #define VMALLOC_VMADDR(x) ((unsigned long)(x))
13 #endif
15 #define BLKIF_HASHSZ 1024
16 #define BLKIF_HASH(_d,_h) (((int)(_d)^(int)(_h))&(BLKIF_HASHSZ-1))
18 static kmem_cache_t *blkif_cachep;
19 static blkif_t *blkif_hash[BLKIF_HASHSZ];
21 blkif_t *blkif_find_by_handle(domid_t domid, unsigned int handle)
22 {
23 blkif_t *blkif = blkif_hash[BLKIF_HASH(domid, handle)];
24 while ( (blkif != NULL) &&
25 ((blkif->domid != domid) || (blkif->handle != handle)) )
26 blkif = blkif->hash_next;
27 return blkif;
28 }
30 static void __blkif_disconnect_complete(void *arg)
31 {
32 blkif_t *blkif = (blkif_t *)arg;
33 ctrl_msg_t cmsg;
34 blkif_be_disconnect_t disc;
36 /*
37 * These can't be done in blkif_disconnect() because at that point there
38 * may be outstanding requests at the disc whose asynchronous responses
39 * must still be notified to the remote driver.
40 */
41 unbind_evtchn_from_irq(blkif->evtchn);
43 #ifdef CONFIG_XEN_BLKDEV_GRANT
44 {
45 /*
46 * Release the shared memory page.
47 */
48 struct gnttab_unmap_grant_ref op;
50 op.host_virt_addr = blkif->shmem_vaddr;
51 op.handle = blkif->shmem_handle;
52 op.dev_bus_addr = 0;
54 if(unlikely(HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1))) {
55 BUG();
56 }
57 }
58 #endif
59 vfree(blkif->blk_ring.sring);
61 /* Construct the deferred response message. */
62 cmsg.type = CMSG_BLKIF_BE;
63 cmsg.subtype = CMSG_BLKIF_BE_DISCONNECT;
64 cmsg.id = blkif->disconnect_rspid;
65 cmsg.length = sizeof(blkif_be_disconnect_t);
66 disc.domid = blkif->domid;
67 disc.blkif_handle = blkif->handle;
68 disc.status = BLKIF_BE_STATUS_OKAY;
69 memcpy(cmsg.msg, &disc, sizeof(disc));
71 /*
72 * Make sure message is constructed /before/ status change, because
73 * after the status change the 'blkif' structure could be deallocated at
74 * any time. Also make sure we send the response /after/ status change,
75 * as otherwise a subsequent CONNECT request could spuriously fail if
76 * another CPU doesn't see the status change yet.
77 */
78 mb();
79 if ( blkif->status != DISCONNECTING )
80 BUG();
81 blkif->status = DISCONNECTED;
82 mb();
84 /* Send the successful response. */
85 ctrl_if_send_response(&cmsg);
86 }
88 void blkif_disconnect_complete(blkif_t *blkif)
89 {
90 INIT_WORK(&blkif->work, __blkif_disconnect_complete, (void *)blkif);
91 schedule_work(&blkif->work);
92 }
94 void blkif_create(blkif_be_create_t *create)
95 {
96 domid_t domid = create->domid;
97 unsigned int handle = create->blkif_handle;
98 blkif_t **pblkif, *blkif;
100 if ( (blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL)) == NULL )
101 {
102 DPRINTK("Could not create blkif: out of memory\n");
103 create->status = BLKIF_BE_STATUS_OUT_OF_MEMORY;
104 return;
105 }
107 memset(blkif, 0, sizeof(*blkif));
108 blkif->domid = domid;
109 blkif->handle = handle;
110 blkif->status = DISCONNECTED;
111 spin_lock_init(&blkif->vbd_lock);
112 spin_lock_init(&blkif->blk_ring_lock);
113 atomic_set(&blkif->refcnt, 0);
115 pblkif = &blkif_hash[BLKIF_HASH(domid, handle)];
116 while ( *pblkif != NULL )
117 {
118 if ( ((*pblkif)->domid == domid) && ((*pblkif)->handle == handle) )
119 {
120 DPRINTK("Could not create blkif: already exists\n");
121 create->status = BLKIF_BE_STATUS_INTERFACE_EXISTS;
122 kmem_cache_free(blkif_cachep, blkif);
123 return;
124 }
125 pblkif = &(*pblkif)->hash_next;
126 }
128 blkif->hash_next = *pblkif;
129 *pblkif = blkif;
131 DPRINTK("Successfully created blkif\n");
132 create->status = BLKIF_BE_STATUS_OKAY;
133 }
135 void blkif_destroy(blkif_be_destroy_t *destroy)
136 {
137 domid_t domid = destroy->domid;
138 unsigned int handle = destroy->blkif_handle;
139 blkif_t **pblkif, *blkif;
141 pblkif = &blkif_hash[BLKIF_HASH(domid, handle)];
142 while ( (blkif = *pblkif) != NULL )
143 {
144 if ( (blkif->domid == domid) && (blkif->handle == handle) )
145 {
146 if ( blkif->status != DISCONNECTED )
147 goto still_connected;
148 goto destroy;
149 }
150 pblkif = &blkif->hash_next;
151 }
153 destroy->status = BLKIF_BE_STATUS_INTERFACE_NOT_FOUND;
154 return;
156 still_connected:
157 destroy->status = BLKIF_BE_STATUS_INTERFACE_CONNECTED;
158 return;
160 destroy:
161 *pblkif = blkif->hash_next;
162 destroy_all_vbds(blkif);
163 kmem_cache_free(blkif_cachep, blkif);
164 destroy->status = BLKIF_BE_STATUS_OKAY;
165 }
167 void blkif_connect(blkif_be_connect_t *connect)
168 {
169 domid_t domid = connect->domid;
170 unsigned int handle = connect->blkif_handle;
171 unsigned int evtchn = connect->evtchn;
172 unsigned long shmem_frame = connect->shmem_frame;
173 struct vm_struct *vma;
174 #ifdef CONFIG_XEN_BLKDEV_GRANT
175 int ref = connect->shmem_ref;
176 #else
177 pgprot_t prot;
178 int error;
179 #endif
180 blkif_t *blkif;
181 blkif_sring_t *sring;
183 blkif = blkif_find_by_handle(domid, handle);
184 if ( unlikely(blkif == NULL) )
185 {
186 DPRINTK("blkif_connect attempted for non-existent blkif (%u,%u)\n",
187 connect->domid, connect->blkif_handle);
188 connect->status = BLKIF_BE_STATUS_INTERFACE_NOT_FOUND;
189 return;
190 }
192 if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
193 {
194 connect->status = BLKIF_BE_STATUS_OUT_OF_MEMORY;
195 return;
196 }
198 #ifndef CONFIG_XEN_BLKDEV_GRANT
199 prot = __pgprot(_KERNPG_TABLE);
200 error = direct_remap_area_pages(&init_mm, VMALLOC_VMADDR(vma->addr),
201 shmem_frame<<PAGE_SHIFT, PAGE_SIZE,
202 prot, domid);
203 if ( error != 0 )
204 {
205 if ( error == -ENOMEM )
206 connect->status = BLKIF_BE_STATUS_OUT_OF_MEMORY;
207 else if ( error == -EFAULT )
208 connect->status = BLKIF_BE_STATUS_MAPPING_ERROR;
209 else
210 connect->status = BLKIF_BE_STATUS_ERROR;
211 vfree(vma->addr);
212 return;
213 }
214 #else
215 { /* Map: Use the Grant table reference */
216 struct gnttab_map_grant_ref op;
217 op.host_virt_addr = VMALLOC_VMADDR(vma->addr);
218 op.flags = GNTMAP_host_map;
219 op.ref = ref;
220 op.dom = domid;
222 BUG_ON( HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1) );
224 handle = op.handle;
226 if (op.handle < 0) {
227 DPRINTK(" Grant table operation failure !\n");
228 connect->status = BLKIF_BE_STATUS_MAPPING_ERROR;
229 vfree(vma->addr);
230 return;
231 }
233 blkif->shmem_ref = ref;
234 blkif->shmem_handle = handle;
235 blkif->shmem_vaddr = VMALLOC_VMADDR(vma->addr);
236 }
237 #endif
239 if ( blkif->status != DISCONNECTED )
240 {
241 connect->status = BLKIF_BE_STATUS_INTERFACE_CONNECTED;
242 vfree(vma->addr);
243 return;
244 }
245 sring = (blkif_sring_t *)vma->addr;
246 SHARED_RING_INIT(sring);
247 BACK_RING_INIT(&blkif->blk_ring, sring, PAGE_SIZE);
249 blkif->evtchn = evtchn;
250 blkif->irq = bind_evtchn_to_irq(evtchn);
251 blkif->shmem_frame = shmem_frame;
252 blkif->status = CONNECTED;
253 blkif_get(blkif);
255 request_irq(blkif->irq, blkif_be_int, 0, "blkif-backend", blkif);
257 connect->status = BLKIF_BE_STATUS_OKAY;
258 }
260 int blkif_disconnect(blkif_be_disconnect_t *disconnect, u8 rsp_id)
261 {
262 domid_t domid = disconnect->domid;
263 unsigned int handle = disconnect->blkif_handle;
264 blkif_t *blkif;
266 blkif = blkif_find_by_handle(domid, handle);
267 if ( unlikely(blkif == NULL) )
268 {
269 DPRINTK("blkif_disconnect attempted for non-existent blkif"
270 " (%u,%u)\n", disconnect->domid, disconnect->blkif_handle);
271 disconnect->status = BLKIF_BE_STATUS_INTERFACE_NOT_FOUND;
272 return 1; /* Caller will send response error message. */
273 }
275 if ( blkif->status == CONNECTED )
276 {
277 blkif->status = DISCONNECTING;
278 blkif->disconnect_rspid = rsp_id;
279 wmb(); /* Let other CPUs see the status change. */
280 free_irq(blkif->irq, blkif);
281 blkif_deschedule(blkif);
282 blkif_put(blkif);
283 return 0; /* Caller should not send response message. */
284 }
286 disconnect->status = BLKIF_BE_STATUS_OKAY;
287 return 1;
288 }
290 void __init blkif_interface_init(void)
291 {
292 blkif_cachep = kmem_cache_create("blkif_cache", sizeof(blkif_t),
293 0, 0, NULL, NULL);
294 memset(blkif_hash, 0, sizeof(blkif_hash));
295 }