brintos

brintos / linux-shallow public Read only

0
0
Text · 8.4 KiB · 73582a0 Raw
369 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 *  Handling of internal CCW device requests.4 *5 *    Copyright IBM Corp. 2009, 20116 *    Author(s): Peter Oberparleiter <peter.oberparleiter@de.ibm.com>7 */8 9#define KMSG_COMPONENT "cio"10#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt11 12#include <linux/types.h>13#include <linux/err.h>14#include <asm/ccwdev.h>15#include <asm/cio.h>16 17#include "io_sch.h"18#include "cio.h"19#include "device.h"20#include "cio_debug.h"21 22/**23 * lpm_adjust - adjust path mask24 * @lpm: path mask to adjust25 * @mask: mask of available paths26 *27 * Shift @lpm right until @lpm and @mask have at least one bit in common or28 * until @lpm is zero. Return the resulting lpm.29 */30int lpm_adjust(int lpm, int mask)31{32	while (lpm && ((lpm & mask) == 0))33		lpm >>= 1;34	return lpm;35}36 37/*38 * Adjust path mask to use next path and reset retry count. Return resulting39 * path mask.40 */41static u16 ccwreq_next_path(struct ccw_device *cdev)42{43	struct ccw_request *req = &cdev->private->req;44 45	if (!req->singlepath) {46		req->mask = 0;47		goto out;48	}49	req->retries	= req->maxretries;50	req->mask	= lpm_adjust(req->mask >> 1, req->lpm);51out:52	return req->mask;53}54 55/*56 * Clean up device state and report to callback.57 */58static void ccwreq_stop(struct ccw_device *cdev, int rc)59{60	struct ccw_request *req = &cdev->private->req;61 62	if (req->done)63		return;64	req->done = 1;65	ccw_device_set_timeout(cdev, 0);66	memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));67	if (rc && rc != -ENODEV && req->drc)68		rc = req->drc;69	req->callback(cdev, req->data, rc);70}71 72/*73 * (Re-)Start the operation until retries and paths are exhausted.74 */75static void ccwreq_do(struct ccw_device *cdev)76{77	struct ccw_request *req = &cdev->private->req;78	struct subchannel *sch = to_subchannel(cdev->dev.parent);79	struct ccw1 *cp = req->cp;80	int rc = -EACCES;81 82	while (req->mask) {83		if (req->retries-- == 0) {84			/* Retries exhausted, try next path. */85			ccwreq_next_path(cdev);86			continue;87		}88		/* Perform start function. */89		memset(&cdev->private->dma_area->irb, 0, sizeof(struct irb));90		rc = cio_start(sch, cp, (u8) req->mask);91		if (rc == 0) {92			/* I/O started successfully. */93			ccw_device_set_timeout(cdev, req->timeout);94			return;95		}96		if (rc == -ENODEV) {97			/* Permanent device error. */98			break;99		}100		if (rc == -EACCES) {101			/* Permant path error. */102			ccwreq_next_path(cdev);103			continue;104		}105		/* Temporary improper status. */106		rc = cio_clear(sch);107		if (rc)108			break;109		return;110	}111	ccwreq_stop(cdev, rc);112}113 114/**115 * ccw_request_start - perform I/O request116 * @cdev: ccw device117 *118 * Perform the I/O request specified by cdev->req.119 */120void ccw_request_start(struct ccw_device *cdev)121{122	struct ccw_request *req = &cdev->private->req;123 124	if (req->singlepath) {125		/* Try all paths twice to counter link flapping. */126		req->mask = 0x8080;127	} else128		req->mask = req->lpm;129 130	req->retries	= req->maxretries;131	req->mask	= lpm_adjust(req->mask, req->lpm);132	req->drc	= 0;133	req->done	= 0;134	req->cancel	= 0;135	if (!req->mask)136		goto out_nopath;137	ccwreq_do(cdev);138	return;139 140out_nopath:141	ccwreq_stop(cdev, -EACCES);142}143 144/**145 * ccw_request_cancel - cancel running I/O request146 * @cdev: ccw device147 *148 * Cancel the I/O request specified by cdev->req. Return non-zero if request149 * has already finished, zero otherwise.150 */151int ccw_request_cancel(struct ccw_device *cdev)152{153	struct subchannel *sch = to_subchannel(cdev->dev.parent);154	struct ccw_request *req = &cdev->private->req;155	int rc;156 157	if (req->done)158		return 1;159	req->cancel = 1;160	rc = cio_clear(sch);161	if (rc)162		ccwreq_stop(cdev, rc);163	return 0;164}165 166/*167 * Return the status of the internal I/O started on the specified ccw device.168 * Perform BASIC SENSE if required.169 */170static enum io_status ccwreq_status(struct ccw_device *cdev, struct irb *lcirb)171{172	struct irb *irb = &cdev->private->dma_area->irb;173	struct cmd_scsw *scsw = &irb->scsw.cmd;174	enum uc_todo todo;175 176	/* Perform BASIC SENSE if needed. */177	if (ccw_device_accumulate_and_sense(cdev, lcirb))178		return IO_RUNNING;179	/* Check for halt/clear interrupt. */180	if (scsw->fctl & (SCSW_FCTL_HALT_FUNC | SCSW_FCTL_CLEAR_FUNC))181		return IO_KILLED;182	/* Check for path error. */183	if (scsw->cc == 3 || scsw->pno)184		return IO_PATH_ERROR;185	/* Handle BASIC SENSE data. */186	if (irb->esw.esw0.erw.cons) {187		CIO_TRACE_EVENT(2, "sensedata");188		CIO_HEX_EVENT(2, &cdev->private->dev_id,189			      sizeof(struct ccw_dev_id));190		CIO_HEX_EVENT(2, &cdev->private->dma_area->irb.ecw,191			      SENSE_MAX_COUNT);192		/* Check for command reject. */193		if (irb->ecw[0] & SNS0_CMD_REJECT)194			return IO_REJECTED;195		/* Ask the driver what to do */196		if (cdev->drv && cdev->drv->uc_handler) {197			todo = cdev->drv->uc_handler(cdev, lcirb);198			CIO_TRACE_EVENT(2, "uc_response");199			CIO_HEX_EVENT(2, &todo, sizeof(todo));200			switch (todo) {201			case UC_TODO_RETRY:202				return IO_STATUS_ERROR;203			case UC_TODO_RETRY_ON_NEW_PATH:204				return IO_PATH_ERROR;205			case UC_TODO_STOP:206				return IO_REJECTED;207			default:208				return IO_STATUS_ERROR;209			}210		}211		/* Assume that unexpected SENSE data implies an error. */212		return IO_STATUS_ERROR;213	}214	/* Check for channel errors. */215	if (scsw->cstat != 0)216		return IO_STATUS_ERROR;217	/* Check for device errors. */218	if (scsw->dstat & ~(DEV_STAT_CHN_END | DEV_STAT_DEV_END))219		return IO_STATUS_ERROR;220	/* Check for final state. */221	if (!(scsw->dstat & DEV_STAT_DEV_END))222		return IO_RUNNING;223	/* Check for other improper status. */224	if (scsw->cc == 1 && (scsw->stctl & SCSW_STCTL_ALERT_STATUS))225		return IO_STATUS_ERROR;226	return IO_DONE;227}228 229/*230 * Log ccw request status.231 */232static void ccwreq_log_status(struct ccw_device *cdev, enum io_status status)233{234	struct ccw_request *req = &cdev->private->req;235	struct {236		struct ccw_dev_id dev_id;237		u16 retries;238		u8 lpm;239		u8 status;240	}  __attribute__ ((packed)) data;241	data.dev_id	= cdev->private->dev_id;242	data.retries	= req->retries;243	data.lpm	= (u8) req->mask;244	data.status	= (u8) status;245	CIO_TRACE_EVENT(2, "reqstat");246	CIO_HEX_EVENT(2, &data, sizeof(data));247}248 249/**250 * ccw_request_handler - interrupt handler for I/O request procedure.251 * @cdev: ccw device252 *253 * Handle interrupt during I/O request procedure.254 */255void ccw_request_handler(struct ccw_device *cdev)256{257	struct irb *irb = this_cpu_ptr(&cio_irb);258	struct ccw_request *req = &cdev->private->req;259	enum io_status status;260	int rc = -EOPNOTSUPP;261 262	/* Check status of I/O request. */263	status = ccwreq_status(cdev, irb);264	if (req->filter)265		status = req->filter(cdev, req->data, irb, status);266	if (status != IO_RUNNING)267		ccw_device_set_timeout(cdev, 0);268	if (status != IO_DONE && status != IO_RUNNING)269		ccwreq_log_status(cdev, status);270	switch (status) {271	case IO_DONE:272		break;273	case IO_RUNNING:274		return;275	case IO_REJECTED:276		goto err;277	case IO_PATH_ERROR:278		goto out_next_path;279	case IO_STATUS_ERROR:280		goto out_restart;281	case IO_KILLED:282		/* Check if request was cancelled on purpose. */283		if (req->cancel) {284			rc = -EIO;285			goto err;286		}287		goto out_restart;288	}289	/* Check back with request initiator. */290	if (!req->check)291		goto out;292	switch (req->check(cdev, req->data)) {293	case 0:294		break;295	case -EAGAIN:296		goto out_restart;297	case -EACCES:298		goto out_next_path;299	default:300		goto err;301	}302out:303	ccwreq_stop(cdev, 0);304	return;305 306out_next_path:307	/* Try next path and restart I/O. */308	if (!ccwreq_next_path(cdev)) {309		rc = -EACCES;310		goto err;311	}312out_restart:313	/* Restart. */314	ccwreq_do(cdev);315	return;316err:317	ccwreq_stop(cdev, rc);318}319 320 321/**322 * ccw_request_timeout - timeout handler for I/O request procedure323 * @cdev: ccw device324 *325 * Handle timeout during I/O request procedure.326 */327void ccw_request_timeout(struct ccw_device *cdev)328{329	struct subchannel *sch = to_subchannel(cdev->dev.parent);330	struct ccw_request *req = &cdev->private->req;331	int rc = -ENODEV, chp;332 333	if (cio_update_schib(sch))334		goto err;335 336	for (chp = 0; chp < 8; chp++) {337		if ((0x80 >> chp) & sch->schib.pmcw.lpum)338			pr_warn("%s: No interrupt was received within %lus (CS=%02x, DS=%02x, CHPID=%x.%02x)\n",339				dev_name(&cdev->dev), req->timeout / HZ,340				scsw_cstat(&sch->schib.scsw),341				scsw_dstat(&sch->schib.scsw),342				sch->schid.cssid,343				sch->schib.pmcw.chpid[chp]);344	}345 346	if (!ccwreq_next_path(cdev)) {347		/* set the final return code for this request */348		req->drc = -ETIME;349	}350	rc = cio_clear(sch);351	if (rc)352		goto err;353	return;354 355err:356	ccwreq_stop(cdev, rc);357}358 359/**360 * ccw_request_notoper - notoper handler for I/O request procedure361 * @cdev: ccw device362 *363 * Handle notoper during I/O request procedure.364 */365void ccw_request_notoper(struct ccw_device *cdev)366{367	ccwreq_stop(cdev, -ENODEV);368}369