162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * CCW device PGID and path verification I/O handling. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright IBM Corp. 2002, 2009 662306a36Sopenharmony_ci * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com> 762306a36Sopenharmony_ci * Martin Schwidefsky <schwidefsky@de.ibm.com> 862306a36Sopenharmony_ci * Peter Oberparleiter <peter.oberparleiter@de.ibm.com> 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <linux/kernel.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/bitops.h> 1462306a36Sopenharmony_ci#include <linux/types.h> 1562306a36Sopenharmony_ci#include <linux/errno.h> 1662306a36Sopenharmony_ci#include <linux/slab.h> 1762306a36Sopenharmony_ci#include <linux/io.h> 1862306a36Sopenharmony_ci#include <asm/ccwdev.h> 1962306a36Sopenharmony_ci#include <asm/cio.h> 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci#include "cio.h" 2262306a36Sopenharmony_ci#include "cio_debug.h" 2362306a36Sopenharmony_ci#include "device.h" 2462306a36Sopenharmony_ci#include "io_sch.h" 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci#define PGID_RETRIES 256 2762306a36Sopenharmony_ci#define PGID_TIMEOUT (10 * HZ) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic void verify_start(struct ccw_device *cdev); 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci/* 3262306a36Sopenharmony_ci * Process path verification data and report result. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic void verify_done(struct ccw_device *cdev, int rc) 3562306a36Sopenharmony_ci{ 3662306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 3762306a36Sopenharmony_ci struct ccw_dev_id *id = &cdev->private->dev_id; 3862306a36Sopenharmony_ci int mpath = cdev->private->flags.mpath; 3962306a36Sopenharmony_ci int pgroup = cdev->private->flags.pgroup; 4062306a36Sopenharmony_ci 4162306a36Sopenharmony_ci if (rc) 4262306a36Sopenharmony_ci goto out; 4362306a36Sopenharmony_ci /* Ensure consistent multipathing state at device and channel. */ 4462306a36Sopenharmony_ci if (sch->config.mp != mpath) { 4562306a36Sopenharmony_ci sch->config.mp = mpath; 4662306a36Sopenharmony_ci rc = cio_commit_config(sch); 4762306a36Sopenharmony_ci } 4862306a36Sopenharmony_ciout: 4962306a36Sopenharmony_ci CIO_MSG_EVENT(2, "vrfy: device 0.%x.%04x: rc=%d pgroup=%d mpath=%d " 5062306a36Sopenharmony_ci "vpm=%02x\n", id->ssid, id->devno, rc, pgroup, mpath, 5162306a36Sopenharmony_ci sch->vpm); 5262306a36Sopenharmony_ci ccw_device_verify_done(cdev, rc); 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci/* 5662306a36Sopenharmony_ci * Create channel program to perform a NOOP. 5762306a36Sopenharmony_ci */ 5862306a36Sopenharmony_cistatic void nop_build_cp(struct ccw_device *cdev) 5962306a36Sopenharmony_ci{ 6062306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 6162306a36Sopenharmony_ci struct ccw1 *cp = cdev->private->dma_area->iccws; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci cp->cmd_code = CCW_CMD_NOOP; 6462306a36Sopenharmony_ci cp->cda = 0; 6562306a36Sopenharmony_ci cp->count = 0; 6662306a36Sopenharmony_ci cp->flags = CCW_FLAG_SLI; 6762306a36Sopenharmony_ci req->cp = cp; 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci/* 7162306a36Sopenharmony_ci * Perform NOOP on a single path. 7262306a36Sopenharmony_ci */ 7362306a36Sopenharmony_cistatic void nop_do(struct ccw_device *cdev) 7462306a36Sopenharmony_ci{ 7562306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 7662306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & sch->opm & 7962306a36Sopenharmony_ci ~cdev->private->path_noirq_mask); 8062306a36Sopenharmony_ci if (!req->lpm) 8162306a36Sopenharmony_ci goto out_nopath; 8262306a36Sopenharmony_ci nop_build_cp(cdev); 8362306a36Sopenharmony_ci ccw_request_start(cdev); 8462306a36Sopenharmony_ci return; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ciout_nopath: 8762306a36Sopenharmony_ci verify_done(cdev, sch->vpm ? 0 : -EACCES); 8862306a36Sopenharmony_ci} 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* 9162306a36Sopenharmony_ci * Adjust NOOP I/O status. 9262306a36Sopenharmony_ci */ 9362306a36Sopenharmony_cistatic enum io_status nop_filter(struct ccw_device *cdev, void *data, 9462306a36Sopenharmony_ci struct irb *irb, enum io_status status) 9562306a36Sopenharmony_ci{ 9662306a36Sopenharmony_ci /* Only subchannel status might indicate a path error. */ 9762306a36Sopenharmony_ci if (status == IO_STATUS_ERROR && irb->scsw.cmd.cstat == 0) 9862306a36Sopenharmony_ci return IO_DONE; 9962306a36Sopenharmony_ci return status; 10062306a36Sopenharmony_ci} 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci/* 10362306a36Sopenharmony_ci * Process NOOP request result for a single path. 10462306a36Sopenharmony_ci */ 10562306a36Sopenharmony_cistatic void nop_callback(struct ccw_device *cdev, void *data, int rc) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 10862306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci switch (rc) { 11162306a36Sopenharmony_ci case 0: 11262306a36Sopenharmony_ci sch->vpm |= req->lpm; 11362306a36Sopenharmony_ci break; 11462306a36Sopenharmony_ci case -ETIME: 11562306a36Sopenharmony_ci cdev->private->path_noirq_mask |= req->lpm; 11662306a36Sopenharmony_ci break; 11762306a36Sopenharmony_ci case -EACCES: 11862306a36Sopenharmony_ci cdev->private->path_notoper_mask |= req->lpm; 11962306a36Sopenharmony_ci break; 12062306a36Sopenharmony_ci default: 12162306a36Sopenharmony_ci goto err; 12262306a36Sopenharmony_ci } 12362306a36Sopenharmony_ci /* Continue on the next path. */ 12462306a36Sopenharmony_ci req->lpm >>= 1; 12562306a36Sopenharmony_ci nop_do(cdev); 12662306a36Sopenharmony_ci return; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cierr: 12962306a36Sopenharmony_ci verify_done(cdev, rc); 13062306a36Sopenharmony_ci} 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci/* 13362306a36Sopenharmony_ci * Create channel program to perform SET PGID on a single path. 13462306a36Sopenharmony_ci */ 13562306a36Sopenharmony_cistatic void spid_build_cp(struct ccw_device *cdev, u8 fn) 13662306a36Sopenharmony_ci{ 13762306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 13862306a36Sopenharmony_ci struct ccw1 *cp = cdev->private->dma_area->iccws; 13962306a36Sopenharmony_ci int i = pathmask_to_pos(req->lpm); 14062306a36Sopenharmony_ci struct pgid *pgid = &cdev->private->dma_area->pgid[i]; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci pgid->inf.fc = fn; 14362306a36Sopenharmony_ci cp->cmd_code = CCW_CMD_SET_PGID; 14462306a36Sopenharmony_ci cp->cda = (u32)virt_to_phys(pgid); 14562306a36Sopenharmony_ci cp->count = sizeof(*pgid); 14662306a36Sopenharmony_ci cp->flags = CCW_FLAG_SLI; 14762306a36Sopenharmony_ci req->cp = cp; 14862306a36Sopenharmony_ci} 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_cistatic void pgid_wipeout_callback(struct ccw_device *cdev, void *data, int rc) 15162306a36Sopenharmony_ci{ 15262306a36Sopenharmony_ci if (rc) { 15362306a36Sopenharmony_ci /* We don't know the path groups' state. Abort. */ 15462306a36Sopenharmony_ci verify_done(cdev, rc); 15562306a36Sopenharmony_ci return; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci /* 15862306a36Sopenharmony_ci * Path groups have been reset. Restart path verification but 15962306a36Sopenharmony_ci * leave paths in path_noirq_mask out. 16062306a36Sopenharmony_ci */ 16162306a36Sopenharmony_ci cdev->private->flags.pgid_unknown = 0; 16262306a36Sopenharmony_ci verify_start(cdev); 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci/* 16662306a36Sopenharmony_ci * Reset pathgroups and restart path verification, leave unusable paths out. 16762306a36Sopenharmony_ci */ 16862306a36Sopenharmony_cistatic void pgid_wipeout_start(struct ccw_device *cdev) 16962306a36Sopenharmony_ci{ 17062306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 17162306a36Sopenharmony_ci struct ccw_dev_id *id = &cdev->private->dev_id; 17262306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 17362306a36Sopenharmony_ci u8 fn; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci CIO_MSG_EVENT(2, "wipe: device 0.%x.%04x: pvm=%02x nim=%02x\n", 17662306a36Sopenharmony_ci id->ssid, id->devno, cdev->private->pgid_valid_mask, 17762306a36Sopenharmony_ci cdev->private->path_noirq_mask); 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci /* Initialize request data. */ 18062306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 18162306a36Sopenharmony_ci req->timeout = PGID_TIMEOUT; 18262306a36Sopenharmony_ci req->maxretries = PGID_RETRIES; 18362306a36Sopenharmony_ci req->lpm = sch->schib.pmcw.pam; 18462306a36Sopenharmony_ci req->callback = pgid_wipeout_callback; 18562306a36Sopenharmony_ci fn = SPID_FUNC_DISBAND; 18662306a36Sopenharmony_ci if (cdev->private->flags.mpath) 18762306a36Sopenharmony_ci fn |= SPID_FUNC_MULTI_PATH; 18862306a36Sopenharmony_ci spid_build_cp(cdev, fn); 18962306a36Sopenharmony_ci ccw_request_start(cdev); 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* 19362306a36Sopenharmony_ci * Perform establish/resign SET PGID on a single path. 19462306a36Sopenharmony_ci */ 19562306a36Sopenharmony_cistatic void spid_do(struct ccw_device *cdev) 19662306a36Sopenharmony_ci{ 19762306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 19862306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 19962306a36Sopenharmony_ci u8 fn; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci /* Use next available path that is not already in correct state. */ 20262306a36Sopenharmony_ci req->lpm = lpm_adjust(req->lpm, cdev->private->pgid_todo_mask); 20362306a36Sopenharmony_ci if (!req->lpm) 20462306a36Sopenharmony_ci goto out_nopath; 20562306a36Sopenharmony_ci /* Channel program setup. */ 20662306a36Sopenharmony_ci if (req->lpm & sch->opm) 20762306a36Sopenharmony_ci fn = SPID_FUNC_ESTABLISH; 20862306a36Sopenharmony_ci else 20962306a36Sopenharmony_ci fn = SPID_FUNC_RESIGN; 21062306a36Sopenharmony_ci if (cdev->private->flags.mpath) 21162306a36Sopenharmony_ci fn |= SPID_FUNC_MULTI_PATH; 21262306a36Sopenharmony_ci spid_build_cp(cdev, fn); 21362306a36Sopenharmony_ci ccw_request_start(cdev); 21462306a36Sopenharmony_ci return; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciout_nopath: 21762306a36Sopenharmony_ci if (cdev->private->flags.pgid_unknown) { 21862306a36Sopenharmony_ci /* At least one SPID could be partially done. */ 21962306a36Sopenharmony_ci pgid_wipeout_start(cdev); 22062306a36Sopenharmony_ci return; 22162306a36Sopenharmony_ci } 22262306a36Sopenharmony_ci verify_done(cdev, sch->vpm ? 0 : -EACCES); 22362306a36Sopenharmony_ci} 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci/* 22662306a36Sopenharmony_ci * Process SET PGID request result for a single path. 22762306a36Sopenharmony_ci */ 22862306a36Sopenharmony_cistatic void spid_callback(struct ccw_device *cdev, void *data, int rc) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 23162306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci switch (rc) { 23462306a36Sopenharmony_ci case 0: 23562306a36Sopenharmony_ci sch->vpm |= req->lpm & sch->opm; 23662306a36Sopenharmony_ci break; 23762306a36Sopenharmony_ci case -ETIME: 23862306a36Sopenharmony_ci cdev->private->flags.pgid_unknown = 1; 23962306a36Sopenharmony_ci cdev->private->path_noirq_mask |= req->lpm; 24062306a36Sopenharmony_ci break; 24162306a36Sopenharmony_ci case -EACCES: 24262306a36Sopenharmony_ci cdev->private->path_notoper_mask |= req->lpm; 24362306a36Sopenharmony_ci break; 24462306a36Sopenharmony_ci case -EOPNOTSUPP: 24562306a36Sopenharmony_ci if (cdev->private->flags.mpath) { 24662306a36Sopenharmony_ci /* Try without multipathing. */ 24762306a36Sopenharmony_ci cdev->private->flags.mpath = 0; 24862306a36Sopenharmony_ci goto out_restart; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci /* Try without pathgrouping. */ 25162306a36Sopenharmony_ci cdev->private->flags.pgroup = 0; 25262306a36Sopenharmony_ci goto out_restart; 25362306a36Sopenharmony_ci default: 25462306a36Sopenharmony_ci goto err; 25562306a36Sopenharmony_ci } 25662306a36Sopenharmony_ci req->lpm >>= 1; 25762306a36Sopenharmony_ci spid_do(cdev); 25862306a36Sopenharmony_ci return; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ciout_restart: 26162306a36Sopenharmony_ci verify_start(cdev); 26262306a36Sopenharmony_ci return; 26362306a36Sopenharmony_cierr: 26462306a36Sopenharmony_ci verify_done(cdev, rc); 26562306a36Sopenharmony_ci} 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_cistatic void spid_start(struct ccw_device *cdev) 26862306a36Sopenharmony_ci{ 26962306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 27062306a36Sopenharmony_ci 27162306a36Sopenharmony_ci /* Initialize request data. */ 27262306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 27362306a36Sopenharmony_ci req->timeout = PGID_TIMEOUT; 27462306a36Sopenharmony_ci req->maxretries = PGID_RETRIES; 27562306a36Sopenharmony_ci req->lpm = 0x80; 27662306a36Sopenharmony_ci req->singlepath = 1; 27762306a36Sopenharmony_ci req->callback = spid_callback; 27862306a36Sopenharmony_ci spid_do(cdev); 27962306a36Sopenharmony_ci} 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_cistatic int pgid_is_reset(struct pgid *p) 28262306a36Sopenharmony_ci{ 28362306a36Sopenharmony_ci char *c; 28462306a36Sopenharmony_ci 28562306a36Sopenharmony_ci for (c = (char *)p + 1; c < (char *)(p + 1); c++) { 28662306a36Sopenharmony_ci if (*c != 0) 28762306a36Sopenharmony_ci return 0; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci return 1; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int pgid_cmp(struct pgid *p1, struct pgid *p2) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci return memcmp((char *) p1 + 1, (char *) p2 + 1, 29562306a36Sopenharmony_ci sizeof(struct pgid) - 1); 29662306a36Sopenharmony_ci} 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci/* 29962306a36Sopenharmony_ci * Determine pathgroup state from PGID data. 30062306a36Sopenharmony_ci */ 30162306a36Sopenharmony_cistatic void pgid_analyze(struct ccw_device *cdev, struct pgid **p, 30262306a36Sopenharmony_ci int *mismatch, u8 *reserved, u8 *reset) 30362306a36Sopenharmony_ci{ 30462306a36Sopenharmony_ci struct pgid *pgid = &cdev->private->dma_area->pgid[0]; 30562306a36Sopenharmony_ci struct pgid *first = NULL; 30662306a36Sopenharmony_ci int lpm; 30762306a36Sopenharmony_ci int i; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci *mismatch = 0; 31062306a36Sopenharmony_ci *reserved = 0; 31162306a36Sopenharmony_ci *reset = 0; 31262306a36Sopenharmony_ci for (i = 0, lpm = 0x80; i < 8; i++, pgid++, lpm >>= 1) { 31362306a36Sopenharmony_ci if ((cdev->private->pgid_valid_mask & lpm) == 0) 31462306a36Sopenharmony_ci continue; 31562306a36Sopenharmony_ci if (pgid->inf.ps.state2 == SNID_STATE2_RESVD_ELSE) 31662306a36Sopenharmony_ci *reserved |= lpm; 31762306a36Sopenharmony_ci if (pgid_is_reset(pgid)) { 31862306a36Sopenharmony_ci *reset |= lpm; 31962306a36Sopenharmony_ci continue; 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci if (!first) { 32262306a36Sopenharmony_ci first = pgid; 32362306a36Sopenharmony_ci continue; 32462306a36Sopenharmony_ci } 32562306a36Sopenharmony_ci if (pgid_cmp(pgid, first) != 0) 32662306a36Sopenharmony_ci *mismatch = 1; 32762306a36Sopenharmony_ci } 32862306a36Sopenharmony_ci if (!first) 32962306a36Sopenharmony_ci first = &channel_subsystems[0]->global_pgid; 33062306a36Sopenharmony_ci *p = first; 33162306a36Sopenharmony_ci} 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_cistatic u8 pgid_to_donepm(struct ccw_device *cdev) 33462306a36Sopenharmony_ci{ 33562306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 33662306a36Sopenharmony_ci struct pgid *pgid; 33762306a36Sopenharmony_ci int i; 33862306a36Sopenharmony_ci int lpm; 33962306a36Sopenharmony_ci u8 donepm = 0; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci /* Set bits for paths which are already in the target state. */ 34262306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 34362306a36Sopenharmony_ci lpm = 0x80 >> i; 34462306a36Sopenharmony_ci if ((cdev->private->pgid_valid_mask & lpm) == 0) 34562306a36Sopenharmony_ci continue; 34662306a36Sopenharmony_ci pgid = &cdev->private->dma_area->pgid[i]; 34762306a36Sopenharmony_ci if (sch->opm & lpm) { 34862306a36Sopenharmony_ci if (pgid->inf.ps.state1 != SNID_STATE1_GROUPED) 34962306a36Sopenharmony_ci continue; 35062306a36Sopenharmony_ci } else { 35162306a36Sopenharmony_ci if (pgid->inf.ps.state1 != SNID_STATE1_UNGROUPED) 35262306a36Sopenharmony_ci continue; 35362306a36Sopenharmony_ci } 35462306a36Sopenharmony_ci if (cdev->private->flags.mpath) { 35562306a36Sopenharmony_ci if (pgid->inf.ps.state3 != SNID_STATE3_MULTI_PATH) 35662306a36Sopenharmony_ci continue; 35762306a36Sopenharmony_ci } else { 35862306a36Sopenharmony_ci if (pgid->inf.ps.state3 != SNID_STATE3_SINGLE_PATH) 35962306a36Sopenharmony_ci continue; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci donepm |= lpm; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci return donepm; 36562306a36Sopenharmony_ci} 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_cistatic void pgid_fill(struct ccw_device *cdev, struct pgid *pgid) 36862306a36Sopenharmony_ci{ 36962306a36Sopenharmony_ci int i; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci for (i = 0; i < 8; i++) 37262306a36Sopenharmony_ci memcpy(&cdev->private->dma_area->pgid[i], pgid, 37362306a36Sopenharmony_ci sizeof(struct pgid)); 37462306a36Sopenharmony_ci} 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci/* 37762306a36Sopenharmony_ci * Process SENSE PGID data and report result. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_cistatic void snid_done(struct ccw_device *cdev, int rc) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci struct ccw_dev_id *id = &cdev->private->dev_id; 38262306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 38362306a36Sopenharmony_ci struct pgid *pgid; 38462306a36Sopenharmony_ci int mismatch = 0; 38562306a36Sopenharmony_ci u8 reserved = 0; 38662306a36Sopenharmony_ci u8 reset = 0; 38762306a36Sopenharmony_ci u8 donepm; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci if (rc) 39062306a36Sopenharmony_ci goto out; 39162306a36Sopenharmony_ci pgid_analyze(cdev, &pgid, &mismatch, &reserved, &reset); 39262306a36Sopenharmony_ci if (reserved == cdev->private->pgid_valid_mask) 39362306a36Sopenharmony_ci rc = -EUSERS; 39462306a36Sopenharmony_ci else if (mismatch) 39562306a36Sopenharmony_ci rc = -EOPNOTSUPP; 39662306a36Sopenharmony_ci else { 39762306a36Sopenharmony_ci donepm = pgid_to_donepm(cdev); 39862306a36Sopenharmony_ci sch->vpm = donepm & sch->opm; 39962306a36Sopenharmony_ci cdev->private->pgid_reset_mask |= reset; 40062306a36Sopenharmony_ci cdev->private->pgid_todo_mask &= 40162306a36Sopenharmony_ci ~(donepm | cdev->private->path_noirq_mask); 40262306a36Sopenharmony_ci pgid_fill(cdev, pgid); 40362306a36Sopenharmony_ci } 40462306a36Sopenharmony_ciout: 40562306a36Sopenharmony_ci CIO_MSG_EVENT(2, "snid: device 0.%x.%04x: rc=%d pvm=%02x vpm=%02x " 40662306a36Sopenharmony_ci "todo=%02x mism=%d rsvd=%02x reset=%02x\n", id->ssid, 40762306a36Sopenharmony_ci id->devno, rc, cdev->private->pgid_valid_mask, sch->vpm, 40862306a36Sopenharmony_ci cdev->private->pgid_todo_mask, mismatch, reserved, reset); 40962306a36Sopenharmony_ci switch (rc) { 41062306a36Sopenharmony_ci case 0: 41162306a36Sopenharmony_ci if (cdev->private->flags.pgid_unknown) { 41262306a36Sopenharmony_ci pgid_wipeout_start(cdev); 41362306a36Sopenharmony_ci return; 41462306a36Sopenharmony_ci } 41562306a36Sopenharmony_ci /* Anything left to do? */ 41662306a36Sopenharmony_ci if (cdev->private->pgid_todo_mask == 0) { 41762306a36Sopenharmony_ci verify_done(cdev, sch->vpm == 0 ? -EACCES : 0); 41862306a36Sopenharmony_ci return; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci /* Perform path-grouping. */ 42162306a36Sopenharmony_ci spid_start(cdev); 42262306a36Sopenharmony_ci break; 42362306a36Sopenharmony_ci case -EOPNOTSUPP: 42462306a36Sopenharmony_ci /* Path-grouping not supported. */ 42562306a36Sopenharmony_ci cdev->private->flags.pgroup = 0; 42662306a36Sopenharmony_ci cdev->private->flags.mpath = 0; 42762306a36Sopenharmony_ci verify_start(cdev); 42862306a36Sopenharmony_ci break; 42962306a36Sopenharmony_ci default: 43062306a36Sopenharmony_ci verify_done(cdev, rc); 43162306a36Sopenharmony_ci } 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci/* 43562306a36Sopenharmony_ci * Create channel program to perform a SENSE PGID on a single path. 43662306a36Sopenharmony_ci */ 43762306a36Sopenharmony_cistatic void snid_build_cp(struct ccw_device *cdev) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 44062306a36Sopenharmony_ci struct ccw1 *cp = cdev->private->dma_area->iccws; 44162306a36Sopenharmony_ci int i = pathmask_to_pos(req->lpm); 44262306a36Sopenharmony_ci 44362306a36Sopenharmony_ci /* Channel program setup. */ 44462306a36Sopenharmony_ci cp->cmd_code = CCW_CMD_SENSE_PGID; 44562306a36Sopenharmony_ci cp->cda = (u32)virt_to_phys(&cdev->private->dma_area->pgid[i]); 44662306a36Sopenharmony_ci cp->count = sizeof(struct pgid); 44762306a36Sopenharmony_ci cp->flags = CCW_FLAG_SLI; 44862306a36Sopenharmony_ci req->cp = cp; 44962306a36Sopenharmony_ci} 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci/* 45262306a36Sopenharmony_ci * Perform SENSE PGID on a single path. 45362306a36Sopenharmony_ci */ 45462306a36Sopenharmony_cistatic void snid_do(struct ccw_device *cdev) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 45762306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 45862306a36Sopenharmony_ci int ret; 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_ci req->lpm = lpm_adjust(req->lpm, sch->schib.pmcw.pam & 46162306a36Sopenharmony_ci ~cdev->private->path_noirq_mask); 46262306a36Sopenharmony_ci if (!req->lpm) 46362306a36Sopenharmony_ci goto out_nopath; 46462306a36Sopenharmony_ci snid_build_cp(cdev); 46562306a36Sopenharmony_ci ccw_request_start(cdev); 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ciout_nopath: 46962306a36Sopenharmony_ci if (cdev->private->pgid_valid_mask) 47062306a36Sopenharmony_ci ret = 0; 47162306a36Sopenharmony_ci else if (cdev->private->path_noirq_mask) 47262306a36Sopenharmony_ci ret = -ETIME; 47362306a36Sopenharmony_ci else 47462306a36Sopenharmony_ci ret = -EACCES; 47562306a36Sopenharmony_ci snid_done(cdev, ret); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* 47962306a36Sopenharmony_ci * Process SENSE PGID request result for single path. 48062306a36Sopenharmony_ci */ 48162306a36Sopenharmony_cistatic void snid_callback(struct ccw_device *cdev, void *data, int rc) 48262306a36Sopenharmony_ci{ 48362306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 48462306a36Sopenharmony_ci 48562306a36Sopenharmony_ci switch (rc) { 48662306a36Sopenharmony_ci case 0: 48762306a36Sopenharmony_ci cdev->private->pgid_valid_mask |= req->lpm; 48862306a36Sopenharmony_ci break; 48962306a36Sopenharmony_ci case -ETIME: 49062306a36Sopenharmony_ci cdev->private->flags.pgid_unknown = 1; 49162306a36Sopenharmony_ci cdev->private->path_noirq_mask |= req->lpm; 49262306a36Sopenharmony_ci break; 49362306a36Sopenharmony_ci case -EACCES: 49462306a36Sopenharmony_ci cdev->private->path_notoper_mask |= req->lpm; 49562306a36Sopenharmony_ci break; 49662306a36Sopenharmony_ci default: 49762306a36Sopenharmony_ci goto err; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci /* Continue on the next path. */ 50062306a36Sopenharmony_ci req->lpm >>= 1; 50162306a36Sopenharmony_ci snid_do(cdev); 50262306a36Sopenharmony_ci return; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_cierr: 50562306a36Sopenharmony_ci snid_done(cdev, rc); 50662306a36Sopenharmony_ci} 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci/* 50962306a36Sopenharmony_ci * Perform path verification. 51062306a36Sopenharmony_ci */ 51162306a36Sopenharmony_cistatic void verify_start(struct ccw_device *cdev) 51262306a36Sopenharmony_ci{ 51362306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 51462306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 51562306a36Sopenharmony_ci struct ccw_dev_id *devid = &cdev->private->dev_id; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci sch->vpm = 0; 51862306a36Sopenharmony_ci sch->lpm = sch->schib.pmcw.pam; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci /* Initialize PGID data. */ 52162306a36Sopenharmony_ci memset(cdev->private->dma_area->pgid, 0, 52262306a36Sopenharmony_ci sizeof(cdev->private->dma_area->pgid)); 52362306a36Sopenharmony_ci cdev->private->pgid_valid_mask = 0; 52462306a36Sopenharmony_ci cdev->private->pgid_todo_mask = sch->schib.pmcw.pam; 52562306a36Sopenharmony_ci cdev->private->path_notoper_mask = 0; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci /* Initialize request data. */ 52862306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 52962306a36Sopenharmony_ci req->timeout = PGID_TIMEOUT; 53062306a36Sopenharmony_ci req->maxretries = PGID_RETRIES; 53162306a36Sopenharmony_ci req->lpm = 0x80; 53262306a36Sopenharmony_ci req->singlepath = 1; 53362306a36Sopenharmony_ci if (cdev->private->flags.pgroup) { 53462306a36Sopenharmony_ci CIO_TRACE_EVENT(4, "snid"); 53562306a36Sopenharmony_ci CIO_HEX_EVENT(4, devid, sizeof(*devid)); 53662306a36Sopenharmony_ci req->callback = snid_callback; 53762306a36Sopenharmony_ci snid_do(cdev); 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci CIO_TRACE_EVENT(4, "nop"); 54062306a36Sopenharmony_ci CIO_HEX_EVENT(4, devid, sizeof(*devid)); 54162306a36Sopenharmony_ci req->filter = nop_filter; 54262306a36Sopenharmony_ci req->callback = nop_callback; 54362306a36Sopenharmony_ci nop_do(cdev); 54462306a36Sopenharmony_ci } 54562306a36Sopenharmony_ci} 54662306a36Sopenharmony_ci 54762306a36Sopenharmony_ci/** 54862306a36Sopenharmony_ci * ccw_device_verify_start - perform path verification 54962306a36Sopenharmony_ci * @cdev: ccw device 55062306a36Sopenharmony_ci * 55162306a36Sopenharmony_ci * Perform an I/O on each available channel path to @cdev to determine which 55262306a36Sopenharmony_ci * paths are operational. The resulting path mask is stored in sch->vpm. 55362306a36Sopenharmony_ci * If device options specify pathgrouping, establish a pathgroup for the 55462306a36Sopenharmony_ci * operational paths. When finished, call ccw_device_verify_done with a 55562306a36Sopenharmony_ci * return code specifying the result. 55662306a36Sopenharmony_ci */ 55762306a36Sopenharmony_civoid ccw_device_verify_start(struct ccw_device *cdev) 55862306a36Sopenharmony_ci{ 55962306a36Sopenharmony_ci CIO_TRACE_EVENT(4, "vrfy"); 56062306a36Sopenharmony_ci CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); 56162306a36Sopenharmony_ci /* 56262306a36Sopenharmony_ci * Initialize pathgroup and multipath state with target values. 56362306a36Sopenharmony_ci * They may change in the course of path verification. 56462306a36Sopenharmony_ci */ 56562306a36Sopenharmony_ci cdev->private->flags.pgroup = cdev->private->options.pgroup; 56662306a36Sopenharmony_ci cdev->private->flags.mpath = cdev->private->options.mpath; 56762306a36Sopenharmony_ci cdev->private->flags.doverify = 0; 56862306a36Sopenharmony_ci cdev->private->path_noirq_mask = 0; 56962306a36Sopenharmony_ci verify_start(cdev); 57062306a36Sopenharmony_ci} 57162306a36Sopenharmony_ci 57262306a36Sopenharmony_ci/* 57362306a36Sopenharmony_ci * Process disband SET PGID request result. 57462306a36Sopenharmony_ci */ 57562306a36Sopenharmony_cistatic void disband_callback(struct ccw_device *cdev, void *data, int rc) 57662306a36Sopenharmony_ci{ 57762306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 57862306a36Sopenharmony_ci struct ccw_dev_id *id = &cdev->private->dev_id; 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci if (rc) 58162306a36Sopenharmony_ci goto out; 58262306a36Sopenharmony_ci /* Ensure consistent multipathing state at device and channel. */ 58362306a36Sopenharmony_ci cdev->private->flags.mpath = 0; 58462306a36Sopenharmony_ci if (sch->config.mp) { 58562306a36Sopenharmony_ci sch->config.mp = 0; 58662306a36Sopenharmony_ci rc = cio_commit_config(sch); 58762306a36Sopenharmony_ci } 58862306a36Sopenharmony_ciout: 58962306a36Sopenharmony_ci CIO_MSG_EVENT(0, "disb: device 0.%x.%04x: rc=%d\n", id->ssid, id->devno, 59062306a36Sopenharmony_ci rc); 59162306a36Sopenharmony_ci ccw_device_disband_done(cdev, rc); 59262306a36Sopenharmony_ci} 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci/** 59562306a36Sopenharmony_ci * ccw_device_disband_start - disband pathgroup 59662306a36Sopenharmony_ci * @cdev: ccw device 59762306a36Sopenharmony_ci * 59862306a36Sopenharmony_ci * Execute a SET PGID channel program on @cdev to disband a previously 59962306a36Sopenharmony_ci * established pathgroup. When finished, call ccw_device_disband_done with 60062306a36Sopenharmony_ci * a return code specifying the result. 60162306a36Sopenharmony_ci */ 60262306a36Sopenharmony_civoid ccw_device_disband_start(struct ccw_device *cdev) 60362306a36Sopenharmony_ci{ 60462306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 60562306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 60662306a36Sopenharmony_ci u8 fn; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci CIO_TRACE_EVENT(4, "disb"); 60962306a36Sopenharmony_ci CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); 61062306a36Sopenharmony_ci /* Request setup. */ 61162306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 61262306a36Sopenharmony_ci req->timeout = PGID_TIMEOUT; 61362306a36Sopenharmony_ci req->maxretries = PGID_RETRIES; 61462306a36Sopenharmony_ci req->lpm = sch->schib.pmcw.pam & sch->opm; 61562306a36Sopenharmony_ci req->singlepath = 1; 61662306a36Sopenharmony_ci req->callback = disband_callback; 61762306a36Sopenharmony_ci fn = SPID_FUNC_DISBAND; 61862306a36Sopenharmony_ci if (cdev->private->flags.mpath) 61962306a36Sopenharmony_ci fn |= SPID_FUNC_MULTI_PATH; 62062306a36Sopenharmony_ci spid_build_cp(cdev, fn); 62162306a36Sopenharmony_ci ccw_request_start(cdev); 62262306a36Sopenharmony_ci} 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_cistruct stlck_data { 62562306a36Sopenharmony_ci struct completion done; 62662306a36Sopenharmony_ci int rc; 62762306a36Sopenharmony_ci}; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_cistatic void stlck_build_cp(struct ccw_device *cdev, void *buf1, void *buf2) 63062306a36Sopenharmony_ci{ 63162306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 63262306a36Sopenharmony_ci struct ccw1 *cp = cdev->private->dma_area->iccws; 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci cp[0].cmd_code = CCW_CMD_STLCK; 63562306a36Sopenharmony_ci cp[0].cda = (u32)virt_to_phys(buf1); 63662306a36Sopenharmony_ci cp[0].count = 32; 63762306a36Sopenharmony_ci cp[0].flags = CCW_FLAG_CC; 63862306a36Sopenharmony_ci cp[1].cmd_code = CCW_CMD_RELEASE; 63962306a36Sopenharmony_ci cp[1].cda = (u32)virt_to_phys(buf2); 64062306a36Sopenharmony_ci cp[1].count = 32; 64162306a36Sopenharmony_ci cp[1].flags = 0; 64262306a36Sopenharmony_ci req->cp = cp; 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_cistatic void stlck_callback(struct ccw_device *cdev, void *data, int rc) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci struct stlck_data *sdata = data; 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci sdata->rc = rc; 65062306a36Sopenharmony_ci complete(&sdata->done); 65162306a36Sopenharmony_ci} 65262306a36Sopenharmony_ci 65362306a36Sopenharmony_ci/** 65462306a36Sopenharmony_ci * ccw_device_stlck_start - perform unconditional release 65562306a36Sopenharmony_ci * @cdev: ccw device 65662306a36Sopenharmony_ci * @data: data pointer to be passed to ccw_device_stlck_done 65762306a36Sopenharmony_ci * @buf1: data pointer used in channel program 65862306a36Sopenharmony_ci * @buf2: data pointer used in channel program 65962306a36Sopenharmony_ci * 66062306a36Sopenharmony_ci * Execute a channel program on @cdev to release an existing PGID reservation. 66162306a36Sopenharmony_ci */ 66262306a36Sopenharmony_cistatic void ccw_device_stlck_start(struct ccw_device *cdev, void *data, 66362306a36Sopenharmony_ci void *buf1, void *buf2) 66462306a36Sopenharmony_ci{ 66562306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 66662306a36Sopenharmony_ci struct ccw_request *req = &cdev->private->req; 66762306a36Sopenharmony_ci 66862306a36Sopenharmony_ci CIO_TRACE_EVENT(4, "stlck"); 66962306a36Sopenharmony_ci CIO_HEX_EVENT(4, &cdev->private->dev_id, sizeof(cdev->private->dev_id)); 67062306a36Sopenharmony_ci /* Request setup. */ 67162306a36Sopenharmony_ci memset(req, 0, sizeof(*req)); 67262306a36Sopenharmony_ci req->timeout = PGID_TIMEOUT; 67362306a36Sopenharmony_ci req->maxretries = PGID_RETRIES; 67462306a36Sopenharmony_ci req->lpm = sch->schib.pmcw.pam & sch->opm; 67562306a36Sopenharmony_ci req->data = data; 67662306a36Sopenharmony_ci req->callback = stlck_callback; 67762306a36Sopenharmony_ci stlck_build_cp(cdev, buf1, buf2); 67862306a36Sopenharmony_ci ccw_request_start(cdev); 67962306a36Sopenharmony_ci} 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci/* 68262306a36Sopenharmony_ci * Perform unconditional reserve + release. 68362306a36Sopenharmony_ci */ 68462306a36Sopenharmony_ciint ccw_device_stlck(struct ccw_device *cdev) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct subchannel *sch = to_subchannel(cdev->dev.parent); 68762306a36Sopenharmony_ci struct stlck_data data; 68862306a36Sopenharmony_ci u8 *buffer; 68962306a36Sopenharmony_ci int rc; 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci /* Check if steal lock operation is valid for this device. */ 69262306a36Sopenharmony_ci if (cdev->drv) { 69362306a36Sopenharmony_ci if (!cdev->private->options.force) 69462306a36Sopenharmony_ci return -EINVAL; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci buffer = kzalloc(64, GFP_DMA | GFP_KERNEL); 69762306a36Sopenharmony_ci if (!buffer) 69862306a36Sopenharmony_ci return -ENOMEM; 69962306a36Sopenharmony_ci init_completion(&data.done); 70062306a36Sopenharmony_ci data.rc = -EIO; 70162306a36Sopenharmony_ci spin_lock_irq(sch->lock); 70262306a36Sopenharmony_ci rc = cio_enable_subchannel(sch, (u32)virt_to_phys(sch)); 70362306a36Sopenharmony_ci if (rc) 70462306a36Sopenharmony_ci goto out_unlock; 70562306a36Sopenharmony_ci /* Perform operation. */ 70662306a36Sopenharmony_ci cdev->private->state = DEV_STATE_STEAL_LOCK; 70762306a36Sopenharmony_ci ccw_device_stlck_start(cdev, &data, &buffer[0], &buffer[32]); 70862306a36Sopenharmony_ci spin_unlock_irq(sch->lock); 70962306a36Sopenharmony_ci /* Wait for operation to finish. */ 71062306a36Sopenharmony_ci if (wait_for_completion_interruptible(&data.done)) { 71162306a36Sopenharmony_ci /* Got a signal. */ 71262306a36Sopenharmony_ci spin_lock_irq(sch->lock); 71362306a36Sopenharmony_ci ccw_request_cancel(cdev); 71462306a36Sopenharmony_ci spin_unlock_irq(sch->lock); 71562306a36Sopenharmony_ci wait_for_completion(&data.done); 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci rc = data.rc; 71862306a36Sopenharmony_ci /* Check results. */ 71962306a36Sopenharmony_ci spin_lock_irq(sch->lock); 72062306a36Sopenharmony_ci cio_disable_subchannel(sch); 72162306a36Sopenharmony_ci cdev->private->state = DEV_STATE_BOXED; 72262306a36Sopenharmony_ciout_unlock: 72362306a36Sopenharmony_ci spin_unlock_irq(sch->lock); 72462306a36Sopenharmony_ci kfree(buffer); 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_ci return rc; 72762306a36Sopenharmony_ci} 728