diff options
| -rw-r--r-- | drivers/s390/cio/device.c | 56 |
1 files changed, 25 insertions, 31 deletions
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 28221030b886..38a79ecfc743 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c | |||
| @@ -296,36 +296,33 @@ static void ccw_device_unregister(struct ccw_device *cdev) | |||
| 296 | device_del(&cdev->dev); | 296 | device_del(&cdev->dev); |
| 297 | } | 297 | } |
| 298 | 298 | ||
| 299 | static void ccw_device_remove_orphan_cb(struct device *dev) | 299 | static void ccw_device_remove_orphan_cb(struct work_struct *work) |
| 300 | { | 300 | { |
| 301 | struct ccw_device *cdev = to_ccwdev(dev); | 301 | struct ccw_device_private *priv; |
| 302 | struct ccw_device *cdev; | ||
| 302 | 303 | ||
| 304 | priv = container_of(work, struct ccw_device_private, kick_work); | ||
| 305 | cdev = priv->cdev; | ||
| 303 | ccw_device_unregister(cdev); | 306 | ccw_device_unregister(cdev); |
| 304 | put_device(&cdev->dev); | 307 | put_device(&cdev->dev); |
| 308 | /* Release cdev reference for workqueue processing. */ | ||
| 309 | put_device(&cdev->dev); | ||
| 305 | } | 310 | } |
| 306 | 311 | ||
| 307 | static void ccw_device_remove_sch_cb(struct device *dev) | 312 | static void ccw_device_call_sch_unregister(struct work_struct *work); |
| 308 | { | ||
| 309 | struct subchannel *sch; | ||
| 310 | |||
| 311 | sch = to_subchannel(dev); | ||
| 312 | css_sch_device_unregister(sch); | ||
| 313 | /* Reset intparm to zeroes. */ | ||
| 314 | sch->schib.pmcw.intparm = 0; | ||
| 315 | cio_modify(sch); | ||
| 316 | put_device(&sch->dev); | ||
| 317 | } | ||
| 318 | 313 | ||
| 319 | static void | 314 | static void |
| 320 | ccw_device_remove_disconnected(struct ccw_device *cdev) | 315 | ccw_device_remove_disconnected(struct ccw_device *cdev) |
| 321 | { | 316 | { |
| 322 | unsigned long flags; | 317 | unsigned long flags; |
| 323 | int rc; | ||
| 324 | 318 | ||
| 325 | /* | 319 | /* |
| 326 | * Forced offline in disconnected state means | 320 | * Forced offline in disconnected state means |
| 327 | * 'throw away device'. | 321 | * 'throw away device'. |
| 328 | */ | 322 | */ |
| 323 | /* Get cdev reference for workqueue processing. */ | ||
| 324 | if (!get_device(&cdev->dev)) | ||
| 325 | return; | ||
| 329 | if (ccw_device_is_orphan(cdev)) { | 326 | if (ccw_device_is_orphan(cdev)) { |
| 330 | /* | 327 | /* |
| 331 | * Deregister ccw device. | 328 | * Deregister ccw device. |
| @@ -335,23 +332,13 @@ ccw_device_remove_disconnected(struct ccw_device *cdev) | |||
| 335 | spin_lock_irqsave(cdev->ccwlock, flags); | 332 | spin_lock_irqsave(cdev->ccwlock, flags); |
| 336 | cdev->private->state = DEV_STATE_NOT_OPER; | 333 | cdev->private->state = DEV_STATE_NOT_OPER; |
| 337 | spin_unlock_irqrestore(cdev->ccwlock, flags); | 334 | spin_unlock_irqrestore(cdev->ccwlock, flags); |
| 338 | rc = device_schedule_callback(&cdev->dev, | 335 | PREPARE_WORK(&cdev->private->kick_work, |
| 339 | ccw_device_remove_orphan_cb); | 336 | ccw_device_remove_orphan_cb); |
| 340 | if (rc) | 337 | } else |
| 341 | CIO_MSG_EVENT(0, "Couldn't unregister orphan " | 338 | /* Deregister subchannel, which will kill the ccw device. */ |
| 342 | "0.%x.%04x\n", | 339 | PREPARE_WORK(&cdev->private->kick_work, |
| 343 | cdev->private->dev_id.ssid, | 340 | ccw_device_call_sch_unregister); |
| 344 | cdev->private->dev_id.devno); | 341 | queue_work(slow_path_wq, &cdev->private->kick_work); |
| 345 | return; | ||
| 346 | } | ||
| 347 | /* Deregister subchannel, which will kill the ccw device. */ | ||
| 348 | rc = device_schedule_callback(cdev->dev.parent, | ||
| 349 | ccw_device_remove_sch_cb); | ||
| 350 | if (rc) | ||
| 351 | CIO_MSG_EVENT(0, "Couldn't unregister disconnected device " | ||
| 352 | "0.%x.%04x\n", | ||
| 353 | cdev->private->dev_id.ssid, | ||
| 354 | cdev->private->dev_id.devno); | ||
| 355 | } | 342 | } |
| 356 | 343 | ||
| 357 | /** | 344 | /** |
| @@ -970,12 +957,17 @@ static void ccw_device_call_sch_unregister(struct work_struct *work) | |||
| 970 | 957 | ||
| 971 | priv = container_of(work, struct ccw_device_private, kick_work); | 958 | priv = container_of(work, struct ccw_device_private, kick_work); |
| 972 | cdev = priv->cdev; | 959 | cdev = priv->cdev; |
| 960 | /* Get subchannel reference for local processing. */ | ||
| 961 | if (!get_device(cdev->dev.parent)) | ||
| 962 | return; | ||
| 973 | sch = to_subchannel(cdev->dev.parent); | 963 | sch = to_subchannel(cdev->dev.parent); |
| 974 | css_sch_device_unregister(sch); | 964 | css_sch_device_unregister(sch); |
| 975 | /* Reset intparm to zeroes. */ | 965 | /* Reset intparm to zeroes. */ |
| 976 | sch->schib.pmcw.intparm = 0; | 966 | sch->schib.pmcw.intparm = 0; |
| 977 | cio_modify(sch); | 967 | cio_modify(sch); |
| 968 | /* Release cdev reference for workqueue processing.*/ | ||
| 978 | put_device(&cdev->dev); | 969 | put_device(&cdev->dev); |
| 970 | /* Release subchannel reference for local processing. */ | ||
| 979 | put_device(&sch->dev); | 971 | put_device(&sch->dev); |
| 980 | } | 972 | } |
| 981 | 973 | ||
| @@ -1001,6 +993,8 @@ io_subchannel_recog_done(struct ccw_device *cdev) | |||
| 1001 | PREPARE_WORK(&cdev->private->kick_work, | 993 | PREPARE_WORK(&cdev->private->kick_work, |
| 1002 | ccw_device_call_sch_unregister); | 994 | ccw_device_call_sch_unregister); |
| 1003 | queue_work(slow_path_wq, &cdev->private->kick_work); | 995 | queue_work(slow_path_wq, &cdev->private->kick_work); |
| 996 | /* Release subchannel reference for asynchronous recognition. */ | ||
| 997 | put_device(&sch->dev); | ||
| 1004 | if (atomic_dec_and_test(&ccw_device_init_count)) | 998 | if (atomic_dec_and_test(&ccw_device_init_count)) |
| 1005 | wake_up(&ccw_device_init_wq); | 999 | wake_up(&ccw_device_init_wq); |
| 1006 | break; | 1000 | break; |
