@ -5,8 +5,7 @@
* Carsten Otte < Cotte @ de . ibm . com >
* Martin Schwidefsky < schwidefsky @ de . ibm . com >
* Bugreports . to . . : < Linux390 @ de . ibm . com >
* ( C ) IBM Corporation , IBM Deutschland Entwicklung GmbH , 1999 - 2001
*
* Copyright IBM Corp . 1999 , 2009
*/
# define KMSG_COMPONENT "dasd"
@ -61,6 +60,7 @@ static int dasd_flush_block_queue(struct dasd_block *);
static void dasd_device_tasklet ( struct dasd_device * ) ;
static void dasd_block_tasklet ( struct dasd_block * ) ;
static void do_kick_device ( struct work_struct * ) ;
static void do_restore_device ( struct work_struct * ) ;
static void dasd_return_cqr_cb ( struct dasd_ccw_req * , void * ) ;
static void dasd_device_timeout ( unsigned long ) ;
static void dasd_block_timeout ( unsigned long ) ;
@ -109,6 +109,7 @@ struct dasd_device *dasd_alloc_device(void)
device - > timer . function = dasd_device_timeout ;
device - > timer . data = ( unsigned long ) device ;
INIT_WORK ( & device - > kick_work , do_kick_device ) ;
INIT_WORK ( & device - > restore_device , do_restore_device ) ;
device - > state = DASD_STATE_NEW ;
device - > target = DASD_STATE_NEW ;
@ -511,6 +512,25 @@ void dasd_kick_device(struct dasd_device *device)
schedule_work ( & device - > kick_work ) ;
}
/*
* dasd_restore_device will schedule a call do do_restore_device to the kernel
* event daemon .
*/
static void do_restore_device ( struct work_struct * work )
{
struct dasd_device * device = container_of ( work , struct dasd_device ,
restore_device ) ;
device - > cdev - > drv - > restore ( device - > cdev ) ;
dasd_put_device ( device ) ;
}
void dasd_restore_device ( struct dasd_device * device )
{
dasd_get_device ( device ) ;
/* queue call to dasd_restore_device to the kernel event daemon. */
schedule_work ( & device - > restore_device ) ;
}
/*
* Set the target state for a device and starts the state change .
*/
@ -908,6 +928,12 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
DBF_DEV_EVENT ( DBF_DEBUG , device , " %s " ,
" start_IO: -EIO device gone, retry " ) ;
break ;
case - EINVAL :
/* most likely caused in power management context */
DBF_DEV_EVENT ( DBF_DEBUG , device , " %s " ,
" start_IO: -EINVAL device currently "
" not accessible " ) ;
break ;
default :
/* internal error 11 - unknown rc */
snprintf ( errorstring , ERRORLENGTH , " 11 %d " , rc ) ;
@ -2400,6 +2426,12 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
case CIO_OPER :
/* FIXME: add a sanity check. */
device - > stopped & = ~ DASD_STOPPED_DC_WAIT ;
if ( device - > stopped & DASD_UNRESUMED_PM ) {
device - > stopped & = ~ DASD_UNRESUMED_PM ;
dasd_restore_device ( device ) ;
ret = 1 ;
break ;
}
dasd_schedule_device_bh ( device ) ;
if ( device - > block )
dasd_schedule_block_bh ( device - > block ) ;
@ -2410,6 +2442,79 @@ int dasd_generic_notify(struct ccw_device *cdev, int event)
return ret ;
}
int dasd_generic_pm_freeze ( struct ccw_device * cdev )
{
struct dasd_ccw_req * cqr , * n ;
int rc ;
struct list_head freeze_queue ;
struct dasd_device * device = dasd_device_from_cdev ( cdev ) ;
if ( IS_ERR ( device ) )
return PTR_ERR ( device ) ;
/* disallow new I/O */
device - > stopped | = DASD_STOPPED_PM ;
/* clear active requests */
INIT_LIST_HEAD ( & freeze_queue ) ;
spin_lock_irq ( get_ccwdev_lock ( cdev ) ) ;
rc = 0 ;
list_for_each_entry_safe ( cqr , n , & device - > ccw_queue , devlist ) {
/* Check status and move request to flush_queue */
if ( cqr - > status = = DASD_CQR_IN_IO ) {
rc = device - > discipline - > term_IO ( cqr ) ;
if ( rc ) {
/* unable to terminate requeust */
dev_err ( & device - > cdev - > dev ,
" Unable to terminate request %p "
" on suspend \n " , cqr ) ;
spin_unlock_irq ( get_ccwdev_lock ( cdev ) ) ;
dasd_put_device ( device ) ;
return rc ;
}
}
list_move_tail ( & cqr - > devlist , & freeze_queue ) ;
}
spin_unlock_irq ( get_ccwdev_lock ( cdev ) ) ;
list_for_each_entry_safe ( cqr , n , & freeze_queue , devlist ) {
wait_event ( dasd_flush_wq ,
( cqr - > status ! = DASD_CQR_CLEAR_PENDING ) ) ;
if ( cqr - > status = = DASD_CQR_CLEARED )
cqr - > status = DASD_CQR_QUEUED ;
}
/* move freeze_queue to start of the ccw_queue */
spin_lock_irq ( get_ccwdev_lock ( cdev ) ) ;
list_splice_tail ( & freeze_queue , & device - > ccw_queue ) ;
spin_unlock_irq ( get_ccwdev_lock ( cdev ) ) ;
if ( device - > discipline - > freeze )
rc = device - > discipline - > freeze ( device ) ;
dasd_put_device ( device ) ;
return rc ;
}
EXPORT_SYMBOL_GPL ( dasd_generic_pm_freeze ) ;
int dasd_generic_restore_device ( struct ccw_device * cdev )
{
struct dasd_device * device = dasd_device_from_cdev ( cdev ) ;
int rc = 0 ;
if ( IS_ERR ( device ) )
return PTR_ERR ( device ) ;
dasd_schedule_device_bh ( device ) ;
if ( device - > block )
dasd_schedule_block_bh ( device - > block ) ;
if ( device - > discipline - > restore )
rc = device - > discipline - > restore ( device ) ;
dasd_put_device ( device ) ;
return rc ;
}
EXPORT_SYMBOL_GPL ( dasd_generic_restore_device ) ;
static struct dasd_ccw_req * dasd_generic_build_rdc ( struct dasd_device * device ,
void * rdc_buffer ,
int rdc_buffer_size ,