Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ccwgroup.c
Go to the documentation of this file.
1 /*
2  * bus driver for ccwgroup
3  *
4  * Copyright IBM Corp. 2002, 2012
5  *
6  * Author(s): Arnd Bergmann ([email protected])
7  * Cornelia Huck ([email protected])
8  */
9 #include <linux/module.h>
10 #include <linux/errno.h>
11 #include <linux/slab.h>
12 #include <linux/list.h>
13 #include <linux/device.h>
14 #include <linux/init.h>
15 #include <linux/ctype.h>
16 #include <linux/dcache.h>
17 
18 #include <asm/cio.h>
19 #include <asm/ccwdev.h>
20 #include <asm/ccwgroup.h>
21 
22 #include "device.h"
23 
24 #define CCW_BUS_ID_SIZE 10
25 
26 /* In Linux 2.4, we had a channel device layer called "chandev"
27  * that did all sorts of obscure stuff for networking devices.
28  * This is another driver that serves as a replacement for just
29  * one of its functions, namely the translation of single subchannels
30  * to devices that use multiple subchannels.
31  */
32 
33 static struct bus_type ccwgroup_bus_type;
34 
35 static void __ccwgroup_remove_symlinks(struct ccwgroup_device *gdev)
36 {
37  int i;
38  char str[8];
39 
40  for (i = 0; i < gdev->count; i++) {
41  sprintf(str, "cdev%d", i);
42  sysfs_remove_link(&gdev->dev.kobj, str);
43  sysfs_remove_link(&gdev->cdev[i]->dev.kobj, "group_device");
44  }
45 }
46 
47 /*
48  * Remove references from ccw devices to ccw group device and from
49  * ccw group device to ccw devices.
50  */
51 static void __ccwgroup_remove_cdev_refs(struct ccwgroup_device *gdev)
52 {
53  struct ccw_device *cdev;
54  int i;
55 
56  for (i = 0; i < gdev->count; i++) {
57  cdev = gdev->cdev[i];
58  if (!cdev)
59  continue;
60  spin_lock_irq(cdev->ccwlock);
61  dev_set_drvdata(&cdev->dev, NULL);
62  spin_unlock_irq(cdev->ccwlock);
63  gdev->cdev[i] = NULL;
64  put_device(&cdev->dev);
65  }
66 }
67 
68 static int ccwgroup_set_online(struct ccwgroup_device *gdev)
69 {
70  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
71  int ret = 0;
72 
73  if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
74  return -EAGAIN;
75  if (gdev->state == CCWGROUP_ONLINE)
76  goto out;
77  if (gdrv->set_online)
78  ret = gdrv->set_online(gdev);
79  if (ret)
80  goto out;
81 
82  gdev->state = CCWGROUP_ONLINE;
83 out:
84  atomic_set(&gdev->onoff, 0);
85  return ret;
86 }
87 
88 static int ccwgroup_set_offline(struct ccwgroup_device *gdev)
89 {
90  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
91  int ret = 0;
92 
93  if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
94  return -EAGAIN;
95  if (gdev->state == CCWGROUP_OFFLINE)
96  goto out;
97  if (gdrv->set_offline)
98  ret = gdrv->set_offline(gdev);
99  if (ret)
100  goto out;
101 
102  gdev->state = CCWGROUP_OFFLINE;
103 out:
104  atomic_set(&gdev->onoff, 0);
105  return ret;
106 }
107 
108 static ssize_t ccwgroup_online_store(struct device *dev,
109  struct device_attribute *attr,
110  const char *buf, size_t count)
111 {
112  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
113  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
114  unsigned long value;
115  int ret;
116 
117  if (!dev->driver)
118  return -EINVAL;
119  if (!try_module_get(gdrv->driver.owner))
120  return -EINVAL;
121 
122  ret = strict_strtoul(buf, 0, &value);
123  if (ret)
124  goto out;
125 
126  if (value == 1)
127  ret = ccwgroup_set_online(gdev);
128  else if (value == 0)
129  ret = ccwgroup_set_offline(gdev);
130  else
131  ret = -EINVAL;
132 out:
133  module_put(gdrv->driver.owner);
134  return (ret == 0) ? count : ret;
135 }
136 
137 static ssize_t ccwgroup_online_show(struct device *dev,
138  struct device_attribute *attr,
139  char *buf)
140 {
141  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
142  int online;
143 
144  online = (gdev->state == CCWGROUP_ONLINE) ? 1 : 0;
145 
146  return scnprintf(buf, PAGE_SIZE, "%d\n", online);
147 }
148 
149 /*
150  * Provide an 'ungroup' attribute so the user can remove group devices no
151  * longer needed or accidentially created. Saves memory :)
152  */
153 static void ccwgroup_ungroup_callback(struct device *dev)
154 {
155  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
156 
157  mutex_lock(&gdev->reg_mutex);
158  if (device_is_registered(&gdev->dev)) {
159  __ccwgroup_remove_symlinks(gdev);
160  device_unregister(dev);
161  __ccwgroup_remove_cdev_refs(gdev);
162  }
163  mutex_unlock(&gdev->reg_mutex);
164 }
165 
166 static ssize_t ccwgroup_ungroup_store(struct device *dev,
167  struct device_attribute *attr,
168  const char *buf, size_t count)
169 {
170  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
171  int rc;
172 
173  /* Prevent concurrent online/offline processing and ungrouping. */
174  if (atomic_cmpxchg(&gdev->onoff, 0, 1) != 0)
175  return -EAGAIN;
176  if (gdev->state != CCWGROUP_OFFLINE) {
177  rc = -EINVAL;
178  goto out;
179  }
180  /* Note that we cannot unregister the device from one of its
181  * attribute methods, so we have to use this roundabout approach.
182  */
183  rc = device_schedule_callback(dev, ccwgroup_ungroup_callback);
184 out:
185  if (rc) {
186  if (rc != -EAGAIN)
187  /* Release onoff "lock" when ungrouping failed. */
188  atomic_set(&gdev->onoff, 0);
189  return rc;
190  }
191  return count;
192 }
193 static DEVICE_ATTR(ungroup, 0200, NULL, ccwgroup_ungroup_store);
194 static DEVICE_ATTR(online, 0644, ccwgroup_online_show, ccwgroup_online_store);
195 
196 static struct attribute *ccwgroup_attrs[] = {
197  &dev_attr_online.attr,
198  &dev_attr_ungroup.attr,
199  NULL,
200 };
201 static struct attribute_group ccwgroup_attr_group = {
202  .attrs = ccwgroup_attrs,
203 };
204 static const struct attribute_group *ccwgroup_attr_groups[] = {
205  &ccwgroup_attr_group,
206  NULL,
207 };
208 
209 static void ccwgroup_release(struct device *dev)
210 {
211  kfree(to_ccwgroupdev(dev));
212 }
213 
214 static int __ccwgroup_create_symlinks(struct ccwgroup_device *gdev)
215 {
216  char str[8];
217  int i, rc;
218 
219  for (i = 0; i < gdev->count; i++) {
220  rc = sysfs_create_link(&gdev->cdev[i]->dev.kobj,
221  &gdev->dev.kobj, "group_device");
222  if (rc) {
223  for (--i; i >= 0; i--)
224  sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
225  "group_device");
226  return rc;
227  }
228  }
229  for (i = 0; i < gdev->count; i++) {
230  sprintf(str, "cdev%d", i);
231  rc = sysfs_create_link(&gdev->dev.kobj,
232  &gdev->cdev[i]->dev.kobj, str);
233  if (rc) {
234  for (--i; i >= 0; i--) {
235  sprintf(str, "cdev%d", i);
236  sysfs_remove_link(&gdev->dev.kobj, str);
237  }
238  for (i = 0; i < gdev->count; i++)
239  sysfs_remove_link(&gdev->cdev[i]->dev.kobj,
240  "group_device");
241  return rc;
242  }
243  }
244  return 0;
245 }
246 
247 static int __get_next_id(const char **buf, struct ccw_dev_id *id)
248 {
249  unsigned int cssid, ssid, devno;
250  int ret = 0, len;
251  char *start, *end;
252 
253  start = (char *)*buf;
254  end = strchr(start, ',');
255  if (!end) {
256  /* Last entry. Strip trailing newline, if applicable. */
257  end = strchr(start, '\n');
258  if (end)
259  *end = '\0';
260  len = strlen(start) + 1;
261  } else {
262  len = end - start + 1;
263  end++;
264  }
265  if (len <= CCW_BUS_ID_SIZE) {
266  if (sscanf(start, "%2x.%1x.%04x", &cssid, &ssid, &devno) != 3)
267  ret = -EINVAL;
268  } else
269  ret = -EINVAL;
270 
271  if (!ret) {
272  id->ssid = ssid;
273  id->devno = devno;
274  }
275  *buf = end;
276  return ret;
277 }
278 
293 int ccwgroup_create_dev(struct device *parent, struct ccwgroup_driver *gdrv,
294  int num_devices, const char *buf)
295 {
296  struct ccwgroup_device *gdev;
297  struct ccw_dev_id dev_id;
298  int rc, i;
299 
300  gdev = kzalloc(sizeof(*gdev) + num_devices * sizeof(gdev->cdev[0]),
301  GFP_KERNEL);
302  if (!gdev)
303  return -ENOMEM;
304 
305  atomic_set(&gdev->onoff, 0);
306  mutex_init(&gdev->reg_mutex);
307  mutex_lock(&gdev->reg_mutex);
308  gdev->count = num_devices;
309  gdev->dev.bus = &ccwgroup_bus_type;
310  gdev->dev.parent = parent;
311  gdev->dev.release = ccwgroup_release;
312  device_initialize(&gdev->dev);
313 
314  for (i = 0; i < num_devices && buf; i++) {
315  rc = __get_next_id(&buf, &dev_id);
316  if (rc != 0)
317  goto error;
318  gdev->cdev[i] = get_ccwdev_by_dev_id(&dev_id);
319  /*
320  * All devices have to be of the same type in
321  * order to be grouped.
322  */
323  if (!gdev->cdev[i] || !gdev->cdev[i]->drv ||
324  gdev->cdev[i]->drv != gdev->cdev[0]->drv ||
325  gdev->cdev[i]->id.driver_info !=
326  gdev->cdev[0]->id.driver_info) {
327  rc = -EINVAL;
328  goto error;
329  }
330  /* Don't allow a device to belong to more than one group. */
331  spin_lock_irq(gdev->cdev[i]->ccwlock);
332  if (dev_get_drvdata(&gdev->cdev[i]->dev)) {
333  spin_unlock_irq(gdev->cdev[i]->ccwlock);
334  rc = -EINVAL;
335  goto error;
336  }
337  dev_set_drvdata(&gdev->cdev[i]->dev, gdev);
338  spin_unlock_irq(gdev->cdev[i]->ccwlock);
339  }
340  /* Check for sufficient number of bus ids. */
341  if (i < num_devices) {
342  rc = -EINVAL;
343  goto error;
344  }
345  /* Check for trailing stuff. */
346  if (i == num_devices && strlen(buf) > 0) {
347  rc = -EINVAL;
348  goto error;
349  }
350 
351  dev_set_name(&gdev->dev, "%s", dev_name(&gdev->cdev[0]->dev));
352  gdev->dev.groups = ccwgroup_attr_groups;
353 
354  if (gdrv) {
355  gdev->dev.driver = &gdrv->driver;
356  rc = gdrv->setup ? gdrv->setup(gdev) : 0;
357  if (rc)
358  goto error;
359  }
360  rc = device_add(&gdev->dev);
361  if (rc)
362  goto error;
363  rc = __ccwgroup_create_symlinks(gdev);
364  if (rc) {
365  device_del(&gdev->dev);
366  goto error;
367  }
368  mutex_unlock(&gdev->reg_mutex);
369  return 0;
370 error:
371  for (i = 0; i < num_devices; i++)
372  if (gdev->cdev[i]) {
373  spin_lock_irq(gdev->cdev[i]->ccwlock);
374  if (dev_get_drvdata(&gdev->cdev[i]->dev) == gdev)
375  dev_set_drvdata(&gdev->cdev[i]->dev, NULL);
376  spin_unlock_irq(gdev->cdev[i]->ccwlock);
377  put_device(&gdev->cdev[i]->dev);
378  gdev->cdev[i] = NULL;
379  }
380  mutex_unlock(&gdev->reg_mutex);
381  put_device(&gdev->dev);
382  return rc;
383 }
385 
386 static int ccwgroup_notifier(struct notifier_block *nb, unsigned long action,
387  void *data)
388 {
389  struct device *dev = data;
390 
391  if (action == BUS_NOTIFY_UNBIND_DRIVER)
392  device_schedule_callback(dev, ccwgroup_ungroup_callback);
393 
394  return NOTIFY_OK;
395 }
396 
397 static struct notifier_block ccwgroup_nb = {
398  .notifier_call = ccwgroup_notifier
399 };
400 
401 static int __init init_ccwgroup(void)
402 {
403  int ret;
404 
405  ret = bus_register(&ccwgroup_bus_type);
406  if (ret)
407  return ret;
408 
409  ret = bus_register_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
410  if (ret)
411  bus_unregister(&ccwgroup_bus_type);
412 
413  return ret;
414 }
415 
416 static void __exit cleanup_ccwgroup(void)
417 {
418  bus_unregister_notifier(&ccwgroup_bus_type, &ccwgroup_nb);
419  bus_unregister(&ccwgroup_bus_type);
420 }
421 
422 module_init(init_ccwgroup);
423 module_exit(cleanup_ccwgroup);
424 
425 /************************** driver stuff ******************************/
426 
427 static int ccwgroup_remove(struct device *dev)
428 {
429  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
430  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
431 
432  if (!dev->driver)
433  return 0;
434  if (gdrv->remove)
435  gdrv->remove(gdev);
436 
437  return 0;
438 }
439 
440 static void ccwgroup_shutdown(struct device *dev)
441 {
442  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
443  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
444 
445  if (!dev->driver)
446  return;
447  if (gdrv->shutdown)
448  gdrv->shutdown(gdev);
449 }
450 
451 static int ccwgroup_pm_prepare(struct device *dev)
452 {
453  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
454  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
455 
456  /* Fail while device is being set online/offline. */
457  if (atomic_read(&gdev->onoff))
458  return -EAGAIN;
459 
460  if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
461  return 0;
462 
463  return gdrv->prepare ? gdrv->prepare(gdev) : 0;
464 }
465 
466 static void ccwgroup_pm_complete(struct device *dev)
467 {
468  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
469  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(dev->driver);
470 
471  if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
472  return;
473 
474  if (gdrv->complete)
475  gdrv->complete(gdev);
476 }
477 
478 static int ccwgroup_pm_freeze(struct device *dev)
479 {
480  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
481  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
482 
483  if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
484  return 0;
485 
486  return gdrv->freeze ? gdrv->freeze(gdev) : 0;
487 }
488 
489 static int ccwgroup_pm_thaw(struct device *dev)
490 {
491  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
492  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
493 
494  if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
495  return 0;
496 
497  return gdrv->thaw ? gdrv->thaw(gdev) : 0;
498 }
499 
500 static int ccwgroup_pm_restore(struct device *dev)
501 {
502  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
503  struct ccwgroup_driver *gdrv = to_ccwgroupdrv(gdev->dev.driver);
504 
505  if (!gdev->dev.driver || gdev->state != CCWGROUP_ONLINE)
506  return 0;
507 
508  return gdrv->restore ? gdrv->restore(gdev) : 0;
509 }
510 
511 static const struct dev_pm_ops ccwgroup_pm_ops = {
512  .prepare = ccwgroup_pm_prepare,
513  .complete = ccwgroup_pm_complete,
514  .freeze = ccwgroup_pm_freeze,
515  .thaw = ccwgroup_pm_thaw,
516  .restore = ccwgroup_pm_restore,
517 };
518 
519 static struct bus_type ccwgroup_bus_type = {
520  .name = "ccwgroup",
521  .remove = ccwgroup_remove,
522  .shutdown = ccwgroup_shutdown,
523  .pm = &ccwgroup_pm_ops,
524 };
525 
533 {
534  /* register our new driver with the core */
535  cdriver->driver.bus = &ccwgroup_bus_type;
536 
537  return driver_register(&cdriver->driver);
538 }
540 
541 static int __ccwgroup_match_all(struct device *dev, void *data)
542 {
543  return 1;
544 }
545 
553 {
554  struct device *dev;
555 
556  /* We don't want ccwgroup devices to live longer than their driver. */
557  while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
558  __ccwgroup_match_all))) {
559  struct ccwgroup_device *gdev = to_ccwgroupdev(dev);
560 
561  mutex_lock(&gdev->reg_mutex);
562  __ccwgroup_remove_symlinks(gdev);
563  device_unregister(dev);
564  __ccwgroup_remove_cdev_refs(gdev);
565  mutex_unlock(&gdev->reg_mutex);
566  put_device(dev);
567  }
568  driver_unregister(&cdriver->driver);
569 }
571 
582 {
583  return 0;
584 }
586 
596 {
597  struct ccwgroup_device *gdev;
598 
599  /* Ignore offlining errors, device is gone anyway. */
601  /* If one of its devices is gone, the whole group is done for. */
602  spin_lock_irq(cdev->ccwlock);
603  gdev = dev_get_drvdata(&cdev->dev);
604  if (!gdev) {
605  spin_unlock_irq(cdev->ccwlock);
606  return;
607  }
608  /* Get ccwgroup device reference for local processing. */
609  get_device(&gdev->dev);
610  spin_unlock_irq(cdev->ccwlock);
611  /* Unregister group device. */
612  mutex_lock(&gdev->reg_mutex);
613  if (device_is_registered(&gdev->dev)) {
614  __ccwgroup_remove_symlinks(gdev);
615  device_unregister(&gdev->dev);
616  __ccwgroup_remove_cdev_refs(gdev);
617  }
618  mutex_unlock(&gdev->reg_mutex);
619  /* Release ccwgroup device reference for local processing. */
620  put_device(&gdev->dev);
621 }
623 MODULE_LICENSE("GPL");