- Update Xen patches to 3.3-rc5 and c/s 1157.
[linux-flexiantxendom0-3.2.10.git] / drivers / xen / blktap2 / sysfs.c
1 #include <linux/types.h>
2 #include <linux/device.h>
3 #include <linux/module.h>
4
5 #include "blktap.h"
6
7 int blktap_debug_level = 1;
8
9 static struct class *class;
10 static DECLARE_WAIT_QUEUE_HEAD(sysfs_wq);
11
12 static inline void
13 blktap_sysfs_get(struct blktap *tap)
14 {
15         atomic_inc(&tap->ring.sysfs_refcnt);
16 }
17
18 static inline void
19 blktap_sysfs_put(struct blktap *tap)
20 {
21         if (atomic_dec_and_test(&tap->ring.sysfs_refcnt))
22                 wake_up(&sysfs_wq);
23 }
24
25 static inline void
26 blktap_sysfs_enter(struct blktap *tap)
27 {
28         blktap_sysfs_get(tap);               /* pin sysfs device */
29         mutex_lock(&tap->ring.sysfs_mutex);  /* serialize sysfs operations */
30 }
31
32 static inline void
33 blktap_sysfs_exit(struct blktap *tap)
34 {
35         mutex_unlock(&tap->ring.sysfs_mutex);
36         blktap_sysfs_put(tap);
37 }
38
39 static ssize_t blktap_sysfs_pause_device(struct device *,
40                                          struct device_attribute *,
41                                          const char *, size_t);
42 static DEVICE_ATTR(pause, S_IWUSR, NULL, blktap_sysfs_pause_device);
43 static ssize_t blktap_sysfs_resume_device(struct device *,
44                                           struct device_attribute *,
45                                           const char *, size_t);
46 static DEVICE_ATTR(resume, S_IWUSR, NULL, blktap_sysfs_resume_device);
47
48 static ssize_t
49 blktap_sysfs_set_name(struct device *dev, struct device_attribute *attr,
50                       const char *buf, size_t size)
51 {
52         int err;
53         struct blktap *tap = dev_get_drvdata(dev);
54
55         blktap_sysfs_enter(tap);
56
57         if (!tap->ring.dev ||
58             test_bit(BLKTAP_SHUTDOWN_REQUESTED, &tap->dev_inuse)) {
59                 err = -ENODEV;
60                 goto out;
61         }
62
63         if (!test_bit(BLKTAP_PAUSED, &tap->dev_inuse)) {
64                 err = -EPERM;
65                 goto out;
66         }
67
68         if (size > BLKTAP2_MAX_MESSAGE_LEN) {
69                 err = -ENAMETOOLONG;
70                 goto out;
71         }
72
73         if (strnlen(buf, size) >= size) {
74                 err = -EINVAL;
75                 goto out;
76         }
77
78         strlcpy(tap->params.name, buf, size);
79         err = size;
80
81 out:
82         blktap_sysfs_exit(tap); 
83         return err;
84 }
85
86 static ssize_t
87 blktap_sysfs_get_name(struct device *dev, struct device_attribute *attr,
88                       char *buf)
89 {
90         ssize_t size;
91         struct blktap *tap = dev_get_drvdata(dev);
92
93         blktap_sysfs_enter(tap);
94
95         if (!tap->ring.dev)
96                 size = -ENODEV;
97         else if (tap->params.name[0])
98                 size = sprintf(buf, "%s\n", tap->params.name);
99         else
100                 size = sprintf(buf, "%d\n", tap->minor);
101
102         blktap_sysfs_exit(tap);
103
104         return size;
105 }
106 static DEVICE_ATTR(name, S_IRUSR | S_IWUSR,
107                    blktap_sysfs_get_name, blktap_sysfs_set_name);
108
109 static ssize_t
110 blktap_sysfs_remove_device(struct device *dev, struct device_attribute *attr,
111                            const char *buf, size_t size)
112 {
113         int err;
114         struct blktap *tap = dev_get_drvdata(dev);
115
116         if (!tap->ring.dev)
117                 return size;
118
119         if (test_and_set_bit(BLKTAP_SHUTDOWN_REQUESTED, &tap->dev_inuse))
120                 return -EBUSY;
121
122         err = blktap_control_destroy_device(tap);
123
124         return (err ? : size);
125 }
126 static DEVICE_ATTR(remove, S_IWUSR, NULL, blktap_sysfs_remove_device);
127
128 static ssize_t
129 blktap_sysfs_pause_device(struct device *dev, struct device_attribute *attr,
130                           const char *buf, size_t size)
131 {
132         int err;
133         struct blktap *tap = dev_get_drvdata(dev);
134
135         blktap_sysfs_enter(tap);
136
137         BTDBG("pausing %u:%u: dev_inuse: %lu\n",
138               MAJOR(tap->ring.devno), MINOR(tap->ring.devno), tap->dev_inuse);
139
140         if (!tap->ring.dev ||
141             test_bit(BLKTAP_SHUTDOWN_REQUESTED, &tap->dev_inuse)) {
142                 err = -ENODEV;
143                 goto out;
144         }
145
146         if (test_bit(BLKTAP_PAUSE_REQUESTED, &tap->dev_inuse)) {
147                 err = -EBUSY;
148                 goto out;
149         }
150
151         if (test_bit(BLKTAP_PAUSED, &tap->dev_inuse)) {
152                 err = 0;
153                 goto out;
154         }
155
156         err = blktap_device_pause(tap);
157         if (!err) {
158                 device_remove_file(dev, &dev_attr_pause);
159                 err = device_create_file(dev, &dev_attr_resume);
160         }
161
162 out:
163         blktap_sysfs_exit(tap);
164
165         return (err ? err : size);
166 }
167
168 static ssize_t
169 blktap_sysfs_resume_device(struct device *dev, struct device_attribute *attr,
170                            const char *buf, size_t size)
171 {
172         int err;
173         struct blktap *tap = dev_get_drvdata(dev);
174
175         blktap_sysfs_enter(tap);
176
177         if (!tap->ring.dev ||
178             test_bit(BLKTAP_SHUTDOWN_REQUESTED, &tap->dev_inuse)) {
179                 err = -ENODEV;
180                 goto out;
181         }
182
183         if (!test_bit(BLKTAP_PAUSED, &tap->dev_inuse)) {
184                 err = -EINVAL;
185                 goto out;
186         }
187
188         err = blktap_device_resume(tap);
189         if (!err) {
190                 device_remove_file(dev, &dev_attr_resume);
191                 err = device_create_file(dev, &dev_attr_pause);
192         }
193
194 out:
195         blktap_sysfs_exit(tap);
196
197         BTDBG("returning %zd\n", (err ? err : size));
198         return (err ? err : size);
199 }
200
201 #ifdef ENABLE_PASSTHROUGH
202 static ssize_t
203 blktap_sysfs_enable_passthrough(struct device *dev,
204                                 struct device_attribute *attr,
205                                 const char *buf, size_t size)
206 {
207         int err;
208         unsigned major, minor;
209         struct blktap *tap = dev_get_drvdata(dev);
210
211         BTINFO("passthrough request enabled\n");
212
213         blktap_sysfs_enter(tap);
214
215         if (!tap->ring.dev ||
216             test_bit(BLKTAP_SHUTDOWN_REQUESTED, &tap->dev_inuse)) {
217                 err = -ENODEV;
218                 goto out;
219         }
220
221         if (!test_bit(BLKTAP_PAUSED, &tap->dev_inuse)) {
222                 err = -EINVAL;
223                 goto out;
224         }
225
226         if (test_bit(BLKTAP_PASSTHROUGH, &tap->dev_inuse)) {
227                 err = -EINVAL;
228                 goto out;
229         }
230
231         err = sscanf(buf, "%x:%x", &major, &minor);
232         if (err != 2) {
233                 err = -EINVAL;
234                 goto out;
235         }
236
237         err = blktap_device_enable_passthrough(tap, major, minor);
238
239 out:
240         blktap_sysfs_exit(tap);
241         BTDBG("returning %d\n", (err ? err : size));
242         return (err ? err : size);
243 }
244 #endif
245
246 static ssize_t
247 blktap_sysfs_debug_device(struct device *dev, struct device_attribute *attr,
248                           char *buf)
249 {
250         char *tmp;
251         int i, ret;
252         struct blktap *tap = dev_get_drvdata(dev);
253
254         tmp = buf;
255         blktap_sysfs_get(tap);
256
257         if (!tap->ring.dev) {
258                 ret = sprintf(tmp, "no device\n");
259                 goto out;
260         }
261
262         tmp += sprintf(tmp, "%s (%u:%u), refcnt: %d, dev_inuse: 0x%08lx\n",
263                        tap->params.name, MAJOR(tap->ring.devno),
264                        MINOR(tap->ring.devno), atomic_read(&tap->refcnt),
265                        tap->dev_inuse);
266         tmp += sprintf(tmp, "capacity: 0x%llx, sector size: 0x%lx, "
267                        "device users: %d\n", tap->params.capacity,
268                        tap->params.sector_size, tap->device.users);
269
270         down_read(&tap->tap_sem);
271
272         tmp += sprintf(tmp, "pending requests: %d\n", tap->pending_cnt);
273         for (i = 0; i < MAX_PENDING_REQS; i++) {
274                 struct blktap_request *req = tap->pending_requests[i];
275                 if (!req)
276                         continue;
277
278                 tmp += sprintf(tmp, "req %d: id: %llu, usr_idx: %d, "
279                                "status: 0x%02x, pendcnt: %d, "
280                                "nr_pages: %u, op: %d, time: %lu:%lu\n",
281                                i, (unsigned long long)req->id, req->usr_idx,
282                                req->status, atomic_read(&req->pendcnt),
283                                req->nr_pages, req->operation, req->time.tv_sec,
284                                req->time.tv_usec);
285         }
286
287         up_read(&tap->tap_sem);
288         ret = (tmp - buf) + 1;
289
290 out:
291         blktap_sysfs_put(tap);
292         BTDBG("%s\n", buf);
293
294         return ret;
295 }
296 static DEVICE_ATTR(debug, S_IRUSR, blktap_sysfs_debug_device, NULL);
297
298 int
299 blktap_sysfs_create(struct blktap *tap)
300 {
301         struct blktap_ring *ring;
302         struct device *dev;
303         int err, state = 0;
304
305         if (!class)
306                 return -ENODEV;
307
308         ring = &tap->ring;
309
310         dev = device_create(class, NULL, ring->devno, tap,
311                             "blktap%d", tap->minor);
312         if (IS_ERR(dev))
313                 return PTR_ERR(dev);
314
315         ring->dev = dev;
316
317         mutex_init(&ring->sysfs_mutex);
318         atomic_set(&ring->sysfs_refcnt, 0);
319         set_bit(BLKTAP_SYSFS, &tap->dev_inuse);
320
321         err = device_create_file(dev, &dev_attr_name);
322         if (!err) {
323                 ++state;
324                 err = device_create_file(dev, &dev_attr_remove);
325         }
326         if (!err) {
327                 ++state;
328                 err = device_create_file(dev, &dev_attr_pause);
329         }
330         if (!err) {
331                 ++state;
332                 err = device_create_file(dev, &dev_attr_debug);
333         }
334
335         switch (state * !!err) {
336         case 3: device_remove_file(dev, &dev_attr_pause);
337         case 2: device_remove_file(dev, &dev_attr_remove);
338         case 1: device_remove_file(dev, &dev_attr_name);
339         }
340
341         return err;
342 }
343
344 static void
345 _blktap_sysfs_destroy(struct device *dev)
346 {
347         struct blktap *tap = dev_get_drvdata(dev);
348
349         device_remove_file(dev, &dev_attr_name);
350         device_remove_file(dev, &dev_attr_remove);
351         device_remove_file(dev, &dev_attr_pause);
352         device_remove_file(dev, &dev_attr_resume);
353         device_remove_file(dev, &dev_attr_debug);
354
355         device_unregister(dev);
356
357         clear_bit(BLKTAP_SYSFS, &tap->dev_inuse);
358
359         blktap_control_finish_destroy(tap);
360 }
361
362 int
363 blktap_sysfs_destroy(struct blktap *tap)
364 {
365         struct blktap_ring *ring;
366         struct device *dev;
367
368         ring = &tap->ring;
369         dev  = ring->dev;
370         if (!class || !dev)
371                 return 0;
372
373         ring->dev = NULL;
374         if (wait_event_interruptible(sysfs_wq,
375                                      !atomic_read(&tap->ring.sysfs_refcnt)))
376                 return -EAGAIN;
377
378         return device_schedule_callback(dev, _blktap_sysfs_destroy);
379 }
380
381 static ssize_t
382 blktap_sysfs_show_verbosity(struct class *class, struct class_attribute *attr,
383                             char *buf)
384 {
385         return sprintf(buf, "%d\n", blktap_debug_level);
386 }
387
388 static ssize_t
389 blktap_sysfs_set_verbosity(struct class *class, struct class_attribute *attr,
390                            const char *buf, size_t size)
391 {
392         int level;
393
394         if (sscanf(buf, "%d", &level) == 1) {
395                 blktap_debug_level = level;
396                 return size;
397         }
398
399         return -EINVAL;
400 }
401 static CLASS_ATTR(verbosity, S_IRUSR | S_IWUSR,
402                   blktap_sysfs_show_verbosity, blktap_sysfs_set_verbosity);
403
404 static ssize_t
405 blktap_sysfs_show_devices(struct class *class, struct class_attribute *attr,
406                           char *buf)
407 {
408         int i, ret;
409         struct blktap *tap;
410
411         ret = 0;
412         for (i = 0; i < CONFIG_XEN_NR_TAP2_DEVICES; i++) {
413                 tap = blktaps[i];
414                 if (!tap)
415                         continue;
416
417                 if (!test_bit(BLKTAP_DEVICE, &tap->dev_inuse))
418                         continue;
419
420                 ret += sprintf(buf + ret, "%d ", tap->minor);
421                 ret += snprintf(buf + ret, sizeof(tap->params.name) - 1,
422                                 tap->params.name);
423                 ret += sprintf(buf + ret, "\n");
424         }
425
426         return ret;
427 }
428 static CLASS_ATTR(devices, S_IRUSR, blktap_sysfs_show_devices, NULL);
429
430 void
431 blktap_sysfs_free(void)
432 {
433         if (!class)
434                 return;
435
436         class_remove_file(class, &class_attr_verbosity);
437         class_remove_file(class, &class_attr_devices);
438
439         class_destroy(class);
440 }
441
442 static char *blktap_devnode(struct device *dev, umode_t *mode)
443 {
444         return kasprintf(GFP_KERNEL, BLKTAP2_DEV_DIR "blktap%u",
445                          MINOR(dev->devt));
446 }
447
448 int __init
449 blktap_sysfs_init(void)
450 {
451         struct class *cls;
452         int err;
453
454         if (class)
455                 return -EEXIST;
456
457         cls = class_create(THIS_MODULE, "blktap2");
458         if (IS_ERR(cls))
459                 return PTR_ERR(cls);
460
461         cls->devnode = blktap_devnode;
462
463         err = class_create_file(cls, &class_attr_verbosity);
464         if (!err) {
465                 err = class_create_file(cls, &class_attr_devices);
466                 if (err)
467                         class_remove_file(cls, &class_attr_verbosity);
468         }
469         if (!err)
470                 class = cls;
471         else
472                 class_destroy(cls);
473
474         return err;
475 }