diff options
Diffstat (limited to 'drivers/s390/cio/device.c')
-rw-r--r-- | drivers/s390/cio/device.c | 246 |
1 files changed, 111 insertions, 135 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index e322111fb369..03355902c582 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
@@ -56,13 +56,12 @@ ccw_bus_match (struct device * dev, struct device_driver * drv) | |||
56 | /* Store modalias string delimited by prefix/suffix string into buffer with | 56 | /* Store modalias string delimited by prefix/suffix string into buffer with |
57 | * specified size. Return length of resulting string (excluding trailing '\0') | 57 | * specified size. Return length of resulting string (excluding trailing '\0') |
58 | * even if string doesn't fit buffer (snprintf semantics). */ | 58 | * even if string doesn't fit buffer (snprintf semantics). */ |
59 | static int snprint_alias(char *buf, size_t size, const char *prefix, | 59 | static int snprint_alias(char *buf, size_t size, |
60 | struct ccw_device_id *id, const char *suffix) | 60 | struct ccw_device_id *id, const char *suffix) |
61 | { | 61 | { |
62 | int len; | 62 | int len; |
63 | 63 | ||
64 | len = snprintf(buf, size, "%sccw:t%04Xm%02X", prefix, id->cu_type, | 64 | len = snprintf(buf, size, "ccw:t%04Xm%02X", id->cu_type, id->cu_model); |
65 | id->cu_model); | ||
66 | if (len > size) | 65 | if (len > size) |
67 | return len; | 66 | return len; |
68 | buf += len; | 67 | buf += len; |
@@ -85,53 +84,40 @@ static int ccw_uevent(struct device *dev, char **envp, int num_envp, | |||
85 | struct ccw_device *cdev = to_ccwdev(dev); | 84 | struct ccw_device *cdev = to_ccwdev(dev); |
86 | struct ccw_device_id *id = &(cdev->id); | 85 | struct ccw_device_id *id = &(cdev->id); |
87 | int i = 0; | 86 | int i = 0; |
88 | int len; | 87 | int len = 0; |
88 | int ret; | ||
89 | char modalias_buf[30]; | ||
89 | 90 | ||
90 | /* CU_TYPE= */ | 91 | /* CU_TYPE= */ |
91 | len = snprintf(buffer, buffer_size, "CU_TYPE=%04X", id->cu_type) + 1; | 92 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, |
92 | if (len > buffer_size || i >= num_envp) | 93 | "CU_TYPE=%04X", id->cu_type); |
93 | return -ENOMEM; | 94 | if (ret) |
94 | envp[i++] = buffer; | 95 | return ret; |
95 | buffer += len; | ||
96 | buffer_size -= len; | ||
97 | 96 | ||
98 | /* CU_MODEL= */ | 97 | /* CU_MODEL= */ |
99 | len = snprintf(buffer, buffer_size, "CU_MODEL=%02X", id->cu_model) + 1; | 98 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, |
100 | if (len > buffer_size || i >= num_envp) | 99 | "CU_MODEL=%02X", id->cu_model); |
101 | return -ENOMEM; | 100 | if (ret) |
102 | envp[i++] = buffer; | 101 | return ret; |
103 | buffer += len; | ||
104 | buffer_size -= len; | ||
105 | 102 | ||
106 | /* The next two can be zero, that's ok for us */ | 103 | /* The next two can be zero, that's ok for us */ |
107 | /* DEV_TYPE= */ | 104 | /* DEV_TYPE= */ |
108 | len = snprintf(buffer, buffer_size, "DEV_TYPE=%04X", id->dev_type) + 1; | 105 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, |
109 | if (len > buffer_size || i >= num_envp) | 106 | "DEV_TYPE=%04X", id->dev_type); |
110 | return -ENOMEM; | 107 | if (ret) |
111 | envp[i++] = buffer; | 108 | return ret; |
112 | buffer += len; | ||
113 | buffer_size -= len; | ||
114 | 109 | ||
115 | /* DEV_MODEL= */ | 110 | /* DEV_MODEL= */ |
116 | len = snprintf(buffer, buffer_size, "DEV_MODEL=%02X", | 111 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, |
117 | (unsigned char) id->dev_model) + 1; | 112 | "DEV_MODEL=%02X", id->dev_model); |
118 | if (len > buffer_size || i >= num_envp) | 113 | if (ret) |
119 | return -ENOMEM; | 114 | return ret; |
120 | envp[i++] = buffer; | ||
121 | buffer += len; | ||
122 | buffer_size -= len; | ||
123 | 115 | ||
124 | /* MODALIAS= */ | 116 | /* MODALIAS= */ |
125 | len = snprint_alias(buffer, buffer_size, "MODALIAS=", id, "") + 1; | 117 | snprint_alias(modalias_buf, sizeof(modalias_buf), id, ""); |
126 | if (len > buffer_size || i >= num_envp) | 118 | ret = add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &len, |
127 | return -ENOMEM; | 119 | "MODALIAS=%s", modalias_buf); |
128 | envp[i++] = buffer; | 120 | return ret; |
129 | buffer += len; | ||
130 | buffer_size -= len; | ||
131 | |||
132 | envp[i] = NULL; | ||
133 | |||
134 | return 0; | ||
135 | } | 121 | } |
136 | 122 | ||
137 | struct bus_type ccw_bus_type; | 123 | struct bus_type ccw_bus_type; |
@@ -230,12 +216,18 @@ static ssize_t | |||
230 | chpids_show (struct device * dev, struct device_attribute *attr, char * buf) | 216 | chpids_show (struct device * dev, struct device_attribute *attr, char * buf) |
231 | { | 217 | { |
232 | struct subchannel *sch = to_subchannel(dev); | 218 | struct subchannel *sch = to_subchannel(dev); |
233 | struct ssd_info *ssd = &sch->ssd_info; | 219 | struct chsc_ssd_info *ssd = &sch->ssd_info; |
234 | ssize_t ret = 0; | 220 | ssize_t ret = 0; |
235 | int chp; | 221 | int chp; |
222 | int mask; | ||
236 | 223 | ||
237 | for (chp = 0; chp < 8; chp++) | 224 | for (chp = 0; chp < 8; chp++) { |
238 | ret += sprintf (buf+ret, "%02x ", ssd->chpid[chp]); | 225 | mask = 0x80 >> chp; |
226 | if (ssd->path_mask & mask) | ||
227 | ret += sprintf(buf + ret, "%02x ", ssd->chpid[chp].id); | ||
228 | else | ||
229 | ret += sprintf(buf + ret, "00 "); | ||
230 | } | ||
239 | ret += sprintf (buf+ret, "\n"); | 231 | ret += sprintf (buf+ret, "\n"); |
240 | return min((ssize_t)PAGE_SIZE, ret); | 232 | return min((ssize_t)PAGE_SIZE, ret); |
241 | } | 233 | } |
@@ -280,7 +272,7 @@ modalias_show (struct device *dev, struct device_attribute *attr, char *buf) | |||
280 | struct ccw_device_id *id = &(cdev->id); | 272 | struct ccw_device_id *id = &(cdev->id); |
281 | int len; | 273 | int len; |
282 | 274 | ||
283 | len = snprint_alias(buf, PAGE_SIZE, "", id, "\n") + 1; | 275 | len = snprint_alias(buf, PAGE_SIZE, id, "\n") + 1; |
284 | 276 | ||
285 | return len > PAGE_SIZE ? PAGE_SIZE : len; | 277 | return len > PAGE_SIZE ? PAGE_SIZE : len; |
286 | } | 278 | } |
@@ -298,16 +290,10 @@ int ccw_device_is_orphan(struct ccw_device *cdev) | |||
298 | return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent)); | 290 | return sch_is_pseudo_sch(to_subchannel(cdev->dev.parent)); |
299 | } | 291 | } |
300 | 292 | ||
301 | static void ccw_device_unregister(struct work_struct *work) | 293 | static void ccw_device_unregister(struct ccw_device *cdev) |
302 | { | 294 | { |
303 | struct ccw_device_private *priv; | ||
304 | struct ccw_device *cdev; | ||
305 | |||
306 | priv = container_of(work, struct ccw_device_private, kick_work); | ||
307 | cdev = priv->cdev; | ||
308 | if (test_and_clear_bit(1, &cdev->private->registered)) | 295 | if (test_and_clear_bit(1, &cdev->private->registered)) |
309 | device_unregister(&cdev->dev); | 296 | device_del(&cdev->dev); |
310 | put_device(&cdev->dev); | ||
311 | } | 297 | } |
312 | 298 | ||
313 | static void | 299 | static void |
@@ -324,11 +310,8 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) | |||
324 | spin_lock_irqsave(cdev->ccwlock, flags); | 310 | spin_lock_irqsave(cdev->ccwlock, flags); |
325 | cdev->private->state = DEV_STATE_NOT_OPER; | 311 | cdev->private->state = DEV_STATE_NOT_OPER; |
326 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 312 | spin_unlock_irqrestore(cdev->ccwlock, flags); |
327 | if (get_device(&cdev->dev)) { | 313 | ccw_device_unregister(cdev); |
328 | PREPARE_WORK(&cdev->private->kick_work, | 314 | put_device(&cdev->dev); |
329 | ccw_device_unregister); | ||
330 | queue_work(ccw_device_work, &cdev->private->kick_work); | ||
331 | } | ||
332 | return ; | 315 | return ; |
333 | } | 316 | } |
334 | sch = to_subchannel(cdev->dev.parent); | 317 | sch = to_subchannel(cdev->dev.parent); |
@@ -413,11 +396,60 @@ ccw_device_set_online(struct ccw_device *cdev) | |||
413 | return (ret == 0) ? -ENODEV : ret; | 396 | return (ret == 0) ? -ENODEV : ret; |
414 | } | 397 | } |
415 | 398 | ||
416 | static ssize_t | 399 | static void online_store_handle_offline(struct ccw_device *cdev) |
417 | online_store (struct device *dev, struct device_attribute *attr, const char *buf, size_t count) | 400 | { |
401 | if (cdev->private->state == DEV_STATE_DISCONNECTED) | ||
402 | ccw_device_remove_disconnected(cdev); | ||
403 | else if (cdev->drv && cdev->drv->set_offline) | ||
404 | ccw_device_set_offline(cdev); | ||
405 | } | ||
406 | |||
407 | static int online_store_recog_and_online(struct ccw_device *cdev) | ||
408 | { | ||
409 | int ret; | ||
410 | |||
411 | /* Do device recognition, if needed. */ | ||
412 | if (cdev->id.cu_type == 0) { | ||
413 | ret = ccw_device_recognition(cdev); | ||
414 | if (ret) { | ||
415 | printk(KERN_WARNING"Couldn't start recognition " | ||
416 | "for device %s (ret=%d)\n", | ||
417 | cdev->dev.bus_id, ret); | ||
418 | return ret; | ||
419 | } | ||
420 | wait_event(cdev->private->wait_q, | ||
421 | cdev->private->flags.recog_done); | ||
422 | } | ||
423 | if (cdev->drv && cdev->drv->set_online) | ||
424 | ccw_device_set_online(cdev); | ||
425 | return 0; | ||
426 | } | ||
427 | static void online_store_handle_online(struct ccw_device *cdev, int force) | ||
428 | { | ||
429 | int ret; | ||
430 | |||
431 | ret = online_store_recog_and_online(cdev); | ||
432 | if (ret) | ||
433 | return; | ||
434 | if (force && cdev->private->state == DEV_STATE_BOXED) { | ||
435 | ret = ccw_device_stlck(cdev); | ||
436 | if (ret) { | ||
437 | printk(KERN_WARNING"ccw_device_stlck for device %s " | ||
438 | "returned %d!\n", cdev->dev.bus_id, ret); | ||
439 | return; | ||
440 | } | ||
441 | if (cdev->id.cu_type == 0) | ||
442 | cdev->private->state = DEV_STATE_NOT_OPER; | ||
443 | online_store_recog_and_online(cdev); | ||
444 | } | ||
445 | |||
446 | } | ||
447 | |||
448 | static ssize_t online_store (struct device *dev, struct device_attribute *attr, | ||
449 | const char *buf, size_t count) | ||
418 | { | 450 | { |
419 | struct ccw_device *cdev = to_ccwdev(dev); | 451 | struct ccw_device *cdev = to_ccwdev(dev); |
420 | int i, force, ret; | 452 | int i, force; |
421 | char *tmp; | 453 | char *tmp; |
422 | 454 | ||
423 | if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) | 455 | if (atomic_cmpxchg(&cdev->private->onoff, 0, 1) != 0) |
@@ -434,51 +466,17 @@ online_store (struct device *dev, struct device_attribute *attr, const char *buf | |||
434 | force = 0; | 466 | force = 0; |
435 | i = simple_strtoul(buf, &tmp, 16); | 467 | i = simple_strtoul(buf, &tmp, 16); |
436 | } | 468 | } |
437 | if (i == 1) { | 469 | |
438 | /* Do device recognition, if needed. */ | 470 | switch (i) { |
439 | if (cdev->id.cu_type == 0) { | 471 | case 0: |
440 | ret = ccw_device_recognition(cdev); | 472 | online_store_handle_offline(cdev); |
441 | if (ret) { | 473 | break; |
442 | printk(KERN_WARNING"Couldn't start recognition " | 474 | case 1: |
443 | "for device %s (ret=%d)\n", | 475 | online_store_handle_online(cdev, force); |
444 | cdev->dev.bus_id, ret); | 476 | break; |
445 | goto out; | 477 | default: |
446 | } | 478 | count = -EINVAL; |
447 | wait_event(cdev->private->wait_q, | ||
448 | cdev->private->flags.recog_done); | ||
449 | } | ||
450 | if (cdev->drv && cdev->drv->set_online) | ||
451 | ccw_device_set_online(cdev); | ||
452 | } else if (i == 0) { | ||
453 | if (cdev->private->state == DEV_STATE_DISCONNECTED) | ||
454 | ccw_device_remove_disconnected(cdev); | ||
455 | else if (cdev->drv && cdev->drv->set_offline) | ||
456 | ccw_device_set_offline(cdev); | ||
457 | } | ||
458 | if (force && cdev->private->state == DEV_STATE_BOXED) { | ||
459 | ret = ccw_device_stlck(cdev); | ||
460 | if (ret) { | ||
461 | printk(KERN_WARNING"ccw_device_stlck for device %s " | ||
462 | "returned %d!\n", cdev->dev.bus_id, ret); | ||
463 | goto out; | ||
464 | } | ||
465 | /* Do device recognition, if needed. */ | ||
466 | if (cdev->id.cu_type == 0) { | ||
467 | cdev->private->state = DEV_STATE_NOT_OPER; | ||
468 | ret = ccw_device_recognition(cdev); | ||
469 | if (ret) { | ||
470 | printk(KERN_WARNING"Couldn't start recognition " | ||
471 | "for device %s (ret=%d)\n", | ||
472 | cdev->dev.bus_id, ret); | ||
473 | goto out; | ||
474 | } | ||
475 | wait_event(cdev->private->wait_q, | ||
476 | cdev->private->flags.recog_done); | ||
477 | } | ||
478 | if (cdev->drv && cdev->drv->set_online) | ||
479 | ccw_device_set_online(cdev); | ||
480 | } | 479 | } |
481 | out: | ||
482 | if (cdev->drv) | 480 | if (cdev->drv) |
483 | module_put(cdev->drv->owner); | 481 | module_put(cdev->drv->owner); |
484 | atomic_set(&cdev->private->onoff, 0); | 482 | atomic_set(&cdev->private->onoff, 0); |
@@ -548,17 +546,10 @@ static struct attribute_group ccwdev_attr_group = { | |||
548 | .attrs = ccwdev_attrs, | 546 | .attrs = ccwdev_attrs, |
549 | }; | 547 | }; |
550 | 548 | ||
551 | static int | 549 | struct attribute_group *ccwdev_attr_groups[] = { |
552 | device_add_files (struct device *dev) | 550 | &ccwdev_attr_group, |
553 | { | 551 | NULL, |
554 | return sysfs_create_group(&dev->kobj, &ccwdev_attr_group); | 552 | }; |
555 | } | ||
556 | |||
557 | static void | ||
558 | device_remove_files(struct device *dev) | ||
559 | { | ||
560 | sysfs_remove_group(&dev->kobj, &ccwdev_attr_group); | ||
561 | } | ||
562 | 553 | ||
563 | /* this is a simple abstraction for device_register that sets the | 554 | /* this is a simple abstraction for device_register that sets the |
564 | * correct bus type and adds the bus specific files */ | 555 | * correct bus type and adds the bus specific files */ |
@@ -573,10 +564,6 @@ static int ccw_device_register(struct ccw_device *cdev) | |||
573 | return ret; | 564 | return ret; |
574 | 565 | ||
575 | set_bit(1, &cdev->private->registered); | 566 | set_bit(1, &cdev->private->registered); |
576 | if ((ret = device_add_files(dev))) { | ||
577 | if (test_and_clear_bit(1, &cdev->private->registered)) | ||
578 | device_del(dev); | ||
579 | } | ||
580 | return ret; | 567 | return ret; |
581 | } | 568 | } |
582 | 569 | ||
@@ -648,10 +635,6 @@ ccw_device_add_changed(struct work_struct *work) | |||
648 | return; | 635 | return; |
649 | } | 636 | } |
650 | set_bit(1, &cdev->private->registered); | 637 | set_bit(1, &cdev->private->registered); |
651 | if (device_add_files(&cdev->dev)) { | ||
652 | if (test_and_clear_bit(1, &cdev->private->registered)) | ||
653 | device_unregister(&cdev->dev); | ||
654 | } | ||
655 | } | 638 | } |
656 | 639 | ||
657 | void ccw_device_do_unreg_rereg(struct work_struct *work) | 640 | void ccw_device_do_unreg_rereg(struct work_struct *work) |
@@ -664,9 +647,7 @@ void ccw_device_do_unreg_rereg(struct work_struct *work) | |||
664 | cdev = priv->cdev; | 647 | cdev = priv->cdev; |
665 | sch = to_subchannel(cdev->dev.parent); | 648 | sch = to_subchannel(cdev->dev.parent); |
666 | 649 | ||
667 | device_remove_files(&cdev->dev); | 650 | ccw_device_unregister(cdev); |
668 | if (test_and_clear_bit(1, &cdev->private->registered)) | ||
669 | device_del(&cdev->dev); | ||
670 | PREPARE_WORK(&cdev->private->kick_work, | 651 | PREPARE_WORK(&cdev->private->kick_work, |
671 | ccw_device_add_changed); | 652 | ccw_device_add_changed); |
672 | queue_work(ccw_device_work, &cdev->private->kick_work); | 653 | queue_work(ccw_device_work, &cdev->private->kick_work); |
@@ -705,6 +686,7 @@ static int io_subchannel_initialize_dev(struct subchannel *sch, | |||
705 | cdev->dev.parent = &sch->dev; | 686 | cdev->dev.parent = &sch->dev; |
706 | cdev->dev.release = ccw_device_release; | 687 | cdev->dev.release = ccw_device_release; |
707 | INIT_LIST_HEAD(&cdev->private->kick_work.entry); | 688 | INIT_LIST_HEAD(&cdev->private->kick_work.entry); |
689 | cdev->dev.groups = ccwdev_attr_groups; | ||
708 | /* Do first half of device_register. */ | 690 | /* Do first half of device_register. */ |
709 | device_initialize(&cdev->dev); | 691 | device_initialize(&cdev->dev); |
710 | if (!get_device(&sch->dev)) { | 692 | if (!get_device(&sch->dev)) { |
@@ -736,6 +718,7 @@ static int io_subchannel_recog(struct ccw_device *, struct subchannel *); | |||
736 | static void sch_attach_device(struct subchannel *sch, | 718 | static void sch_attach_device(struct subchannel *sch, |
737 | struct ccw_device *cdev) | 719 | struct ccw_device *cdev) |
738 | { | 720 | { |
721 | css_update_ssd_info(sch); | ||
739 | spin_lock_irq(sch->lock); | 722 | spin_lock_irq(sch->lock); |
740 | sch->dev.driver_data = cdev; | 723 | sch->dev.driver_data = cdev; |
741 | cdev->private->schid = sch->schid; | 724 | cdev->private->schid = sch->schid; |
@@ -871,7 +854,7 @@ io_subchannel_register(struct work_struct *work) | |||
871 | priv = container_of(work, struct ccw_device_private, kick_work); | 854 | priv = container_of(work, struct ccw_device_private, kick_work); |
872 | cdev = priv->cdev; | 855 | cdev = priv->cdev; |
873 | sch = to_subchannel(cdev->dev.parent); | 856 | sch = to_subchannel(cdev->dev.parent); |
874 | 857 | css_update_ssd_info(sch); | |
875 | /* | 858 | /* |
876 | * io_subchannel_register() will also be called after device | 859 | * io_subchannel_register() will also be called after device |
877 | * recognition has been done for a boxed device (which will already | 860 | * recognition has been done for a boxed device (which will already |
@@ -1133,15 +1116,8 @@ io_subchannel_remove (struct subchannel *sch) | |||
1133 | sch->dev.driver_data = NULL; | 1116 | sch->dev.driver_data = NULL; |
1134 | cdev->private->state = DEV_STATE_NOT_OPER; | 1117 | cdev->private->state = DEV_STATE_NOT_OPER; |
1135 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 1118 | spin_unlock_irqrestore(cdev->ccwlock, flags); |
1136 | /* | 1119 | ccw_device_unregister(cdev); |
1137 | * Put unregistration on workqueue to avoid livelocks on the css bus | 1120 | put_device(&cdev->dev); |
1138 | * semaphore. | ||
1139 | */ | ||
1140 | if (get_device(&cdev->dev)) { | ||
1141 | PREPARE_WORK(&cdev->private->kick_work, | ||
1142 | ccw_device_unregister); | ||
1143 | queue_work(ccw_device_work, &cdev->private->kick_work); | ||
1144 | } | ||
1145 | return 0; | 1121 | return 0; |
1146 | } | 1122 | } |
1147 | 1123 | ||