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