Skip to content

Commit

Permalink
s390/dasd: add autoquiesce feature
Browse files Browse the repository at this point in the history
Add the internal logic to check for autoquiesce triggers and handle
them.

Quiesce and resume are functions that tell Linux to stop/resume
issuing I/Os to a specific DASD.
The DASD driver allows a manual quiesce/resume via ioctl.

Autoquiesce will define an amount of triggers that will lead to
an automatic quiesce if a certain event occurs.
There is no automatic resume.

All events will be reported via DASD Extended Error Reporting (EER)
if configured.

Signed-off-by: Stefan Haberland <sth@linux.ibm.com>
Reviewed-by: Jan Hoeppner <hoeppner@linux.ibm.com>
Reviewed-by: Halil Pasic <pasic@linux.ibm.com>
Link: https://lore.kernel.org/r/20230405142017.2446986-3-sth@linux.ibm.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
  • Loading branch information
Stefan Haberland authored and Jens Axboe committed Apr 12, 2023
1 parent 861d53d commit 1cee297
Show file tree
Hide file tree
Showing 4 changed files with 48 additions and 17 deletions.
2 changes: 2 additions & 0 deletions arch/s390/include/uapi/asm/dasd.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ typedef struct dasd_information2_t {
* 0x040: give access to raw eckd data
* 0x080: enable discard support
* 0x100: enable autodisable for IFCC errors (default)
* 0x200: enable requeue of all requests on autoquiesce
*/
#define DASD_FEATURE_READONLY 0x001
#define DASD_FEATURE_USEDIAG 0x002
Expand All @@ -88,6 +89,7 @@ typedef struct dasd_information2_t {
#define DASD_FEATURE_USERAW 0x040
#define DASD_FEATURE_DISCARD 0x080
#define DASD_FEATURE_PATH_AUTODISABLE 0x100
#define DASD_FEATURE_REQUEUEQUIESCE 0x200
#define DASD_FEATURE_DEFAULT DASD_FEATURE_PATH_AUTODISABLE

#define DASD_PARTN_BITS 2
Expand Down
60 changes: 43 additions & 17 deletions drivers/s390/block/dasd.c
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,8 @@ static void dasd_profile_init(struct dasd_profile *, struct dentry *);
static void dasd_profile_exit(struct dasd_profile *);
static void dasd_hosts_init(struct dentry *, struct dasd_device *);
static void dasd_hosts_exit(struct dasd_device *);

static int dasd_handle_autoquiesce(struct dasd_device *, struct dasd_ccw_req *,
unsigned int);
/*
* SECTION: Operations on the device structure.
*/
Expand Down Expand Up @@ -2325,7 +2326,7 @@ static int _dasd_sleep_on(struct dasd_ccw_req *maincqr, int interruptible)
/* Non-temporary stop condition will trigger fail fast */
if (device->stopped & ~DASD_STOPPED_PENDING &&
test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
(!dasd_eer_enabled(device))) {
!dasd_eer_enabled(device) && device->aq_mask == 0) {
cqr->status = DASD_CQR_FAILED;
cqr->intrc = -ENOLINK;
continue;
Expand Down Expand Up @@ -2801,20 +2802,18 @@ static void __dasd_process_block_ccw_queue(struct dasd_block *block,
dasd_log_sense(cqr, &cqr->irb);
}

/* First of all call extended error reporting. */
if (dasd_eer_enabled(base) &&
cqr->status == DASD_CQR_FAILED) {
dasd_eer_write(base, cqr, DASD_EER_FATALERROR);

/* restart request */
/*
* First call extended error reporting and check for autoquiesce
*/
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
if (cqr->status == DASD_CQR_FAILED &&
dasd_handle_autoquiesce(base, cqr, DASD_EER_FATALERROR)) {
cqr->status = DASD_CQR_FILLED;
cqr->retries = 255;
spin_lock_irqsave(get_ccwdev_lock(base->cdev), flags);
dasd_device_set_stop_bits(base, DASD_STOPPED_QUIESCE);
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev),
flags);
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);
goto restart;
}
spin_unlock_irqrestore(get_ccwdev_lock(base->cdev), flags);

/* Process finished ERP request. */
if (cqr->refers) {
Expand Down Expand Up @@ -2856,7 +2855,7 @@ static void __dasd_block_start_head(struct dasd_block *block)
/* Non-temporary stop condition will trigger fail fast */
if (block->base->stopped & ~DASD_STOPPED_PENDING &&
test_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags) &&
(!dasd_eer_enabled(block->base))) {
!dasd_eer_enabled(block->base) && block->base->aq_mask == 0) {
cqr->status = DASD_CQR_FAILED;
cqr->intrc = -ENOLINK;
dasd_schedule_block_bh(block);
Expand Down Expand Up @@ -3670,8 +3669,8 @@ int dasd_generic_last_path_gone(struct dasd_device *device)
dev_warn(&device->cdev->dev, "No operational channel path is left "
"for the device\n");
DBF_DEV_EVENT(DBF_WARNING, device, "%s", "last path gone");
/* First of all call extended error reporting. */
dasd_eer_write(device, NULL, DASD_EER_NOPATH);
/* First call extended error reporting and check for autoquiesce. */
dasd_handle_autoquiesce(device, NULL, DASD_EER_NOPATH);

if (device->state < DASD_STATE_BASIC)
return 0;
Expand Down Expand Up @@ -3803,7 +3802,8 @@ void dasd_generic_path_event(struct ccw_device *cdev, int *path_event)
"No verified channel paths remain for the device\n");
DBF_DEV_EVENT(DBF_WARNING, device,
"%s", "last verified path gone");
dasd_eer_write(device, NULL, DASD_EER_NOPATH);
/* First call extended error reporting and check for autoquiesce. */
dasd_handle_autoquiesce(device, NULL, DASD_EER_NOPATH);
dasd_device_set_stop_bits(device,
DASD_STOPPED_DC_WAIT);
}
Expand All @@ -3825,7 +3825,8 @@ EXPORT_SYMBOL_GPL(dasd_generic_verify_path);
void dasd_generic_space_exhaust(struct dasd_device *device,
struct dasd_ccw_req *cqr)
{
dasd_eer_write(device, NULL, DASD_EER_NOSPC);
/* First call extended error reporting and check for autoquiesce. */
dasd_handle_autoquiesce(device, NULL, DASD_EER_NOSPC);

if (device->state < DASD_STATE_BASIC)
return;
Expand Down Expand Up @@ -3958,6 +3959,31 @@ void dasd_schedule_requeue(struct dasd_device *device)
}
EXPORT_SYMBOL(dasd_schedule_requeue);

static int dasd_handle_autoquiesce(struct dasd_device *device,
struct dasd_ccw_req *cqr,
unsigned int reason)
{
/* in any case write eer message with reason */
if (dasd_eer_enabled(device))
dasd_eer_write(device, cqr, reason);

if (!test_bit(reason, &device->aq_mask))
return 0;

/* notify eer about autoquiesce */
if (dasd_eer_enabled(device))
dasd_eer_write(device, NULL, DASD_EER_AUTOQUIESCE);

pr_info("%s: The DASD has been put in the quiesce state\n",
dev_name(&device->cdev->dev));
dasd_device_set_stop_bits(device, DASD_STOPPED_QUIESCE);

if (device->features & DASD_FEATURE_REQUEUEQUIESCE)
dasd_schedule_requeue(device);

return 1;
}

static struct dasd_ccw_req *dasd_generic_build_rdc(struct dasd_device *device,
int rdc_buffer_size,
int magic)
Expand Down
1 change: 1 addition & 0 deletions drivers/s390/block/dasd_eer.c
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,7 @@ void dasd_eer_write(struct dasd_device *device, struct dasd_ccw_req *cqr,
break;
case DASD_EER_NOPATH:
case DASD_EER_NOSPC:
case DASD_EER_AUTOQUIESCE:
dasd_eer_write_standard_trigger(device, NULL, id);
break;
case DASD_EER_STATECHANGE:
Expand Down
2 changes: 2 additions & 0 deletions drivers/s390/block/dasd_int.h
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ extern struct dasd_discipline *dasd_diag_discipline_pointer;
#define DASD_EER_STATECHANGE 3
#define DASD_EER_PPRCSUSPEND 4
#define DASD_EER_NOSPC 5
#define DASD_EER_AUTOQUIESCE 31

/* DASD path handling */

Expand Down Expand Up @@ -627,6 +628,7 @@ struct dasd_device {
struct dasd_format_entry format_entry;
struct kset *paths_info;
struct dasd_copy_relation *copy;
unsigned long aq_mask;
};

struct dasd_block {
Expand Down

0 comments on commit 1cee297

Please sign in to comment.