diff options
author | Peter Oberparleiter <peter.oberparleiter@de.ibm.com> | 2008-10-10 15:33:05 -0400 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2008-10-10 15:33:47 -0400 |
commit | 46fbe4e46ddb88805245a24f684400b50ead68a7 (patch) | |
tree | 0a8257d7802a8b15567bc8275b770a6b79e69a9d /drivers/s390/cio/device.c | |
parent | 4bcb3a37180ee4dffaef8298f373b334a7bedabb (diff) |
[S390] cio: move device unregistration to dedicated work queue
Use dedicated slow path work queue when unregistering a device due to
a user action. This ensures serialialization of other register/
unregister requests.
Signed-off-by: Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/cio/device.c')
-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; |