Commit 1cee2975 authored by Stefan Haberland's avatar Stefan Haberland Committed by Jens Axboe
Browse files

s390/dasd: add autoquiesce feature



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: default avatarStefan Haberland <sth@linux.ibm.com>
Reviewed-by: default avatarJan Hoeppner <hoeppner@linux.ibm.com>
Reviewed-by: default avatarHalil Pasic <pasic@linux.ibm.com>
Link: https://lore.kernel.org/r/20230405142017.2446986-3-sth@linux.ibm.com


Signed-off-by: default avatarJens Axboe <axboe@kernel.dk>
parent 861d53db
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -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
@@ -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
+43 −17
Original line number Diff line number Diff line
@@ -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.
 */
@@ -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;
@@ -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) {
@@ -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);
@@ -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;
@@ -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);
	}
@@ -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;
@@ -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)
+1 −0
Original line number Diff line number Diff line
@@ -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:
+2 −0
Original line number Diff line number Diff line
@@ -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 */

@@ -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 {