2 * interface management.
4 * Copyright (c) 2008, FUJITSU Limited
6 * Based on the blkback driver code.
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:
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:
21 * The above copyright notice and this permission notice shall be included in
22 * all copies or substantial portions of the Software.
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
33 #include <scsi/scsi.h>
34 #include <scsi/scsi_host.h>
35 #include <scsi/scsi_device.h>
38 #include <xen/evtchn.h>
39 #include <linux/kthread.h>
40 #include <linux/delay.h>
43 static struct kmem_cache *scsiback_cachep;
45 struct vscsibk_info *vscsibk_info_alloc(domid_t domid)
47 struct vscsibk_info *info;
49 info = kmem_cache_alloc(scsiback_cachep, GFP_KERNEL|__GFP_ZERO);
51 return ERR_PTR(-ENOMEM);
54 spin_lock_init(&info->ring_lock);
55 atomic_set(&info->nr_unreplied_reqs, 0);
56 init_waitqueue_head(&info->wq);
57 init_waitqueue_head(&info->waiting_to_free);
62 static int map_frontend_page( struct vscsibk_info *info,
63 unsigned long ring_ref)
65 struct gnttab_map_grant_ref op;
68 gnttab_set_map_op(&op, (unsigned long)info->ring_area->addr,
69 GNTMAP_host_map, ring_ref,
73 err = HYPERVISOR_grant_table_op(GNTTABOP_map_grant_ref, &op, 1);
76 } while(op.status == GNTST_eagain);
79 printk(KERN_ERR "scsiback: Grant table operation failure !\n");
83 info->shmem_ref = ring_ref;
84 info->shmem_handle = op.handle;
89 static void unmap_frontend_page(struct vscsibk_info *info)
91 struct gnttab_unmap_grant_ref op;
94 gnttab_set_unmap_op(&op, (unsigned long)info->ring_area->addr,
95 GNTMAP_host_map, info->shmem_handle);
97 err = HYPERVISOR_grant_table_op(GNTTABOP_unmap_grant_ref, &op, 1);
102 int scsiback_init_sring(struct vscsibk_info *info,
103 unsigned long ring_ref, unsigned int evtchn)
105 struct vscsiif_sring *sring;
109 printk(KERN_ERR "scsiback: Already connected through?\n");
113 info->ring_area = alloc_vm_area(PAGE_SIZE);
117 err = map_frontend_page(info, ring_ref);
121 sring = (struct vscsiif_sring *) info->ring_area->addr;
122 BACK_RING_INIT(&info->ring, sring, PAGE_SIZE);
124 err = bind_interdomain_evtchn_to_irqhandler(
126 scsiback_intr, 0, "vscsiif-backend", info);
136 unmap_frontend_page(info);
138 free_vm_area(info->ring_area);
143 void scsiback_disconnect(struct vscsibk_info *info)
146 kthread_stop(info->kthread);
147 info->kthread = NULL;
150 wait_event(info->waiting_to_free,
151 atomic_read(&info->nr_unreplied_reqs) == 0);
154 unbind_from_irqhandler(info->irq, info);
158 if (info->ring.sring) {
159 unmap_frontend_page(info);
160 free_vm_area(info->ring_area);
161 info->ring.sring = NULL;
165 void scsiback_free(struct vscsibk_info *info)
167 kmem_cache_free(scsiback_cachep, info);
170 int __init scsiback_interface_init(void)
172 scsiback_cachep = kmem_cache_create("vscsiif_cache",
173 sizeof(struct vscsibk_info), 0, 0, NULL);
174 if (!scsiback_cachep) {
175 printk(KERN_ERR "scsiback: can't init scsi cache\n");
182 void scsiback_interface_exit(void)
184 kmem_cache_destroy(scsiback_cachep);