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

Commit a4d26c6a authored by Stefan Weinhuber's avatar Stefan Weinhuber Committed by Martin Schwidefsky
Browse files

[S390] dasd: do path verification for paths added at runtime



When a new path is added at runtime, the CIO layer will call the drivers
path_event callback. The DASD device driver uses this callback to trigger
a path verification for the new path. The driver will use only those
paths for I/O, which have been successfully verified.

Signed-off-by: default avatarStefan Weinhuber <wein@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent ef19298b
Loading
Loading
Loading
Loading
+158 −44
Original line number Diff line number Diff line
@@ -913,6 +913,11 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
	cqr->startclk = get_clock();
	cqr->starttime = jiffies;
	cqr->retries--;
	if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) {
		cqr->lpm &= device->path_data.opm;
		if (!cqr->lpm)
			cqr->lpm = device->path_data.opm;
	}
	if (cqr->cpmode == 1) {
		rc = ccw_device_tm_start(device->cdev, cqr->cpaddr,
					 (long) cqr, cqr->lpm);
@@ -925,35 +930,53 @@ int dasd_start_IO(struct dasd_ccw_req *cqr)
		cqr->status = DASD_CQR_IN_IO;
		break;
	case -EBUSY:
		DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
			      "start_IO: device busy, retry later");
		break;
	case -ETIMEDOUT:
		DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
			      "start_IO: request timeout, retry later");
		break;
	case -EACCES:
		/* -EACCES indicates that the request used only a
		 * subset of the available pathes and all these
		 * pathes are gone.
		 * Do a retry with all available pathes.
		 */
		cqr->lpm = LPM_ANYPATH;
		/* -EACCES indicates that the request used only a subset of the
		 * available paths and all these paths are gone. If the lpm of
		 * this request was only a subset of the opm (e.g. the ppm) then
		 * we just do a retry with all available paths.
		 * If we already use the full opm, something is amiss, and we
		 * need a full path verification.
		 */
		if (test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags)) {
			DBF_DEV_EVENT(DBF_WARNING, device,
				      "start_IO: selected paths gone (%x)",
				      cqr->lpm);
		} else if (cqr->lpm != device->path_data.opm) {
			cqr->lpm = device->path_data.opm;
			DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
			      "start_IO: selected pathes gone,"
			      " retry on all pathes");
				      "start_IO: selected paths gone,"
				      " retry on all paths");
		} else {
			DBF_DEV_EVENT(DBF_WARNING, device, "%s",
				      "start_IO: all paths in opm gone,"
				      " do path verification");
			dasd_generic_last_path_gone(device);
			device->path_data.opm = 0;
			device->path_data.ppm = 0;
			device->path_data.npm = 0;
			device->path_data.tbvpm =
				ccw_device_get_path_mask(device->cdev);
		}
		break;
	case -ENODEV:
		DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
			      "start_IO: -ENODEV device gone, retry");
		break;
	case -EIO:
		DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
			      "start_IO: -EIO device gone, retry");
		break;
	case -EINVAL:
		/* most likely caused in power management context */
		DBF_DEV_EVENT(DBF_DEBUG, device, "%s",
		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
			      "start_IO: -EINVAL device currently "
			      "not accessible");
		break;
@@ -1175,12 +1198,13 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
		 */
		if (!test_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags) &&
		    cqr->retries > 0) {
			if (cqr->lpm == LPM_ANYPATH)
			if (cqr->lpm == device->path_data.opm)
				DBF_DEV_EVENT(DBF_DEBUG, device,
					      "default ERP in fastpath "
					      "(%i retries left)",
					      cqr->retries);
			cqr->lpm    = LPM_ANYPATH;
			if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))
				cqr->lpm = device->path_data.opm;
			cqr->status = DASD_CQR_QUEUED;
			next = cqr;
		} else
@@ -1364,8 +1388,14 @@ static void __dasd_device_start_head(struct dasd_device *device)
	cqr = list_entry(device->ccw_queue.next, struct dasd_ccw_req, devlist);
	if (cqr->status != DASD_CQR_QUEUED)
		return;
	/* when device is stopped, return request to previous layer */
	if (device->stopped) {
	/* when device is stopped, return request to previous layer
	 * exception: only the disconnect or unresumed bits are set and the
	 * cqr is a path verification request
	 */
	if (device->stopped &&
	    !(!(device->stopped & ~(DASD_STOPPED_DC_WAIT | DASD_UNRESUMED_PM))
	      && test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))) {
		cqr->intrc = -EAGAIN;
		cqr->status = DASD_CQR_CLEARED;
		dasd_schedule_device_bh(device);
		return;
@@ -1381,6 +1411,23 @@ static void __dasd_device_start_head(struct dasd_device *device)
		dasd_device_set_timer(device, 50);
}

static void __dasd_device_check_path_events(struct dasd_device *device)
{
	int rc;

	if (device->path_data.tbvpm) {
		if (device->stopped & ~(DASD_STOPPED_DC_WAIT |
					DASD_UNRESUMED_PM))
			return;
		rc = device->discipline->verify_path(
			device, device->path_data.tbvpm);
		if (rc)
			dasd_device_set_timer(device, 50);
		else
			device->path_data.tbvpm = 0;
	}
};

/*
 * Go through all request on the dasd_device request queue,
 * terminate them on the cdev if necessary, and return them to the
@@ -1455,6 +1502,7 @@ static void dasd_device_tasklet(struct dasd_device *device)
	__dasd_device_check_expire(device);
	/* find final requests on ccw queue */
	__dasd_device_process_ccw_queue(device, &final_queue);
	__dasd_device_check_path_events(device);
	spin_unlock_irq(get_ccwdev_lock(device->cdev));
	/* Now call the callback function of requests with final status */
	__dasd_device_process_final_queue(device, &final_queue);
@@ -2586,55 +2634,121 @@ int dasd_generic_set_offline(struct ccw_device *cdev)
	return 0;
}

int dasd_generic_notify(struct ccw_device *cdev, int event)
int dasd_generic_last_path_gone(struct dasd_device *device)
{
	struct dasd_device *device;
	struct dasd_ccw_req *cqr;
	int ret;

	device = dasd_device_from_cdev_locked(cdev);
	if (IS_ERR(device))
		return 0;
	ret = 0;
	switch (event) {
	case CIO_GONE:
	case CIO_BOXED:
	case CIO_NO_PATH:
	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);

	if (device->state < DASD_STATE_BASIC)
			break;
		return 0;
	/* Device is active. We want to keep it. */
	list_for_each_entry(cqr, &device->ccw_queue, devlist)
			if (cqr->status == DASD_CQR_IN_IO) {
		if ((cqr->status == DASD_CQR_IN_IO) ||
		    (cqr->status == DASD_CQR_CLEAR_PENDING)) {
			cqr->status = DASD_CQR_QUEUED;
			cqr->retries++;
		}
	dasd_device_set_stop_bits(device, DASD_STOPPED_DC_WAIT);
	dasd_device_clear_timer(device);
	dasd_schedule_device_bh(device);
		ret = 1;
		break;
	case CIO_OPER:
		/* FIXME: add a sanity check. */
	return 1;
}
EXPORT_SYMBOL_GPL(dasd_generic_last_path_gone);

int dasd_generic_path_operational(struct dasd_device *device)
{
	dev_info(&device->cdev->dev, "A channel path to the device has become "
		 "operational\n");
	DBF_DEV_EVENT(DBF_WARNING, device, "%s", "path operational");
	dasd_device_remove_stop_bits(device, DASD_STOPPED_DC_WAIT);
	if (device->stopped & DASD_UNRESUMED_PM) {
		dasd_device_remove_stop_bits(device, DASD_UNRESUMED_PM);
		dasd_restore_device(device);
			ret = 1;
			break;
		return 1;
	}
	dasd_schedule_device_bh(device);
	if (device->block)
		dasd_schedule_block_bh(device->block);
	return 1;
}
EXPORT_SYMBOL_GPL(dasd_generic_path_operational);

int dasd_generic_notify(struct ccw_device *cdev, int event)
{
	struct dasd_device *device;
	int ret;

	device = dasd_device_from_cdev_locked(cdev);
	if (IS_ERR(device))
		return 0;
	ret = 0;
	switch (event) {
	case CIO_GONE:
	case CIO_BOXED:
	case CIO_NO_PATH:
		device->path_data.opm = 0;
		device->path_data.ppm = 0;
		device->path_data.npm = 0;
		ret = dasd_generic_last_path_gone(device);
		break;
	case CIO_OPER:
		ret = 1;
		if (device->path_data.opm)
			ret = dasd_generic_path_operational(device);
		break;
	}
	dasd_put_device(device);
	return ret;
}

void dasd_generic_path_event(struct ccw_device *cdev, int *path_event)
{
	int chp;
	__u8 oldopm, eventlpm;
	struct dasd_device *device;

	device = dasd_device_from_cdev_locked(cdev);
	if (IS_ERR(device))
		return;
	for (chp = 0; chp < 8; chp++) {
		eventlpm = 0x80 >> chp;
		if (path_event[chp] & PE_PATH_GONE) {
			oldopm = device->path_data.opm;
			device->path_data.opm &= ~eventlpm;
			device->path_data.ppm &= ~eventlpm;
			device->path_data.npm &= ~eventlpm;
			if (oldopm && !device->path_data.opm)
				dasd_generic_last_path_gone(device);
		}
		if (path_event[chp] & PE_PATH_AVAILABLE) {
			device->path_data.opm &= ~eventlpm;
			device->path_data.ppm &= ~eventlpm;
			device->path_data.npm &= ~eventlpm;
			device->path_data.tbvpm |= eventlpm;
			dasd_schedule_device_bh(device);
		}
	}
	dasd_put_device(device);
}
EXPORT_SYMBOL_GPL(dasd_generic_path_event);

int dasd_generic_verify_path(struct dasd_device *device, __u8 lpm)
{
	if (!device->path_data.opm && lpm) {
		device->path_data.opm = lpm;
		dasd_generic_path_operational(device);
	} else
		device->path_data.opm |= lpm;
	return 0;
}
EXPORT_SYMBOL_GPL(dasd_generic_verify_path);


int dasd_generic_pm_freeze(struct ccw_device *cdev)
{
	struct dasd_ccw_req *cqr, *n;
+8 −8
Original line number Diff line number Diff line
@@ -152,9 +152,9 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
	opm = ccw_device_get_path_mask(device->cdev);
	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
	//FIXME: start with get_opm ?
	if (erp->lpm == 0)
		erp->lpm = LPM_ANYPATH & ~(erp->irb.esw.esw0.sublog.lpum);
		erp->lpm = device->path_data.opm &
			~(erp->irb.esw.esw0.sublog.lpum);
	else
		erp->lpm &= ~(erp->irb.esw.esw0.sublog.lpum);

@@ -270,10 +270,11 @@ static struct dasd_ccw_req *dasd_3990_erp_action_1(struct dasd_ccw_req *erp)
{
	erp->function = dasd_3990_erp_action_1;
	dasd_3990_erp_alternate_path(erp);
	if (erp->status == DASD_CQR_FAILED) {
	if (erp->status == DASD_CQR_FAILED &&
	    !test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) {
		erp->status = DASD_CQR_FILLED;
		erp->retries = 10;
		erp->lpm = LPM_ANYPATH;
		erp->lpm = erp->startdev->path_data.opm;
		erp->function = dasd_3990_erp_action_1_sec;
	}
	return erp;
@@ -1907,15 +1908,14 @@ dasd_3990_erp_compound_retry(struct dasd_ccw_req * erp, char *sense)
static void
dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
{

	if (sense[25] & DASD_SENSE_BIT_3) {
		dasd_3990_erp_alternate_path(erp);

		if (erp->status == DASD_CQR_FAILED) {
		if (erp->status == DASD_CQR_FAILED &&
		    !test_bit(DASD_CQR_VERIFY_PATH, &erp->flags)) {
			/* reset the lpm and the status to be able to
			 * try further actions. */

			erp->lpm = 0;
			erp->lpm = erp->startdev->path_data.opm;
			erp->status = DASD_CQR_NEED_ERP;
		}
	}
+1 −0
Original line number Diff line number Diff line
@@ -639,6 +639,7 @@ dasd_put_device_wake(struct dasd_device *device)
{
	wake_up(&dasd_delete_wq);
}
EXPORT_SYMBOL_GPL(dasd_put_device_wake);

/*
 * Return dasd_device structure associated with cdev.
+1 −0
Original line number Diff line number Diff line
@@ -619,6 +619,7 @@ static struct dasd_discipline dasd_diag_discipline = {
	.ebcname = "DIAG",
	.max_blocks = DIAG_MAX_BLOCKS,
	.check_device = dasd_diag_check_device,
	.verify_path = dasd_generic_verify_path,
	.fill_geometry = dasd_diag_fill_geometry,
	.start_IO = dasd_start_diag,
	.term_IO = dasd_diag_term_IO,
+221 −40
Original line number Diff line number Diff line
@@ -90,6 +90,18 @@ static struct {
} *dasd_reserve_req;
static DEFINE_MUTEX(dasd_reserve_mutex);

/* definitions for the path verification worker */
struct path_verification_work_data {
	struct work_struct worker;
	struct dasd_device *device;
	struct dasd_ccw_req cqr;
	struct ccw1 ccw;
	__u8 rcd_buffer[DASD_ECKD_RCD_DATA_SIZE];
	int isglobal;
	__u8 tbvpm;
};
static struct path_verification_work_data *path_verification_worker;
static DEFINE_MUTEX(dasd_path_verification_mutex);

/* initial attempt at a probe function. this can be simplified once
 * the other detection code is gone */
@@ -755,26 +767,27 @@ static int dasd_eckd_get_uid(struct dasd_device *device, struct dasd_uid *uid)
	return -EINVAL;
}

static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
						    void *rcd_buffer,
						    struct ciw *ciw, __u8 lpm)
static void dasd_eckd_fill_rcd_cqr(struct dasd_device *device,
				   struct dasd_ccw_req *cqr,
				   __u8 *rcd_buffer,
				   __u8 lpm)
{
	struct dasd_ccw_req *cqr;
	struct ccw1 *ccw;

	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */, ciw->count,
				   device);

	if (IS_ERR(cqr)) {
		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
			      "Could not allocate RCD request");
		return cqr;
	}
	/*
	 * buffer has to start with EBCDIC "V1.0" to show
	 * support for virtual device SNEQ
	 */
	rcd_buffer[0] = 0xE5;
	rcd_buffer[1] = 0xF1;
	rcd_buffer[2] = 0x4B;
	rcd_buffer[3] = 0xF0;

	ccw = cqr->cpaddr;
	ccw->cmd_code = ciw->cmd;
	ccw->cmd_code = DASD_ECKD_CCW_RCD;
	ccw->flags = 0;
	ccw->cda = (__u32)(addr_t)rcd_buffer;
	ccw->count = ciw->count;
	ccw->count = DASD_ECKD_RCD_DATA_SIZE;
	cqr->magic = DASD_ECKD_MAGIC;

	cqr->startdev = device;
	cqr->memdev = device;
@@ -784,7 +797,29 @@ static struct dasd_ccw_req *dasd_eckd_build_rcd_lpm(struct dasd_device *device,
	cqr->retries = 256;
	cqr->buildclk = get_clock();
	cqr->status = DASD_CQR_FILLED;
	return cqr;
	set_bit(DASD_CQR_VERIFY_PATH, &cqr->flags);
}

static int dasd_eckd_read_conf_immediately(struct dasd_device *device,
					   struct dasd_ccw_req *cqr,
					   __u8 *rcd_buffer,
					   __u8 lpm)
{
	struct ciw *ciw;
	int rc;
	/*
	 * sanity check: scan for RCD command in extended SenseID data
	 * some devices do not support RCD
	 */
	ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
	if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD)
		return -EOPNOTSUPP;

	dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buffer, lpm);
	clear_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
	cqr->retries = 5;
	rc = dasd_sleep_on_immediatly(cqr);
	return rc;
}

static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
@@ -797,32 +832,29 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
	struct dasd_ccw_req *cqr;

	/*
	 * scan for RCD command in extended SenseID data
	 * sanity check: scan for RCD command in extended SenseID data
	 * some devices do not support RCD
	 */
	ciw = ccw_device_get_ciw(device->cdev, CIW_TYPE_RCD);
	if (!ciw || ciw->cmd == 0) {
	if (!ciw || ciw->cmd != DASD_ECKD_CCW_RCD) {
		ret = -EOPNOTSUPP;
		goto out_error;
	}
	rcd_buf = kzalloc(ciw->count, GFP_KERNEL | GFP_DMA);
	rcd_buf = kzalloc(DASD_ECKD_RCD_DATA_SIZE, GFP_KERNEL | GFP_DMA);
	if (!rcd_buf) {
		ret = -ENOMEM;
		goto out_error;
	}

	/*
	 * buffer has to start with EBCDIC "V1.0" to show
	 * support for virtual device SNEQ
	 */
	rcd_buf[0] = 0xE5;
	rcd_buf[1] = 0xF1;
	rcd_buf[2] = 0x4B;
	rcd_buf[3] = 0xF0;
	cqr = dasd_eckd_build_rcd_lpm(device, rcd_buf, ciw, lpm);
	cqr = dasd_smalloc_request(DASD_ECKD_MAGIC, 1 /* RCD */,
				   0, /* use rcd_buf as data ara */
				   device);
	if (IS_ERR(cqr)) {
		ret =  PTR_ERR(cqr);
		DBF_DEV_EVENT(DBF_WARNING, device, "%s",
			      "Could not allocate RCD request");
		ret = -ENOMEM;
		goto out_error;
	}
	dasd_eckd_fill_rcd_cqr(device, cqr, rcd_buf, lpm);
	ret = dasd_sleep_on(cqr);
	/*
	 * on success we update the user input parms
@@ -831,7 +863,7 @@ static int dasd_eckd_read_conf_lpm(struct dasd_device *device,
	if (ret)
		goto out_error;

	*rcd_buffer_size = ciw->count;
	*rcd_buffer_size = DASD_ECKD_RCD_DATA_SIZE;
	*rcd_buffer = rcd_buf;
	return 0;
out_error:
@@ -901,18 +933,18 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
	void *conf_data;
	int conf_len, conf_data_saved;
	int rc;
	__u8 lpm;
	__u8 lpm, opm;
	struct dasd_eckd_private *private;
	struct dasd_eckd_path *path_data;
	struct dasd_path *path_data;

	private = (struct dasd_eckd_private *) device->private;
	path_data = (struct dasd_eckd_path *) &private->path_data;
	path_data->opm = ccw_device_get_path_mask(device->cdev);
	path_data = &device->path_data;
	opm = ccw_device_get_path_mask(device->cdev);
	lpm = 0x80;
	conf_data_saved = 0;
	/* get configuration data per operational path */
	for (lpm = 0x80; lpm; lpm>>= 1) {
		if (lpm & path_data->opm){
		if (lpm & opm) {
			rc = dasd_eckd_read_conf_lpm(device, &conf_data,
						     &conf_len, lpm);
			if (rc && rc != -EOPNOTSUPP) {	/* -EOPNOTSUPP is ok */
@@ -925,6 +957,8 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
				DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
						"No configuration data "
						"retrieved");
				/* no further analysis possible */
				path_data->opm |= lpm;
				continue;	/* no error */
			}
			/* save first valid configuration data */
@@ -948,6 +982,7 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
				path_data->ppm |= lpm;
				break;
			}
			path_data->opm |= lpm;
			if (conf_data != private->conf_data)
				kfree(conf_data);
		}
@@ -955,6 +990,140 @@ static int dasd_eckd_read_conf(struct dasd_device *device)
	return 0;
}

static int verify_fcx_max_data(struct dasd_device *device, __u8 lpm)
{
	struct dasd_eckd_private *private;
	int mdc;
	u32 fcx_max_data;

	private = (struct dasd_eckd_private *) device->private;
	if (private->fcx_max_data) {
		mdc = ccw_device_get_mdc(device->cdev, lpm);
		if ((mdc < 0)) {
			dev_warn(&device->cdev->dev,
				 "Detecting the maximum data size for zHPF "
				 "requests failed (rc=%d) for a new path %x\n",
				 mdc, lpm);
			return mdc;
		}
		fcx_max_data = mdc * FCX_MAX_DATA_FACTOR;
		if (fcx_max_data < private->fcx_max_data) {
			dev_warn(&device->cdev->dev,
				 "The maximum data size for zHPF requests %u "
				 "on a new path %x is below the active maximum "
				 "%u\n", fcx_max_data, lpm,
				 private->fcx_max_data);
			return -EACCES;
		}
	}
	return 0;
}

static void do_path_verification_work(struct work_struct *work)
{
	struct path_verification_work_data *data;
	struct dasd_device *device;
	__u8 lpm, opm, npm, ppm, epm;
	unsigned long flags;
	int rc;

	data = container_of(work, struct path_verification_work_data, worker);
	device = data->device;

	opm = 0;
	npm = 0;
	ppm = 0;
	epm = 0;
	for (lpm = 0x80; lpm; lpm >>= 1) {
		if (lpm & data->tbvpm) {
			memset(data->rcd_buffer, 0, sizeof(data->rcd_buffer));
			memset(&data->cqr, 0, sizeof(data->cqr));
			data->cqr.cpaddr = &data->ccw;
			rc = dasd_eckd_read_conf_immediately(device, &data->cqr,
							     data->rcd_buffer,
							     lpm);
			if (!rc) {
				switch (dasd_eckd_path_access(data->rcd_buffer,
						     DASD_ECKD_RCD_DATA_SIZE)) {
				case 0x02:
					npm |= lpm;
					break;
				case 0x03:
					ppm |= lpm;
					break;
				}
				opm |= lpm;
			} else if (rc == -EOPNOTSUPP) {
				DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
				       "path verification: No configuration "
				       "data retrieved");
				opm |= lpm;
			} else if (rc == -EAGAIN) {
				DBF_EVENT_DEVID(DBF_WARNING, device->cdev, "%s",
					"path verification: device is stopped,"
					" try again later");
				epm |= lpm;
			} else {
				dev_warn(&device->cdev->dev,
					 "Reading device feature codes failed "
					 "(rc=%d) for new path %x\n", rc, lpm);
				continue;
			}
			if (verify_fcx_max_data(device, lpm)) {
				opm &= ~lpm;
				npm &= ~lpm;
				ppm &= ~lpm;
			}
		}
	}
	/*
	 * There is a small chance that a path is lost again between
	 * above path verification and the following modification of
	 * the device opm mask. We could avoid that race here by using
	 * yet another path mask, but we rather deal with this unlikely
	 * situation in dasd_start_IO.
	 */
	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
	if (!device->path_data.opm && opm) {
		device->path_data.opm = opm;
		dasd_generic_path_operational(device);
	} else
		device->path_data.opm |= opm;
	device->path_data.npm |= npm;
	device->path_data.ppm |= ppm;
	device->path_data.tbvpm |= epm;
	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);

	dasd_put_device(device);
	if (data->isglobal)
		mutex_unlock(&dasd_path_verification_mutex);
	else
		kfree(data);
}

static int dasd_eckd_verify_path(struct dasd_device *device, __u8 lpm)
{
	struct path_verification_work_data *data;

	data = kmalloc(sizeof(*data), GFP_ATOMIC | GFP_DMA);
	if (!data) {
		if (mutex_trylock(&dasd_path_verification_mutex)) {
			data = path_verification_worker;
			data->isglobal = 1;
		} else
			return -ENOMEM;
	} else {
		memset(data, 0, sizeof(*data));
		data->isglobal = 0;
	}
	INIT_WORK(&data->worker, do_path_verification_work);
	dasd_get_device(device);
	data->device = device;
	data->tbvpm = lpm;
	schedule_work(&data->worker);
	return 0;
}

static int dasd_eckd_read_features(struct dasd_device *device)
{
	struct dasd_psf_prssd_data *prssdp;
@@ -1749,6 +1918,7 @@ static void dasd_eckd_handle_terminated_request(struct dasd_ccw_req *cqr)
	if (cqr->block && (cqr->startdev != cqr->block->base)) {
		dasd_eckd_reset_ccw_to_base_io(cqr);
		cqr->startdev = cqr->block->base;
		cqr->lpm = cqr->block->base->path_data.opm;
	}
};

@@ -2017,7 +2187,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_single(
	cqr->memdev = startdev;
	cqr->block = block;
	cqr->expires = startdev->default_expires * HZ;	/* default 5 minutes */
	cqr->lpm = private->path_data.ppm;
	cqr->lpm = startdev->path_data.ppm;
	cqr->retries = 256;
	cqr->buildclk = get_clock();
	cqr->status = DASD_CQR_FILLED;
@@ -2194,7 +2364,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_cmd_track(
	cqr->memdev = startdev;
	cqr->block = block;
	cqr->expires = startdev->default_expires * HZ;	/* default 5 minutes */
	cqr->lpm = private->path_data.ppm;
	cqr->lpm = startdev->path_data.ppm;
	cqr->retries = 256;
	cqr->buildclk = get_clock();
	cqr->status = DASD_CQR_FILLED;
@@ -2484,7 +2654,7 @@ static struct dasd_ccw_req *dasd_eckd_build_cp_tpm_track(
	cqr->memdev = startdev;
	cqr->block = block;
	cqr->expires = startdev->default_expires * HZ;	/* default 5 minutes */
	cqr->lpm = private->path_data.ppm;
	cqr->lpm = startdev->path_data.ppm;
	cqr->retries = 256;
	cqr->buildclk = get_clock();
	cqr->status = DASD_CQR_FILLED;
@@ -3624,6 +3794,7 @@ static struct ccw_driver dasd_eckd_driver = {
	.set_offline = dasd_generic_set_offline,
	.set_online  = dasd_eckd_set_online,
	.notify      = dasd_generic_notify,
	.path_event  = dasd_generic_path_event,
	.freeze      = dasd_generic_pm_freeze,
	.thaw	     = dasd_generic_restore_device,
	.restore     = dasd_generic_restore_device,
@@ -3651,6 +3822,7 @@ static struct dasd_discipline dasd_eckd_discipline = {
	.check_device = dasd_eckd_check_characteristics,
	.uncheck_device = dasd_eckd_uncheck_device,
	.do_analysis = dasd_eckd_do_analysis,
	.verify_path = dasd_eckd_verify_path,
	.ready_to_online = dasd_eckd_ready_to_online,
	.online_to_ready = dasd_eckd_online_to_ready,
	.fill_geometry = dasd_eckd_fill_geometry,
@@ -3683,11 +3855,19 @@ dasd_eckd_init(void)
				   GFP_KERNEL | GFP_DMA);
	if (!dasd_reserve_req)
		return -ENOMEM;
	path_verification_worker = kmalloc(sizeof(*path_verification_worker),
				   GFP_KERNEL | GFP_DMA);
	if (!path_verification_worker) {
		kfree(dasd_reserve_req);
		return -ENOMEM;
	}
	ret = ccw_driver_register(&dasd_eckd_driver);
	if (!ret)
		wait_for_device_probe();
	else
	else {
		kfree(path_verification_worker);
		kfree(dasd_reserve_req);
	}
	return ret;
}

@@ -3695,6 +3875,7 @@ static void __exit
dasd_eckd_cleanup(void)
{
	ccw_driver_unregister(&dasd_eckd_driver);
	kfree(path_verification_worker);
	kfree(dasd_reserve_req);
}

Loading