162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
462306a36Sopenharmony_ci *		    Horst Hummel <Horst.Hummel@de.ibm.com>
562306a36Sopenharmony_ci *		    Carsten Otte <Cotte@de.ibm.com>
662306a36Sopenharmony_ci *		    Martin Schwidefsky <schwidefsky@de.ibm.com>
762306a36Sopenharmony_ci * Bugreports.to..: <Linux390@de.ibm.com>
862306a36Sopenharmony_ci * Copyright IBM Corp. 1999, 2001
962306a36Sopenharmony_ci *
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define KMSG_COMPONENT "dasd"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/ctype.h>
1562306a36Sopenharmony_ci#include <linux/init.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include <asm/debug.h>
1862306a36Sopenharmony_ci#include <asm/ebcdic.h>
1962306a36Sopenharmony_ci#include <linux/uaccess.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci/* This is ugly... */
2262306a36Sopenharmony_ci#define PRINTK_HEADER "dasd_erp:"
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#include "dasd_int.h"
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct dasd_ccw_req *
2762306a36Sopenharmony_cidasd_alloc_erp_request(unsigned int magic, int cplength, int datasize,
2862306a36Sopenharmony_ci		       struct dasd_device * device)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	unsigned long flags;
3162306a36Sopenharmony_ci	struct dasd_ccw_req *cqr;
3262306a36Sopenharmony_ci	char *data;
3362306a36Sopenharmony_ci	int size;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	/* Sanity checks */
3662306a36Sopenharmony_ci	BUG_ON(datasize > PAGE_SIZE ||
3762306a36Sopenharmony_ci	       (cplength*sizeof(struct ccw1)) > PAGE_SIZE);
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	size = (sizeof(struct dasd_ccw_req) + 7L) & -8L;
4062306a36Sopenharmony_ci	if (cplength > 0)
4162306a36Sopenharmony_ci		size += cplength * sizeof(struct ccw1);
4262306a36Sopenharmony_ci	if (datasize > 0)
4362306a36Sopenharmony_ci		size += datasize;
4462306a36Sopenharmony_ci	spin_lock_irqsave(&device->mem_lock, flags);
4562306a36Sopenharmony_ci	cqr = (struct dasd_ccw_req *)
4662306a36Sopenharmony_ci		dasd_alloc_chunk(&device->erp_chunks, size);
4762306a36Sopenharmony_ci	spin_unlock_irqrestore(&device->mem_lock, flags);
4862306a36Sopenharmony_ci	if (cqr == NULL)
4962306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
5062306a36Sopenharmony_ci	memset(cqr, 0, sizeof(struct dasd_ccw_req));
5162306a36Sopenharmony_ci	INIT_LIST_HEAD(&cqr->devlist);
5262306a36Sopenharmony_ci	INIT_LIST_HEAD(&cqr->blocklist);
5362306a36Sopenharmony_ci	data = (char *) cqr + ((sizeof(struct dasd_ccw_req) + 7L) & -8L);
5462306a36Sopenharmony_ci	cqr->cpaddr = NULL;
5562306a36Sopenharmony_ci	if (cplength > 0) {
5662306a36Sopenharmony_ci		cqr->cpaddr = (struct ccw1 *) data;
5762306a36Sopenharmony_ci		data += cplength*sizeof(struct ccw1);
5862306a36Sopenharmony_ci		memset(cqr->cpaddr, 0, cplength*sizeof(struct ccw1));
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci	cqr->data = NULL;
6162306a36Sopenharmony_ci	if (datasize > 0) {
6262306a36Sopenharmony_ci		cqr->data = data;
6362306a36Sopenharmony_ci 		memset(cqr->data, 0, datasize);
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci	cqr->magic = magic;
6662306a36Sopenharmony_ci	ASCEBC((char *) &cqr->magic, 4);
6762306a36Sopenharmony_ci	set_bit(DASD_CQR_FLAGS_USE_ERP, &cqr->flags);
6862306a36Sopenharmony_ci	dasd_get_device(device);
6962306a36Sopenharmony_ci	return cqr;
7062306a36Sopenharmony_ci}
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_civoid
7362306a36Sopenharmony_cidasd_free_erp_request(struct dasd_ccw_req *cqr, struct dasd_device * device)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	unsigned long flags;
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	spin_lock_irqsave(&device->mem_lock, flags);
7862306a36Sopenharmony_ci	dasd_free_chunk(&device->erp_chunks, cqr);
7962306a36Sopenharmony_ci	spin_unlock_irqrestore(&device->mem_lock, flags);
8062306a36Sopenharmony_ci	atomic_dec(&device->ref_count);
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci/*
8562306a36Sopenharmony_ci * dasd_default_erp_action just retries the current cqr
8662306a36Sopenharmony_ci */
8762306a36Sopenharmony_cistruct dasd_ccw_req *
8862306a36Sopenharmony_cidasd_default_erp_action(struct dasd_ccw_req *cqr)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct dasd_device *device;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	device = cqr->startdev;
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci        /* just retry - there is nothing to save ... I got no sense data.... */
9562306a36Sopenharmony_ci        if (cqr->retries > 0) {
9662306a36Sopenharmony_ci		DBF_DEV_EVENT(DBF_DEBUG, device,
9762306a36Sopenharmony_ci                             "default ERP called (%i retries left)",
9862306a36Sopenharmony_ci                             cqr->retries);
9962306a36Sopenharmony_ci		if (!test_bit(DASD_CQR_VERIFY_PATH, &cqr->flags))
10062306a36Sopenharmony_ci			cqr->lpm = dasd_path_get_opm(device);
10162306a36Sopenharmony_ci		cqr->status = DASD_CQR_FILLED;
10262306a36Sopenharmony_ci        } else {
10362306a36Sopenharmony_ci		pr_err("%s: default ERP has run out of retries and failed\n",
10462306a36Sopenharmony_ci		       dev_name(&device->cdev->dev));
10562306a36Sopenharmony_ci		cqr->status = DASD_CQR_FAILED;
10662306a36Sopenharmony_ci		cqr->stopclk = get_tod_clock();
10762306a36Sopenharmony_ci        }
10862306a36Sopenharmony_ci        return cqr;
10962306a36Sopenharmony_ci}				/* end dasd_default_erp_action */
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci/*
11262306a36Sopenharmony_ci * DESCRIPTION
11362306a36Sopenharmony_ci *   Frees all ERPs of the current ERP Chain and set the status
11462306a36Sopenharmony_ci *   of the original CQR either to DASD_CQR_DONE if ERP was successful
11562306a36Sopenharmony_ci *   or to DASD_CQR_FAILED if ERP was NOT successful.
11662306a36Sopenharmony_ci *   NOTE: This function is only called if no discipline postaction
11762306a36Sopenharmony_ci *	   is available
11862306a36Sopenharmony_ci *
11962306a36Sopenharmony_ci * PARAMETER
12062306a36Sopenharmony_ci *   erp		current erp_head
12162306a36Sopenharmony_ci *
12262306a36Sopenharmony_ci * RETURN VALUES
12362306a36Sopenharmony_ci *   cqr		pointer to the original CQR
12462306a36Sopenharmony_ci */
12562306a36Sopenharmony_cistruct dasd_ccw_req *dasd_default_erp_postaction(struct dasd_ccw_req *cqr)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	int success;
12862306a36Sopenharmony_ci	unsigned long startclk, stopclk;
12962306a36Sopenharmony_ci	struct dasd_device *startdev;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	BUG_ON(cqr->refers == NULL || cqr->function == NULL);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	success = cqr->status == DASD_CQR_DONE;
13462306a36Sopenharmony_ci	startclk = cqr->startclk;
13562306a36Sopenharmony_ci	stopclk = cqr->stopclk;
13662306a36Sopenharmony_ci	startdev = cqr->startdev;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	/* free all ERPs - but NOT the original cqr */
13962306a36Sopenharmony_ci	while (cqr->refers != NULL) {
14062306a36Sopenharmony_ci		struct dasd_ccw_req *refers;
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci		refers = cqr->refers;
14362306a36Sopenharmony_ci		/* remove the request from the block queue */
14462306a36Sopenharmony_ci		list_del(&cqr->blocklist);
14562306a36Sopenharmony_ci		/* free the finished erp request */
14662306a36Sopenharmony_ci		dasd_free_erp_request(cqr, cqr->memdev);
14762306a36Sopenharmony_ci		cqr = refers;
14862306a36Sopenharmony_ci	}
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	/* set corresponding status to original cqr */
15162306a36Sopenharmony_ci	cqr->startclk = startclk;
15262306a36Sopenharmony_ci	cqr->stopclk = stopclk;
15362306a36Sopenharmony_ci	cqr->startdev = startdev;
15462306a36Sopenharmony_ci	if (success)
15562306a36Sopenharmony_ci		cqr->status = DASD_CQR_DONE;
15662306a36Sopenharmony_ci	else {
15762306a36Sopenharmony_ci		cqr->status = DASD_CQR_FAILED;
15862306a36Sopenharmony_ci		cqr->stopclk = get_tod_clock();
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return cqr;
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci}				/* end default_erp_postaction */
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_civoid
16662306a36Sopenharmony_cidasd_log_sense(struct dasd_ccw_req *cqr, struct irb *irb)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	struct dasd_device *device;
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ci	device = cqr->startdev;
17162306a36Sopenharmony_ci	if (cqr->intrc == -ETIMEDOUT) {
17262306a36Sopenharmony_ci		dev_err(&device->cdev->dev,
17362306a36Sopenharmony_ci			"A timeout error occurred for cqr %p\n", cqr);
17462306a36Sopenharmony_ci		return;
17562306a36Sopenharmony_ci	}
17662306a36Sopenharmony_ci	if (cqr->intrc == -ENOLINK) {
17762306a36Sopenharmony_ci		dev_err(&device->cdev->dev,
17862306a36Sopenharmony_ci			"A transport error occurred for cqr %p\n", cqr);
17962306a36Sopenharmony_ci		return;
18062306a36Sopenharmony_ci	}
18162306a36Sopenharmony_ci	/* dump sense data */
18262306a36Sopenharmony_ci	if (device->discipline && device->discipline->dump_sense)
18362306a36Sopenharmony_ci		device->discipline->dump_sense(device, cqr, irb);
18462306a36Sopenharmony_ci}
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_civoid
18762306a36Sopenharmony_cidasd_log_sense_dbf(struct dasd_ccw_req *cqr, struct irb *irb)
18862306a36Sopenharmony_ci{
18962306a36Sopenharmony_ci	struct dasd_device *device;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	device = cqr->startdev;
19262306a36Sopenharmony_ci	/* dump sense data to s390 debugfeature*/
19362306a36Sopenharmony_ci	if (device->discipline && device->discipline->dump_sense_dbf)
19462306a36Sopenharmony_ci		device->discipline->dump_sense_dbf(device, irb, "log");
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_log_sense_dbf);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_default_erp_action);
19962306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_default_erp_postaction);
20062306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_alloc_erp_request);
20162306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_free_erp_request);
20262306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_log_sense);
20362306a36Sopenharmony_ci
204