Update to 3.4-final.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / blktap2 / control.c
1 #include <linux/module.h>
2 #include <linux/miscdevice.h>
3
4 #include "blktap.h"
5
6 static DEFINE_SPINLOCK(blktap_control_lock);
7 struct blktap *blktaps[CONFIG_XEN_NR_TAP2_DEVICES];
8
9 static int ring_major;
10 static int device_major;
11 static int blktap_control_registered;
12
13 static void
14 blktap_control_initialize_tap(struct blktap *tap)
15 {
16         int minor = tap->minor;
17
18         memset(tap, 0, sizeof(*tap));
19         set_bit(BLKTAP_CONTROL, &tap->dev_inuse);
20         init_rwsem(&tap->tap_sem);
21         sg_init_table(tap->sg, BLKIF_MAX_SEGMENTS_PER_REQUEST);
22         init_waitqueue_head(&tap->wq);
23         atomic_set(&tap->refcnt, 0);
24
25         tap->minor = minor;
26 }
27
28 static struct blktap *
29 blktap_control_create_tap(void)
30 {
31         int minor;
32         struct blktap *tap;
33
34         tap = kmalloc(sizeof(*tap), GFP_KERNEL);
35         if (unlikely(!tap))
36                 return NULL;
37
38         blktap_control_initialize_tap(tap);
39
40         spin_lock_irq(&blktap_control_lock);
41         for (minor = 0; minor < CONFIG_XEN_NR_TAP2_DEVICES; minor++)
42                 if (!blktaps[minor])
43                         break;
44
45         if (minor == CONFIG_XEN_NR_TAP2_DEVICES) {
46                 kfree(tap);
47                 tap = NULL;
48                 goto out;
49         }
50
51         tap->minor = minor;
52         blktaps[minor] = tap;
53
54 out:
55         spin_unlock_irq(&blktap_control_lock);
56         return tap;
57 }
58
59 static struct blktap *
60 blktap_control_allocate_tap(void)
61 {
62         int err, minor;
63         struct blktap *tap;
64
65         /*
66          * This is called only from the ioctl, which
67          * means we should always have interrupts enabled.
68          */
69         BUG_ON(irqs_disabled());
70
71         spin_lock_irq(&blktap_control_lock);
72
73         for (minor = 0; minor < CONFIG_XEN_NR_TAP2_DEVICES; minor++) {
74                 tap = blktaps[minor];
75                 if (!tap)
76                         goto found;
77
78                 if (!tap->dev_inuse) {
79                         blktap_control_initialize_tap(tap);
80                         goto found;
81                 }
82         }
83
84         tap = NULL;
85
86 found:
87         spin_unlock_irq(&blktap_control_lock);
88
89         if (!tap) {
90                 tap = blktap_control_create_tap();
91                 if (!tap)
92                         return NULL;
93         }
94
95         err = blktap_ring_create(tap);
96         if (err) {
97                 BTERR("ring creation failed: %d\n", err);
98                 clear_bit(BLKTAP_CONTROL, &tap->dev_inuse);
99                 return NULL;
100         }
101
102         BTINFO("allocated tap %p\n", tap);
103         return tap;
104 }
105
106 static long
107 blktap_control_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
108 {
109         unsigned long dev;
110         struct blktap *tap;
111
112         switch (cmd) {
113         case BLKTAP2_IOCTL_ALLOC_TAP: {
114                 struct blktap_handle h;
115
116                 tap = blktap_control_allocate_tap();
117                 if (!tap) {
118                         BTERR("error allocating device\n");
119                         return -ENOMEM;
120                 }
121
122                 h.ring   = ring_major;
123                 h.device = device_major;
124                 h.minor  = tap->minor;
125
126                 if (copy_to_user((struct blktap_handle __user *)arg,
127                                  &h, sizeof(h))) {
128                         blktap_control_destroy_device(tap);
129                         return -EFAULT;
130                 }
131
132                 return 0;
133         }
134
135         case BLKTAP2_IOCTL_FREE_TAP:
136                 dev = arg;
137
138                 if (dev >= CONFIG_XEN_NR_TAP2_DEVICES || !blktaps[dev])
139                         return -EINVAL;
140
141                 blktap_control_destroy_device(blktaps[dev]);
142                 return 0;
143         }
144
145         return -ENOIOCTLCMD;
146 }
147
148 static const struct file_operations blktap_control_file_operations = {
149         .owner    = THIS_MODULE,
150         .unlocked_ioctl = blktap_control_ioctl,
151 };
152
153 static struct miscdevice blktap_misc = {
154         .minor    = MISC_DYNAMIC_MINOR,
155         .name     = "blktap-control",
156         .nodename = BLKTAP2_DEV_DIR "control",
157         .fops     = &blktap_control_file_operations,
158 };
159
160 int
161 blktap_control_destroy_device(struct blktap *tap)
162 {
163         int err;
164         unsigned long inuse;
165
166         if (!tap)
167                 return 0;
168
169         set_bit(BLKTAP_SHUTDOWN_REQUESTED, &tap->dev_inuse);
170
171         for (;;) {
172                 inuse = tap->dev_inuse;
173                 err   = blktap_device_destroy(tap);
174                 if (err)
175                         goto wait;
176
177                 inuse = tap->dev_inuse;
178                 err   = blktap_ring_destroy(tap);
179                 if (err)
180                         goto wait;
181
182                 inuse = tap->dev_inuse;
183                 err   = blktap_sysfs_destroy(tap);
184                 if (err)
185                         goto wait;
186
187                 break;
188
189         wait:
190                 BTDBG("inuse: 0x%lx, dev_inuse: 0x%lx\n",
191                       inuse, tap->dev_inuse);
192                 if (wait_event_interruptible(tap->wq, tap->dev_inuse != inuse))
193                         break;
194         }
195
196         clear_bit(BLKTAP_SHUTDOWN_REQUESTED, &tap->dev_inuse);
197
198         if (blktap_control_finish_destroy(tap))
199                 err = 0;
200
201         return err;
202 }
203
204 int
205 blktap_control_finish_destroy(struct blktap *tap)
206 {
207         if (tap->dev_inuse == (1UL << BLKTAP_CONTROL))
208                 clear_bit(BLKTAP_CONTROL, &tap->dev_inuse);
209         return !tap->dev_inuse;
210 }
211
212 static int __init
213 blktap_control_init(void)
214 {
215         int err;
216
217         err = misc_register(&blktap_misc);
218         if (err) {
219                 BTERR("misc_register failed for control device");
220                 return err;
221         }
222
223         blktap_control_registered = 1;
224         return 0;
225 }
226
227 static void
228 blktap_control_free(void)
229 {
230         int i;
231
232         for (i = 0; i < CONFIG_XEN_NR_TAP2_DEVICES; i++)
233                 blktap_control_destroy_device(blktaps[i]);
234
235         if (blktap_control_registered)
236                 if (misc_deregister(&blktap_misc) < 0)
237                         BTERR("misc_deregister failed for control device");
238 }
239
240 static void
241 blktap_exit(void)
242 {
243         blktap_control_free();
244         blktap_ring_free();
245         blktap_sysfs_free();
246         blktap_device_free();
247         blktap_request_pool_free();
248 }
249
250 static int __init
251 blktap_init(void)
252 {
253         int err;
254
255         err = blktap_request_pool_init();
256         if (err)
257                 return err;
258
259         err = blktap_device_init(&device_major);
260         if (err)
261                 goto fail;
262
263         err = blktap_ring_init(&ring_major);
264         if (err)
265                 goto fail;
266
267         err = blktap_sysfs_init();
268         if (err)
269                 goto fail;
270
271         err = blktap_control_init();
272         if (err)
273                 goto fail;
274
275         return 0;
276
277 fail:
278         blktap_exit();
279         return err;
280 }
281
282 module_init(blktap_init);
283 module_exit(blktap_exit);
284 MODULE_LICENSE("Dual BSD/GPL");
285 MODULE_ALIAS("devname:" BLKTAP2_DEV_DIR "control");