diff options
author | Stefan Haberland <stefan.haberland@de.ibm.com> | 2012-11-28 07:43:38 -0500 |
---|---|---|
committer | Martin Schwidefsky <schwidefsky@de.ibm.com> | 2012-11-30 09:40:44 -0500 |
commit | d07dc5d8ab6f15353c866e2768c389abdc1faba6 (patch) | |
tree | 9ecf558a0e3d128534b56200b0f411aab315fc0f /drivers/s390/block | |
parent | 55d3a85cd2fa3274a6dfa0901a3be342b433bfa0 (diff) |
s390/dasd: add safe offline interface
The regular behavior of the DASD device driver when setting a device
offline is to return all outstanding I/O as failed. This behavior is
different from that of other System z operating systems and may lead
to unexpected data loss. Adding an explicit 'safe' offline function
will allow customers to use DASDs in the way they expect them to work.
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')
-rw-r--r-- | drivers/s390/block/dasd.c | 97 | ||||
-rw-r--r-- | drivers/s390/block/dasd_devmap.c | 34 | ||||
-rw-r--r-- | drivers/s390/block/dasd_int.h | 2 |
3 files changed, 116 insertions, 17 deletions
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 0595c763dafd..29225e1c159c 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c | |||
@@ -349,6 +349,16 @@ static int dasd_state_basic_to_ready(struct dasd_device *device) | |||
349 | return rc; | 349 | return rc; |
350 | } | 350 | } |
351 | 351 | ||
352 | static inline | ||
353 | int _wait_for_empty_queues(struct dasd_device *device) | ||
354 | { | ||
355 | if (device->block) | ||
356 | return list_empty(&device->ccw_queue) && | ||
357 | list_empty(&device->block->ccw_queue); | ||
358 | else | ||
359 | return list_empty(&device->ccw_queue); | ||
360 | } | ||
361 | |||
352 | /* | 362 | /* |
353 | * Remove device from block device layer. Destroy dirty buffers. | 363 | * Remove device from block device layer. Destroy dirty buffers. |
354 | * Forget format information. Check if the target level is basic | 364 | * Forget format information. Check if the target level is basic |
@@ -1841,6 +1851,13 @@ static void __dasd_device_check_expire(struct dasd_device *device) | |||
1841 | cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); | 1851 | cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist); |
1842 | if ((cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) && | 1852 | if ((cqr->status == DASD_CQR_IN_IO && cqr->expires != 0) && |
1843 | (time_after_eq(jiffies, cqr->expires + cqr->starttime))) { | 1853 | (time_after_eq(jiffies, cqr->expires + cqr->starttime))) { |
1854 | if (test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { | ||
1855 | /* | ||
1856 | * IO in safe offline processing should not | ||
1857 | * run out of retries | ||
1858 | */ | ||
1859 | cqr->retries++; | ||
1860 | } | ||
1844 | if (device->discipline->term_IO(cqr) != 0) { | 1861 | if (device->discipline->term_IO(cqr) != 0) { |
1845 | /* Hmpf, try again in 5 sec */ | 1862 | /* Hmpf, try again in 5 sec */ |
1846 | dev_err(&device->cdev->dev, | 1863 | dev_err(&device->cdev->dev, |
@@ -3024,11 +3041,11 @@ void dasd_generic_remove(struct ccw_device *cdev) | |||
3024 | 3041 | ||
3025 | cdev->handler = NULL; | 3042 | cdev->handler = NULL; |
3026 | 3043 | ||
3027 | dasd_remove_sysfs_files(cdev); | ||
3028 | device = dasd_device_from_cdev(cdev); | 3044 | device = dasd_device_from_cdev(cdev); |
3029 | if (IS_ERR(device)) | 3045 | if (IS_ERR(device)) |
3030 | return; | 3046 | return; |
3031 | if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) { | 3047 | if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags) && |
3048 | !test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { | ||
3032 | /* Already doing offline processing */ | 3049 | /* Already doing offline processing */ |
3033 | dasd_put_device(device); | 3050 | dasd_put_device(device); |
3034 | return; | 3051 | return; |
@@ -3048,6 +3065,8 @@ void dasd_generic_remove(struct ccw_device *cdev) | |||
3048 | */ | 3065 | */ |
3049 | if (block) | 3066 | if (block) |
3050 | dasd_free_block(block); | 3067 | dasd_free_block(block); |
3068 | |||
3069 | dasd_remove_sysfs_files(cdev); | ||
3051 | } | 3070 | } |
3052 | 3071 | ||
3053 | /* | 3072 | /* |
@@ -3126,16 +3145,13 @@ int dasd_generic_set_offline(struct ccw_device *cdev) | |||
3126 | { | 3145 | { |
3127 | struct dasd_device *device; | 3146 | struct dasd_device *device; |
3128 | struct dasd_block *block; | 3147 | struct dasd_block *block; |
3129 | int max_count, open_count; | 3148 | int max_count, open_count, rc; |
3130 | 3149 | ||
3150 | rc = 0; | ||
3131 | device = dasd_device_from_cdev(cdev); | 3151 | device = dasd_device_from_cdev(cdev); |
3132 | if (IS_ERR(device)) | 3152 | if (IS_ERR(device)) |
3133 | return PTR_ERR(device); | 3153 | return PTR_ERR(device); |
3134 | if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) { | 3154 | |
3135 | /* Already doing offline processing */ | ||
3136 | dasd_put_device(device); | ||
3137 | return 0; | ||
3138 | } | ||
3139 | /* | 3155 | /* |
3140 | * We must make sure that this device is currently not in use. | 3156 | * We must make sure that this device is currently not in use. |
3141 | * The open_count is increased for every opener, that includes | 3157 | * The open_count is increased for every opener, that includes |
@@ -3159,6 +3175,54 @@ int dasd_generic_set_offline(struct ccw_device *cdev) | |||
3159 | return -EBUSY; | 3175 | return -EBUSY; |
3160 | } | 3176 | } |
3161 | } | 3177 | } |
3178 | |||
3179 | if (test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { | ||
3180 | /* | ||
3181 | * safe offline allready running | ||
3182 | * could only be called by normal offline so safe_offline flag | ||
3183 | * needs to be removed to run normal offline and kill all I/O | ||
3184 | */ | ||
3185 | if (test_and_set_bit(DASD_FLAG_OFFLINE, &device->flags)) { | ||
3186 | /* Already doing normal offline processing */ | ||
3187 | dasd_put_device(device); | ||
3188 | return -EBUSY; | ||
3189 | } else | ||
3190 | clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags); | ||
3191 | |||
3192 | } else | ||
3193 | if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) { | ||
3194 | /* Already doing offline processing */ | ||
3195 | dasd_put_device(device); | ||
3196 | return -EBUSY; | ||
3197 | } | ||
3198 | |||
3199 | /* | ||
3200 | * if safe_offline called set safe_offline_running flag and | ||
3201 | * clear safe_offline so that a call to normal offline | ||
3202 | * can overrun safe_offline processing | ||
3203 | */ | ||
3204 | if (test_and_clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags) && | ||
3205 | !test_and_set_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { | ||
3206 | /* | ||
3207 | * If we want to set the device safe offline all IO operations | ||
3208 | * should be finished before continuing the offline process | ||
3209 | * so sync bdev first and then wait for our queues to become | ||
3210 | * empty | ||
3211 | */ | ||
3212 | /* sync blockdev and partitions */ | ||
3213 | rc = fsync_bdev(device->block->bdev); | ||
3214 | if (rc != 0) | ||
3215 | goto interrupted; | ||
3216 | |||
3217 | /* schedule device tasklet and wait for completion */ | ||
3218 | dasd_schedule_device_bh(device); | ||
3219 | rc = wait_event_interruptible(shutdown_waitq, | ||
3220 | _wait_for_empty_queues(device)); | ||
3221 | if (rc != 0) | ||
3222 | goto interrupted; | ||
3223 | } | ||
3224 | |||
3225 | set_bit(DASD_FLAG_OFFLINE, &device->flags); | ||
3162 | dasd_set_target_state(device, DASD_STATE_NEW); | 3226 | dasd_set_target_state(device, DASD_STATE_NEW); |
3163 | /* dasd_delete_device destroys the device reference. */ | 3227 | /* dasd_delete_device destroys the device reference. */ |
3164 | block = device->block; | 3228 | block = device->block; |
@@ -3170,6 +3234,14 @@ int dasd_generic_set_offline(struct ccw_device *cdev) | |||
3170 | if (block) | 3234 | if (block) |
3171 | dasd_free_block(block); | 3235 | dasd_free_block(block); |
3172 | return 0; | 3236 | return 0; |
3237 | |||
3238 | interrupted: | ||
3239 | /* interrupted by signal */ | ||
3240 | clear_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags); | ||
3241 | clear_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags); | ||
3242 | clear_bit(DASD_FLAG_OFFLINE, &device->flags); | ||
3243 | dasd_put_device(device); | ||
3244 | return rc; | ||
3173 | } | 3245 | } |
3174 | 3246 | ||
3175 | int dasd_generic_last_path_gone(struct dasd_device *device) | 3247 | int dasd_generic_last_path_gone(struct dasd_device *device) |
@@ -3489,15 +3561,6 @@ char *dasd_get_sense(struct irb *irb) | |||
3489 | } | 3561 | } |
3490 | EXPORT_SYMBOL_GPL(dasd_get_sense); | 3562 | EXPORT_SYMBOL_GPL(dasd_get_sense); |
3491 | 3563 | ||
3492 | static inline int _wait_for_empty_queues(struct dasd_device *device) | ||
3493 | { | ||
3494 | if (device->block) | ||
3495 | return list_empty(&device->ccw_queue) && | ||
3496 | list_empty(&device->block->ccw_queue); | ||
3497 | else | ||
3498 | return list_empty(&device->ccw_queue); | ||
3499 | } | ||
3500 | |||
3501 | void dasd_generic_shutdown(struct ccw_device *cdev) | 3564 | void dasd_generic_shutdown(struct ccw_device *cdev) |
3502 | { | 3565 | { |
3503 | struct dasd_device *device; | 3566 | struct dasd_device *device; |
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c index b2b8c18eeced..4d12370337aa 100644 --- a/drivers/s390/block/dasd_devmap.c +++ b/drivers/s390/block/dasd_devmap.c | |||
@@ -952,6 +952,39 @@ static DEVICE_ATTR(raw_track_access, 0644, dasd_use_raw_show, | |||
952 | dasd_use_raw_store); | 952 | dasd_use_raw_store); |
953 | 953 | ||
954 | static ssize_t | 954 | static ssize_t |
955 | dasd_safe_offline_store(struct device *dev, struct device_attribute *attr, | ||
956 | const char *buf, size_t count) | ||
957 | { | ||
958 | struct ccw_device *cdev = to_ccwdev(dev); | ||
959 | struct dasd_device *device; | ||
960 | int rc; | ||
961 | |||
962 | device = dasd_device_from_cdev(cdev); | ||
963 | if (IS_ERR(device)) { | ||
964 | rc = PTR_ERR(device); | ||
965 | goto out; | ||
966 | } | ||
967 | |||
968 | if (test_bit(DASD_FLAG_OFFLINE, &device->flags) || | ||
969 | test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) { | ||
970 | /* Already doing offline processing */ | ||
971 | dasd_put_device(device); | ||
972 | rc = -EBUSY; | ||
973 | goto out; | ||
974 | } | ||
975 | |||
976 | set_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags); | ||
977 | dasd_put_device(device); | ||
978 | |||
979 | rc = ccw_device_set_offline(cdev); | ||
980 | |||
981 | out: | ||
982 | return rc ? rc : count; | ||
983 | } | ||
984 | |||
985 | static DEVICE_ATTR(safe_offline, 0200, NULL, dasd_safe_offline_store); | ||
986 | |||
987 | static ssize_t | ||
955 | dasd_discipline_show(struct device *dev, struct device_attribute *attr, | 988 | dasd_discipline_show(struct device *dev, struct device_attribute *attr, |
956 | char *buf) | 989 | char *buf) |
957 | { | 990 | { |
@@ -1320,6 +1353,7 @@ static struct attribute * dasd_attrs[] = { | |||
1320 | &dev_attr_expires.attr, | 1353 | &dev_attr_expires.attr, |
1321 | &dev_attr_reservation_policy.attr, | 1354 | &dev_attr_reservation_policy.attr, |
1322 | &dev_attr_last_known_reservation_state.attr, | 1355 | &dev_attr_last_known_reservation_state.attr, |
1356 | &dev_attr_safe_offline.attr, | ||
1323 | NULL, | 1357 | NULL, |
1324 | }; | 1358 | }; |
1325 | 1359 | ||
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h index 7ff93eea673d..899e3f5a56e5 100644 --- a/drivers/s390/block/dasd_int.h +++ b/drivers/s390/block/dasd_int.h | |||
@@ -516,6 +516,8 @@ struct dasd_block { | |||
516 | #define DASD_FLAG_IS_RESERVED 7 /* The device is reserved */ | 516 | #define DASD_FLAG_IS_RESERVED 7 /* The device is reserved */ |
517 | #define DASD_FLAG_LOCK_STOLEN 8 /* The device lock was stolen */ | 517 | #define DASD_FLAG_LOCK_STOLEN 8 /* The device lock was stolen */ |
518 | #define DASD_FLAG_SUSPENDED 9 /* The device was suspended */ | 518 | #define DASD_FLAG_SUSPENDED 9 /* The device was suspended */ |
519 | #define DASD_FLAG_SAFE_OFFLINE 10 /* safe offline processing requested*/ | ||
520 | #define DASD_FLAG_SAFE_OFFLINE_RUNNING 11 /* safe offline running */ | ||
519 | 521 | ||
520 | 522 | ||
521 | void dasd_put_device_wake(struct dasd_device *); | 523 | void dasd_put_device_wake(struct dasd_device *); |