aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/block/dasd.c
diff options
context:
space:
mode:
authorStefan Haberland <stefan.haberland@de.ibm.com>2009-06-16 04:30:25 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-06-16 04:31:10 -0400
commitd41dd122acf960db78c9ddc87684b43751dd36d9 (patch)
tree6005ea4914d1d2556b182ae86d82185d792b2f30 /drivers/s390/block/dasd.c
parentad285ae9fc6b9c0058f2a558b43fe8817685ebfa (diff)
[S390] pm: dasd power management callbacks.
Introduce the power management callbacks to the dasd driver. On suspend the dasd devices are stopped and removed from the focus of alias management. On resume they are reinitialized by rereading the device characteristics and adding the device to the alias management. In case the device has gone away during suspend it will caught in the suspend state with stopped flag set to UNRESUMED. After it appears again the restore function is called again. Signed-off-by: Stefan Haberland <stefan.haberland@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/block/dasd.c')
-rw-r--r--drivers/s390/block/dasd.c109
1 files changed, 107 insertions, 2 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index 442bb98a2821..e5b84db0aa03 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -5,8 +5,7 @@
5 * Carsten Otte <Cotte@de.ibm.com> 5 * Carsten Otte <Cotte@de.ibm.com>
6 * Martin Schwidefsky <schwidefsky@de.ibm.com> 6 * Martin Schwidefsky <schwidefsky@de.ibm.com>
7 * Bugreports.to..: <Linux390@de.ibm.com> 7 * Bugreports.to..: <Linux390@de.ibm.com>
8 * (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999-2001 8 * Copyright IBM Corp. 1999, 2009
9 *
10 */ 9 */
11 10
12#define KMSG_COMPONENT "dasd" 11#define KMSG_COMPONENT "dasd"
@@ -61,6 +60,7 @@ static int dasd_flush_block_queue(struct dasd_block *);
61static void dasd_device_tasklet(struct dasd_device *); 60static void dasd_device_tasklet(struct dasd_device *);
62static void dasd_block_tasklet(struct dasd_block *); 61static void dasd_block_tasklet(struct dasd_block *);
63static void do_kick_device(struct work_struct *); 62static void do_kick_device(struct work_struct *);
63static void do_restore_device(struct work_struct *);
64static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *); 64static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
65static void dasd_device_timeout(unsigned long); 65static void dasd_device_timeout(unsigned long);
66static void dasd_block_timeout(unsigned long); 66static void dasd_block_timeout(unsigned long);
@@ -109,6 +109,7 @@ struct dasd_device *dasd_alloc_device(void)
109 device->timer.function = dasd_device_timeout; 109 device->timer.function = dasd_device_timeout;
110 device->timer.data = (unsigned long) device; 110 device->timer.data = (unsigned long) device;
111 INIT_WORK(&device->kick_work, do_kick_device); 111 INIT_WORK(&device->kick_work, do_kick_device);
112 INIT_WORK(&device->restore_device, do_restore_device);
112 device->state = DASD_STATE_NEW; 113 device->state = DASD_STATE_NEW;
113 device->target = DASD_STATE_NEW; 114 device->target = DASD_STATE_NEW;
114 115
@@ -512,6 +513,25 @@ void dasd_kick_device(struct dasd_device *device)
512} 513}
513 514
514/* 515/*
516 * dasd_restore_device will schedule a call do do_restore_device to the kernel
517 * event daemon.
518 */
519static void do_restore_device(struct work_struct *work)
520{
521 struct dasd_device *device = container_of(work, struct dasd_device,
522 restore_device);
523 device->cdev->drv->restore(device->cdev);
524 dasd_put_device(device);
525}
526
527void dasd_restore_device(struct dasd_device *device)
528{
529 dasd_get_device(device);
530 /* queue call to dasd_restore_device to the kernel event daemon. */
531 schedule_work(&device->restore_device);
532}
533
534/*
515 * Set the target state for a device and starts the state change. 535 * Set the target state for a device and starts the state change.
516 */ 536 */
517void dasd_set_target_state(struct dasd_device *device, int target) 537void dasd_set_target_state(struct dasd_device *device, int target)
@@ -908,6 +928,12 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
908 DBF_DEV_EVENT(DBF_DEBUG, device, "%s", 928 DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
909 "start_IO: -EIO device gone, retry"); 929 "start_IO: -EIO device gone, retry");
910 break; 930 break;
931 case -EINVAL:
932 /* most likely caused in power management context */
933 DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
934 "start_IO: -EINVAL device currently "
935 "not accessible");
936 break;
911 default: 937 default:
912 /* internal error 11 - unknown rc */ 938 /* internal error 11 - unknown rc */
913 snprintf(errorstring, ERRORLENGTH, "11 %d", rc); 939 snprintf(errorstring, ERRORLENGTH, "11 %d", rc);
@@ -2400,6 +2426,12 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
2400 case CIO_OPER: 2426 case CIO_OPER:
2401 /* FIXME: add a sanity check. */ 2427 /* FIXME: add a sanity check. */
2402 device->stopped &= ~DASD_STOPPED_DC_WAIT; 2428 device->stopped &= ~DASD_STOPPED_DC_WAIT;
2429 if (device->stopped & DASD_UNRESUMED_PM) {
2430 device->stopped &= ~DASD_UNRESUMED_PM;
2431 dasd_restore_device(device);
2432 ret = 1;
2433 break;
2434 }
2403 dasd_schedule_device_bh(device); 2435 dasd_schedule_device_bh(device);
2404 if (device->block) 2436 if (device->block)
2405 dasd_schedule_block_bh(device->block); 2437 dasd_schedule_block_bh(device->block);
@@ -2410,6 +2442,79 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
2410 return ret; 2442 return ret;
2411} 2443}
2412 2444
2445int dasd_generic_pm_freeze(struct ccw_device *cdev)
2446{
2447 struct dasd_ccw_req *cqr, *n;
2448 int rc;
2449 struct list_head freeze_queue;
2450 struct dasd_device *device = dasd_device_from_cdev(cdev);
2451
2452 if (IS_ERR(device))
2453 return PTR_ERR(device);
2454 /* disallow new I/O */
2455 device->stopped |= DASD_STOPPED_PM;
2456 /* clear active requests */
2457 INIT_LIST_HEAD(&freeze_queue);
2458 spin_lock_irq(get_ccwdev_lock(cdev));
2459 rc = 0;
2460 list_for_each_entry_safe(cqr, n, &device->ccw_queue, devlist) {
2461 /* Check status and move request to flush_queue */
2462 if (cqr->status == DASD_CQR_IN_IO) {
2463 rc = device->discipline->term_IO(cqr);
2464 if (rc) {
2465 /* unable to terminate requeust */
2466 dev_err(&device->cdev->dev,
2467 "Unable to terminate request %p "
2468 "on suspend\n", cqr);
2469 spin_unlock_irq(get_ccwdev_lock(cdev));
2470 dasd_put_device(device);
2471 return rc;
2472 }
2473 }
2474 list_move_tail(&cqr->devlist, &freeze_queue);
2475 }
2476
2477 spin_unlock_irq(get_ccwdev_lock(cdev));
2478
2479 list_for_each_entry_safe(cqr, n, &freeze_queue, devlist) {
2480 wait_event(dasd_flush_wq,
2481 (cqr->status != DASD_CQR_CLEAR_PENDING));
2482 if (cqr->status == DASD_CQR_CLEARED)
2483 cqr->status = DASD_CQR_QUEUED;
2484 }
2485 /* move freeze_queue to start of the ccw_queue */
2486 spin_lock_irq(get_ccwdev_lock(cdev));
2487 list_splice_tail(&freeze_queue, &device->ccw_queue);
2488 spin_unlock_irq(get_ccwdev_lock(cdev));
2489
2490 if (device->discipline->freeze)
2491 rc = device->discipline->freeze(device);
2492
2493 dasd_put_device(device);
2494 return rc;
2495}
2496EXPORT_SYMBOL_GPL(dasd_generic_pm_freeze);
2497
2498int dasd_generic_restore_device(struct ccw_device *cdev)
2499{
2500 struct dasd_device *device = dasd_device_from_cdev(cdev);
2501 int rc = 0;
2502
2503 if (IS_ERR(device))
2504 return PTR_ERR(device);
2505
2506 dasd_schedule_device_bh(device);
2507 if (device->block)
2508 dasd_schedule_block_bh(device->block);
2509
2510 if (device->discipline->restore)
2511 rc = device->discipline->restore(device);
2512
2513 dasd_put_device(device);
2514 return rc;
2515}
2516EXPORT_SYMBOL_GPL(dasd_generic_restore_device);
2517
2413static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device, 2518static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
2414 void *rdc_buffer, 2519 void *rdc_buffer,
2415 int rdc_buffer_size, 2520 int rdc_buffer_size,