Commit 59a9ed5f authored by Stefan Haberland's avatar Stefan Haberland Committed by Martin Schwidefsky

s390/dasd: reorder lcu and device lock

Reorder lcu and device lock to get rid of the error-prone trylock
mechanism.

The locking order is lcu lock -> device lock.
This protects against changes to the lcu device lists and enables us
to iterate over the devices, take the cdev lock and make changes to
the device structures.

The complicated part is the summary unit check handler that gets an
interrupt on one device of the lcu that leads to structural changes of
the whole lcu itself. This work needs to be done even if devices on
the lcu disappear. So a device independent worker is used.
The old approach tried to update some lcu structures and set up the
lcu worker in the interrupt context with the device lock held.
But this forced the lock order "cdev lock -> lcu lock" that made it
hard to have the lcu lock held and iterate over all devices and change
them.

The new approach is to schedule a device specific worker that gets
out of the interrupt context and rid of the device lock for summary
unit checks. This worker is able to take the lcu lock and schedule the
lcu worker that updates all devices. The time between interrupt and
worker execution is no problem because the devices in the lcu reject
all I/O in this time with an appropriate error. The dasd driver can
deal with this situation and re-drive the I/O later on.
Signed-off-by: default avatarStefan Haberland <sth@linux.vnet.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 1e3c1dd1
This diff is collapsed.
...@@ -1682,6 +1682,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device) ...@@ -1682,6 +1682,8 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
/* setup work queue for validate server*/ /* setup work queue for validate server*/
INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server); INIT_WORK(&device->kick_validate, dasd_eckd_do_validate_server);
/* setup work queue for summary unit check */
INIT_WORK(&device->suc_work, dasd_alias_handle_summary_unit_check);
if (!ccw_device_is_pathgroup(device->cdev)) { if (!ccw_device_is_pathgroup(device->cdev)) {
dev_warn(&device->cdev->dev, dev_warn(&device->cdev->dev,
...@@ -2549,14 +2551,6 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device, ...@@ -2549,14 +2551,6 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device,
device->state == DASD_STATE_ONLINE && device->state == DASD_STATE_ONLINE &&
!test_bit(DASD_FLAG_OFFLINE, &device->flags) && !test_bit(DASD_FLAG_OFFLINE, &device->flags) &&
!test_bit(DASD_FLAG_SUSPENDED, &device->flags)) { !test_bit(DASD_FLAG_SUSPENDED, &device->flags)) {
/*
* the state change could be caused by an alias
* reassignment remove device from alias handling
* to prevent new requests from being scheduled on
* the wrong alias device
*/
dasd_alias_remove_device(device);
/* schedule worker to reload device */ /* schedule worker to reload device */
dasd_reload_device(device); dasd_reload_device(device);
} }
...@@ -2571,7 +2565,27 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device, ...@@ -2571,7 +2565,27 @@ static void dasd_eckd_check_for_device_change(struct dasd_device *device,
/* summary unit check */ /* summary unit check */
if ((sense[27] & DASD_SENSE_BIT_0) && (sense[7] == 0x0D) && if ((sense[27] & DASD_SENSE_BIT_0) && (sense[7] == 0x0D) &&
(scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) { (scsw_dstat(&irb->scsw) & DEV_STAT_UNIT_CHECK)) {
dasd_alias_handle_summary_unit_check(device, irb); if (test_and_set_bit(DASD_FLAG_SUC, &device->flags)) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"eckd suc: device already notified");
return;
}
sense = dasd_get_sense(irb);
if (!sense) {
DBF_DEV_EVENT(DBF_WARNING, device, "%s",
"eckd suc: no reason code available");
clear_bit(DASD_FLAG_SUC, &device->flags);
return;
}
private->suc_reason = sense[8];
DBF_DEV_EVENT(DBF_NOTICE, device, "%s %x",
"eckd handle summary unit check: reason",
private->suc_reason);
dasd_get_device(device);
if (!schedule_work(&device->suc_work))
dasd_put_device(device);
return; return;
} }
...@@ -4495,6 +4509,12 @@ static int dasd_eckd_reload_device(struct dasd_device *device) ...@@ -4495,6 +4509,12 @@ static int dasd_eckd_reload_device(struct dasd_device *device)
struct dasd_uid uid; struct dasd_uid uid;
unsigned long flags; unsigned long flags;
/*
* remove device from alias handling to prevent new requests
* from being scheduled on the wrong alias device
*/
dasd_alias_remove_device(device);
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags); spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
old_base = private->uid.base_unit_addr; old_base = private->uid.base_unit_addr;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags); spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
......
...@@ -525,6 +525,7 @@ struct dasd_eckd_private { ...@@ -525,6 +525,7 @@ struct dasd_eckd_private {
int count; int count;
u32 fcx_max_data; u32 fcx_max_data;
char suc_reason;
}; };
...@@ -534,7 +535,7 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *); ...@@ -534,7 +535,7 @@ void dasd_alias_disconnect_device_from_lcu(struct dasd_device *);
int dasd_alias_add_device(struct dasd_device *); int dasd_alias_add_device(struct dasd_device *);
int dasd_alias_remove_device(struct dasd_device *); int dasd_alias_remove_device(struct dasd_device *);
struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *); struct dasd_device *dasd_alias_get_start_dev(struct dasd_device *);
void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *); void dasd_alias_handle_summary_unit_check(struct work_struct *);
void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *); void dasd_eckd_reset_ccw_to_base_io(struct dasd_ccw_req *);
void dasd_alias_lcu_setup_complete(struct dasd_device *); void dasd_alias_lcu_setup_complete(struct dasd_device *);
void dasd_alias_wait_for_lcu_setup(struct dasd_device *); void dasd_alias_wait_for_lcu_setup(struct dasd_device *);
......
...@@ -470,6 +470,7 @@ struct dasd_device { ...@@ -470,6 +470,7 @@ struct dasd_device {
struct work_struct restore_device; struct work_struct restore_device;
struct work_struct reload_device; struct work_struct reload_device;
struct work_struct kick_validate; struct work_struct kick_validate;
struct work_struct suc_work;
struct timer_list timer; struct timer_list timer;
debug_info_t *debug_area; debug_info_t *debug_area;
...@@ -542,6 +543,7 @@ struct dasd_attention_data { ...@@ -542,6 +543,7 @@ struct dasd_attention_data {
#define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */ #define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */
#define DASD_FLAG_ABORTALL 12 /* Abort all noretry requests */ #define DASD_FLAG_ABORTALL 12 /* Abort all noretry requests */
#define DASD_FLAG_PATH_VERIFY 13 /* Path verification worker running */ #define DASD_FLAG_PATH_VERIFY 13 /* Path verification worker running */
#define DASD_FLAG_SUC 14 /* unhandled summary unit check */
#define DASD_SLEEPON_START_TAG ((void *) 1) #define DASD_SLEEPON_START_TAG ((void *) 1)
#define DASD_SLEEPON_END_TAG ((void *) 2) #define DASD_SLEEPON_END_TAG ((void *) 2)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment