1 /******************************************************************************
2 * arch/xen/drivers/blkif/backend/interface.c
4 * Block-device interface management.
6 * Copyright (c) 2004, Keir Fraser
11 #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
12 #define VMALLOC_VMADDR(x) ((unsigned long)(x))
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)
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;
30 static void __blkif_disconnect_complete(void *arg)
32 blkif_t *blkif = (blkif_t *)arg;
34 blkif_be_disconnect_t disc;
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.
41 unbind_evtchn_from_irq(blkif->evtchn);
42 vfree(blkif->blk_ring.sring);
44 /* Construct the deferred response message. */
45 cmsg.type = CMSG_BLKIF_BE;
46 cmsg.subtype = CMSG_BLKIF_BE_DISCONNECT;
47 cmsg.id = blkif->disconnect_rspid;
48 cmsg.length = sizeof(blkif_be_disconnect_t);
49 disc.domid = blkif->domid;
50 disc.blkif_handle = blkif->handle;
51 disc.status = BLKIF_BE_STATUS_OKAY;
52 memcpy(cmsg.msg, &disc, sizeof(disc));
55 * Make sure message is constructed /before/ status change, because
56 * after the status change the 'blkif' structure could be deallocated at
57 * any time. Also make sure we send the response /after/ status change,
58 * as otherwise a subsequent CONNECT request could spuriously fail if
59 * another CPU doesn't see the status change yet.
62 if ( blkif->status != DISCONNECTING )
64 blkif->status = DISCONNECTED;
67 /* Send the successful response. */
68 ctrl_if_send_response(&cmsg);
71 void blkif_disconnect_complete(blkif_t *blkif)
73 INIT_WORK(&blkif->work, __blkif_disconnect_complete, (void *)blkif);
74 schedule_work(&blkif->work);
77 void blkif_create(blkif_be_create_t *create)
79 domid_t domid = create->domid;
80 unsigned int handle = create->blkif_handle;
81 blkif_t **pblkif, *blkif;
83 if ( (blkif = kmem_cache_alloc(blkif_cachep, GFP_KERNEL)) == NULL )
85 DPRINTK("Could not create blkif: out of memory\n");
86 create->status = BLKIF_BE_STATUS_OUT_OF_MEMORY;
90 memset(blkif, 0, sizeof(*blkif));
92 blkif->handle = handle;
93 blkif->status = DISCONNECTED;
94 spin_lock_init(&blkif->vbd_lock);
95 spin_lock_init(&blkif->blk_ring_lock);
96 atomic_set(&blkif->refcnt, 0);
98 pblkif = &blkif_hash[BLKIF_HASH(domid, handle)];
99 while ( *pblkif != NULL )
101 if ( ((*pblkif)->domid == domid) && ((*pblkif)->handle == handle) )
103 DPRINTK("Could not create blkif: already exists\n");
104 create->status = BLKIF_BE_STATUS_INTERFACE_EXISTS;
105 kmem_cache_free(blkif_cachep, blkif);
108 pblkif = &(*pblkif)->hash_next;
111 blkif->hash_next = *pblkif;
114 DPRINTK("Successfully created blkif\n");
115 create->status = BLKIF_BE_STATUS_OKAY;
118 void blkif_destroy(blkif_be_destroy_t *destroy)
120 domid_t domid = destroy->domid;
121 unsigned int handle = destroy->blkif_handle;
122 blkif_t **pblkif, *blkif;
124 pblkif = &blkif_hash[BLKIF_HASH(domid, handle)];
125 while ( (blkif = *pblkif) != NULL )
127 if ( (blkif->domid == domid) && (blkif->handle == handle) )
129 if ( blkif->status != DISCONNECTED )
130 goto still_connected;
133 pblkif = &blkif->hash_next;
136 destroy->status = BLKIF_BE_STATUS_INTERFACE_NOT_FOUND;
140 destroy->status = BLKIF_BE_STATUS_INTERFACE_CONNECTED;
144 *pblkif = blkif->hash_next;
145 destroy_all_vbds(blkif);
146 kmem_cache_free(blkif_cachep, blkif);
147 destroy->status = BLKIF_BE_STATUS_OKAY;
150 void blkif_connect(blkif_be_connect_t *connect)
152 domid_t domid = connect->domid;
153 unsigned int handle = connect->blkif_handle;
154 unsigned int evtchn = connect->evtchn;
155 unsigned long shmem_frame = connect->shmem_frame;
156 struct vm_struct *vma;
160 blkif_sring_t *sring;
162 blkif = blkif_find_by_handle(domid, handle);
163 if ( unlikely(blkif == NULL) )
165 DPRINTK("blkif_connect attempted for non-existent blkif (%u,%u)\n",
166 connect->domid, connect->blkif_handle);
167 connect->status = BLKIF_BE_STATUS_INTERFACE_NOT_FOUND;
171 if ( (vma = get_vm_area(PAGE_SIZE, VM_IOREMAP)) == NULL )
173 connect->status = BLKIF_BE_STATUS_OUT_OF_MEMORY;
177 prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY | _PAGE_ACCESSED);
178 error = direct_remap_area_pages(&init_mm, VMALLOC_VMADDR(vma->addr),
179 shmem_frame<<PAGE_SHIFT, PAGE_SIZE,
183 if ( error == -ENOMEM )
184 connect->status = BLKIF_BE_STATUS_OUT_OF_MEMORY;
185 else if ( error == -EFAULT )
186 connect->status = BLKIF_BE_STATUS_MAPPING_ERROR;
188 connect->status = BLKIF_BE_STATUS_ERROR;
193 if ( blkif->status != DISCONNECTED )
195 connect->status = BLKIF_BE_STATUS_INTERFACE_CONNECTED;
199 sring = (blkif_sring_t *)vma->addr;
200 SHARED_RING_INIT(sring);
201 BACK_RING_INIT(&blkif->blk_ring, sring, PAGE_SIZE);
203 blkif->evtchn = evtchn;
204 blkif->irq = bind_evtchn_to_irq(evtchn);
205 blkif->shmem_frame = shmem_frame;
206 blkif->status = CONNECTED;
209 request_irq(blkif->irq, blkif_be_int, 0, "blkif-backend", blkif);
211 connect->status = BLKIF_BE_STATUS_OKAY;
214 int blkif_disconnect(blkif_be_disconnect_t *disconnect, u8 rsp_id)
216 domid_t domid = disconnect->domid;
217 unsigned int handle = disconnect->blkif_handle;
220 blkif = blkif_find_by_handle(domid, handle);
221 if ( unlikely(blkif == NULL) )
223 DPRINTK("blkif_disconnect attempted for non-existent blkif"
224 " (%u,%u)\n", disconnect->domid, disconnect->blkif_handle);
225 disconnect->status = BLKIF_BE_STATUS_INTERFACE_NOT_FOUND;
226 return 1; /* Caller will send response error message. */
229 if ( blkif->status == CONNECTED )
231 blkif->status = DISCONNECTING;
232 blkif->disconnect_rspid = rsp_id;
233 wmb(); /* Let other CPUs see the status change. */
234 free_irq(blkif->irq, blkif);
235 blkif_deschedule(blkif);
237 return 0; /* Caller should not send response message. */
240 disconnect->status = BLKIF_BE_STATUS_OKAY;
244 void __init blkif_interface_init(void)
246 blkif_cachep = kmem_cache_create("blkif_cache", sizeof(blkif_t),
248 memset(blkif_hash, 0, sizeof(blkif_hash));