Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / scsiback / interface.c
1 /*
2  * interface management.
3  *
4  * Copyright (c) 2008, FUJITSU Limited
5  *
6  * Based on the blkback driver code.
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 <scsi/scsi.h>
34 #include <scsi/scsi_host.h>
35 #include <scsi/scsi_device.h>
36 #include "common.h"
37
38 #include <xen/evtchn.h>
39 #include <linux/kthread.h>
40 #include <linux/delay.h>
41 #include <linux/vmalloc.h>
42
43
44 static struct kmem_cache *scsiback_cachep;
45
46 struct vscsibk_info *vscsibk_info_alloc(domid_t domid)
47 {
48         struct vscsibk_info *info;
49
50         info = kmem_cache_zalloc(scsiback_cachep, GFP_KERNEL);
51         if (!info)
52                 return ERR_PTR(-ENOMEM);
53
54         info->domid = domid;
55         spin_lock_init(&info->ring_lock);
56         atomic_set(&info->nr_unreplied_reqs, 0);
57         init_waitqueue_head(&info->wq);
58         init_waitqueue_head(&info->waiting_to_free);
59
60         return info;
61 }
62
63 int scsiback_init_sring(struct vscsibk_info *info, grant_ref_t ring_ref,
64                         evtchn_port_t evtchn)
65 {
66         struct vm_struct *area;
67         struct vscsiif_sring *sring;
68         int err;
69
70         if (info->irq) {
71                 pr_err("scsiback: Already connected through?\n");
72                 return -1;
73         }
74
75         area = xenbus_map_ring_valloc(info->dev, ring_ref);
76         if (IS_ERR(area))
77                 return PTR_ERR(area);
78         info->ring_area = area;
79
80         sring = (struct vscsiif_sring *)area->addr;
81         BACK_RING_INIT(&info->ring, sring, PAGE_SIZE);
82
83         err = bind_interdomain_evtchn_to_irqhandler(
84                         info->domid, evtchn,
85                         scsiback_intr, 0, "vscsiif-backend", info);
86
87         if (err < 0)
88                 goto unmap_page;
89                 
90         info->irq = err;
91
92         return 0;
93
94 unmap_page:
95         xenbus_unmap_ring_vfree(info->dev, area);
96
97         return err;
98 }
99
100 void scsiback_disconnect(struct vscsibk_info *info)
101 {
102         if (info->kthread) {
103                 kthread_stop(info->kthread);
104                 info->kthread = NULL;
105         }
106
107         wait_event(info->waiting_to_free, 
108                 atomic_read(&info->nr_unreplied_reqs) == 0);
109
110         if (info->irq) {
111                 unbind_from_irqhandler(info->irq, info);
112                 info->irq = 0;
113         }
114
115         if (info->ring.sring) {
116                 xenbus_unmap_ring_vfree(info->dev, info->ring_area);
117                 info->ring.sring = NULL;
118         }
119 }
120
121 void scsiback_free(struct vscsibk_info *info)
122 {
123         kmem_cache_free(scsiback_cachep, info);
124 }
125
126 int __init scsiback_interface_init(void)
127 {
128         scsiback_cachep = kmem_cache_create("vscsiif_cache",
129                 sizeof(struct vscsibk_info), 0, 0, NULL);
130         if (!scsiback_cachep) {
131                 pr_err("scsiback: can't init scsi cache\n");
132                 return -ENOMEM;
133         }
134         
135         return 0;
136 }
137
138 void scsiback_interface_exit(void)
139 {
140         kmem_cache_destroy(scsiback_cachep);
141 }