Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 501183f2 authored by Stefan Haberland's avatar Stefan Haberland Committed by Martin Schwidefsky
Browse files

[S390] dasd: add dynamic pav toleration



For base Parallel Access Volume (PAV) there is a fixed mapping of
base and alias devices. With dynamic PAV this mapping can be changed
so that an alias device is used with another base device.
This patch enables the DASD device driver to tolerate dynamic PAV
changes.

Signed-off-by: default avatarStefan Haberland <stefan.haberland@de.ibm.com>
Signed-off-by: default avatarHeiko Carstens <heiko.carstens@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent f3cb31e4
Loading
Loading
Loading
Loading
+22 −0
Original line number Original line Diff line number Diff line
@@ -65,6 +65,7 @@ static void dasd_device_tasklet(struct dasd_device *);
static void dasd_block_tasklet(struct dasd_block *);
static void dasd_block_tasklet(struct dasd_block *);
static void do_kick_device(struct work_struct *);
static void do_kick_device(struct work_struct *);
static void do_restore_device(struct work_struct *);
static void do_restore_device(struct work_struct *);
static void do_reload_device(struct work_struct *);
static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
static void dasd_return_cqr_cb(struct dasd_ccw_req *, void *);
static void dasd_device_timeout(unsigned long);
static void dasd_device_timeout(unsigned long);
static void dasd_block_timeout(unsigned long);
static void dasd_block_timeout(unsigned long);
@@ -115,6 +116,7 @@ struct dasd_device *dasd_alloc_device(void)
	device->timer.data = (unsigned long) device;
	device->timer.data = (unsigned long) device;
	INIT_WORK(&device->kick_work, do_kick_device);
	INIT_WORK(&device->kick_work, do_kick_device);
	INIT_WORK(&device->restore_device, do_restore_device);
	INIT_WORK(&device->restore_device, do_restore_device);
	INIT_WORK(&device->reload_device, do_reload_device);
	device->state = DASD_STATE_NEW;
	device->state = DASD_STATE_NEW;
	device->target = DASD_STATE_NEW;
	device->target = DASD_STATE_NEW;
	mutex_init(&device->state_mutex);
	mutex_init(&device->state_mutex);
@@ -520,6 +522,26 @@ void dasd_kick_device(struct dasd_device *device)
	schedule_work(&device->kick_work);
	schedule_work(&device->kick_work);
}
}


/*
 * dasd_reload_device will schedule a call do do_reload_device to the kernel
 * event daemon.
 */
static void do_reload_device(struct work_struct *work)
{
	struct dasd_device *device = container_of(work, struct dasd_device,
						  reload_device);
	device->discipline->reload(device);
	dasd_put_device(device);
}

void dasd_reload_device(struct dasd_device *device)
{
	dasd_get_device(device);
	/* queue call to dasd_reload_device to the kernel event daemon. */
	schedule_work(&device->reload_device);
}
EXPORT_SYMBOL(dasd_reload_device);

/*
/*
 * dasd_restore_device will schedule a call do do_restore_device to the kernel
 * dasd_restore_device will schedule a call do do_restore_device to the kernel
 * event daemon.
 * event daemon.
+20 −0
Original line number Original line Diff line number Diff line
@@ -1418,9 +1418,29 @@ static struct dasd_ccw_req *dasd_3990_erp_inspect_alias(
						struct dasd_ccw_req *erp)
						struct dasd_ccw_req *erp)
{
{
	struct dasd_ccw_req *cqr = erp->refers;
	struct dasd_ccw_req *cqr = erp->refers;
	char *sense;


	if (cqr->block &&
	if (cqr->block &&
	    (cqr->block->base != cqr->startdev)) {
	    (cqr->block->base != cqr->startdev)) {

		sense = dasd_get_sense(&erp->refers->irb);
		/*
		 * dynamic pav may have changed base alias mapping
		 */
		if (!test_bit(DASD_FLAG_OFFLINE, &cqr->startdev->flags) && sense
		    && (sense[0] == 0x10) && (sense[7] == 0x0F)
		    && (sense[8] == 0x67)) {
			/*
			 * remove device from alias handling to prevent new
			 * requests from being scheduled on the
			 * wrong alias device
			 */
			dasd_alias_remove_device(cqr->startdev);

			/* schedule worker to reload device */
			dasd_reload_device(cqr->startdev);
		}

		if (cqr->startdev->features & DASD_FEATURE_ERPLOG) {
		if (cqr->startdev->features & DASD_FEATURE_ERPLOG) {
			DBF_DEV_EVENT(DBF_ERR, cqr->startdev,
			DBF_DEV_EVENT(DBF_ERR, cqr->startdev,
				    "ERP on alias device for request %p,"
				    "ERP on alias device for request %p,"
+8 −0
Original line number Original line Diff line number Diff line
@@ -642,6 +642,14 @@ int dasd_alias_add_device(struct dasd_device *device)
	return rc;
	return rc;
}
}


int dasd_alias_update_add_device(struct dasd_device *device)
{
	struct dasd_eckd_private *private;
	private = (struct dasd_eckd_private *) device->private;
	private->lcu->flags |= UPDATE_PENDING;
	return dasd_alias_add_device(device);
}

int dasd_alias_remove_device(struct dasd_device *device)
int dasd_alias_remove_device(struct dasd_device *device)
{
{
	struct dasd_eckd_private *private;
	struct dasd_eckd_private *private;
+68 −2
Original line number Original line Diff line number Diff line
@@ -1451,6 +1451,7 @@ static int dasd_eckd_ready_to_online(struct dasd_device *device)


static int dasd_eckd_online_to_ready(struct dasd_device *device)
static int dasd_eckd_online_to_ready(struct dasd_device *device)
{
{
	cancel_work_sync(&device->reload_device);
	return dasd_alias_remove_device(device);
	return dasd_alias_remove_device(device);
};
};


@@ -1709,10 +1710,27 @@ static void dasd_eckd_handle_unsolicited_interrupt(struct dasd_device *device,
{
{
	char mask;
	char mask;
	char *sense = NULL;
	char *sense = NULL;
	struct dasd_eckd_private *private;


	private = (struct dasd_eckd_private *) device->private;
	/* first of all check for state change pending interrupt */
	/* first of all check for state change pending interrupt */
	mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
	mask = DEV_STAT_ATTENTION | DEV_STAT_DEV_END | DEV_STAT_UNIT_EXCEP;
	if ((scsw_dstat(&irb->scsw) & mask) == mask) {
	if ((scsw_dstat(&irb->scsw) & mask) == mask) {
		/* for alias only and not in offline processing*/
		if (!device->block && private->lcu &&
		    !test_bit(DASD_FLAG_OFFLINE, &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 */
			dasd_reload_device(device);
		}

		dasd_generic_handle_state_change(device);
		dasd_generic_handle_state_change(device);
		return;
		return;
	}
	}
@@ -3259,7 +3277,7 @@ static void dasd_eckd_dump_sense(struct dasd_device *device,
		dasd_eckd_dump_sense_ccw(device, req, irb);
		dasd_eckd_dump_sense_ccw(device, req, irb);
}
}


int dasd_eckd_pm_freeze(struct dasd_device *device)
static int dasd_eckd_pm_freeze(struct dasd_device *device)
{
{
	/*
	/*
	 * the device should be disconnected from our LCU structure
	 * the device should be disconnected from our LCU structure
@@ -3272,7 +3290,7 @@ int dasd_eckd_pm_freeze(struct dasd_device *device)
	return 0;
	return 0;
}
}


int dasd_eckd_restore_device(struct dasd_device *device)
static int dasd_eckd_restore_device(struct dasd_device *device)
{
{
	struct dasd_eckd_private *private;
	struct dasd_eckd_private *private;
	struct dasd_eckd_characteristics temp_rdc_data;
	struct dasd_eckd_characteristics temp_rdc_data;
@@ -3336,6 +3354,53 @@ int dasd_eckd_restore_device(struct dasd_device *device)
	return -1;
	return -1;
}
}


static int dasd_eckd_reload_device(struct dasd_device *device)
{
	struct dasd_eckd_private *private;
	int rc, old_base;
	char uid[60];

	private = (struct dasd_eckd_private *) device->private;
	old_base = private->uid.base_unit_addr;
	/* Read Configuration Data */
	rc = dasd_eckd_read_conf(device);
	if (rc)
		goto out_err;

	rc = dasd_eckd_generate_uid(device, &private->uid);
	if (rc)
		goto out_err;

	dasd_set_uid(device->cdev, &private->uid);

	/*
	 * update unit address configuration and
	 * add device to alias management
	 */
	dasd_alias_update_add_device(device);

	if (old_base != private->uid.base_unit_addr) {
		if (strlen(private->uid.vduit) > 0)
			snprintf(uid, 60, "%s.%s.%04x.%02x.%s",
				 private->uid.vendor, private->uid.serial,
				 private->uid.ssid, private->uid.base_unit_addr,
				 private->uid.vduit);
		else
			snprintf(uid, 60, "%s.%s.%04x.%02x",
				 private->uid.vendor, private->uid.serial,
				 private->uid.ssid,
				 private->uid.base_unit_addr);

		dev_info(&device->cdev->dev,
			 "An Alias device was reassigned to a new base device "
			 "with UID: %s\n", uid);
	}
	return 0;

out_err:
	return -1;
}

static struct ccw_driver dasd_eckd_driver = {
static struct ccw_driver dasd_eckd_driver = {
	.name	     = "dasd-eckd",
	.name	     = "dasd-eckd",
	.owner	     = THIS_MODULE,
	.owner	     = THIS_MODULE,
@@ -3389,6 +3454,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
	.ioctl = dasd_eckd_ioctl,
	.ioctl = dasd_eckd_ioctl,
	.freeze = dasd_eckd_pm_freeze,
	.freeze = dasd_eckd_pm_freeze,
	.restore = dasd_eckd_restore_device,
	.restore = dasd_eckd_restore_device,
	.reload = dasd_eckd_reload_device,
};
};


static int __init
static int __init
+1 −1
Original line number Original line Diff line number Diff line
@@ -426,7 +426,6 @@ struct alias_pav_group {
	struct dasd_device *next;
	struct dasd_device *next;
};
};



struct dasd_eckd_private {
struct dasd_eckd_private {
	struct dasd_eckd_characteristics rdc_data;
	struct dasd_eckd_characteristics rdc_data;
	u8 *conf_data;
	u8 *conf_data;
@@ -463,4 +462,5 @@ void dasd_alias_handle_summary_unit_check(struct dasd_device *, struct irb *);
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 *);
int dasd_alias_update_add_device(struct dasd_device *);
#endif				/* DASD_ECKD_H */
#endif				/* DASD_ECKD_H */
Loading