18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci *  CCW device PGID and path verification I/O handling.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci *    Copyright IBM Corp. 2002, 2009
68c2ecf20Sopenharmony_ci *    Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
78c2ecf20Sopenharmony_ci *		 Martin Schwidefsky <schwidefsky@de.ibm.com>
88c2ecf20Sopenharmony_ci *		 Peter Oberparleiter <peter.oberparleiter@de.ibm.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/kernel.h>
128c2ecf20Sopenharmony_ci#include <linux/string.h>
138c2ecf20Sopenharmony_ci#include <linux/bitops.h>
148c2ecf20Sopenharmony_ci#include <linux/types.h>
158c2ecf20Sopenharmony_ci#include <linux/errno.h>
168c2ecf20Sopenharmony_ci#include <linux/slab.h>
178c2ecf20Sopenharmony_ci#include <asm/ccwdev.h>
188c2ecf20Sopenharmony_ci#include <asm/cio.h>
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci#include "cio.h"
218c2ecf20Sopenharmony_ci#include "cio_debug.h"
228c2ecf20Sopenharmony_ci#include "device.h"
238c2ecf20Sopenharmony_ci#include "io_sch.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci#define PGID_RETRIES	256
268c2ecf20Sopenharmony_ci#define PGID_TIMEOUT	(10 * HZ)
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic void verify_start(struct ccw_device *cdev);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * Process path verification data and report result.
328c2ecf20Sopenharmony_ci */
338c2ecf20Sopenharmony_cistatic void verify_done(struct ccw_device *cdev, int rc)
348c2ecf20Sopenharmony_ci{
358c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
368c2ecf20Sopenharmony_ci	struct ccw_dev_id *id = &cdev->private->dev_id;
378c2ecf20Sopenharmony_ci	int mpath = cdev->private->flags.mpath;
388c2ecf20Sopenharmony_ci	int pgroup = cdev->private->flags.pgroup;
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	if (rc)
418c2ecf20Sopenharmony_ci		goto out;
428c2ecf20Sopenharmony_ci	/* Ensure consistent multipathing state at device and channel. */
438c2ecf20Sopenharmony_ci	if (sch->config.mp != mpath) {
448c2ecf20Sopenharmony_ci		sch->config.mp = mpath;
458c2ecf20Sopenharmony_ci		rc = cio_commit_config(sch);
468c2ecf20Sopenharmony_ci	}
478c2ecf20Sopenharmony_ciout:
488c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d "
498c2ecf20Sopenharmony_ci			 "vpm=%02x\n", id->ssid, id->devno, rc, pgroup, mpath,
508c2ecf20Sopenharmony_ci			 sch->vpm);
518c2ecf20Sopenharmony_ci	ccw_device_verify_done(cdev, rc);
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/*
558c2ecf20Sopenharmony_ci * Create channel program to perform a NOOP.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistatic void nop_build_cp(struct ccw_device *cdev)
588c2ecf20Sopenharmony_ci{
598c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
608c2ecf20Sopenharmony_ci	struct ccw1 *cp = cdev->private->dma_area->iccws;
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	cp->cmd_code	= CCW_CMD_NOOP;
638c2ecf20Sopenharmony_ci	cp->cda		= 0;
648c2ecf20Sopenharmony_ci	cp->count	= 0;
658c2ecf20Sopenharmony_ci	cp->flags	= CCW_FLAG_SLI;
668c2ecf20Sopenharmony_ci	req->cp		= cp;
678c2ecf20Sopenharmony_ci}
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/*
708c2ecf20Sopenharmony_ci * Perform NOOP on a single path.
718c2ecf20Sopenharmony_ci */
728c2ecf20Sopenharmony_cistatic void nop_do(struct ccw_device *cdev)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
758c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm &
788c2ecf20Sopenharmony_ci			      ~cdev->private->path_noirq_mask);
798c2ecf20Sopenharmony_ci	if (!req->lpm)
808c2ecf20Sopenharmony_ci		goto out_nopath;
818c2ecf20Sopenharmony_ci	nop_build_cp(cdev);
828c2ecf20Sopenharmony_ci	ccw_request_start(cdev);
838c2ecf20Sopenharmony_ci	return;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ciout_nopath:
868c2ecf20Sopenharmony_ci	verify_done(cdev, sch->vpm ? 0 : -EACCES);
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci/*
908c2ecf20Sopenharmony_ci * Adjust NOOP I/O status.
918c2ecf20Sopenharmony_ci */
928c2ecf20Sopenharmony_cistatic enum io_status nop_filter(struct ccw_device *cdev, void *data,
938c2ecf20Sopenharmony_ci				 struct irb *irb, enum io_status status)
948c2ecf20Sopenharmony_ci{
958c2ecf20Sopenharmony_ci	/* Only subchannel status might indicate a path error. */
968c2ecf20Sopenharmony_ci	if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0)
978c2ecf20Sopenharmony_ci		return IO_DONE;
988c2ecf20Sopenharmony_ci	return status;
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci/*
1028c2ecf20Sopenharmony_ci * Process NOOP request result for a single path.
1038c2ecf20Sopenharmony_ci */
1048c2ecf20Sopenharmony_cistatic void nop_callback(struct ccw_device *cdev, void *data, int rc)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
1078c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	switch (rc) {
1108c2ecf20Sopenharmony_ci	case 0:
1118c2ecf20Sopenharmony_ci		sch->vpm |= req->lpm;
1128c2ecf20Sopenharmony_ci		break;
1138c2ecf20Sopenharmony_ci	case -ETIME:
1148c2ecf20Sopenharmony_ci		cdev->private->path_noirq_mask |= req->lpm;
1158c2ecf20Sopenharmony_ci		break;
1168c2ecf20Sopenharmony_ci	case -EACCES:
1178c2ecf20Sopenharmony_ci		cdev->private->path_notoper_mask |= req->lpm;
1188c2ecf20Sopenharmony_ci		break;
1198c2ecf20Sopenharmony_ci	default:
1208c2ecf20Sopenharmony_ci		goto err;
1218c2ecf20Sopenharmony_ci	}
1228c2ecf20Sopenharmony_ci	/* Continue on the next path. */
1238c2ecf20Sopenharmony_ci	req->lpm >>= 1;
1248c2ecf20Sopenharmony_ci	nop_do(cdev);
1258c2ecf20Sopenharmony_ci	return;
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_cierr:
1288c2ecf20Sopenharmony_ci	verify_done(cdev, rc);
1298c2ecf20Sopenharmony_ci}
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci/*
1328c2ecf20Sopenharmony_ci * Create channel program to perform SET PGID on a single path.
1338c2ecf20Sopenharmony_ci */
1348c2ecf20Sopenharmony_cistatic void spid_build_cp(struct ccw_device *cdev, u8 fn)
1358c2ecf20Sopenharmony_ci{
1368c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
1378c2ecf20Sopenharmony_ci	struct ccw1 *cp = cdev->private->dma_area->iccws;
1388c2ecf20Sopenharmony_ci	int i = pathmask_to_pos(req->lpm);
1398c2ecf20Sopenharmony_ci	struct pgid *pgid = &cdev->private->dma_area->pgid[i];
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	pgid->inf.fc	= fn;
1428c2ecf20Sopenharmony_ci	cp->cmd_code	= CCW_CMD_SET_PGID;
1438c2ecf20Sopenharmony_ci	cp->cda		= (u32) (addr_t) pgid;
1448c2ecf20Sopenharmony_ci	cp->count	= sizeof(*pgid);
1458c2ecf20Sopenharmony_ci	cp->flags	= CCW_FLAG_SLI;
1468c2ecf20Sopenharmony_ci	req->cp		= cp;
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void pgid_wipeout_callback(struct ccw_device *cdev, void *data, int rc)
1508c2ecf20Sopenharmony_ci{
1518c2ecf20Sopenharmony_ci	if (rc) {
1528c2ecf20Sopenharmony_ci		/* We don't know the path groups' state. Abort. */
1538c2ecf20Sopenharmony_ci		verify_done(cdev, rc);
1548c2ecf20Sopenharmony_ci		return;
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci	/*
1578c2ecf20Sopenharmony_ci	 * Path groups have been reset. Restart path verification but
1588c2ecf20Sopenharmony_ci	 * leave paths in path_noirq_mask out.
1598c2ecf20Sopenharmony_ci	 */
1608c2ecf20Sopenharmony_ci	cdev->private->flags.pgid_unknown = 0;
1618c2ecf20Sopenharmony_ci	verify_start(cdev);
1628c2ecf20Sopenharmony_ci}
1638c2ecf20Sopenharmony_ci
1648c2ecf20Sopenharmony_ci/*
1658c2ecf20Sopenharmony_ci * Reset pathgroups and restart path verification, leave unusable paths out.
1668c2ecf20Sopenharmony_ci */
1678c2ecf20Sopenharmony_cistatic void pgid_wipeout_start(struct ccw_device *cdev)
1688c2ecf20Sopenharmony_ci{
1698c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
1708c2ecf20Sopenharmony_ci	struct ccw_dev_id *id = &cdev->private->dev_id;
1718c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
1728c2ecf20Sopenharmony_ci	u8 fn;
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(2, "wipe: device 0.%x.%04x: pvm=%02x nim=%02x\n",
1758c2ecf20Sopenharmony_ci		      id->ssid, id->devno, cdev->private->pgid_valid_mask,
1768c2ecf20Sopenharmony_ci		      cdev->private->path_noirq_mask);
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	/* Initialize request data. */
1798c2ecf20Sopenharmony_ci	memset(req, 0, sizeof(*req));
1808c2ecf20Sopenharmony_ci	req->timeout	= PGID_TIMEOUT;
1818c2ecf20Sopenharmony_ci	req->maxretries	= PGID_RETRIES;
1828c2ecf20Sopenharmony_ci	req->lpm	= sch->schib.pmcw.pam;
1838c2ecf20Sopenharmony_ci	req->callback	= pgid_wipeout_callback;
1848c2ecf20Sopenharmony_ci	fn = SPID_FUNC_DISBAND;
1858c2ecf20Sopenharmony_ci	if (cdev->private->flags.mpath)
1868c2ecf20Sopenharmony_ci		fn |= SPID_FUNC_MULTI_PATH;
1878c2ecf20Sopenharmony_ci	spid_build_cp(cdev, fn);
1888c2ecf20Sopenharmony_ci	ccw_request_start(cdev);
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci/*
1928c2ecf20Sopenharmony_ci * Perform establish/resign SET PGID on a single path.
1938c2ecf20Sopenharmony_ci */
1948c2ecf20Sopenharmony_cistatic void spid_do(struct ccw_device *cdev)
1958c2ecf20Sopenharmony_ci{
1968c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
1978c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
1988c2ecf20Sopenharmony_ci	u8 fn;
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_ci	/* Use next available path that is not already in correct state. */
2018c2ecf20Sopenharmony_ci	req->lpm = lpm_adjust(req->lpm, cdev->private->pgid_todo_mask);
2028c2ecf20Sopenharmony_ci	if (!req->lpm)
2038c2ecf20Sopenharmony_ci		goto out_nopath;
2048c2ecf20Sopenharmony_ci	/* Channel program setup. */
2058c2ecf20Sopenharmony_ci	if (req->lpm & sch->opm)
2068c2ecf20Sopenharmony_ci		fn = SPID_FUNC_ESTABLISH;
2078c2ecf20Sopenharmony_ci	else
2088c2ecf20Sopenharmony_ci		fn = SPID_FUNC_RESIGN;
2098c2ecf20Sopenharmony_ci	if (cdev->private->flags.mpath)
2108c2ecf20Sopenharmony_ci		fn |= SPID_FUNC_MULTI_PATH;
2118c2ecf20Sopenharmony_ci	spid_build_cp(cdev, fn);
2128c2ecf20Sopenharmony_ci	ccw_request_start(cdev);
2138c2ecf20Sopenharmony_ci	return;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ciout_nopath:
2168c2ecf20Sopenharmony_ci	if (cdev->private->flags.pgid_unknown) {
2178c2ecf20Sopenharmony_ci		/* At least one SPID could be partially done. */
2188c2ecf20Sopenharmony_ci		pgid_wipeout_start(cdev);
2198c2ecf20Sopenharmony_ci		return;
2208c2ecf20Sopenharmony_ci	}
2218c2ecf20Sopenharmony_ci	verify_done(cdev, sch->vpm ? 0 : -EACCES);
2228c2ecf20Sopenharmony_ci}
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci/*
2258c2ecf20Sopenharmony_ci * Process SET PGID request result for a single path.
2268c2ecf20Sopenharmony_ci */
2278c2ecf20Sopenharmony_cistatic void spid_callback(struct ccw_device *cdev, void *data, int rc)
2288c2ecf20Sopenharmony_ci{
2298c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
2308c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	switch (rc) {
2338c2ecf20Sopenharmony_ci	case 0:
2348c2ecf20Sopenharmony_ci		sch->vpm |= req->lpm & sch->opm;
2358c2ecf20Sopenharmony_ci		break;
2368c2ecf20Sopenharmony_ci	case -ETIME:
2378c2ecf20Sopenharmony_ci		cdev->private->flags.pgid_unknown = 1;
2388c2ecf20Sopenharmony_ci		cdev->private->path_noirq_mask |= req->lpm;
2398c2ecf20Sopenharmony_ci		break;
2408c2ecf20Sopenharmony_ci	case -EACCES:
2418c2ecf20Sopenharmony_ci		cdev->private->path_notoper_mask |= req->lpm;
2428c2ecf20Sopenharmony_ci		break;
2438c2ecf20Sopenharmony_ci	case -EOPNOTSUPP:
2448c2ecf20Sopenharmony_ci		if (cdev->private->flags.mpath) {
2458c2ecf20Sopenharmony_ci			/* Try without multipathing. */
2468c2ecf20Sopenharmony_ci			cdev->private->flags.mpath = 0;
2478c2ecf20Sopenharmony_ci			goto out_restart;
2488c2ecf20Sopenharmony_ci		}
2498c2ecf20Sopenharmony_ci		/* Try without pathgrouping. */
2508c2ecf20Sopenharmony_ci		cdev->private->flags.pgroup = 0;
2518c2ecf20Sopenharmony_ci		goto out_restart;
2528c2ecf20Sopenharmony_ci	default:
2538c2ecf20Sopenharmony_ci		goto err;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci	req->lpm >>= 1;
2568c2ecf20Sopenharmony_ci	spid_do(cdev);
2578c2ecf20Sopenharmony_ci	return;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ciout_restart:
2608c2ecf20Sopenharmony_ci	verify_start(cdev);
2618c2ecf20Sopenharmony_ci	return;
2628c2ecf20Sopenharmony_cierr:
2638c2ecf20Sopenharmony_ci	verify_done(cdev, rc);
2648c2ecf20Sopenharmony_ci}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_cistatic void spid_start(struct ccw_device *cdev)
2678c2ecf20Sopenharmony_ci{
2688c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	/* Initialize request data. */
2718c2ecf20Sopenharmony_ci	memset(req, 0, sizeof(*req));
2728c2ecf20Sopenharmony_ci	req->timeout	= PGID_TIMEOUT;
2738c2ecf20Sopenharmony_ci	req->maxretries	= PGID_RETRIES;
2748c2ecf20Sopenharmony_ci	req->lpm	= 0x80;
2758c2ecf20Sopenharmony_ci	req->singlepath	= 1;
2768c2ecf20Sopenharmony_ci	req->callback	= spid_callback;
2778c2ecf20Sopenharmony_ci	spid_do(cdev);
2788c2ecf20Sopenharmony_ci}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_cistatic int pgid_is_reset(struct pgid *p)
2818c2ecf20Sopenharmony_ci{
2828c2ecf20Sopenharmony_ci	char *c;
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	for (c = (char *)p + 1; c < (char *)(p + 1); c++) {
2858c2ecf20Sopenharmony_ci		if (*c != 0)
2868c2ecf20Sopenharmony_ci			return 0;
2878c2ecf20Sopenharmony_ci	}
2888c2ecf20Sopenharmony_ci	return 1;
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic int pgid_cmp(struct pgid *p1, struct pgid *p2)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	return memcmp((char *) p1 + 1, (char *) p2 + 1,
2948c2ecf20Sopenharmony_ci		      sizeof(struct pgid) - 1);
2958c2ecf20Sopenharmony_ci}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci/*
2988c2ecf20Sopenharmony_ci * Determine pathgroup state from PGID data.
2998c2ecf20Sopenharmony_ci */
3008c2ecf20Sopenharmony_cistatic void pgid_analyze(struct ccw_device *cdev, struct pgid **p,
3018c2ecf20Sopenharmony_ci			 int *mismatch, u8 *reserved, u8 *reset)
3028c2ecf20Sopenharmony_ci{
3038c2ecf20Sopenharmony_ci	struct pgid *pgid = &cdev->private->dma_area->pgid[0];
3048c2ecf20Sopenharmony_ci	struct pgid *first = NULL;
3058c2ecf20Sopenharmony_ci	int lpm;
3068c2ecf20Sopenharmony_ci	int i;
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	*mismatch = 0;
3098c2ecf20Sopenharmony_ci	*reserved = 0;
3108c2ecf20Sopenharmony_ci	*reset = 0;
3118c2ecf20Sopenharmony_ci	for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) {
3128c2ecf20Sopenharmony_ci		if ((cdev->private->pgid_valid_mask & lpm) == 0)
3138c2ecf20Sopenharmony_ci			continue;
3148c2ecf20Sopenharmony_ci		if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE)
3158c2ecf20Sopenharmony_ci			*reserved |= lpm;
3168c2ecf20Sopenharmony_ci		if (pgid_is_reset(pgid)) {
3178c2ecf20Sopenharmony_ci			*reset |= lpm;
3188c2ecf20Sopenharmony_ci			continue;
3198c2ecf20Sopenharmony_ci		}
3208c2ecf20Sopenharmony_ci		if (!first) {
3218c2ecf20Sopenharmony_ci			first = pgid;
3228c2ecf20Sopenharmony_ci			continue;
3238c2ecf20Sopenharmony_ci		}
3248c2ecf20Sopenharmony_ci		if (pgid_cmp(pgid, first) != 0)
3258c2ecf20Sopenharmony_ci			*mismatch = 1;
3268c2ecf20Sopenharmony_ci	}
3278c2ecf20Sopenharmony_ci	if (!first)
3288c2ecf20Sopenharmony_ci		first = &channel_subsystems[0]->global_pgid;
3298c2ecf20Sopenharmony_ci	*p = first;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci
3328c2ecf20Sopenharmony_cistatic u8 pgid_to_donepm(struct ccw_device *cdev)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
3358c2ecf20Sopenharmony_ci	struct pgid *pgid;
3368c2ecf20Sopenharmony_ci	int i;
3378c2ecf20Sopenharmony_ci	int lpm;
3388c2ecf20Sopenharmony_ci	u8 donepm = 0;
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	/* Set bits for paths which are already in the target state. */
3418c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++) {
3428c2ecf20Sopenharmony_ci		lpm = 0x80 >> i;
3438c2ecf20Sopenharmony_ci		if ((cdev->private->pgid_valid_mask & lpm) == 0)
3448c2ecf20Sopenharmony_ci			continue;
3458c2ecf20Sopenharmony_ci		pgid = &cdev->private->dma_area->pgid[i];
3468c2ecf20Sopenharmony_ci		if (sch->opm & lpm) {
3478c2ecf20Sopenharmony_ci			if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED)
3488c2ecf20Sopenharmony_ci				continue;
3498c2ecf20Sopenharmony_ci		} else {
3508c2ecf20Sopenharmony_ci			if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED)
3518c2ecf20Sopenharmony_ci				continue;
3528c2ecf20Sopenharmony_ci		}
3538c2ecf20Sopenharmony_ci		if (cdev->private->flags.mpath) {
3548c2ecf20Sopenharmony_ci			if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH)
3558c2ecf20Sopenharmony_ci				continue;
3568c2ecf20Sopenharmony_ci		} else {
3578c2ecf20Sopenharmony_ci			if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH)
3588c2ecf20Sopenharmony_ci				continue;
3598c2ecf20Sopenharmony_ci		}
3608c2ecf20Sopenharmony_ci		donepm |= lpm;
3618c2ecf20Sopenharmony_ci	}
3628c2ecf20Sopenharmony_ci
3638c2ecf20Sopenharmony_ci	return donepm;
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_cistatic void pgid_fill(struct ccw_device *cdev, struct pgid *pgid)
3678c2ecf20Sopenharmony_ci{
3688c2ecf20Sopenharmony_ci	int i;
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	for (i = 0; i < 8; i++)
3718c2ecf20Sopenharmony_ci		memcpy(&cdev->private->dma_area->pgid[i], pgid,
3728c2ecf20Sopenharmony_ci		       sizeof(struct pgid));
3738c2ecf20Sopenharmony_ci}
3748c2ecf20Sopenharmony_ci
3758c2ecf20Sopenharmony_ci/*
3768c2ecf20Sopenharmony_ci * Process SENSE PGID data and report result.
3778c2ecf20Sopenharmony_ci */
3788c2ecf20Sopenharmony_cistatic void snid_done(struct ccw_device *cdev, int rc)
3798c2ecf20Sopenharmony_ci{
3808c2ecf20Sopenharmony_ci	struct ccw_dev_id *id = &cdev->private->dev_id;
3818c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
3828c2ecf20Sopenharmony_ci	struct pgid *pgid;
3838c2ecf20Sopenharmony_ci	int mismatch = 0;
3848c2ecf20Sopenharmony_ci	u8 reserved = 0;
3858c2ecf20Sopenharmony_ci	u8 reset = 0;
3868c2ecf20Sopenharmony_ci	u8 donepm;
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	if (rc)
3898c2ecf20Sopenharmony_ci		goto out;
3908c2ecf20Sopenharmony_ci	pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset);
3918c2ecf20Sopenharmony_ci	if (reserved == cdev->private->pgid_valid_mask)
3928c2ecf20Sopenharmony_ci		rc = -EUSERS;
3938c2ecf20Sopenharmony_ci	else if (mismatch)
3948c2ecf20Sopenharmony_ci		rc = -EOPNOTSUPP;
3958c2ecf20Sopenharmony_ci	else {
3968c2ecf20Sopenharmony_ci		donepm = pgid_to_donepm(cdev);
3978c2ecf20Sopenharmony_ci		sch->vpm = donepm & sch->opm;
3988c2ecf20Sopenharmony_ci		cdev->private->pgid_reset_mask |= reset;
3998c2ecf20Sopenharmony_ci		cdev->private->pgid_todo_mask &=
4008c2ecf20Sopenharmony_ci			~(donepm | cdev->private->path_noirq_mask);
4018c2ecf20Sopenharmony_ci		pgid_fill(cdev, pgid);
4028c2ecf20Sopenharmony_ci	}
4038c2ecf20Sopenharmony_ciout:
4048c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x "
4058c2ecf20Sopenharmony_ci		      "todo=%02x mism=%d rsvd=%02x reset=%02x\n", id->ssid,
4068c2ecf20Sopenharmony_ci		      id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm,
4078c2ecf20Sopenharmony_ci		      cdev->private->pgid_todo_mask, mismatch, reserved, reset);
4088c2ecf20Sopenharmony_ci	switch (rc) {
4098c2ecf20Sopenharmony_ci	case 0:
4108c2ecf20Sopenharmony_ci		if (cdev->private->flags.pgid_unknown) {
4118c2ecf20Sopenharmony_ci			pgid_wipeout_start(cdev);
4128c2ecf20Sopenharmony_ci			return;
4138c2ecf20Sopenharmony_ci		}
4148c2ecf20Sopenharmony_ci		/* Anything left to do? */
4158c2ecf20Sopenharmony_ci		if (cdev->private->pgid_todo_mask == 0) {
4168c2ecf20Sopenharmony_ci			verify_done(cdev, sch->vpm == 0 ? -EACCES : 0);
4178c2ecf20Sopenharmony_ci			return;
4188c2ecf20Sopenharmony_ci		}
4198c2ecf20Sopenharmony_ci		/* Perform path-grouping. */
4208c2ecf20Sopenharmony_ci		spid_start(cdev);
4218c2ecf20Sopenharmony_ci		break;
4228c2ecf20Sopenharmony_ci	case -EOPNOTSUPP:
4238c2ecf20Sopenharmony_ci		/* Path-grouping not supported. */
4248c2ecf20Sopenharmony_ci		cdev->private->flags.pgroup = 0;
4258c2ecf20Sopenharmony_ci		cdev->private->flags.mpath = 0;
4268c2ecf20Sopenharmony_ci		verify_start(cdev);
4278c2ecf20Sopenharmony_ci		break;
4288c2ecf20Sopenharmony_ci	default:
4298c2ecf20Sopenharmony_ci		verify_done(cdev, rc);
4308c2ecf20Sopenharmony_ci	}
4318c2ecf20Sopenharmony_ci}
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci/*
4348c2ecf20Sopenharmony_ci * Create channel program to perform a SENSE PGID on a single path.
4358c2ecf20Sopenharmony_ci */
4368c2ecf20Sopenharmony_cistatic void snid_build_cp(struct ccw_device *cdev)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
4398c2ecf20Sopenharmony_ci	struct ccw1 *cp = cdev->private->dma_area->iccws;
4408c2ecf20Sopenharmony_ci	int i = pathmask_to_pos(req->lpm);
4418c2ecf20Sopenharmony_ci
4428c2ecf20Sopenharmony_ci	/* Channel program setup. */
4438c2ecf20Sopenharmony_ci	cp->cmd_code	= CCW_CMD_SENSE_PGID;
4448c2ecf20Sopenharmony_ci	cp->cda		= (u32) (addr_t) &cdev->private->dma_area->pgid[i];
4458c2ecf20Sopenharmony_ci	cp->count	= sizeof(struct pgid);
4468c2ecf20Sopenharmony_ci	cp->flags	= CCW_FLAG_SLI;
4478c2ecf20Sopenharmony_ci	req->cp		= cp;
4488c2ecf20Sopenharmony_ci}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci/*
4518c2ecf20Sopenharmony_ci * Perform SENSE PGID on a single path.
4528c2ecf20Sopenharmony_ci */
4538c2ecf20Sopenharmony_cistatic void snid_do(struct ccw_device *cdev)
4548c2ecf20Sopenharmony_ci{
4558c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
4568c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
4578c2ecf20Sopenharmony_ci	int ret;
4588c2ecf20Sopenharmony_ci
4598c2ecf20Sopenharmony_ci	req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam &
4608c2ecf20Sopenharmony_ci			      ~cdev->private->path_noirq_mask);
4618c2ecf20Sopenharmony_ci	if (!req->lpm)
4628c2ecf20Sopenharmony_ci		goto out_nopath;
4638c2ecf20Sopenharmony_ci	snid_build_cp(cdev);
4648c2ecf20Sopenharmony_ci	ccw_request_start(cdev);
4658c2ecf20Sopenharmony_ci	return;
4668c2ecf20Sopenharmony_ci
4678c2ecf20Sopenharmony_ciout_nopath:
4688c2ecf20Sopenharmony_ci	if (cdev->private->pgid_valid_mask)
4698c2ecf20Sopenharmony_ci		ret = 0;
4708c2ecf20Sopenharmony_ci	else if (cdev->private->path_noirq_mask)
4718c2ecf20Sopenharmony_ci		ret = -ETIME;
4728c2ecf20Sopenharmony_ci	else
4738c2ecf20Sopenharmony_ci		ret = -EACCES;
4748c2ecf20Sopenharmony_ci	snid_done(cdev, ret);
4758c2ecf20Sopenharmony_ci}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci/*
4788c2ecf20Sopenharmony_ci * Process SENSE PGID request result for single path.
4798c2ecf20Sopenharmony_ci */
4808c2ecf20Sopenharmony_cistatic void snid_callback(struct ccw_device *cdev, void *data, int rc)
4818c2ecf20Sopenharmony_ci{
4828c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
4838c2ecf20Sopenharmony_ci
4848c2ecf20Sopenharmony_ci	switch (rc) {
4858c2ecf20Sopenharmony_ci	case 0:
4868c2ecf20Sopenharmony_ci		cdev->private->pgid_valid_mask |= req->lpm;
4878c2ecf20Sopenharmony_ci		break;
4888c2ecf20Sopenharmony_ci	case -ETIME:
4898c2ecf20Sopenharmony_ci		cdev->private->flags.pgid_unknown = 1;
4908c2ecf20Sopenharmony_ci		cdev->private->path_noirq_mask |= req->lpm;
4918c2ecf20Sopenharmony_ci		break;
4928c2ecf20Sopenharmony_ci	case -EACCES:
4938c2ecf20Sopenharmony_ci		cdev->private->path_notoper_mask |= req->lpm;
4948c2ecf20Sopenharmony_ci		break;
4958c2ecf20Sopenharmony_ci	default:
4968c2ecf20Sopenharmony_ci		goto err;
4978c2ecf20Sopenharmony_ci	}
4988c2ecf20Sopenharmony_ci	/* Continue on the next path. */
4998c2ecf20Sopenharmony_ci	req->lpm >>= 1;
5008c2ecf20Sopenharmony_ci	snid_do(cdev);
5018c2ecf20Sopenharmony_ci	return;
5028c2ecf20Sopenharmony_ci
5038c2ecf20Sopenharmony_cierr:
5048c2ecf20Sopenharmony_ci	snid_done(cdev, rc);
5058c2ecf20Sopenharmony_ci}
5068c2ecf20Sopenharmony_ci
5078c2ecf20Sopenharmony_ci/*
5088c2ecf20Sopenharmony_ci * Perform path verification.
5098c2ecf20Sopenharmony_ci */
5108c2ecf20Sopenharmony_cistatic void verify_start(struct ccw_device *cdev)
5118c2ecf20Sopenharmony_ci{
5128c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
5138c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
5148c2ecf20Sopenharmony_ci	struct ccw_dev_id *devid = &cdev->private->dev_id;
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	sch->vpm = 0;
5178c2ecf20Sopenharmony_ci	sch->lpm = sch->schib.pmcw.pam;
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci	/* Initialize PGID data. */
5208c2ecf20Sopenharmony_ci	memset(cdev->private->dma_area->pgid, 0,
5218c2ecf20Sopenharmony_ci	       sizeof(cdev->private->dma_area->pgid));
5228c2ecf20Sopenharmony_ci	cdev->private->pgid_valid_mask = 0;
5238c2ecf20Sopenharmony_ci	cdev->private->pgid_todo_mask = sch->schib.pmcw.pam;
5248c2ecf20Sopenharmony_ci	cdev->private->path_notoper_mask = 0;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	/* Initialize request data. */
5278c2ecf20Sopenharmony_ci	memset(req, 0, sizeof(*req));
5288c2ecf20Sopenharmony_ci	req->timeout	= PGID_TIMEOUT;
5298c2ecf20Sopenharmony_ci	req->maxretries	= PGID_RETRIES;
5308c2ecf20Sopenharmony_ci	req->lpm	= 0x80;
5318c2ecf20Sopenharmony_ci	req->singlepath	= 1;
5328c2ecf20Sopenharmony_ci	if (cdev->private->flags.pgroup) {
5338c2ecf20Sopenharmony_ci		CIO_TRACE_EVENT(4, "snid");
5348c2ecf20Sopenharmony_ci		CIO_HEX_EVENT(4, devid, sizeof(*devid));
5358c2ecf20Sopenharmony_ci		req->callback	= snid_callback;
5368c2ecf20Sopenharmony_ci		snid_do(cdev);
5378c2ecf20Sopenharmony_ci	} else {
5388c2ecf20Sopenharmony_ci		CIO_TRACE_EVENT(4, "nop");
5398c2ecf20Sopenharmony_ci		CIO_HEX_EVENT(4, devid, sizeof(*devid));
5408c2ecf20Sopenharmony_ci		req->filter	= nop_filter;
5418c2ecf20Sopenharmony_ci		req->callback	= nop_callback;
5428c2ecf20Sopenharmony_ci		nop_do(cdev);
5438c2ecf20Sopenharmony_ci	}
5448c2ecf20Sopenharmony_ci}
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci/**
5478c2ecf20Sopenharmony_ci * ccw_device_verify_start - perform path verification
5488c2ecf20Sopenharmony_ci * @cdev: ccw device
5498c2ecf20Sopenharmony_ci *
5508c2ecf20Sopenharmony_ci * Perform an I/O on each available channel path to @cdev to determine which
5518c2ecf20Sopenharmony_ci * paths are operational. The resulting path mask is stored in sch->vpm.
5528c2ecf20Sopenharmony_ci * If device options specify pathgrouping, establish a pathgroup for the
5538c2ecf20Sopenharmony_ci * operational paths. When finished, call ccw_device_verify_done with a
5548c2ecf20Sopenharmony_ci * return code specifying the result.
5558c2ecf20Sopenharmony_ci */
5568c2ecf20Sopenharmony_civoid ccw_device_verify_start(struct ccw_device *cdev)
5578c2ecf20Sopenharmony_ci{
5588c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(4, "vrfy");
5598c2ecf20Sopenharmony_ci	CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
5608c2ecf20Sopenharmony_ci	/*
5618c2ecf20Sopenharmony_ci	 * Initialize pathgroup and multipath state with target values.
5628c2ecf20Sopenharmony_ci	 * They may change in the course of path verification.
5638c2ecf20Sopenharmony_ci	 */
5648c2ecf20Sopenharmony_ci	cdev->private->flags.pgroup = cdev->private->options.pgroup;
5658c2ecf20Sopenharmony_ci	cdev->private->flags.mpath = cdev->private->options.mpath;
5668c2ecf20Sopenharmony_ci	cdev->private->flags.doverify = 0;
5678c2ecf20Sopenharmony_ci	cdev->private->path_noirq_mask = 0;
5688c2ecf20Sopenharmony_ci	verify_start(cdev);
5698c2ecf20Sopenharmony_ci}
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci/*
5728c2ecf20Sopenharmony_ci * Process disband SET PGID request result.
5738c2ecf20Sopenharmony_ci */
5748c2ecf20Sopenharmony_cistatic void disband_callback(struct ccw_device *cdev, void *data, int rc)
5758c2ecf20Sopenharmony_ci{
5768c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
5778c2ecf20Sopenharmony_ci	struct ccw_dev_id *id = &cdev->private->dev_id;
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_ci	if (rc)
5808c2ecf20Sopenharmony_ci		goto out;
5818c2ecf20Sopenharmony_ci	/* Ensure consistent multipathing state at device and channel. */
5828c2ecf20Sopenharmony_ci	cdev->private->flags.mpath = 0;
5838c2ecf20Sopenharmony_ci	if (sch->config.mp) {
5848c2ecf20Sopenharmony_ci		sch->config.mp = 0;
5858c2ecf20Sopenharmony_ci		rc = cio_commit_config(sch);
5868c2ecf20Sopenharmony_ci	}
5878c2ecf20Sopenharmony_ciout:
5888c2ecf20Sopenharmony_ci	CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n", id->ssid, id->devno,
5898c2ecf20Sopenharmony_ci		      rc);
5908c2ecf20Sopenharmony_ci	ccw_device_disband_done(cdev, rc);
5918c2ecf20Sopenharmony_ci}
5928c2ecf20Sopenharmony_ci
5938c2ecf20Sopenharmony_ci/**
5948c2ecf20Sopenharmony_ci * ccw_device_disband_start - disband pathgroup
5958c2ecf20Sopenharmony_ci * @cdev: ccw device
5968c2ecf20Sopenharmony_ci *
5978c2ecf20Sopenharmony_ci * Execute a SET PGID channel program on @cdev to disband a previously
5988c2ecf20Sopenharmony_ci * established pathgroup. When finished, call ccw_device_disband_done with
5998c2ecf20Sopenharmony_ci * a return code specifying the result.
6008c2ecf20Sopenharmony_ci */
6018c2ecf20Sopenharmony_civoid ccw_device_disband_start(struct ccw_device *cdev)
6028c2ecf20Sopenharmony_ci{
6038c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
6048c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
6058c2ecf20Sopenharmony_ci	u8 fn;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(4, "disb");
6088c2ecf20Sopenharmony_ci	CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
6098c2ecf20Sopenharmony_ci	/* Request setup. */
6108c2ecf20Sopenharmony_ci	memset(req, 0, sizeof(*req));
6118c2ecf20Sopenharmony_ci	req->timeout	= PGID_TIMEOUT;
6128c2ecf20Sopenharmony_ci	req->maxretries	= PGID_RETRIES;
6138c2ecf20Sopenharmony_ci	req->lpm	= sch->schib.pmcw.pam & sch->opm;
6148c2ecf20Sopenharmony_ci	req->singlepath	= 1;
6158c2ecf20Sopenharmony_ci	req->callback	= disband_callback;
6168c2ecf20Sopenharmony_ci	fn = SPID_FUNC_DISBAND;
6178c2ecf20Sopenharmony_ci	if (cdev->private->flags.mpath)
6188c2ecf20Sopenharmony_ci		fn |= SPID_FUNC_MULTI_PATH;
6198c2ecf20Sopenharmony_ci	spid_build_cp(cdev, fn);
6208c2ecf20Sopenharmony_ci	ccw_request_start(cdev);
6218c2ecf20Sopenharmony_ci}
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_cistruct stlck_data {
6248c2ecf20Sopenharmony_ci	struct completion done;
6258c2ecf20Sopenharmony_ci	int rc;
6268c2ecf20Sopenharmony_ci};
6278c2ecf20Sopenharmony_ci
6288c2ecf20Sopenharmony_cistatic void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2)
6298c2ecf20Sopenharmony_ci{
6308c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
6318c2ecf20Sopenharmony_ci	struct ccw1 *cp = cdev->private->dma_area->iccws;
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	cp[0].cmd_code = CCW_CMD_STLCK;
6348c2ecf20Sopenharmony_ci	cp[0].cda = (u32) (addr_t) buf1;
6358c2ecf20Sopenharmony_ci	cp[0].count = 32;
6368c2ecf20Sopenharmony_ci	cp[0].flags = CCW_FLAG_CC;
6378c2ecf20Sopenharmony_ci	cp[1].cmd_code = CCW_CMD_RELEASE;
6388c2ecf20Sopenharmony_ci	cp[1].cda = (u32) (addr_t) buf2;
6398c2ecf20Sopenharmony_ci	cp[1].count = 32;
6408c2ecf20Sopenharmony_ci	cp[1].flags = 0;
6418c2ecf20Sopenharmony_ci	req->cp = cp;
6428c2ecf20Sopenharmony_ci}
6438c2ecf20Sopenharmony_ci
6448c2ecf20Sopenharmony_cistatic void stlck_callback(struct ccw_device *cdev, void *data, int rc)
6458c2ecf20Sopenharmony_ci{
6468c2ecf20Sopenharmony_ci	struct stlck_data *sdata = data;
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci	sdata->rc = rc;
6498c2ecf20Sopenharmony_ci	complete(&sdata->done);
6508c2ecf20Sopenharmony_ci}
6518c2ecf20Sopenharmony_ci
6528c2ecf20Sopenharmony_ci/**
6538c2ecf20Sopenharmony_ci * ccw_device_stlck_start - perform unconditional release
6548c2ecf20Sopenharmony_ci * @cdev: ccw device
6558c2ecf20Sopenharmony_ci * @data: data pointer to be passed to ccw_device_stlck_done
6568c2ecf20Sopenharmony_ci * @buf1: data pointer used in channel program
6578c2ecf20Sopenharmony_ci * @buf2: data pointer used in channel program
6588c2ecf20Sopenharmony_ci *
6598c2ecf20Sopenharmony_ci * Execute a channel program on @cdev to release an existing PGID reservation.
6608c2ecf20Sopenharmony_ci */
6618c2ecf20Sopenharmony_cistatic void ccw_device_stlck_start(struct ccw_device *cdev, void *data,
6628c2ecf20Sopenharmony_ci				   void *buf1, void *buf2)
6638c2ecf20Sopenharmony_ci{
6648c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
6658c2ecf20Sopenharmony_ci	struct ccw_request *req = &cdev->private->req;
6668c2ecf20Sopenharmony_ci
6678c2ecf20Sopenharmony_ci	CIO_TRACE_EVENT(4, "stlck");
6688c2ecf20Sopenharmony_ci	CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id));
6698c2ecf20Sopenharmony_ci	/* Request setup. */
6708c2ecf20Sopenharmony_ci	memset(req, 0, sizeof(*req));
6718c2ecf20Sopenharmony_ci	req->timeout	= PGID_TIMEOUT;
6728c2ecf20Sopenharmony_ci	req->maxretries	= PGID_RETRIES;
6738c2ecf20Sopenharmony_ci	req->lpm	= sch->schib.pmcw.pam & sch->opm;
6748c2ecf20Sopenharmony_ci	req->data	= data;
6758c2ecf20Sopenharmony_ci	req->callback	= stlck_callback;
6768c2ecf20Sopenharmony_ci	stlck_build_cp(cdev, buf1, buf2);
6778c2ecf20Sopenharmony_ci	ccw_request_start(cdev);
6788c2ecf20Sopenharmony_ci}
6798c2ecf20Sopenharmony_ci
6808c2ecf20Sopenharmony_ci/*
6818c2ecf20Sopenharmony_ci * Perform unconditional reserve + release.
6828c2ecf20Sopenharmony_ci */
6838c2ecf20Sopenharmony_ciint ccw_device_stlck(struct ccw_device *cdev)
6848c2ecf20Sopenharmony_ci{
6858c2ecf20Sopenharmony_ci	struct subchannel *sch = to_subchannel(cdev->dev.parent);
6868c2ecf20Sopenharmony_ci	struct stlck_data data;
6878c2ecf20Sopenharmony_ci	u8 *buffer;
6888c2ecf20Sopenharmony_ci	int rc;
6898c2ecf20Sopenharmony_ci
6908c2ecf20Sopenharmony_ci	/* Check if steal lock operation is valid for this device. */
6918c2ecf20Sopenharmony_ci	if (cdev->drv) {
6928c2ecf20Sopenharmony_ci		if (!cdev->private->options.force)
6938c2ecf20Sopenharmony_ci			return -EINVAL;
6948c2ecf20Sopenharmony_ci	}
6958c2ecf20Sopenharmony_ci	buffer = kzalloc(64, GFP_DMA | GFP_KERNEL);
6968c2ecf20Sopenharmony_ci	if (!buffer)
6978c2ecf20Sopenharmony_ci		return -ENOMEM;
6988c2ecf20Sopenharmony_ci	init_completion(&data.done);
6998c2ecf20Sopenharmony_ci	data.rc = -EIO;
7008c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
7018c2ecf20Sopenharmony_ci	rc = cio_enable_subchannel(sch, (u32) (addr_t) sch);
7028c2ecf20Sopenharmony_ci	if (rc)
7038c2ecf20Sopenharmony_ci		goto out_unlock;
7048c2ecf20Sopenharmony_ci	/* Perform operation. */
7058c2ecf20Sopenharmony_ci	cdev->private->state = DEV_STATE_STEAL_LOCK;
7068c2ecf20Sopenharmony_ci	ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]);
7078c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
7088c2ecf20Sopenharmony_ci	/* Wait for operation to finish. */
7098c2ecf20Sopenharmony_ci	if (wait_for_completion_interruptible(&data.done)) {
7108c2ecf20Sopenharmony_ci		/* Got a signal. */
7118c2ecf20Sopenharmony_ci		spin_lock_irq(sch->lock);
7128c2ecf20Sopenharmony_ci		ccw_request_cancel(cdev);
7138c2ecf20Sopenharmony_ci		spin_unlock_irq(sch->lock);
7148c2ecf20Sopenharmony_ci		wait_for_completion(&data.done);
7158c2ecf20Sopenharmony_ci	}
7168c2ecf20Sopenharmony_ci	rc = data.rc;
7178c2ecf20Sopenharmony_ci	/* Check results. */
7188c2ecf20Sopenharmony_ci	spin_lock_irq(sch->lock);
7198c2ecf20Sopenharmony_ci	cio_disable_subchannel(sch);
7208c2ecf20Sopenharmony_ci	cdev->private->state = DEV_STATE_BOXED;
7218c2ecf20Sopenharmony_ciout_unlock:
7228c2ecf20Sopenharmony_ci	spin_unlock_irq(sch->lock);
7238c2ecf20Sopenharmony_ci	kfree(buffer);
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	return rc;
7268c2ecf20Sopenharmony_ci}
727