tcp: Overflow bug in Vegas
[sfrench/cifs-2.6.git] / drivers / s390 / cio / ccwgroup.c
1 /*
2  *  drivers/s390/cio/ccwgroup.c
3  *  bus driver for ccwgroup
4  *
5  *    Copyright (C) 2002 IBM Deutschland Entwicklung GmbH,
6  *                       IBM Corporation
7  *    Author(s): Arnd Bergmann (arndb@de.ibm.com)
8  *               Cornelia Huck (cornelia.huck@de.ibm.com)
9  */
10 #include <linux/module.h>
11 #include <linux/errno.h>
12 #include <linux/slab.h>
13 #include <linux/list.h>
14 #include <linux/device.h>
15 #include <linux/init.h>
16 #include <linux/ctype.h>
17 #include <linux/dcache.h>
18
19 #include <asm/semaphore.h>
20 #include <asm/ccwdev.h>
21 #include <asm/ccwgroup.h>
22
23 /* In Linux 2.4, we had a channel device layer called "chandev"
24  * that did all sorts of obscure stuff for networking devices.
25  * This is another driver that serves as a replacement for just
26  * one of its functions, namely the translation of single subchannels
27  * to devices that use multiple subchannels.
28  */
29
30 /* a device matches a driver if all its slave devices match the same
31  * entry of the driver */
32 static int
33 ccwgroup_bus_match (struct device * dev, struct device_driver * drv)
34 {
35         struct ccwgroup_device *gdev;
36         struct ccwgroup_driver *gdrv;
37
38         gdev = to_ccwgroupdev(dev);
39         gdrv = to_ccwgroupdrv(drv);
40
41         if (gdev->creator_id == gdrv->driver_id)
42                 return 1;
43
44         return 0;
45 }
46 static int
47 ccwgroup_uevent (struct device *dev, struct kobj_uevent_env *env)
48 {
49         /* TODO */
50         return 0;
51 }
52
53 static struct bus_type ccwgroup_bus_type;
54
55 static void
56 __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
57 {
58         int i;
59         char str[8];
60
61         for (i = 0; i < gdev->count; i++) {
62                 sprintf(str, "cdev%d", i);
63                 sysfs_remove_link(&gdev->dev.kobj, str);
64                 sysfs_remove_link(&gdev->cdev[i]->dev.kobj, "group_device");
65         }
66         
67 }
68
69 /*
70  * Provide an 'ungroup' attribute so the user can remove group devices no
71  * longer needed or accidentially created. Saves memory :)
72  */
73 static void ccwgroup_ungroup_callback(struct device *dev)
74 {
75         struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
76
77         mutex_lock(&gdev->reg_mutex);
78         if (device_is_registered(&gdev->dev)) {
79                 __ccwgroup_remove_symlinks(gdev);
80                 device_unregister(dev);
81         }
82         mutex_unlock(&gdev->reg_mutex);
83 }
84
85 static ssize_t
86 ccwgroup_ungroup_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
87 {
88         struct ccwgroup_device *gdev;
89         int rc;
90
91         gdev = to_ccwgroupdev(dev);
92
93         if (gdev->state != CCWGROUP_OFFLINE)
94                 return -EINVAL;
95
96         /* Note that we cannot unregister the device from one of its
97          * attribute methods, so we have to use this roundabout approach.
98          */
99         rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
100         if (rc)
101                 count = rc;
102         return count;
103 }
104
105 static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
106
107 static void
108 ccwgroup_release (struct device *dev)
109 {
110         struct ccwgroup_device *gdev;
111         int i;
112
113         gdev = to_ccwgroupdev(dev);
114
115         for (i = 0; i < gdev->count; i++) {
116                 dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
117                 put_device(&gdev->cdev[i]->dev);
118         }
119         kfree(gdev);
120 }
121
122 static int
123 __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
124 {
125         char str[8];
126         int i, rc;
127
128         for (i = 0; i < gdev->count; i++) {
129                 rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj, &gdev->dev.kobj,
130                                        "group_device");
131                 if (rc) {
132                         for (--i; i >= 0; i--)
133                                 sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
134                                                   "group_device");
135                         return rc;
136                 }
137         }
138         for (i = 0; i < gdev->count; i++) {
139                 sprintf(str, "cdev%d", i);
140                 rc = sysfs_create_link(&gdev->dev.kobj, &gdev->cdev[i]->dev.kobj,
141                                        str);
142                 if (rc) {
143                         for (--i; i >= 0; i--) {
144                                 sprintf(str, "cdev%d", i);
145                                 sysfs_remove_link(&gdev->dev.kobj, str);
146                         }
147                         for (i = 0; i < gdev->count; i++)
148                                 sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
149                                                   "group_device");
150                         return rc;
151                 }
152         }
153         return 0;
154 }
155
156 static int __get_next_bus_id(const char **buf, char *bus_id)
157 {
158         int rc, len;
159         char *start, *end;
160
161         start = (char *)*buf;
162         end = strchr(start, ',');
163         if (!end) {
164                 /* Last entry. Strip trailing newline, if applicable. */
165                 end = strchr(start, '\n');
166                 if (end)
167                         *end = '\0';
168                 len = strlen(start) + 1;
169         } else {
170                 len = end - start + 1;
171                 end++;
172         }
173         if (len < BUS_ID_SIZE) {
174                 strlcpy(bus_id, start, len);
175                 rc = 0;
176         } else
177                 rc = -EINVAL;
178         *buf = end;
179         return rc;
180 }
181
182 static int __is_valid_bus_id(char bus_id[BUS_ID_SIZE])
183 {
184         int cssid, ssid, devno;
185
186         /* Must be of form %x.%x.%04x */
187         if (sscanf(bus_id, "%x.%1x.%04x", &cssid, &ssid, &devno) != 3)
188                 return 0;
189         return 1;
190 }
191
192 /**
193  * ccwgroup_create_from_string() - create and register a ccw group device
194  * @root: parent device for the new device
195  * @creator_id: identifier of creating driver
196  * @cdrv: ccw driver of slave devices
197  * @num_devices: number of slave devices
198  * @buf: buffer containing comma separated bus ids of slave devices
199  *
200  * Create and register a new ccw group device as a child of @root. Slave
201  * devices are obtained from the list of bus ids given in @buf and must all
202  * belong to @cdrv.
203  * Returns:
204  *  %0 on success and an error code on failure.
205  * Context:
206  *  non-atomic
207  */
208 int ccwgroup_create_from_string(struct device *root, unsigned int creator_id,
209                                 struct ccw_driver *cdrv, int num_devices,
210                                 const char *buf)
211 {
212         struct ccwgroup_device *gdev;
213         int rc, i;
214         char tmp_bus_id[BUS_ID_SIZE];
215         const char *curr_buf;
216
217         gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
218                        GFP_KERNEL);
219         if (!gdev)
220                 return -ENOMEM;
221
222         atomic_set(&gdev->onoff, 0);
223         mutex_init(&gdev->reg_mutex);
224         mutex_lock(&gdev->reg_mutex);
225         curr_buf = buf;
226         for (i = 0; i < num_devices && curr_buf; i++) {
227                 rc = __get_next_bus_id(&curr_buf, tmp_bus_id);
228                 if (rc != 0)
229                         goto error;
230                 if (!__is_valid_bus_id(tmp_bus_id)) {
231                         rc = -EINVAL;
232                         goto error;
233                 }
234                 gdev->cdev[i] = get_ccwdev_by_busid(cdrv, tmp_bus_id);
235                 /*
236                  * All devices have to be of the same type in
237                  * order to be grouped.
238                  */
239                 if (!gdev->cdev[i]
240                     || gdev->cdev[i]->id.driver_info !=
241                     gdev->cdev[0]->id.driver_info) {
242                         rc = -EINVAL;
243                         goto error;
244                 }
245                 /* Don't allow a device to belong to more than one group. */
246                 if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
247                         rc = -EINVAL;
248                         goto error;
249                 }
250                 dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
251         }
252         /* Check for sufficient number of bus ids. */
253         if (i < num_devices && !curr_buf) {
254                 rc = -EINVAL;
255                 goto error;
256         }
257         /* Check for trailing stuff. */
258         if (i == num_devices && strlen(curr_buf) > 0) {
259                 rc = -EINVAL;
260                 goto error;
261         }
262         gdev->creator_id = creator_id;
263         gdev->count = num_devices;
264         gdev->dev.bus = &ccwgroup_bus_type;
265         gdev->dev.parent = root;
266         gdev->dev.release = ccwgroup_release;
267
268         snprintf (gdev->dev.bus_id, BUS_ID_SIZE, "%s",
269                         gdev->cdev[0]->dev.bus_id);
270
271         rc = device_register(&gdev->dev);
272         if (rc)
273                 goto error;
274         get_device(&gdev->dev);
275         rc = device_create_file(&gdev->dev, &dev_attr_ungroup);
276
277         if (rc) {
278                 device_unregister(&gdev->dev);
279                 goto error;
280         }
281
282         rc = __ccwgroup_create_symlinks(gdev);
283         if (!rc) {
284                 mutex_unlock(&gdev->reg_mutex);
285                 put_device(&gdev->dev);
286                 return 0;
287         }
288         device_remove_file(&gdev->dev, &dev_attr_ungroup);
289         device_unregister(&gdev->dev);
290 error:
291         for (i = 0; i < num_devices; i++)
292                 if (gdev->cdev[i]) {
293                         if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
294                                 dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
295                         put_device(&gdev->cdev[i]->dev);
296                 }
297         mutex_unlock(&gdev->reg_mutex);
298         put_device(&gdev->dev);
299         return rc;
300 }
301 EXPORT_SYMBOL(ccwgroup_create_from_string);
302
303 static int __init
304 init_ccwgroup (void)
305 {
306         return bus_register (&ccwgroup_bus_type);
307 }
308
309 static void __exit
310 cleanup_ccwgroup (void)
311 {
312         bus_unregister (&ccwgroup_bus_type);
313 }
314
315 module_init(init_ccwgroup);
316 module_exit(cleanup_ccwgroup);
317
318 /************************** driver stuff ******************************/
319
320 static int
321 ccwgroup_set_online(struct ccwgroup_device *gdev)
322 {
323         struct ccwgroup_driver *gdrv;
324         int ret;
325
326         if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
327                 return -EAGAIN;
328         if (gdev->state == CCWGROUP_ONLINE) {
329                 ret = 0;
330                 goto out;
331         }
332         if (!gdev->dev.driver) {
333                 ret = -EINVAL;
334                 goto out;
335         }
336         gdrv = to_ccwgroupdrv (gdev->dev.driver);
337         if ((ret = gdrv->set_online ? gdrv->set_online(gdev) : 0))
338                 goto out;
339
340         gdev->state = CCWGROUP_ONLINE;
341  out:
342         atomic_set(&gdev->onoff, 0);
343         return ret;
344 }
345
346 static int
347 ccwgroup_set_offline(struct ccwgroup_device *gdev)
348 {
349         struct ccwgroup_driver *gdrv;
350         int ret;
351
352         if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
353                 return -EAGAIN;
354         if (gdev->state == CCWGROUP_OFFLINE) {
355                 ret = 0;
356                 goto out;
357         }
358         if (!gdev->dev.driver) {
359                 ret = -EINVAL;
360                 goto out;
361         }
362         gdrv = to_ccwgroupdrv (gdev->dev.driver);
363         if ((ret = gdrv->set_offline ? gdrv->set_offline(gdev) : 0))
364                 goto out;
365
366         gdev->state = CCWGROUP_OFFLINE;
367  out:
368         atomic_set(&gdev->onoff, 0);
369         return ret;
370 }
371
372 static ssize_t
373 ccwgroup_online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
374 {
375         struct ccwgroup_device *gdev;
376         struct ccwgroup_driver *gdrv;
377         unsigned int value;
378         int ret;
379
380         gdev = to_ccwgroupdev(dev);
381         if (!dev->driver)
382                 return count;
383
384         gdrv = to_ccwgroupdrv (gdev->dev.driver);
385         if (!try_module_get(gdrv->owner))
386                 return -EINVAL;
387
388         value = simple_strtoul(buf, NULL, 0);
389         ret = count;
390         if (value == 1)
391                 ccwgroup_set_online(gdev);
392         else if (value == 0)
393                 ccwgroup_set_offline(gdev);
394         else
395                 ret = -EINVAL;
396         module_put(gdrv->owner);
397         return ret;
398 }
399
400 static ssize_t
401 ccwgroup_online_show (struct device *dev, struct device_attribute *attr, char *buf)
402 {
403         int online;
404
405         online = (to_ccwgroupdev(dev)->state == CCWGROUP_ONLINE);
406
407         return sprintf(buf, online ? "1\n" : "0\n");
408 }
409
410 static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
411
412 static int
413 ccwgroup_probe (struct device *dev)
414 {
415         struct ccwgroup_device *gdev;
416         struct ccwgroup_driver *gdrv;
417
418         int ret;
419
420         gdev = to_ccwgroupdev(dev);
421         gdrv = to_ccwgroupdrv(dev->driver);
422
423         if ((ret = device_create_file(dev, &dev_attr_online)))
424                 return ret;
425
426         ret = gdrv->probe ? gdrv->probe(gdev) : -ENODEV;
427         if (ret)
428                 device_remove_file(dev, &dev_attr_online);
429
430         return ret;
431 }
432
433 static int
434 ccwgroup_remove (struct device *dev)
435 {
436         struct ccwgroup_device *gdev;
437         struct ccwgroup_driver *gdrv;
438
439         gdev = to_ccwgroupdev(dev);
440         gdrv = to_ccwgroupdrv(dev->driver);
441
442         device_remove_file(dev, &dev_attr_online);
443
444         if (gdrv && gdrv->remove)
445                 gdrv->remove(gdev);
446         return 0;
447 }
448
449 static void ccwgroup_shutdown(struct device *dev)
450 {
451         struct ccwgroup_device *gdev;
452         struct ccwgroup_driver *gdrv;
453
454         gdev = to_ccwgroupdev(dev);
455         gdrv = to_ccwgroupdrv(dev->driver);
456         if (gdrv && gdrv->shutdown)
457                 gdrv->shutdown(gdev);
458 }
459
460 static struct bus_type ccwgroup_bus_type = {
461         .name   = "ccwgroup",
462         .match  = ccwgroup_bus_match,
463         .uevent = ccwgroup_uevent,
464         .probe  = ccwgroup_probe,
465         .remove = ccwgroup_remove,
466         .shutdown = ccwgroup_shutdown,
467 };
468
469 /**
470  * ccwgroup_driver_register() - register a ccw group driver
471  * @cdriver: driver to be registered
472  *
473  * This function is mainly a wrapper around driver_register().
474  */
475 int ccwgroup_driver_register(struct ccwgroup_driver *cdriver)
476 {
477         /* register our new driver with the core */
478         cdriver->driver.bus = &ccwgroup_bus_type;
479         cdriver->driver.name = cdriver->name;
480         cdriver->driver.owner = cdriver->owner;
481
482         return driver_register(&cdriver->driver);
483 }
484
485 static int
486 __ccwgroup_match_all(struct device *dev, void *data)
487 {
488         return 1;
489 }
490
491 /**
492  * ccwgroup_driver_unregister() - deregister a ccw group driver
493  * @cdriver: driver to be deregistered
494  *
495  * This function is mainly a wrapper around driver_unregister().
496  */
497 void ccwgroup_driver_unregister(struct ccwgroup_driver *cdriver)
498 {
499         struct device *dev;
500
501         /* We don't want ccwgroup devices to live longer than their driver. */
502         get_driver(&cdriver->driver);
503         while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
504                                          __ccwgroup_match_all))) {
505                 struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
506
507                 mutex_lock(&gdev->reg_mutex);
508                 __ccwgroup_remove_symlinks(gdev);
509                 device_unregister(dev);
510                 mutex_unlock(&gdev->reg_mutex);
511                 put_device(dev);
512         }
513         put_driver(&cdriver->driver);
514         driver_unregister(&cdriver->driver);
515 }
516
517 /**
518  * ccwgroup_probe_ccwdev() - probe function for slave devices
519  * @cdev: ccw device to be probed
520  *
521  * This is a dummy probe function for ccw devices that are slave devices in
522  * a ccw group device.
523  * Returns:
524  *  always %0
525  */
526 int ccwgroup_probe_ccwdev(struct ccw_device *cdev)
527 {
528         return 0;
529 }
530
531 static struct ccwgroup_device *
532 __ccwgroup_get_gdev_by_cdev(struct ccw_device *cdev)
533 {
534         struct ccwgroup_device *gdev;
535
536         gdev = dev_get_drvdata(&cdev->dev);
537         if (gdev) {
538                 if (get_device(&gdev->dev)) {
539                         mutex_lock(&gdev->reg_mutex);
540                         if (device_is_registered(&gdev->dev))
541                                 return gdev;
542                         mutex_unlock(&gdev->reg_mutex);
543                         put_device(&gdev->dev);
544                 }
545                 return NULL;
546         }
547         return NULL;
548 }
549
550 /**
551  * ccwgroup_remove_ccwdev() - remove function for slave devices
552  * @cdev: ccw device to be removed
553  *
554  * This is a remove function for ccw devices that are slave devices in a ccw
555  * group device. It sets the ccw device offline and also deregisters the
556  * embedding ccw group device.
557  */
558 void ccwgroup_remove_ccwdev(struct ccw_device *cdev)
559 {
560         struct ccwgroup_device *gdev;
561
562         /* Ignore offlining errors, device is gone anyway. */
563         ccw_device_set_offline(cdev);
564         /* If one of its devices is gone, the whole group is done for. */
565         gdev = __ccwgroup_get_gdev_by_cdev(cdev);
566         if (gdev) {
567                 __ccwgroup_remove_symlinks(gdev);
568                 device_unregister(&gdev->dev);
569                 mutex_unlock(&gdev->reg_mutex);
570                 put_device(&gdev->dev);
571         }
572 }
573
574 MODULE_LICENSE("GPL");
575 EXPORT_SYMBOL(ccwgroup_driver_register);
576 EXPORT_SYMBOL(ccwgroup_driver_unregister);
577 EXPORT_SYMBOL(ccwgroup_probe_ccwdev);
578 EXPORT_SYMBOL(ccwgroup_remove_ccwdev);