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 * Device mapping and dasd= parameter parsing functions. All devmap
1162306a36Sopenharmony_ci * functions may not be called from interrupt context. In particular
1262306a36Sopenharmony_ci * dasd_get_device is a no-no from interrupt context.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci */
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define KMSG_COMPONENT "dasd"
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/ctype.h>
1962306a36Sopenharmony_ci#include <linux/init.h>
2062306a36Sopenharmony_ci#include <linux/module.h>
2162306a36Sopenharmony_ci#include <linux/slab.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <asm/debug.h>
2462306a36Sopenharmony_ci#include <linux/uaccess.h>
2562306a36Sopenharmony_ci#include <asm/ipl.h>
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci/* This is ugly... */
2862306a36Sopenharmony_ci#define PRINTK_HEADER "dasd_devmap:"
2962306a36Sopenharmony_ci#define DASD_MAX_PARAMS 256
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci#include "dasd_int.h"
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistruct kmem_cache *dasd_page_cache;
3462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dasd_page_cache);
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci/*
3762306a36Sopenharmony_ci * dasd_devmap_t is used to store the features and the relation
3862306a36Sopenharmony_ci * between device number and device index. To find a dasd_devmap_t
3962306a36Sopenharmony_ci * that corresponds to a device number of a device index each
4062306a36Sopenharmony_ci * dasd_devmap_t is added to two linked lists, one to search by
4162306a36Sopenharmony_ci * the device number and one to search by the device index. As
4262306a36Sopenharmony_ci * soon as big minor numbers are available the device index list
4362306a36Sopenharmony_ci * can be removed since the device number will then be identical
4462306a36Sopenharmony_ci * to the device index.
4562306a36Sopenharmony_ci */
4662306a36Sopenharmony_cistruct dasd_devmap {
4762306a36Sopenharmony_ci	struct list_head list;
4862306a36Sopenharmony_ci	char bus_id[DASD_BUS_ID_SIZE];
4962306a36Sopenharmony_ci        unsigned int devindex;
5062306a36Sopenharmony_ci        unsigned short features;
5162306a36Sopenharmony_ci	struct dasd_device *device;
5262306a36Sopenharmony_ci	struct dasd_copy_relation *copy;
5362306a36Sopenharmony_ci	unsigned int aq_mask;
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci/*
5762306a36Sopenharmony_ci * Parameter parsing functions for dasd= parameter. The syntax is:
5862306a36Sopenharmony_ci *   <devno>		: (0x)?[0-9a-fA-F]+
5962306a36Sopenharmony_ci *   <busid>		: [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+
6062306a36Sopenharmony_ci *   <feature>		: ro
6162306a36Sopenharmony_ci *   <feature_list>	: \(<feature>(:<feature>)*\)
6262306a36Sopenharmony_ci *   <devno-range>	: <devno>(-<devno>)?<feature_list>?
6362306a36Sopenharmony_ci *   <busid-range>	: <busid>(-<busid>)?<feature_list>?
6462306a36Sopenharmony_ci *   <devices>		: <devno-range>|<busid-range>
6562306a36Sopenharmony_ci *   <dasd_module>	: dasd_diag_mod|dasd_eckd_mod|dasd_fba_mod
6662306a36Sopenharmony_ci *
6762306a36Sopenharmony_ci *   <dasd>		: autodetect|probeonly|<devices>(,<devices>)*
6862306a36Sopenharmony_ci */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ciint dasd_probeonly =  0;	/* is true, when probeonly mode is active */
7162306a36Sopenharmony_ciint dasd_autodetect = 0;	/* is true, when autodetection is active */
7262306a36Sopenharmony_ciint dasd_nopav = 0;		/* is true, when PAV is disabled */
7362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dasd_nopav);
7462306a36Sopenharmony_ciint dasd_nofcx;			/* disable High Performance Ficon */
7562306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dasd_nofcx);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/*
7862306a36Sopenharmony_ci * char *dasd[] is intended to hold the ranges supplied by the dasd= statement
7962306a36Sopenharmony_ci * it is named 'dasd' to directly be filled by insmod with the comma separated
8062306a36Sopenharmony_ci * strings when running as a module.
8162306a36Sopenharmony_ci */
8262306a36Sopenharmony_cistatic char *dasd[DASD_MAX_PARAMS];
8362306a36Sopenharmony_cimodule_param_array(dasd, charp, NULL, S_IRUGO);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci/*
8662306a36Sopenharmony_ci * Single spinlock to protect devmap and servermap structures and lists.
8762306a36Sopenharmony_ci */
8862306a36Sopenharmony_cistatic DEFINE_SPINLOCK(dasd_devmap_lock);
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci/*
9162306a36Sopenharmony_ci * Hash lists for devmap structures.
9262306a36Sopenharmony_ci */
9362306a36Sopenharmony_cistatic struct list_head dasd_hashlists[256];
9462306a36Sopenharmony_ciint dasd_max_devindex;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_cistatic struct dasd_devmap *dasd_add_busid(const char *, int);
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic inline int
9962306a36Sopenharmony_cidasd_hash_busid(const char *bus_id)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	int hash, i;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	hash = 0;
10462306a36Sopenharmony_ci	for (i = 0; (i < DASD_BUS_ID_SIZE) && *bus_id; i++, bus_id++)
10562306a36Sopenharmony_ci		hash += *bus_id;
10662306a36Sopenharmony_ci	return hash & 0xff;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_ci#ifndef MODULE
11062306a36Sopenharmony_cistatic int __init dasd_call_setup(char *opt)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	static int i __initdata;
11362306a36Sopenharmony_ci	char *tmp;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	while (i < DASD_MAX_PARAMS) {
11662306a36Sopenharmony_ci		tmp = strsep(&opt, ",");
11762306a36Sopenharmony_ci		if (!tmp)
11862306a36Sopenharmony_ci			break;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci		dasd[i++] = tmp;
12162306a36Sopenharmony_ci	}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	return 1;
12462306a36Sopenharmony_ci}
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci__setup ("dasd=", dasd_call_setup);
12762306a36Sopenharmony_ci#endif	/* #ifndef MODULE */
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci#define	DASD_IPLDEV	"ipldev"
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci/*
13262306a36Sopenharmony_ci * Read a device busid/devno from a string.
13362306a36Sopenharmony_ci */
13462306a36Sopenharmony_cistatic int dasd_busid(char *str, int *id0, int *id1, int *devno)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	unsigned int val;
13762306a36Sopenharmony_ci	char *tok;
13862306a36Sopenharmony_ci
13962306a36Sopenharmony_ci	/* Interpret ipldev busid */
14062306a36Sopenharmony_ci	if (strncmp(DASD_IPLDEV, str, strlen(DASD_IPLDEV)) == 0) {
14162306a36Sopenharmony_ci		if (ipl_info.type != IPL_TYPE_CCW) {
14262306a36Sopenharmony_ci			pr_err("The IPL device is not a CCW device\n");
14362306a36Sopenharmony_ci			return -EINVAL;
14462306a36Sopenharmony_ci		}
14562306a36Sopenharmony_ci		*id0 = 0;
14662306a36Sopenharmony_ci		*id1 = ipl_info.data.ccw.dev_id.ssid;
14762306a36Sopenharmony_ci		*devno = ipl_info.data.ccw.dev_id.devno;
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci		return 0;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	/* Old style 0xXXXX or XXXX */
15362306a36Sopenharmony_ci	if (!kstrtouint(str, 16, &val)) {
15462306a36Sopenharmony_ci		*id0 = *id1 = 0;
15562306a36Sopenharmony_ci		if (val > 0xffff)
15662306a36Sopenharmony_ci			return -EINVAL;
15762306a36Sopenharmony_ci		*devno = val;
15862306a36Sopenharmony_ci		return 0;
15962306a36Sopenharmony_ci	}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	/* New style x.y.z busid */
16262306a36Sopenharmony_ci	tok = strsep(&str, ".");
16362306a36Sopenharmony_ci	if (kstrtouint(tok, 16, &val) || val > 0xff)
16462306a36Sopenharmony_ci		return -EINVAL;
16562306a36Sopenharmony_ci	*id0 = val;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	tok = strsep(&str, ".");
16862306a36Sopenharmony_ci	if (kstrtouint(tok, 16, &val) || val > 0xff)
16962306a36Sopenharmony_ci		return -EINVAL;
17062306a36Sopenharmony_ci	*id1 = val;
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_ci	tok = strsep(&str, ".");
17362306a36Sopenharmony_ci	if (kstrtouint(tok, 16, &val) || val > 0xffff)
17462306a36Sopenharmony_ci		return -EINVAL;
17562306a36Sopenharmony_ci	*devno = val;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci/*
18162306a36Sopenharmony_ci * Read colon separated list of dasd features.
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_cistatic int __init dasd_feature_list(char *str)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	int features, len, rc;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	features = 0;
18862306a36Sopenharmony_ci	rc = 0;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	if (!str)
19162306a36Sopenharmony_ci		return DASD_FEATURE_DEFAULT;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	while (1) {
19462306a36Sopenharmony_ci		for (len = 0;
19562306a36Sopenharmony_ci		     str[len] && str[len] != ':' && str[len] != ')'; len++);
19662306a36Sopenharmony_ci		if (len == 2 && !strncmp(str, "ro", 2))
19762306a36Sopenharmony_ci			features |= DASD_FEATURE_READONLY;
19862306a36Sopenharmony_ci		else if (len == 4 && !strncmp(str, "diag", 4))
19962306a36Sopenharmony_ci			features |= DASD_FEATURE_USEDIAG;
20062306a36Sopenharmony_ci		else if (len == 3 && !strncmp(str, "raw", 3))
20162306a36Sopenharmony_ci			features |= DASD_FEATURE_USERAW;
20262306a36Sopenharmony_ci		else if (len == 6 && !strncmp(str, "erplog", 6))
20362306a36Sopenharmony_ci			features |= DASD_FEATURE_ERPLOG;
20462306a36Sopenharmony_ci		else if (len == 8 && !strncmp(str, "failfast", 8))
20562306a36Sopenharmony_ci			features |= DASD_FEATURE_FAILFAST;
20662306a36Sopenharmony_ci		else {
20762306a36Sopenharmony_ci			pr_warn("%.*s is not a supported device option\n",
20862306a36Sopenharmony_ci				len, str);
20962306a36Sopenharmony_ci			rc = -EINVAL;
21062306a36Sopenharmony_ci		}
21162306a36Sopenharmony_ci		str += len;
21262306a36Sopenharmony_ci		if (*str != ':')
21362306a36Sopenharmony_ci			break;
21462306a36Sopenharmony_ci		str++;
21562306a36Sopenharmony_ci	}
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci	return rc ? : features;
21862306a36Sopenharmony_ci}
21962306a36Sopenharmony_ci
22062306a36Sopenharmony_ci/*
22162306a36Sopenharmony_ci * Try to match the first element on the comma separated parse string
22262306a36Sopenharmony_ci * with one of the known keywords. If a keyword is found, take the approprate
22362306a36Sopenharmony_ci * action and return a pointer to the residual string. If the first element
22462306a36Sopenharmony_ci * could not be matched to any keyword then return an error code.
22562306a36Sopenharmony_ci */
22662306a36Sopenharmony_cistatic int __init dasd_parse_keyword(char *keyword)
22762306a36Sopenharmony_ci{
22862306a36Sopenharmony_ci	int length = strlen(keyword);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	if (strncmp("autodetect", keyword, length) == 0) {
23162306a36Sopenharmony_ci		dasd_autodetect = 1;
23262306a36Sopenharmony_ci		pr_info("The autodetection mode has been activated\n");
23362306a36Sopenharmony_ci		return 0;
23462306a36Sopenharmony_ci        }
23562306a36Sopenharmony_ci	if (strncmp("probeonly", keyword, length) == 0) {
23662306a36Sopenharmony_ci		dasd_probeonly = 1;
23762306a36Sopenharmony_ci		pr_info("The probeonly mode has been activated\n");
23862306a36Sopenharmony_ci		return 0;
23962306a36Sopenharmony_ci        }
24062306a36Sopenharmony_ci	if (strncmp("nopav", keyword, length) == 0) {
24162306a36Sopenharmony_ci		if (MACHINE_IS_VM)
24262306a36Sopenharmony_ci			pr_info("'nopav' is not supported on z/VM\n");
24362306a36Sopenharmony_ci		else {
24462306a36Sopenharmony_ci			dasd_nopav = 1;
24562306a36Sopenharmony_ci			pr_info("PAV support has be deactivated\n");
24662306a36Sopenharmony_ci		}
24762306a36Sopenharmony_ci		return 0;
24862306a36Sopenharmony_ci	}
24962306a36Sopenharmony_ci	if (strncmp("nofcx", keyword, length) == 0) {
25062306a36Sopenharmony_ci		dasd_nofcx = 1;
25162306a36Sopenharmony_ci		pr_info("High Performance FICON support has been "
25262306a36Sopenharmony_ci			"deactivated\n");
25362306a36Sopenharmony_ci		return 0;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	if (strncmp("fixedbuffers", keyword, length) == 0) {
25662306a36Sopenharmony_ci		if (dasd_page_cache)
25762306a36Sopenharmony_ci			return 0;
25862306a36Sopenharmony_ci		dasd_page_cache =
25962306a36Sopenharmony_ci			kmem_cache_create("dasd_page_cache", PAGE_SIZE,
26062306a36Sopenharmony_ci					  PAGE_SIZE, SLAB_CACHE_DMA,
26162306a36Sopenharmony_ci					  NULL);
26262306a36Sopenharmony_ci		if (!dasd_page_cache)
26362306a36Sopenharmony_ci			DBF_EVENT(DBF_WARNING, "%s", "Failed to create slab, "
26462306a36Sopenharmony_ci				"fixed buffer mode disabled.");
26562306a36Sopenharmony_ci		else
26662306a36Sopenharmony_ci			DBF_EVENT(DBF_INFO, "%s",
26762306a36Sopenharmony_ci				 "turning on fixed buffer mode");
26862306a36Sopenharmony_ci		return 0;
26962306a36Sopenharmony_ci	}
27062306a36Sopenharmony_ci
27162306a36Sopenharmony_ci	return -EINVAL;
27262306a36Sopenharmony_ci}
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci/*
27562306a36Sopenharmony_ci * Split a string of a device range into its pieces and return the from, to, and
27662306a36Sopenharmony_ci * feature parts separately.
27762306a36Sopenharmony_ci * e.g.:
27862306a36Sopenharmony_ci * 0.0.1234-0.0.5678(ro:erplog) -> from: 0.0.1234 to: 0.0.5678 features: ro:erplog
27962306a36Sopenharmony_ci * 0.0.8765(raw) -> from: 0.0.8765 to: null features: raw
28062306a36Sopenharmony_ci * 0x4321 -> from: 0x4321 to: null features: null
28162306a36Sopenharmony_ci */
28262306a36Sopenharmony_cistatic int __init dasd_evaluate_range_param(char *range, char **from_str,
28362306a36Sopenharmony_ci					    char **to_str, char **features_str)
28462306a36Sopenharmony_ci{
28562306a36Sopenharmony_ci	int rc = 0;
28662306a36Sopenharmony_ci
28762306a36Sopenharmony_ci	/* Do we have a range or a single device? */
28862306a36Sopenharmony_ci	if (strchr(range, '-')) {
28962306a36Sopenharmony_ci		*from_str = strsep(&range, "-");
29062306a36Sopenharmony_ci		*to_str = strsep(&range, "(");
29162306a36Sopenharmony_ci		*features_str = strsep(&range, ")");
29262306a36Sopenharmony_ci	} else {
29362306a36Sopenharmony_ci		*from_str = strsep(&range, "(");
29462306a36Sopenharmony_ci		*features_str = strsep(&range, ")");
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	if (*features_str && !range) {
29862306a36Sopenharmony_ci		pr_warn("A closing parenthesis ')' is missing in the dasd= parameter\n");
29962306a36Sopenharmony_ci		rc = -EINVAL;
30062306a36Sopenharmony_ci	}
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	return rc;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci/*
30662306a36Sopenharmony_ci * Try to interprete the range string as a device number or a range of devices.
30762306a36Sopenharmony_ci * If the interpretation is successful, create the matching dasd_devmap entries.
30862306a36Sopenharmony_ci * If interpretation fails or in case of an error, return an error code.
30962306a36Sopenharmony_ci */
31062306a36Sopenharmony_cistatic int __init dasd_parse_range(const char *range)
31162306a36Sopenharmony_ci{
31262306a36Sopenharmony_ci	struct dasd_devmap *devmap;
31362306a36Sopenharmony_ci	int from, from_id0, from_id1;
31462306a36Sopenharmony_ci	int to, to_id0, to_id1;
31562306a36Sopenharmony_ci	int features;
31662306a36Sopenharmony_ci	char bus_id[DASD_BUS_ID_SIZE + 1];
31762306a36Sopenharmony_ci	char *features_str = NULL;
31862306a36Sopenharmony_ci	char *from_str = NULL;
31962306a36Sopenharmony_ci	char *to_str = NULL;
32062306a36Sopenharmony_ci	int rc = 0;
32162306a36Sopenharmony_ci	char *tmp;
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	tmp = kstrdup(range, GFP_KERNEL);
32462306a36Sopenharmony_ci	if (!tmp)
32562306a36Sopenharmony_ci		return -ENOMEM;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	if (dasd_evaluate_range_param(tmp, &from_str, &to_str, &features_str)) {
32862306a36Sopenharmony_ci		rc = -EINVAL;
32962306a36Sopenharmony_ci		goto out;
33062306a36Sopenharmony_ci	}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_ci	if (dasd_busid(from_str, &from_id0, &from_id1, &from)) {
33362306a36Sopenharmony_ci		rc = -EINVAL;
33462306a36Sopenharmony_ci		goto out;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	to = from;
33862306a36Sopenharmony_ci	to_id0 = from_id0;
33962306a36Sopenharmony_ci	to_id1 = from_id1;
34062306a36Sopenharmony_ci	if (to_str) {
34162306a36Sopenharmony_ci		if (dasd_busid(to_str, &to_id0, &to_id1, &to)) {
34262306a36Sopenharmony_ci			rc = -EINVAL;
34362306a36Sopenharmony_ci			goto out;
34462306a36Sopenharmony_ci		}
34562306a36Sopenharmony_ci		if (from_id0 != to_id0 || from_id1 != to_id1 || from > to) {
34662306a36Sopenharmony_ci			pr_err("%s is not a valid device range\n", range);
34762306a36Sopenharmony_ci			rc = -EINVAL;
34862306a36Sopenharmony_ci			goto out;
34962306a36Sopenharmony_ci		}
35062306a36Sopenharmony_ci	}
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci	features = dasd_feature_list(features_str);
35362306a36Sopenharmony_ci	if (features < 0) {
35462306a36Sopenharmony_ci		rc = -EINVAL;
35562306a36Sopenharmony_ci		goto out;
35662306a36Sopenharmony_ci	}
35762306a36Sopenharmony_ci	/* each device in dasd= parameter should be set initially online */
35862306a36Sopenharmony_ci	features |= DASD_FEATURE_INITIAL_ONLINE;
35962306a36Sopenharmony_ci	while (from <= to) {
36062306a36Sopenharmony_ci		sprintf(bus_id, "%01x.%01x.%04x", from_id0, from_id1, from++);
36162306a36Sopenharmony_ci		devmap = dasd_add_busid(bus_id, features);
36262306a36Sopenharmony_ci		if (IS_ERR(devmap)) {
36362306a36Sopenharmony_ci			rc = PTR_ERR(devmap);
36462306a36Sopenharmony_ci			goto out;
36562306a36Sopenharmony_ci		}
36662306a36Sopenharmony_ci	}
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_ciout:
36962306a36Sopenharmony_ci	kfree(tmp);
37062306a36Sopenharmony_ci
37162306a36Sopenharmony_ci	return rc;
37262306a36Sopenharmony_ci}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci/*
37562306a36Sopenharmony_ci * Parse parameters stored in dasd[]
37662306a36Sopenharmony_ci * The 'dasd=...' parameter allows to specify a comma separated list of
37762306a36Sopenharmony_ci * keywords and device ranges. The parameters in that list will be stored as
37862306a36Sopenharmony_ci * separate elementes in dasd[].
37962306a36Sopenharmony_ci */
38062306a36Sopenharmony_ciint __init dasd_parse(void)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	int rc, i;
38362306a36Sopenharmony_ci	char *cur;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	rc = 0;
38662306a36Sopenharmony_ci	for (i = 0; i < DASD_MAX_PARAMS; i++) {
38762306a36Sopenharmony_ci		cur = dasd[i];
38862306a36Sopenharmony_ci		if (!cur)
38962306a36Sopenharmony_ci			break;
39062306a36Sopenharmony_ci		if (*cur == '\0')
39162306a36Sopenharmony_ci			continue;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		rc = dasd_parse_keyword(cur);
39462306a36Sopenharmony_ci		if (rc)
39562306a36Sopenharmony_ci			rc = dasd_parse_range(cur);
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		if (rc)
39862306a36Sopenharmony_ci			break;
39962306a36Sopenharmony_ci	}
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	return rc;
40262306a36Sopenharmony_ci}
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/*
40562306a36Sopenharmony_ci * Add a devmap for the device specified by busid. It is possible that
40662306a36Sopenharmony_ci * the devmap already exists (dasd= parameter). The order of the devices
40762306a36Sopenharmony_ci * added through this function will define the kdevs for the individual
40862306a36Sopenharmony_ci * devices.
40962306a36Sopenharmony_ci */
41062306a36Sopenharmony_cistatic struct dasd_devmap *
41162306a36Sopenharmony_cidasd_add_busid(const char *bus_id, int features)
41262306a36Sopenharmony_ci{
41362306a36Sopenharmony_ci	struct dasd_devmap *devmap, *new, *tmp;
41462306a36Sopenharmony_ci	int hash;
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci	new = kzalloc(sizeof(struct dasd_devmap), GFP_KERNEL);
41762306a36Sopenharmony_ci	if (!new)
41862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
41962306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
42062306a36Sopenharmony_ci	devmap = NULL;
42162306a36Sopenharmony_ci	hash = dasd_hash_busid(bus_id);
42262306a36Sopenharmony_ci	list_for_each_entry(tmp, &dasd_hashlists[hash], list)
42362306a36Sopenharmony_ci		if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) {
42462306a36Sopenharmony_ci			devmap = tmp;
42562306a36Sopenharmony_ci			break;
42662306a36Sopenharmony_ci		}
42762306a36Sopenharmony_ci	if (!devmap) {
42862306a36Sopenharmony_ci		/* This bus_id is new. */
42962306a36Sopenharmony_ci		new->devindex = dasd_max_devindex++;
43062306a36Sopenharmony_ci		strscpy(new->bus_id, bus_id, DASD_BUS_ID_SIZE);
43162306a36Sopenharmony_ci		new->features = features;
43262306a36Sopenharmony_ci		new->device = NULL;
43362306a36Sopenharmony_ci		list_add(&new->list, &dasd_hashlists[hash]);
43462306a36Sopenharmony_ci		devmap = new;
43562306a36Sopenharmony_ci		new = NULL;
43662306a36Sopenharmony_ci	}
43762306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
43862306a36Sopenharmony_ci	kfree(new);
43962306a36Sopenharmony_ci	return devmap;
44062306a36Sopenharmony_ci}
44162306a36Sopenharmony_ci
44262306a36Sopenharmony_cistatic struct dasd_devmap *
44362306a36Sopenharmony_cidasd_find_busid_locked(const char *bus_id)
44462306a36Sopenharmony_ci{
44562306a36Sopenharmony_ci	struct dasd_devmap *devmap, *tmp;
44662306a36Sopenharmony_ci	int hash;
44762306a36Sopenharmony_ci
44862306a36Sopenharmony_ci	devmap = ERR_PTR(-ENODEV);
44962306a36Sopenharmony_ci	hash = dasd_hash_busid(bus_id);
45062306a36Sopenharmony_ci	list_for_each_entry(tmp, &dasd_hashlists[hash], list) {
45162306a36Sopenharmony_ci		if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) {
45262306a36Sopenharmony_ci			devmap = tmp;
45362306a36Sopenharmony_ci			break;
45462306a36Sopenharmony_ci		}
45562306a36Sopenharmony_ci	}
45662306a36Sopenharmony_ci	return devmap;
45762306a36Sopenharmony_ci}
45862306a36Sopenharmony_ci
45962306a36Sopenharmony_ci/*
46062306a36Sopenharmony_ci * Find devmap for device with given bus_id.
46162306a36Sopenharmony_ci */
46262306a36Sopenharmony_cistatic struct dasd_devmap *
46362306a36Sopenharmony_cidasd_find_busid(const char *bus_id)
46462306a36Sopenharmony_ci{
46562306a36Sopenharmony_ci	struct dasd_devmap *devmap;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
46862306a36Sopenharmony_ci	devmap = dasd_find_busid_locked(bus_id);
46962306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
47062306a36Sopenharmony_ci	return devmap;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci/*
47462306a36Sopenharmony_ci * Check if busid has been added to the list of dasd ranges.
47562306a36Sopenharmony_ci */
47662306a36Sopenharmony_ciint
47762306a36Sopenharmony_cidasd_busid_known(const char *bus_id)
47862306a36Sopenharmony_ci{
47962306a36Sopenharmony_ci	return IS_ERR(dasd_find_busid(bus_id)) ? -ENOENT : 0;
48062306a36Sopenharmony_ci}
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_ci/*
48362306a36Sopenharmony_ci * Forget all about the device numbers added so far.
48462306a36Sopenharmony_ci * This may only be called at module unload or system shutdown.
48562306a36Sopenharmony_ci */
48662306a36Sopenharmony_cistatic void
48762306a36Sopenharmony_cidasd_forget_ranges(void)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	struct dasd_devmap *devmap, *n;
49062306a36Sopenharmony_ci	int i;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
49362306a36Sopenharmony_ci	for (i = 0; i < 256; i++) {
49462306a36Sopenharmony_ci		list_for_each_entry_safe(devmap, n, &dasd_hashlists[i], list) {
49562306a36Sopenharmony_ci			BUG_ON(devmap->device != NULL);
49662306a36Sopenharmony_ci			list_del(&devmap->list);
49762306a36Sopenharmony_ci			kfree(devmap);
49862306a36Sopenharmony_ci		}
49962306a36Sopenharmony_ci	}
50062306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
50162306a36Sopenharmony_ci}
50262306a36Sopenharmony_ci
50362306a36Sopenharmony_ci/*
50462306a36Sopenharmony_ci * Find the device struct by its device index.
50562306a36Sopenharmony_ci */
50662306a36Sopenharmony_cistruct dasd_device *
50762306a36Sopenharmony_cidasd_device_from_devindex(int devindex)
50862306a36Sopenharmony_ci{
50962306a36Sopenharmony_ci	struct dasd_devmap *devmap, *tmp;
51062306a36Sopenharmony_ci	struct dasd_device *device;
51162306a36Sopenharmony_ci	int i;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
51462306a36Sopenharmony_ci	devmap = NULL;
51562306a36Sopenharmony_ci	for (i = 0; (i < 256) && !devmap; i++)
51662306a36Sopenharmony_ci		list_for_each_entry(tmp, &dasd_hashlists[i], list)
51762306a36Sopenharmony_ci			if (tmp->devindex == devindex) {
51862306a36Sopenharmony_ci				/* Found the devmap for the device. */
51962306a36Sopenharmony_ci				devmap = tmp;
52062306a36Sopenharmony_ci				break;
52162306a36Sopenharmony_ci			}
52262306a36Sopenharmony_ci	if (devmap && devmap->device) {
52362306a36Sopenharmony_ci		device = devmap->device;
52462306a36Sopenharmony_ci		dasd_get_device(device);
52562306a36Sopenharmony_ci	} else
52662306a36Sopenharmony_ci		device = ERR_PTR(-ENODEV);
52762306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
52862306a36Sopenharmony_ci	return device;
52962306a36Sopenharmony_ci}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci/*
53262306a36Sopenharmony_ci * Return devmap for cdev. If no devmap exists yet, create one and
53362306a36Sopenharmony_ci * connect it to the cdev.
53462306a36Sopenharmony_ci */
53562306a36Sopenharmony_cistatic struct dasd_devmap *
53662306a36Sopenharmony_cidasd_devmap_from_cdev(struct ccw_device *cdev)
53762306a36Sopenharmony_ci{
53862306a36Sopenharmony_ci	struct dasd_devmap *devmap;
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(&cdev->dev));
54162306a36Sopenharmony_ci	if (IS_ERR(devmap))
54262306a36Sopenharmony_ci		devmap = dasd_add_busid(dev_name(&cdev->dev),
54362306a36Sopenharmony_ci					DASD_FEATURE_DEFAULT);
54462306a36Sopenharmony_ci	return devmap;
54562306a36Sopenharmony_ci}
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci/*
54862306a36Sopenharmony_ci * Create a dasd device structure for cdev.
54962306a36Sopenharmony_ci */
55062306a36Sopenharmony_cistruct dasd_device *
55162306a36Sopenharmony_cidasd_create_device(struct ccw_device *cdev)
55262306a36Sopenharmony_ci{
55362306a36Sopenharmony_ci	struct dasd_devmap *devmap;
55462306a36Sopenharmony_ci	struct dasd_device *device;
55562306a36Sopenharmony_ci	unsigned long flags;
55662306a36Sopenharmony_ci	int rc;
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	devmap = dasd_devmap_from_cdev(cdev);
55962306a36Sopenharmony_ci	if (IS_ERR(devmap))
56062306a36Sopenharmony_ci		return (void *) devmap;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci	device = dasd_alloc_device();
56362306a36Sopenharmony_ci	if (IS_ERR(device))
56462306a36Sopenharmony_ci		return device;
56562306a36Sopenharmony_ci	atomic_set(&device->ref_count, 3);
56662306a36Sopenharmony_ci
56762306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
56862306a36Sopenharmony_ci	if (!devmap->device) {
56962306a36Sopenharmony_ci		devmap->device = device;
57062306a36Sopenharmony_ci		device->devindex = devmap->devindex;
57162306a36Sopenharmony_ci		device->features = devmap->features;
57262306a36Sopenharmony_ci		get_device(&cdev->dev);
57362306a36Sopenharmony_ci		device->cdev = cdev;
57462306a36Sopenharmony_ci		rc = 0;
57562306a36Sopenharmony_ci	} else
57662306a36Sopenharmony_ci		/* Someone else was faster. */
57762306a36Sopenharmony_ci		rc = -EBUSY;
57862306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_ci	if (rc) {
58162306a36Sopenharmony_ci		dasd_free_device(device);
58262306a36Sopenharmony_ci		return ERR_PTR(rc);
58362306a36Sopenharmony_ci	}
58462306a36Sopenharmony_ci
58562306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
58662306a36Sopenharmony_ci	dev_set_drvdata(&cdev->dev, device);
58762306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci	device->paths_info = kset_create_and_add("paths_info", NULL,
59062306a36Sopenharmony_ci						 &device->cdev->dev.kobj);
59162306a36Sopenharmony_ci	if (!device->paths_info)
59262306a36Sopenharmony_ci		dev_warn(&cdev->dev, "Could not create paths_info kset\n");
59362306a36Sopenharmony_ci
59462306a36Sopenharmony_ci	return device;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_ci/*
59862306a36Sopenharmony_ci * allocate a PPRC data structure and call the discipline function to fill
59962306a36Sopenharmony_ci */
60062306a36Sopenharmony_cistatic int dasd_devmap_get_pprc_status(struct dasd_device *device,
60162306a36Sopenharmony_ci				       struct dasd_pprc_data_sc4 **data)
60262306a36Sopenharmony_ci{
60362306a36Sopenharmony_ci	struct dasd_pprc_data_sc4 *temp;
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_ci	if (!device->discipline || !device->discipline->pprc_status) {
60662306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "Unable to query copy relation status\n");
60762306a36Sopenharmony_ci		return -EOPNOTSUPP;
60862306a36Sopenharmony_ci	}
60962306a36Sopenharmony_ci	temp = kzalloc(sizeof(*temp), GFP_KERNEL);
61062306a36Sopenharmony_ci	if (!temp)
61162306a36Sopenharmony_ci		return -ENOMEM;
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	/* get PPRC information from storage */
61462306a36Sopenharmony_ci	if (device->discipline->pprc_status(device, temp)) {
61562306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "Error during copy relation status query\n");
61662306a36Sopenharmony_ci		kfree(temp);
61762306a36Sopenharmony_ci		return -EINVAL;
61862306a36Sopenharmony_ci	}
61962306a36Sopenharmony_ci	*data = temp;
62062306a36Sopenharmony_ci
62162306a36Sopenharmony_ci	return 0;
62262306a36Sopenharmony_ci}
62362306a36Sopenharmony_ci
62462306a36Sopenharmony_ci/*
62562306a36Sopenharmony_ci * find an entry in a PPRC device_info array by a given UID
62662306a36Sopenharmony_ci * depending on the primary/secondary state of the device it has to be
62762306a36Sopenharmony_ci * matched with the respective fields
62862306a36Sopenharmony_ci */
62962306a36Sopenharmony_cistatic int dasd_devmap_entry_from_pprc_data(struct dasd_pprc_data_sc4 *data,
63062306a36Sopenharmony_ci					    struct dasd_uid uid,
63162306a36Sopenharmony_ci					    bool primary)
63262306a36Sopenharmony_ci{
63362306a36Sopenharmony_ci	int i;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
63662306a36Sopenharmony_ci		if (primary) {
63762306a36Sopenharmony_ci			if (data->dev_info[i].prim_cu_ssid == uid.ssid &&
63862306a36Sopenharmony_ci			    data->dev_info[i].primary == uid.real_unit_addr)
63962306a36Sopenharmony_ci				return i;
64062306a36Sopenharmony_ci		} else {
64162306a36Sopenharmony_ci			if (data->dev_info[i].sec_cu_ssid == uid.ssid &&
64262306a36Sopenharmony_ci			    data->dev_info[i].secondary == uid.real_unit_addr)
64362306a36Sopenharmony_ci				return i;
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci	return -1;
64762306a36Sopenharmony_ci}
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_ci/*
65062306a36Sopenharmony_ci * check the consistency of a specified copy relation by checking
65162306a36Sopenharmony_ci * the following things:
65262306a36Sopenharmony_ci *
65362306a36Sopenharmony_ci *   - is the given device part of a copy pair setup
65462306a36Sopenharmony_ci *   - does the state of the device match the state in the PPRC status data
65562306a36Sopenharmony_ci *   - does the device UID match with the UID in the PPRC status data
65662306a36Sopenharmony_ci *   - to prevent misrouted IO check if the given device is present in all
65762306a36Sopenharmony_ci *     related PPRC status data
65862306a36Sopenharmony_ci */
65962306a36Sopenharmony_cistatic int dasd_devmap_check_copy_relation(struct dasd_device *device,
66062306a36Sopenharmony_ci					   struct dasd_copy_entry *entry,
66162306a36Sopenharmony_ci					   struct dasd_pprc_data_sc4 *data,
66262306a36Sopenharmony_ci					   struct dasd_copy_relation *copy)
66362306a36Sopenharmony_ci{
66462306a36Sopenharmony_ci	struct dasd_pprc_data_sc4 *tmp_dat;
66562306a36Sopenharmony_ci	struct dasd_device *tmp_dev;
66662306a36Sopenharmony_ci	struct dasd_uid uid;
66762306a36Sopenharmony_ci	int i, j;
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	if (!device->discipline || !device->discipline->get_uid ||
67062306a36Sopenharmony_ci	    device->discipline->get_uid(device, &uid))
67162306a36Sopenharmony_ci		return 1;
67262306a36Sopenharmony_ci
67362306a36Sopenharmony_ci	i = dasd_devmap_entry_from_pprc_data(data, uid, entry->primary);
67462306a36Sopenharmony_ci	if (i < 0) {
67562306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "Device not part of a copy relation\n");
67662306a36Sopenharmony_ci		return 1;
67762306a36Sopenharmony_ci	}
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	/* double check which role the current device has */
68062306a36Sopenharmony_ci	if (entry->primary) {
68162306a36Sopenharmony_ci		if (data->dev_info[i].flags & 0x80) {
68262306a36Sopenharmony_ci			dev_warn(&device->cdev->dev, "Copy pair secondary is setup as primary\n");
68362306a36Sopenharmony_ci			return 1;
68462306a36Sopenharmony_ci		}
68562306a36Sopenharmony_ci		if (data->dev_info[i].prim_cu_ssid != uid.ssid ||
68662306a36Sopenharmony_ci		    data->dev_info[i].primary != uid.real_unit_addr) {
68762306a36Sopenharmony_ci			dev_warn(&device->cdev->dev,
68862306a36Sopenharmony_ci				 "Primary device %s does not match copy pair status primary device %04x\n",
68962306a36Sopenharmony_ci				 dev_name(&device->cdev->dev),
69062306a36Sopenharmony_ci				 data->dev_info[i].prim_cu_ssid |
69162306a36Sopenharmony_ci				 data->dev_info[i].primary);
69262306a36Sopenharmony_ci			return 1;
69362306a36Sopenharmony_ci		}
69462306a36Sopenharmony_ci	} else {
69562306a36Sopenharmony_ci		if (!(data->dev_info[i].flags & 0x80)) {
69662306a36Sopenharmony_ci			dev_warn(&device->cdev->dev, "Copy pair primary is setup as secondary\n");
69762306a36Sopenharmony_ci			return 1;
69862306a36Sopenharmony_ci		}
69962306a36Sopenharmony_ci		if (data->dev_info[i].sec_cu_ssid != uid.ssid ||
70062306a36Sopenharmony_ci		    data->dev_info[i].secondary != uid.real_unit_addr) {
70162306a36Sopenharmony_ci			dev_warn(&device->cdev->dev,
70262306a36Sopenharmony_ci				 "Secondary device %s does not match copy pair status secondary device %04x\n",
70362306a36Sopenharmony_ci				 dev_name(&device->cdev->dev),
70462306a36Sopenharmony_ci				 data->dev_info[i].sec_cu_ssid |
70562306a36Sopenharmony_ci				 data->dev_info[i].secondary);
70662306a36Sopenharmony_ci			return 1;
70762306a36Sopenharmony_ci		}
70862306a36Sopenharmony_ci	}
70962306a36Sopenharmony_ci
71062306a36Sopenharmony_ci	/*
71162306a36Sopenharmony_ci	 * the current device has to be part of the copy relation of all
71262306a36Sopenharmony_ci	 * entries to prevent misrouted IO to another copy pair
71362306a36Sopenharmony_ci	 */
71462306a36Sopenharmony_ci	for (j = 0; j < DASD_CP_ENTRIES; j++) {
71562306a36Sopenharmony_ci		if (entry == &copy->entry[j])
71662306a36Sopenharmony_ci			tmp_dev = device;
71762306a36Sopenharmony_ci		else
71862306a36Sopenharmony_ci			tmp_dev = copy->entry[j].device;
71962306a36Sopenharmony_ci
72062306a36Sopenharmony_ci		if (!tmp_dev)
72162306a36Sopenharmony_ci			continue;
72262306a36Sopenharmony_ci
72362306a36Sopenharmony_ci		if (dasd_devmap_get_pprc_status(tmp_dev, &tmp_dat))
72462306a36Sopenharmony_ci			return 1;
72562306a36Sopenharmony_ci
72662306a36Sopenharmony_ci		if (dasd_devmap_entry_from_pprc_data(tmp_dat, uid, entry->primary) < 0) {
72762306a36Sopenharmony_ci			dev_warn(&tmp_dev->cdev->dev,
72862306a36Sopenharmony_ci				 "Copy pair relation does not contain device: %s\n",
72962306a36Sopenharmony_ci				 dev_name(&device->cdev->dev));
73062306a36Sopenharmony_ci			kfree(tmp_dat);
73162306a36Sopenharmony_ci			return 1;
73262306a36Sopenharmony_ci		}
73362306a36Sopenharmony_ci		kfree(tmp_dat);
73462306a36Sopenharmony_ci	}
73562306a36Sopenharmony_ci	return 0;
73662306a36Sopenharmony_ci}
73762306a36Sopenharmony_ci
73862306a36Sopenharmony_ci/* delete device from copy relation entry */
73962306a36Sopenharmony_cistatic void dasd_devmap_delete_copy_relation_device(struct dasd_device *device)
74062306a36Sopenharmony_ci{
74162306a36Sopenharmony_ci	struct dasd_copy_relation *copy;
74262306a36Sopenharmony_ci	int i;
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	if (!device->copy)
74562306a36Sopenharmony_ci		return;
74662306a36Sopenharmony_ci
74762306a36Sopenharmony_ci	copy = device->copy;
74862306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
74962306a36Sopenharmony_ci		if (copy->entry[i].device == device)
75062306a36Sopenharmony_ci			copy->entry[i].device = NULL;
75162306a36Sopenharmony_ci	}
75262306a36Sopenharmony_ci	dasd_put_device(device);
75362306a36Sopenharmony_ci	device->copy = NULL;
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci/*
75762306a36Sopenharmony_ci * read all required information for a copy relation setup and setup the device
75862306a36Sopenharmony_ci * accordingly
75962306a36Sopenharmony_ci */
76062306a36Sopenharmony_ciint dasd_devmap_set_device_copy_relation(struct ccw_device *cdev,
76162306a36Sopenharmony_ci					 bool pprc_enabled)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	struct dasd_pprc_data_sc4 *data = NULL;
76462306a36Sopenharmony_ci	struct dasd_copy_entry *entry = NULL;
76562306a36Sopenharmony_ci	struct dasd_copy_relation *copy;
76662306a36Sopenharmony_ci	struct dasd_devmap *devmap;
76762306a36Sopenharmony_ci	struct dasd_device *device;
76862306a36Sopenharmony_ci	int i, rc = 0;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	devmap = dasd_devmap_from_cdev(cdev);
77162306a36Sopenharmony_ci	if (IS_ERR(devmap))
77262306a36Sopenharmony_ci		return PTR_ERR(devmap);
77362306a36Sopenharmony_ci
77462306a36Sopenharmony_ci	device = devmap->device;
77562306a36Sopenharmony_ci	if (!device)
77662306a36Sopenharmony_ci		return -ENODEV;
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_ci	copy = devmap->copy;
77962306a36Sopenharmony_ci	/* no copy pair setup for this device */
78062306a36Sopenharmony_ci	if (!copy)
78162306a36Sopenharmony_ci		goto out;
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	rc = dasd_devmap_get_pprc_status(device, &data);
78462306a36Sopenharmony_ci	if (rc)
78562306a36Sopenharmony_ci		return rc;
78662306a36Sopenharmony_ci
78762306a36Sopenharmony_ci	/* print error if PPRC is requested but not enabled on storage server */
78862306a36Sopenharmony_ci	if (!pprc_enabled) {
78962306a36Sopenharmony_ci		dev_err(&cdev->dev, "Copy relation not enabled on storage server\n");
79062306a36Sopenharmony_ci		rc = -EINVAL;
79162306a36Sopenharmony_ci		goto out;
79262306a36Sopenharmony_ci	}
79362306a36Sopenharmony_ci
79462306a36Sopenharmony_ci	if (!data->dev_info[0].state) {
79562306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "Copy pair setup requested for device not in copy relation\n");
79662306a36Sopenharmony_ci		rc = -EINVAL;
79762306a36Sopenharmony_ci		goto out;
79862306a36Sopenharmony_ci	}
79962306a36Sopenharmony_ci	/* find entry */
80062306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
80162306a36Sopenharmony_ci		if (copy->entry[i].configured &&
80262306a36Sopenharmony_ci		    strncmp(dev_name(&cdev->dev),
80362306a36Sopenharmony_ci			    copy->entry[i].busid, DASD_BUS_ID_SIZE) == 0) {
80462306a36Sopenharmony_ci			entry = &copy->entry[i];
80562306a36Sopenharmony_ci			break;
80662306a36Sopenharmony_ci		}
80762306a36Sopenharmony_ci	}
80862306a36Sopenharmony_ci	if (!entry) {
80962306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "Copy relation entry not found\n");
81062306a36Sopenharmony_ci		rc = -EINVAL;
81162306a36Sopenharmony_ci		goto out;
81262306a36Sopenharmony_ci	}
81362306a36Sopenharmony_ci	/* check if the copy relation is valid */
81462306a36Sopenharmony_ci	if (dasd_devmap_check_copy_relation(device, entry, data, copy)) {
81562306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "Copy relation faulty\n");
81662306a36Sopenharmony_ci		rc = -EINVAL;
81762306a36Sopenharmony_ci		goto out;
81862306a36Sopenharmony_ci	}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_ci	dasd_get_device(device);
82162306a36Sopenharmony_ci	copy->entry[i].device = device;
82262306a36Sopenharmony_ci	device->copy = copy;
82362306a36Sopenharmony_ciout:
82462306a36Sopenharmony_ci	kfree(data);
82562306a36Sopenharmony_ci	return rc;
82662306a36Sopenharmony_ci}
82762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dasd_devmap_set_device_copy_relation);
82862306a36Sopenharmony_ci
82962306a36Sopenharmony_ci/*
83062306a36Sopenharmony_ci * Wait queue for dasd_delete_device waits.
83162306a36Sopenharmony_ci */
83262306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(dasd_delete_wq);
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_ci/*
83562306a36Sopenharmony_ci * Remove a dasd device structure. The passed referenced
83662306a36Sopenharmony_ci * is destroyed.
83762306a36Sopenharmony_ci */
83862306a36Sopenharmony_civoid
83962306a36Sopenharmony_cidasd_delete_device(struct dasd_device *device)
84062306a36Sopenharmony_ci{
84162306a36Sopenharmony_ci	struct ccw_device *cdev;
84262306a36Sopenharmony_ci	struct dasd_devmap *devmap;
84362306a36Sopenharmony_ci	unsigned long flags;
84462306a36Sopenharmony_ci
84562306a36Sopenharmony_ci	/* First remove device pointer from devmap. */
84662306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(&device->cdev->dev));
84762306a36Sopenharmony_ci	BUG_ON(IS_ERR(devmap));
84862306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
84962306a36Sopenharmony_ci	if (devmap->device != device) {
85062306a36Sopenharmony_ci		spin_unlock(&dasd_devmap_lock);
85162306a36Sopenharmony_ci		dasd_put_device(device);
85262306a36Sopenharmony_ci		return;
85362306a36Sopenharmony_ci	}
85462306a36Sopenharmony_ci	devmap->device = NULL;
85562306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
85662306a36Sopenharmony_ci
85762306a36Sopenharmony_ci	/* Disconnect dasd_device structure from ccw_device structure. */
85862306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
85962306a36Sopenharmony_ci	dev_set_drvdata(&device->cdev->dev, NULL);
86062306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	/* Removve copy relation */
86362306a36Sopenharmony_ci	dasd_devmap_delete_copy_relation_device(device);
86462306a36Sopenharmony_ci	/*
86562306a36Sopenharmony_ci	 * Drop ref_count by 3, one for the devmap reference, one for
86662306a36Sopenharmony_ci	 * the cdev reference and one for the passed reference.
86762306a36Sopenharmony_ci	 */
86862306a36Sopenharmony_ci	atomic_sub(3, &device->ref_count);
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	/* Wait for reference counter to drop to zero. */
87162306a36Sopenharmony_ci	wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
87262306a36Sopenharmony_ci
87362306a36Sopenharmony_ci	dasd_generic_free_discipline(device);
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	kset_unregister(device->paths_info);
87662306a36Sopenharmony_ci
87762306a36Sopenharmony_ci	/* Disconnect dasd_device structure from ccw_device structure. */
87862306a36Sopenharmony_ci	cdev = device->cdev;
87962306a36Sopenharmony_ci	device->cdev = NULL;
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ci	/* Put ccw_device structure. */
88262306a36Sopenharmony_ci	put_device(&cdev->dev);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	/* Now the device structure can be freed. */
88562306a36Sopenharmony_ci	dasd_free_device(device);
88662306a36Sopenharmony_ci}
88762306a36Sopenharmony_ci
88862306a36Sopenharmony_ci/*
88962306a36Sopenharmony_ci * Reference counter dropped to zero. Wake up waiter
89062306a36Sopenharmony_ci * in dasd_delete_device.
89162306a36Sopenharmony_ci */
89262306a36Sopenharmony_civoid
89362306a36Sopenharmony_cidasd_put_device_wake(struct dasd_device *device)
89462306a36Sopenharmony_ci{
89562306a36Sopenharmony_ci	wake_up(&dasd_delete_wq);
89662306a36Sopenharmony_ci}
89762306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dasd_put_device_wake);
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ci/*
90062306a36Sopenharmony_ci * Return dasd_device structure associated with cdev.
90162306a36Sopenharmony_ci * This function needs to be called with the ccw device
90262306a36Sopenharmony_ci * lock held. It can be used from interrupt context.
90362306a36Sopenharmony_ci */
90462306a36Sopenharmony_cistruct dasd_device *
90562306a36Sopenharmony_cidasd_device_from_cdev_locked(struct ccw_device *cdev)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct dasd_device *device = dev_get_drvdata(&cdev->dev);
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci	if (!device)
91062306a36Sopenharmony_ci		return ERR_PTR(-ENODEV);
91162306a36Sopenharmony_ci	dasd_get_device(device);
91262306a36Sopenharmony_ci	return device;
91362306a36Sopenharmony_ci}
91462306a36Sopenharmony_ci
91562306a36Sopenharmony_ci/*
91662306a36Sopenharmony_ci * Return dasd_device structure associated with cdev.
91762306a36Sopenharmony_ci */
91862306a36Sopenharmony_cistruct dasd_device *
91962306a36Sopenharmony_cidasd_device_from_cdev(struct ccw_device *cdev)
92062306a36Sopenharmony_ci{
92162306a36Sopenharmony_ci	struct dasd_device *device;
92262306a36Sopenharmony_ci	unsigned long flags;
92362306a36Sopenharmony_ci
92462306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
92562306a36Sopenharmony_ci	device = dasd_device_from_cdev_locked(cdev);
92662306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
92762306a36Sopenharmony_ci	return device;
92862306a36Sopenharmony_ci}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_civoid dasd_add_link_to_gendisk(struct gendisk *gdp, struct dasd_device *device)
93162306a36Sopenharmony_ci{
93262306a36Sopenharmony_ci	struct dasd_devmap *devmap;
93362306a36Sopenharmony_ci
93462306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(&device->cdev->dev));
93562306a36Sopenharmony_ci	if (IS_ERR(devmap))
93662306a36Sopenharmony_ci		return;
93762306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
93862306a36Sopenharmony_ci	gdp->private_data = devmap;
93962306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
94062306a36Sopenharmony_ci}
94162306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_add_link_to_gendisk);
94262306a36Sopenharmony_ci
94362306a36Sopenharmony_cistruct dasd_device *dasd_device_from_gendisk(struct gendisk *gdp)
94462306a36Sopenharmony_ci{
94562306a36Sopenharmony_ci	struct dasd_device *device;
94662306a36Sopenharmony_ci	struct dasd_devmap *devmap;
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_ci	if (!gdp->private_data)
94962306a36Sopenharmony_ci		return NULL;
95062306a36Sopenharmony_ci	device = NULL;
95162306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
95262306a36Sopenharmony_ci	devmap = gdp->private_data;
95362306a36Sopenharmony_ci	if (devmap && devmap->device) {
95462306a36Sopenharmony_ci		device = devmap->device;
95562306a36Sopenharmony_ci		dasd_get_device(device);
95662306a36Sopenharmony_ci	}
95762306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
95862306a36Sopenharmony_ci	return device;
95962306a36Sopenharmony_ci}
96062306a36Sopenharmony_ci
96162306a36Sopenharmony_ci/*
96262306a36Sopenharmony_ci * SECTION: files in sysfs
96362306a36Sopenharmony_ci */
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci/*
96662306a36Sopenharmony_ci * failfast controls the behaviour, if no path is available
96762306a36Sopenharmony_ci */
96862306a36Sopenharmony_cistatic ssize_t dasd_ff_show(struct device *dev, struct device_attribute *attr,
96962306a36Sopenharmony_ci			    char *buf)
97062306a36Sopenharmony_ci{
97162306a36Sopenharmony_ci	struct dasd_devmap *devmap;
97262306a36Sopenharmony_ci	int ff_flag;
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
97562306a36Sopenharmony_ci	if (!IS_ERR(devmap))
97662306a36Sopenharmony_ci		ff_flag = (devmap->features & DASD_FEATURE_FAILFAST) != 0;
97762306a36Sopenharmony_ci	else
97862306a36Sopenharmony_ci		ff_flag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_FAILFAST) != 0;
97962306a36Sopenharmony_ci	return sysfs_emit(buf, ff_flag ? "1\n" : "0\n");
98062306a36Sopenharmony_ci}
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_cistatic ssize_t dasd_ff_store(struct device *dev, struct device_attribute *attr,
98362306a36Sopenharmony_ci	      const char *buf, size_t count)
98462306a36Sopenharmony_ci{
98562306a36Sopenharmony_ci	unsigned int val;
98662306a36Sopenharmony_ci	int rc;
98762306a36Sopenharmony_ci
98862306a36Sopenharmony_ci	if (kstrtouint(buf, 0, &val) || val > 1)
98962306a36Sopenharmony_ci		return -EINVAL;
99062306a36Sopenharmony_ci
99162306a36Sopenharmony_ci	rc = dasd_set_feature(to_ccwdev(dev), DASD_FEATURE_FAILFAST, val);
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	return rc ? : count;
99462306a36Sopenharmony_ci}
99562306a36Sopenharmony_ci
99662306a36Sopenharmony_cistatic DEVICE_ATTR(failfast, 0644, dasd_ff_show, dasd_ff_store);
99762306a36Sopenharmony_ci
99862306a36Sopenharmony_ci/*
99962306a36Sopenharmony_ci * readonly controls the readonly status of a dasd
100062306a36Sopenharmony_ci */
100162306a36Sopenharmony_cistatic ssize_t
100262306a36Sopenharmony_cidasd_ro_show(struct device *dev, struct device_attribute *attr, char *buf)
100362306a36Sopenharmony_ci{
100462306a36Sopenharmony_ci	struct dasd_devmap *devmap;
100562306a36Sopenharmony_ci	struct dasd_device *device;
100662306a36Sopenharmony_ci	int ro_flag = 0;
100762306a36Sopenharmony_ci
100862306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
100962306a36Sopenharmony_ci	if (IS_ERR(devmap))
101062306a36Sopenharmony_ci		goto out;
101162306a36Sopenharmony_ci
101262306a36Sopenharmony_ci	ro_flag = !!(devmap->features & DASD_FEATURE_READONLY);
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
101562306a36Sopenharmony_ci	device = devmap->device;
101662306a36Sopenharmony_ci	if (device)
101762306a36Sopenharmony_ci		ro_flag |= test_bit(DASD_FLAG_DEVICE_RO, &device->flags);
101862306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
101962306a36Sopenharmony_ci
102062306a36Sopenharmony_ciout:
102162306a36Sopenharmony_ci	return sysfs_emit(buf, ro_flag ? "1\n" : "0\n");
102262306a36Sopenharmony_ci}
102362306a36Sopenharmony_ci
102462306a36Sopenharmony_cistatic ssize_t
102562306a36Sopenharmony_cidasd_ro_store(struct device *dev, struct device_attribute *attr,
102662306a36Sopenharmony_ci	      const char *buf, size_t count)
102762306a36Sopenharmony_ci{
102862306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
102962306a36Sopenharmony_ci	struct dasd_device *device;
103062306a36Sopenharmony_ci	unsigned long flags;
103162306a36Sopenharmony_ci	unsigned int val;
103262306a36Sopenharmony_ci	int rc;
103362306a36Sopenharmony_ci
103462306a36Sopenharmony_ci	if (kstrtouint(buf, 0, &val) || val > 1)
103562306a36Sopenharmony_ci		return -EINVAL;
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	rc = dasd_set_feature(cdev, DASD_FEATURE_READONLY, val);
103862306a36Sopenharmony_ci	if (rc)
103962306a36Sopenharmony_ci		return rc;
104062306a36Sopenharmony_ci
104162306a36Sopenharmony_ci	device = dasd_device_from_cdev(cdev);
104262306a36Sopenharmony_ci	if (IS_ERR(device))
104362306a36Sopenharmony_ci		return count;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
104662306a36Sopenharmony_ci	val = val || test_bit(DASD_FLAG_DEVICE_RO, &device->flags);
104762306a36Sopenharmony_ci
104862306a36Sopenharmony_ci	if (!device->block || !device->block->gdp ||
104962306a36Sopenharmony_ci	    test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
105062306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
105162306a36Sopenharmony_ci		goto out;
105262306a36Sopenharmony_ci	}
105362306a36Sopenharmony_ci	/* Increase open_count to avoid losing the block device */
105462306a36Sopenharmony_ci	atomic_inc(&device->block->open_count);
105562306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	set_disk_ro(device->block->gdp, val);
105862306a36Sopenharmony_ci	atomic_dec(&device->block->open_count);
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ciout:
106162306a36Sopenharmony_ci	dasd_put_device(device);
106262306a36Sopenharmony_ci
106362306a36Sopenharmony_ci	return count;
106462306a36Sopenharmony_ci}
106562306a36Sopenharmony_ci
106662306a36Sopenharmony_cistatic DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
106762306a36Sopenharmony_ci/*
106862306a36Sopenharmony_ci * erplog controls the logging of ERP related data
106962306a36Sopenharmony_ci * (e.g. failing channel programs).
107062306a36Sopenharmony_ci */
107162306a36Sopenharmony_cistatic ssize_t
107262306a36Sopenharmony_cidasd_erplog_show(struct device *dev, struct device_attribute *attr, char *buf)
107362306a36Sopenharmony_ci{
107462306a36Sopenharmony_ci	struct dasd_devmap *devmap;
107562306a36Sopenharmony_ci	int erplog;
107662306a36Sopenharmony_ci
107762306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
107862306a36Sopenharmony_ci	if (!IS_ERR(devmap))
107962306a36Sopenharmony_ci		erplog = (devmap->features & DASD_FEATURE_ERPLOG) != 0;
108062306a36Sopenharmony_ci	else
108162306a36Sopenharmony_ci		erplog = (DASD_FEATURE_DEFAULT & DASD_FEATURE_ERPLOG) != 0;
108262306a36Sopenharmony_ci	return sysfs_emit(buf, erplog ? "1\n" : "0\n");
108362306a36Sopenharmony_ci}
108462306a36Sopenharmony_ci
108562306a36Sopenharmony_cistatic ssize_t
108662306a36Sopenharmony_cidasd_erplog_store(struct device *dev, struct device_attribute *attr,
108762306a36Sopenharmony_ci	      const char *buf, size_t count)
108862306a36Sopenharmony_ci{
108962306a36Sopenharmony_ci	unsigned int val;
109062306a36Sopenharmony_ci	int rc;
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	if (kstrtouint(buf, 0, &val) || val > 1)
109362306a36Sopenharmony_ci		return -EINVAL;
109462306a36Sopenharmony_ci
109562306a36Sopenharmony_ci	rc = dasd_set_feature(to_ccwdev(dev), DASD_FEATURE_ERPLOG, val);
109662306a36Sopenharmony_ci
109762306a36Sopenharmony_ci	return rc ? : count;
109862306a36Sopenharmony_ci}
109962306a36Sopenharmony_ci
110062306a36Sopenharmony_cistatic DEVICE_ATTR(erplog, 0644, dasd_erplog_show, dasd_erplog_store);
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci/*
110362306a36Sopenharmony_ci * use_diag controls whether the driver should use diag rather than ssch
110462306a36Sopenharmony_ci * to talk to the device
110562306a36Sopenharmony_ci */
110662306a36Sopenharmony_cistatic ssize_t
110762306a36Sopenharmony_cidasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
110862306a36Sopenharmony_ci{
110962306a36Sopenharmony_ci	struct dasd_devmap *devmap;
111062306a36Sopenharmony_ci	int use_diag;
111162306a36Sopenharmony_ci
111262306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
111362306a36Sopenharmony_ci	if (!IS_ERR(devmap))
111462306a36Sopenharmony_ci		use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0;
111562306a36Sopenharmony_ci	else
111662306a36Sopenharmony_ci		use_diag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USEDIAG) != 0;
111762306a36Sopenharmony_ci	return sprintf(buf, use_diag ? "1\n" : "0\n");
111862306a36Sopenharmony_ci}
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_cistatic ssize_t
112162306a36Sopenharmony_cidasd_use_diag_store(struct device *dev, struct device_attribute *attr,
112262306a36Sopenharmony_ci		    const char *buf, size_t count)
112362306a36Sopenharmony_ci{
112462306a36Sopenharmony_ci	struct dasd_devmap *devmap;
112562306a36Sopenharmony_ci	unsigned int val;
112662306a36Sopenharmony_ci	ssize_t rc;
112762306a36Sopenharmony_ci
112862306a36Sopenharmony_ci	devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
112962306a36Sopenharmony_ci	if (IS_ERR(devmap))
113062306a36Sopenharmony_ci		return PTR_ERR(devmap);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (kstrtouint(buf, 0, &val) || val > 1)
113362306a36Sopenharmony_ci		return -EINVAL;
113462306a36Sopenharmony_ci
113562306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
113662306a36Sopenharmony_ci	/* Changing diag discipline flag is only allowed in offline state. */
113762306a36Sopenharmony_ci	rc = count;
113862306a36Sopenharmony_ci	if (!devmap->device && !(devmap->features & DASD_FEATURE_USERAW)) {
113962306a36Sopenharmony_ci		if (val)
114062306a36Sopenharmony_ci			devmap->features |= DASD_FEATURE_USEDIAG;
114162306a36Sopenharmony_ci		else
114262306a36Sopenharmony_ci			devmap->features &= ~DASD_FEATURE_USEDIAG;
114362306a36Sopenharmony_ci	} else
114462306a36Sopenharmony_ci		rc = -EPERM;
114562306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
114662306a36Sopenharmony_ci	return rc;
114762306a36Sopenharmony_ci}
114862306a36Sopenharmony_ci
114962306a36Sopenharmony_cistatic DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
115062306a36Sopenharmony_ci
115162306a36Sopenharmony_ci/*
115262306a36Sopenharmony_ci * use_raw controls whether the driver should give access to raw eckd data or
115362306a36Sopenharmony_ci * operate in standard mode
115462306a36Sopenharmony_ci */
115562306a36Sopenharmony_cistatic ssize_t
115662306a36Sopenharmony_cidasd_use_raw_show(struct device *dev, struct device_attribute *attr, char *buf)
115762306a36Sopenharmony_ci{
115862306a36Sopenharmony_ci	struct dasd_devmap *devmap;
115962306a36Sopenharmony_ci	int use_raw;
116062306a36Sopenharmony_ci
116162306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
116262306a36Sopenharmony_ci	if (!IS_ERR(devmap))
116362306a36Sopenharmony_ci		use_raw = (devmap->features & DASD_FEATURE_USERAW) != 0;
116462306a36Sopenharmony_ci	else
116562306a36Sopenharmony_ci		use_raw = (DASD_FEATURE_DEFAULT & DASD_FEATURE_USERAW) != 0;
116662306a36Sopenharmony_ci	return sprintf(buf, use_raw ? "1\n" : "0\n");
116762306a36Sopenharmony_ci}
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_cistatic ssize_t
117062306a36Sopenharmony_cidasd_use_raw_store(struct device *dev, struct device_attribute *attr,
117162306a36Sopenharmony_ci		    const char *buf, size_t count)
117262306a36Sopenharmony_ci{
117362306a36Sopenharmony_ci	struct dasd_devmap *devmap;
117462306a36Sopenharmony_ci	ssize_t rc;
117562306a36Sopenharmony_ci	unsigned long val;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci	devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
117862306a36Sopenharmony_ci	if (IS_ERR(devmap))
117962306a36Sopenharmony_ci		return PTR_ERR(devmap);
118062306a36Sopenharmony_ci
118162306a36Sopenharmony_ci	if ((kstrtoul(buf, 10, &val) != 0) || val > 1)
118262306a36Sopenharmony_ci		return -EINVAL;
118362306a36Sopenharmony_ci
118462306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
118562306a36Sopenharmony_ci	/* Changing diag discipline flag is only allowed in offline state. */
118662306a36Sopenharmony_ci	rc = count;
118762306a36Sopenharmony_ci	if (!devmap->device && !(devmap->features & DASD_FEATURE_USEDIAG)) {
118862306a36Sopenharmony_ci		if (val)
118962306a36Sopenharmony_ci			devmap->features |= DASD_FEATURE_USERAW;
119062306a36Sopenharmony_ci		else
119162306a36Sopenharmony_ci			devmap->features &= ~DASD_FEATURE_USERAW;
119262306a36Sopenharmony_ci	} else
119362306a36Sopenharmony_ci		rc = -EPERM;
119462306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
119562306a36Sopenharmony_ci	return rc;
119662306a36Sopenharmony_ci}
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_cistatic DEVICE_ATTR(raw_track_access, 0644, dasd_use_raw_show,
119962306a36Sopenharmony_ci		   dasd_use_raw_store);
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_cistatic ssize_t
120262306a36Sopenharmony_cidasd_safe_offline_store(struct device *dev, struct device_attribute *attr,
120362306a36Sopenharmony_ci			const char *buf, size_t count)
120462306a36Sopenharmony_ci{
120562306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
120662306a36Sopenharmony_ci	struct dasd_device *device;
120762306a36Sopenharmony_ci	unsigned long flags;
120862306a36Sopenharmony_ci	int rc;
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
121162306a36Sopenharmony_ci	device = dasd_device_from_cdev_locked(cdev);
121262306a36Sopenharmony_ci	if (IS_ERR(device)) {
121362306a36Sopenharmony_ci		rc = PTR_ERR(device);
121462306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
121562306a36Sopenharmony_ci		goto out;
121662306a36Sopenharmony_ci	}
121762306a36Sopenharmony_ci
121862306a36Sopenharmony_ci	if (test_bit(DASD_FLAG_OFFLINE, &device->flags) ||
121962306a36Sopenharmony_ci	    test_bit(DASD_FLAG_SAFE_OFFLINE_RUNNING, &device->flags)) {
122062306a36Sopenharmony_ci		/* Already doing offline processing */
122162306a36Sopenharmony_ci		dasd_put_device(device);
122262306a36Sopenharmony_ci		spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
122362306a36Sopenharmony_ci		rc = -EBUSY;
122462306a36Sopenharmony_ci		goto out;
122562306a36Sopenharmony_ci	}
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci	set_bit(DASD_FLAG_SAFE_OFFLINE, &device->flags);
122862306a36Sopenharmony_ci	dasd_put_device(device);
122962306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
123062306a36Sopenharmony_ci
123162306a36Sopenharmony_ci	rc = ccw_device_set_offline(cdev);
123262306a36Sopenharmony_ci
123362306a36Sopenharmony_ciout:
123462306a36Sopenharmony_ci	return rc ? rc : count;
123562306a36Sopenharmony_ci}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_cistatic DEVICE_ATTR(safe_offline, 0200, NULL, dasd_safe_offline_store);
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_cistatic ssize_t
124062306a36Sopenharmony_cidasd_access_show(struct device *dev, struct device_attribute *attr,
124162306a36Sopenharmony_ci		 char *buf)
124262306a36Sopenharmony_ci{
124362306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
124462306a36Sopenharmony_ci	struct dasd_device *device;
124562306a36Sopenharmony_ci	int count;
124662306a36Sopenharmony_ci
124762306a36Sopenharmony_ci	device = dasd_device_from_cdev(cdev);
124862306a36Sopenharmony_ci	if (IS_ERR(device))
124962306a36Sopenharmony_ci		return PTR_ERR(device);
125062306a36Sopenharmony_ci
125162306a36Sopenharmony_ci	if (!device->discipline)
125262306a36Sopenharmony_ci		count = -ENODEV;
125362306a36Sopenharmony_ci	else if (!device->discipline->host_access_count)
125462306a36Sopenharmony_ci		count = -EOPNOTSUPP;
125562306a36Sopenharmony_ci	else
125662306a36Sopenharmony_ci		count = device->discipline->host_access_count(device);
125762306a36Sopenharmony_ci
125862306a36Sopenharmony_ci	dasd_put_device(device);
125962306a36Sopenharmony_ci	if (count < 0)
126062306a36Sopenharmony_ci		return count;
126162306a36Sopenharmony_ci
126262306a36Sopenharmony_ci	return sprintf(buf, "%d\n", count);
126362306a36Sopenharmony_ci}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_cistatic DEVICE_ATTR(host_access_count, 0444, dasd_access_show, NULL);
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_cistatic ssize_t
126862306a36Sopenharmony_cidasd_discipline_show(struct device *dev, struct device_attribute *attr,
126962306a36Sopenharmony_ci		     char *buf)
127062306a36Sopenharmony_ci{
127162306a36Sopenharmony_ci	struct dasd_device *device;
127262306a36Sopenharmony_ci	ssize_t len;
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
127562306a36Sopenharmony_ci	if (IS_ERR(device))
127662306a36Sopenharmony_ci		goto out;
127762306a36Sopenharmony_ci	else if (!device->discipline) {
127862306a36Sopenharmony_ci		dasd_put_device(device);
127962306a36Sopenharmony_ci		goto out;
128062306a36Sopenharmony_ci	} else {
128162306a36Sopenharmony_ci		len = sysfs_emit(buf, "%s\n",
128262306a36Sopenharmony_ci				 device->discipline->name);
128362306a36Sopenharmony_ci		dasd_put_device(device);
128462306a36Sopenharmony_ci		return len;
128562306a36Sopenharmony_ci	}
128662306a36Sopenharmony_ciout:
128762306a36Sopenharmony_ci	len = sysfs_emit(buf, "none\n");
128862306a36Sopenharmony_ci	return len;
128962306a36Sopenharmony_ci}
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_cistatic DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
129262306a36Sopenharmony_ci
129362306a36Sopenharmony_cistatic ssize_t
129462306a36Sopenharmony_cidasd_device_status_show(struct device *dev, struct device_attribute *attr,
129562306a36Sopenharmony_ci		     char *buf)
129662306a36Sopenharmony_ci{
129762306a36Sopenharmony_ci	struct dasd_device *device;
129862306a36Sopenharmony_ci	ssize_t len;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
130162306a36Sopenharmony_ci	if (!IS_ERR(device)) {
130262306a36Sopenharmony_ci		switch (device->state) {
130362306a36Sopenharmony_ci		case DASD_STATE_NEW:
130462306a36Sopenharmony_ci			len = sysfs_emit(buf, "new\n");
130562306a36Sopenharmony_ci			break;
130662306a36Sopenharmony_ci		case DASD_STATE_KNOWN:
130762306a36Sopenharmony_ci			len = sysfs_emit(buf, "detected\n");
130862306a36Sopenharmony_ci			break;
130962306a36Sopenharmony_ci		case DASD_STATE_BASIC:
131062306a36Sopenharmony_ci			len = sysfs_emit(buf, "basic\n");
131162306a36Sopenharmony_ci			break;
131262306a36Sopenharmony_ci		case DASD_STATE_UNFMT:
131362306a36Sopenharmony_ci			len = sysfs_emit(buf, "unformatted\n");
131462306a36Sopenharmony_ci			break;
131562306a36Sopenharmony_ci		case DASD_STATE_READY:
131662306a36Sopenharmony_ci			len = sysfs_emit(buf, "ready\n");
131762306a36Sopenharmony_ci			break;
131862306a36Sopenharmony_ci		case DASD_STATE_ONLINE:
131962306a36Sopenharmony_ci			len = sysfs_emit(buf, "online\n");
132062306a36Sopenharmony_ci			break;
132162306a36Sopenharmony_ci		default:
132262306a36Sopenharmony_ci			len = sysfs_emit(buf, "no stat\n");
132362306a36Sopenharmony_ci			break;
132462306a36Sopenharmony_ci		}
132562306a36Sopenharmony_ci		dasd_put_device(device);
132662306a36Sopenharmony_ci	} else
132762306a36Sopenharmony_ci		len = sysfs_emit(buf, "unknown\n");
132862306a36Sopenharmony_ci	return len;
132962306a36Sopenharmony_ci}
133062306a36Sopenharmony_ci
133162306a36Sopenharmony_cistatic DEVICE_ATTR(status, 0444, dasd_device_status_show, NULL);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_cistatic ssize_t dasd_alias_show(struct device *dev,
133462306a36Sopenharmony_ci			       struct device_attribute *attr, char *buf)
133562306a36Sopenharmony_ci{
133662306a36Sopenharmony_ci	struct dasd_device *device;
133762306a36Sopenharmony_ci	struct dasd_uid uid;
133862306a36Sopenharmony_ci
133962306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
134062306a36Sopenharmony_ci	if (IS_ERR(device))
134162306a36Sopenharmony_ci		return sprintf(buf, "0\n");
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (device->discipline && device->discipline->get_uid &&
134462306a36Sopenharmony_ci	    !device->discipline->get_uid(device, &uid)) {
134562306a36Sopenharmony_ci		if (uid.type == UA_BASE_PAV_ALIAS ||
134662306a36Sopenharmony_ci		    uid.type == UA_HYPER_PAV_ALIAS) {
134762306a36Sopenharmony_ci			dasd_put_device(device);
134862306a36Sopenharmony_ci			return sprintf(buf, "1\n");
134962306a36Sopenharmony_ci		}
135062306a36Sopenharmony_ci	}
135162306a36Sopenharmony_ci	dasd_put_device(device);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci	return sprintf(buf, "0\n");
135462306a36Sopenharmony_ci}
135562306a36Sopenharmony_ci
135662306a36Sopenharmony_cistatic DEVICE_ATTR(alias, 0444, dasd_alias_show, NULL);
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_cistatic ssize_t dasd_vendor_show(struct device *dev,
135962306a36Sopenharmony_ci				struct device_attribute *attr, char *buf)
136062306a36Sopenharmony_ci{
136162306a36Sopenharmony_ci	struct dasd_device *device;
136262306a36Sopenharmony_ci	struct dasd_uid uid;
136362306a36Sopenharmony_ci	char *vendor;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
136662306a36Sopenharmony_ci	vendor = "";
136762306a36Sopenharmony_ci	if (IS_ERR(device))
136862306a36Sopenharmony_ci		return sysfs_emit(buf, "%s\n", vendor);
136962306a36Sopenharmony_ci
137062306a36Sopenharmony_ci	if (device->discipline && device->discipline->get_uid &&
137162306a36Sopenharmony_ci	    !device->discipline->get_uid(device, &uid))
137262306a36Sopenharmony_ci			vendor = uid.vendor;
137362306a36Sopenharmony_ci
137462306a36Sopenharmony_ci	dasd_put_device(device);
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", vendor);
137762306a36Sopenharmony_ci}
137862306a36Sopenharmony_ci
137962306a36Sopenharmony_cistatic DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL);
138062306a36Sopenharmony_ci
138162306a36Sopenharmony_cistatic ssize_t
138262306a36Sopenharmony_cidasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
138362306a36Sopenharmony_ci{
138462306a36Sopenharmony_ci	char uid_string[DASD_UID_STRLEN];
138562306a36Sopenharmony_ci	struct dasd_device *device;
138662306a36Sopenharmony_ci	struct dasd_uid uid;
138762306a36Sopenharmony_ci	char ua_string[3];
138862306a36Sopenharmony_ci
138962306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
139062306a36Sopenharmony_ci	uid_string[0] = 0;
139162306a36Sopenharmony_ci	if (IS_ERR(device))
139262306a36Sopenharmony_ci		return sysfs_emit(buf, "%s\n", uid_string);
139362306a36Sopenharmony_ci
139462306a36Sopenharmony_ci	if (device->discipline && device->discipline->get_uid &&
139562306a36Sopenharmony_ci	    !device->discipline->get_uid(device, &uid)) {
139662306a36Sopenharmony_ci		switch (uid.type) {
139762306a36Sopenharmony_ci		case UA_BASE_DEVICE:
139862306a36Sopenharmony_ci			snprintf(ua_string, sizeof(ua_string), "%02x",
139962306a36Sopenharmony_ci				 uid.real_unit_addr);
140062306a36Sopenharmony_ci			break;
140162306a36Sopenharmony_ci		case UA_BASE_PAV_ALIAS:
140262306a36Sopenharmony_ci			snprintf(ua_string, sizeof(ua_string), "%02x",
140362306a36Sopenharmony_ci				 uid.base_unit_addr);
140462306a36Sopenharmony_ci			break;
140562306a36Sopenharmony_ci		case UA_HYPER_PAV_ALIAS:
140662306a36Sopenharmony_ci			snprintf(ua_string, sizeof(ua_string), "xx");
140762306a36Sopenharmony_ci			break;
140862306a36Sopenharmony_ci		default:
140962306a36Sopenharmony_ci			/* should not happen, treat like base device */
141062306a36Sopenharmony_ci			snprintf(ua_string, sizeof(ua_string), "%02x",
141162306a36Sopenharmony_ci				 uid.real_unit_addr);
141262306a36Sopenharmony_ci			break;
141362306a36Sopenharmony_ci		}
141462306a36Sopenharmony_ci
141562306a36Sopenharmony_ci		if (strlen(uid.vduit) > 0)
141662306a36Sopenharmony_ci			snprintf(uid_string, sizeof(uid_string),
141762306a36Sopenharmony_ci				 "%s.%s.%04x.%s.%s",
141862306a36Sopenharmony_ci				 uid.vendor, uid.serial, uid.ssid, ua_string,
141962306a36Sopenharmony_ci				 uid.vduit);
142062306a36Sopenharmony_ci		else
142162306a36Sopenharmony_ci			snprintf(uid_string, sizeof(uid_string),
142262306a36Sopenharmony_ci				 "%s.%s.%04x.%s",
142362306a36Sopenharmony_ci				 uid.vendor, uid.serial, uid.ssid, ua_string);
142462306a36Sopenharmony_ci	}
142562306a36Sopenharmony_ci	dasd_put_device(device);
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", uid_string);
142862306a36Sopenharmony_ci}
142962306a36Sopenharmony_cistatic DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL);
143062306a36Sopenharmony_ci
143162306a36Sopenharmony_ci/*
143262306a36Sopenharmony_ci * extended error-reporting
143362306a36Sopenharmony_ci */
143462306a36Sopenharmony_cistatic ssize_t
143562306a36Sopenharmony_cidasd_eer_show(struct device *dev, struct device_attribute *attr, char *buf)
143662306a36Sopenharmony_ci{
143762306a36Sopenharmony_ci	struct dasd_devmap *devmap;
143862306a36Sopenharmony_ci	int eer_flag;
143962306a36Sopenharmony_ci
144062306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
144162306a36Sopenharmony_ci	if (!IS_ERR(devmap) && devmap->device)
144262306a36Sopenharmony_ci		eer_flag = dasd_eer_enabled(devmap->device);
144362306a36Sopenharmony_ci	else
144462306a36Sopenharmony_ci		eer_flag = 0;
144562306a36Sopenharmony_ci	return sysfs_emit(buf, eer_flag ? "1\n" : "0\n");
144662306a36Sopenharmony_ci}
144762306a36Sopenharmony_ci
144862306a36Sopenharmony_cistatic ssize_t
144962306a36Sopenharmony_cidasd_eer_store(struct device *dev, struct device_attribute *attr,
145062306a36Sopenharmony_ci	       const char *buf, size_t count)
145162306a36Sopenharmony_ci{
145262306a36Sopenharmony_ci	struct dasd_device *device;
145362306a36Sopenharmony_ci	unsigned int val;
145462306a36Sopenharmony_ci	int rc = 0;
145562306a36Sopenharmony_ci
145662306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
145762306a36Sopenharmony_ci	if (IS_ERR(device))
145862306a36Sopenharmony_ci		return PTR_ERR(device);
145962306a36Sopenharmony_ci
146062306a36Sopenharmony_ci	if (kstrtouint(buf, 0, &val) || val > 1)
146162306a36Sopenharmony_ci		return -EINVAL;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	if (val)
146462306a36Sopenharmony_ci		rc = dasd_eer_enable(device);
146562306a36Sopenharmony_ci	else
146662306a36Sopenharmony_ci		dasd_eer_disable(device);
146762306a36Sopenharmony_ci
146862306a36Sopenharmony_ci	dasd_put_device(device);
146962306a36Sopenharmony_ci
147062306a36Sopenharmony_ci	return rc ? : count;
147162306a36Sopenharmony_ci}
147262306a36Sopenharmony_ci
147362306a36Sopenharmony_cistatic DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci/*
147662306a36Sopenharmony_ci * aq_mask controls if the DASD should be quiesced on certain triggers
147762306a36Sopenharmony_ci * The aq_mask attribute is interpreted as bitmap of the DASD_EER_* triggers.
147862306a36Sopenharmony_ci */
147962306a36Sopenharmony_cistatic ssize_t dasd_aq_mask_show(struct device *dev, struct device_attribute *attr,
148062306a36Sopenharmony_ci				 char *buf)
148162306a36Sopenharmony_ci{
148262306a36Sopenharmony_ci	struct dasd_devmap *devmap;
148362306a36Sopenharmony_ci	unsigned int aq_mask = 0;
148462306a36Sopenharmony_ci
148562306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
148662306a36Sopenharmony_ci	if (!IS_ERR(devmap))
148762306a36Sopenharmony_ci		aq_mask = devmap->aq_mask;
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", aq_mask);
149062306a36Sopenharmony_ci}
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_cistatic ssize_t dasd_aq_mask_store(struct device *dev, struct device_attribute *attr,
149362306a36Sopenharmony_ci				  const char *buf, size_t count)
149462306a36Sopenharmony_ci{
149562306a36Sopenharmony_ci	struct dasd_devmap *devmap;
149662306a36Sopenharmony_ci	unsigned int val;
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	if (kstrtouint(buf, 0, &val) || val > DASD_EER_VALID)
149962306a36Sopenharmony_ci		return -EINVAL;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
150262306a36Sopenharmony_ci	if (IS_ERR(devmap))
150362306a36Sopenharmony_ci		return PTR_ERR(devmap);
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
150662306a36Sopenharmony_ci	devmap->aq_mask = val;
150762306a36Sopenharmony_ci	if (devmap->device)
150862306a36Sopenharmony_ci		devmap->device->aq_mask = devmap->aq_mask;
150962306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	return count;
151262306a36Sopenharmony_ci}
151362306a36Sopenharmony_ci
151462306a36Sopenharmony_cistatic DEVICE_ATTR(aq_mask, 0644, dasd_aq_mask_show, dasd_aq_mask_store);
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci/*
151762306a36Sopenharmony_ci * aq_requeue controls if requests are returned to the blocklayer on quiesce
151862306a36Sopenharmony_ci * or if requests are only not started
151962306a36Sopenharmony_ci */
152062306a36Sopenharmony_cistatic ssize_t dasd_aqr_show(struct device *dev, struct device_attribute *attr,
152162306a36Sopenharmony_ci			     char *buf)
152262306a36Sopenharmony_ci{
152362306a36Sopenharmony_ci	struct dasd_devmap *devmap;
152462306a36Sopenharmony_ci	int flag;
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
152762306a36Sopenharmony_ci	if (!IS_ERR(devmap))
152862306a36Sopenharmony_ci		flag = (devmap->features & DASD_FEATURE_REQUEUEQUIESCE) != 0;
152962306a36Sopenharmony_ci	else
153062306a36Sopenharmony_ci		flag = (DASD_FEATURE_DEFAULT &
153162306a36Sopenharmony_ci			DASD_FEATURE_REQUEUEQUIESCE) != 0;
153262306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", flag);
153362306a36Sopenharmony_ci}
153462306a36Sopenharmony_ci
153562306a36Sopenharmony_cistatic ssize_t dasd_aqr_store(struct device *dev, struct device_attribute *attr,
153662306a36Sopenharmony_ci			      const char *buf, size_t count)
153762306a36Sopenharmony_ci{
153862306a36Sopenharmony_ci	bool val;
153962306a36Sopenharmony_ci	int rc;
154062306a36Sopenharmony_ci
154162306a36Sopenharmony_ci	if (kstrtobool(buf, &val))
154262306a36Sopenharmony_ci		return -EINVAL;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	rc = dasd_set_feature(to_ccwdev(dev), DASD_FEATURE_REQUEUEQUIESCE, val);
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	return rc ? : count;
154762306a36Sopenharmony_ci}
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_cistatic DEVICE_ATTR(aq_requeue, 0644, dasd_aqr_show, dasd_aqr_store);
155062306a36Sopenharmony_ci
155162306a36Sopenharmony_ci/*
155262306a36Sopenharmony_ci * aq_timeouts controls how much retries have to time out until
155362306a36Sopenharmony_ci * a device gets autoquiesced
155462306a36Sopenharmony_ci */
155562306a36Sopenharmony_cistatic ssize_t
155662306a36Sopenharmony_cidasd_aq_timeouts_show(struct device *dev, struct device_attribute *attr,
155762306a36Sopenharmony_ci		      char *buf)
155862306a36Sopenharmony_ci{
155962306a36Sopenharmony_ci	struct dasd_device *device;
156062306a36Sopenharmony_ci	int len;
156162306a36Sopenharmony_ci
156262306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
156362306a36Sopenharmony_ci	if (IS_ERR(device))
156462306a36Sopenharmony_ci		return -ENODEV;
156562306a36Sopenharmony_ci	len = sysfs_emit(buf, "%u\n", device->aq_timeouts);
156662306a36Sopenharmony_ci	dasd_put_device(device);
156762306a36Sopenharmony_ci	return len;
156862306a36Sopenharmony_ci}
156962306a36Sopenharmony_ci
157062306a36Sopenharmony_cistatic ssize_t
157162306a36Sopenharmony_cidasd_aq_timeouts_store(struct device *dev, struct device_attribute *attr,
157262306a36Sopenharmony_ci		       const char *buf, size_t count)
157362306a36Sopenharmony_ci{
157462306a36Sopenharmony_ci	struct dasd_device *device;
157562306a36Sopenharmony_ci	unsigned int val;
157662306a36Sopenharmony_ci
157762306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
157862306a36Sopenharmony_ci	if (IS_ERR(device))
157962306a36Sopenharmony_ci		return -ENODEV;
158062306a36Sopenharmony_ci
158162306a36Sopenharmony_ci	if ((kstrtouint(buf, 10, &val) != 0) ||
158262306a36Sopenharmony_ci	    val > DASD_RETRIES_MAX || val == 0) {
158362306a36Sopenharmony_ci		dasd_put_device(device);
158462306a36Sopenharmony_ci		return -EINVAL;
158562306a36Sopenharmony_ci	}
158662306a36Sopenharmony_ci
158762306a36Sopenharmony_ci	if (val)
158862306a36Sopenharmony_ci		device->aq_timeouts = val;
158962306a36Sopenharmony_ci
159062306a36Sopenharmony_ci	dasd_put_device(device);
159162306a36Sopenharmony_ci	return count;
159262306a36Sopenharmony_ci}
159362306a36Sopenharmony_ci
159462306a36Sopenharmony_cistatic DEVICE_ATTR(aq_timeouts, 0644, dasd_aq_timeouts_show,
159562306a36Sopenharmony_ci		   dasd_aq_timeouts_store);
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci/*
159862306a36Sopenharmony_ci * expiration time for default requests
159962306a36Sopenharmony_ci */
160062306a36Sopenharmony_cistatic ssize_t
160162306a36Sopenharmony_cidasd_expires_show(struct device *dev, struct device_attribute *attr, char *buf)
160262306a36Sopenharmony_ci{
160362306a36Sopenharmony_ci	struct dasd_device *device;
160462306a36Sopenharmony_ci	int len;
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
160762306a36Sopenharmony_ci	if (IS_ERR(device))
160862306a36Sopenharmony_ci		return -ENODEV;
160962306a36Sopenharmony_ci	len = sysfs_emit(buf, "%lu\n", device->default_expires);
161062306a36Sopenharmony_ci	dasd_put_device(device);
161162306a36Sopenharmony_ci	return len;
161262306a36Sopenharmony_ci}
161362306a36Sopenharmony_ci
161462306a36Sopenharmony_cistatic ssize_t
161562306a36Sopenharmony_cidasd_expires_store(struct device *dev, struct device_attribute *attr,
161662306a36Sopenharmony_ci	       const char *buf, size_t count)
161762306a36Sopenharmony_ci{
161862306a36Sopenharmony_ci	struct dasd_device *device;
161962306a36Sopenharmony_ci	unsigned long val;
162062306a36Sopenharmony_ci
162162306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
162262306a36Sopenharmony_ci	if (IS_ERR(device))
162362306a36Sopenharmony_ci		return -ENODEV;
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	if ((kstrtoul(buf, 10, &val) != 0) ||
162662306a36Sopenharmony_ci	    (val > DASD_EXPIRES_MAX) || val == 0) {
162762306a36Sopenharmony_ci		dasd_put_device(device);
162862306a36Sopenharmony_ci		return -EINVAL;
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	if (val)
163262306a36Sopenharmony_ci		device->default_expires = val;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	dasd_put_device(device);
163562306a36Sopenharmony_ci	return count;
163662306a36Sopenharmony_ci}
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_cistatic DEVICE_ATTR(expires, 0644, dasd_expires_show, dasd_expires_store);
163962306a36Sopenharmony_ci
164062306a36Sopenharmony_cistatic ssize_t
164162306a36Sopenharmony_cidasd_retries_show(struct device *dev, struct device_attribute *attr, char *buf)
164262306a36Sopenharmony_ci{
164362306a36Sopenharmony_ci	struct dasd_device *device;
164462306a36Sopenharmony_ci	int len;
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
164762306a36Sopenharmony_ci	if (IS_ERR(device))
164862306a36Sopenharmony_ci		return -ENODEV;
164962306a36Sopenharmony_ci	len = sysfs_emit(buf, "%lu\n", device->default_retries);
165062306a36Sopenharmony_ci	dasd_put_device(device);
165162306a36Sopenharmony_ci	return len;
165262306a36Sopenharmony_ci}
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_cistatic ssize_t
165562306a36Sopenharmony_cidasd_retries_store(struct device *dev, struct device_attribute *attr,
165662306a36Sopenharmony_ci		   const char *buf, size_t count)
165762306a36Sopenharmony_ci{
165862306a36Sopenharmony_ci	struct dasd_device *device;
165962306a36Sopenharmony_ci	unsigned long val;
166062306a36Sopenharmony_ci
166162306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
166262306a36Sopenharmony_ci	if (IS_ERR(device))
166362306a36Sopenharmony_ci		return -ENODEV;
166462306a36Sopenharmony_ci
166562306a36Sopenharmony_ci	if ((kstrtoul(buf, 10, &val) != 0) ||
166662306a36Sopenharmony_ci	    (val > DASD_RETRIES_MAX)) {
166762306a36Sopenharmony_ci		dasd_put_device(device);
166862306a36Sopenharmony_ci		return -EINVAL;
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	if (val)
167262306a36Sopenharmony_ci		device->default_retries = val;
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_ci	dasd_put_device(device);
167562306a36Sopenharmony_ci	return count;
167662306a36Sopenharmony_ci}
167762306a36Sopenharmony_ci
167862306a36Sopenharmony_cistatic DEVICE_ATTR(retries, 0644, dasd_retries_show, dasd_retries_store);
167962306a36Sopenharmony_ci
168062306a36Sopenharmony_cistatic ssize_t
168162306a36Sopenharmony_cidasd_timeout_show(struct device *dev, struct device_attribute *attr,
168262306a36Sopenharmony_ci		  char *buf)
168362306a36Sopenharmony_ci{
168462306a36Sopenharmony_ci	struct dasd_device *device;
168562306a36Sopenharmony_ci	int len;
168662306a36Sopenharmony_ci
168762306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
168862306a36Sopenharmony_ci	if (IS_ERR(device))
168962306a36Sopenharmony_ci		return -ENODEV;
169062306a36Sopenharmony_ci	len = sysfs_emit(buf, "%lu\n", device->blk_timeout);
169162306a36Sopenharmony_ci	dasd_put_device(device);
169262306a36Sopenharmony_ci	return len;
169362306a36Sopenharmony_ci}
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_cistatic ssize_t
169662306a36Sopenharmony_cidasd_timeout_store(struct device *dev, struct device_attribute *attr,
169762306a36Sopenharmony_ci		   const char *buf, size_t count)
169862306a36Sopenharmony_ci{
169962306a36Sopenharmony_ci	struct dasd_device *device;
170062306a36Sopenharmony_ci	unsigned long val;
170162306a36Sopenharmony_ci
170262306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
170362306a36Sopenharmony_ci	if (IS_ERR(device) || !device->block)
170462306a36Sopenharmony_ci		return -ENODEV;
170562306a36Sopenharmony_ci
170662306a36Sopenharmony_ci	if ((kstrtoul(buf, 10, &val) != 0) ||
170762306a36Sopenharmony_ci	    val > UINT_MAX / HZ) {
170862306a36Sopenharmony_ci		dasd_put_device(device);
170962306a36Sopenharmony_ci		return -EINVAL;
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci	if (!device->block->gdp) {
171262306a36Sopenharmony_ci		dasd_put_device(device);
171362306a36Sopenharmony_ci		return -ENODEV;
171462306a36Sopenharmony_ci	}
171562306a36Sopenharmony_ci
171662306a36Sopenharmony_ci	device->blk_timeout = val;
171762306a36Sopenharmony_ci	blk_queue_rq_timeout(device->block->gdp->queue, val * HZ);
171862306a36Sopenharmony_ci
171962306a36Sopenharmony_ci	dasd_put_device(device);
172062306a36Sopenharmony_ci	return count;
172162306a36Sopenharmony_ci}
172262306a36Sopenharmony_ci
172362306a36Sopenharmony_cistatic DEVICE_ATTR(timeout, 0644,
172462306a36Sopenharmony_ci		   dasd_timeout_show, dasd_timeout_store);
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci
172762306a36Sopenharmony_cistatic ssize_t
172862306a36Sopenharmony_cidasd_path_reset_store(struct device *dev, struct device_attribute *attr,
172962306a36Sopenharmony_ci		      const char *buf, size_t count)
173062306a36Sopenharmony_ci{
173162306a36Sopenharmony_ci	struct dasd_device *device;
173262306a36Sopenharmony_ci	unsigned int val;
173362306a36Sopenharmony_ci
173462306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
173562306a36Sopenharmony_ci	if (IS_ERR(device))
173662306a36Sopenharmony_ci		return -ENODEV;
173762306a36Sopenharmony_ci
173862306a36Sopenharmony_ci	if ((kstrtouint(buf, 16, &val) != 0) || val > 0xff)
173962306a36Sopenharmony_ci		val = 0;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	if (device->discipline && device->discipline->reset_path)
174262306a36Sopenharmony_ci		device->discipline->reset_path(device, (__u8) val);
174362306a36Sopenharmony_ci
174462306a36Sopenharmony_ci	dasd_put_device(device);
174562306a36Sopenharmony_ci	return count;
174662306a36Sopenharmony_ci}
174762306a36Sopenharmony_ci
174862306a36Sopenharmony_cistatic DEVICE_ATTR(path_reset, 0200, NULL, dasd_path_reset_store);
174962306a36Sopenharmony_ci
175062306a36Sopenharmony_cistatic ssize_t dasd_hpf_show(struct device *dev, struct device_attribute *attr,
175162306a36Sopenharmony_ci			     char *buf)
175262306a36Sopenharmony_ci{
175362306a36Sopenharmony_ci	struct dasd_device *device;
175462306a36Sopenharmony_ci	int hpf;
175562306a36Sopenharmony_ci
175662306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
175762306a36Sopenharmony_ci	if (IS_ERR(device))
175862306a36Sopenharmony_ci		return -ENODEV;
175962306a36Sopenharmony_ci	if (!device->discipline || !device->discipline->hpf_enabled) {
176062306a36Sopenharmony_ci		dasd_put_device(device);
176162306a36Sopenharmony_ci		return sysfs_emit(buf, "%d\n", dasd_nofcx);
176262306a36Sopenharmony_ci	}
176362306a36Sopenharmony_ci	hpf = device->discipline->hpf_enabled(device);
176462306a36Sopenharmony_ci	dasd_put_device(device);
176562306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", hpf);
176662306a36Sopenharmony_ci}
176762306a36Sopenharmony_ci
176862306a36Sopenharmony_cistatic DEVICE_ATTR(hpf, 0444, dasd_hpf_show, NULL);
176962306a36Sopenharmony_ci
177062306a36Sopenharmony_cistatic ssize_t dasd_reservation_policy_show(struct device *dev,
177162306a36Sopenharmony_ci					    struct device_attribute *attr,
177262306a36Sopenharmony_ci					    char *buf)
177362306a36Sopenharmony_ci{
177462306a36Sopenharmony_ci	struct dasd_devmap *devmap;
177562306a36Sopenharmony_ci	int rc = 0;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
177862306a36Sopenharmony_ci	if (IS_ERR(devmap)) {
177962306a36Sopenharmony_ci		rc = sysfs_emit(buf, "ignore\n");
178062306a36Sopenharmony_ci	} else {
178162306a36Sopenharmony_ci		spin_lock(&dasd_devmap_lock);
178262306a36Sopenharmony_ci		if (devmap->features & DASD_FEATURE_FAILONSLCK)
178362306a36Sopenharmony_ci			rc = sysfs_emit(buf, "fail\n");
178462306a36Sopenharmony_ci		else
178562306a36Sopenharmony_ci			rc = sysfs_emit(buf, "ignore\n");
178662306a36Sopenharmony_ci		spin_unlock(&dasd_devmap_lock);
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci	return rc;
178962306a36Sopenharmony_ci}
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_cistatic ssize_t dasd_reservation_policy_store(struct device *dev,
179262306a36Sopenharmony_ci					     struct device_attribute *attr,
179362306a36Sopenharmony_ci					     const char *buf, size_t count)
179462306a36Sopenharmony_ci{
179562306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);
179662306a36Sopenharmony_ci	int rc;
179762306a36Sopenharmony_ci
179862306a36Sopenharmony_ci	if (sysfs_streq("ignore", buf))
179962306a36Sopenharmony_ci		rc = dasd_set_feature(cdev, DASD_FEATURE_FAILONSLCK, 0);
180062306a36Sopenharmony_ci	else if (sysfs_streq("fail", buf))
180162306a36Sopenharmony_ci		rc = dasd_set_feature(cdev, DASD_FEATURE_FAILONSLCK, 1);
180262306a36Sopenharmony_ci	else
180362306a36Sopenharmony_ci		rc = -EINVAL;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	return rc ? : count;
180662306a36Sopenharmony_ci}
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_cistatic DEVICE_ATTR(reservation_policy, 0644,
180962306a36Sopenharmony_ci		   dasd_reservation_policy_show, dasd_reservation_policy_store);
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_cistatic ssize_t dasd_reservation_state_show(struct device *dev,
181262306a36Sopenharmony_ci					   struct device_attribute *attr,
181362306a36Sopenharmony_ci					   char *buf)
181462306a36Sopenharmony_ci{
181562306a36Sopenharmony_ci	struct dasd_device *device;
181662306a36Sopenharmony_ci	int rc = 0;
181762306a36Sopenharmony_ci
181862306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
181962306a36Sopenharmony_ci	if (IS_ERR(device))
182062306a36Sopenharmony_ci		return sysfs_emit(buf, "none\n");
182162306a36Sopenharmony_ci
182262306a36Sopenharmony_ci	if (test_bit(DASD_FLAG_IS_RESERVED, &device->flags))
182362306a36Sopenharmony_ci		rc = sysfs_emit(buf, "reserved\n");
182462306a36Sopenharmony_ci	else if (test_bit(DASD_FLAG_LOCK_STOLEN, &device->flags))
182562306a36Sopenharmony_ci		rc = sysfs_emit(buf, "lost\n");
182662306a36Sopenharmony_ci	else
182762306a36Sopenharmony_ci		rc = sysfs_emit(buf, "none\n");
182862306a36Sopenharmony_ci	dasd_put_device(device);
182962306a36Sopenharmony_ci	return rc;
183062306a36Sopenharmony_ci}
183162306a36Sopenharmony_ci
183262306a36Sopenharmony_cistatic ssize_t dasd_reservation_state_store(struct device *dev,
183362306a36Sopenharmony_ci					    struct device_attribute *attr,
183462306a36Sopenharmony_ci					    const char *buf, size_t count)
183562306a36Sopenharmony_ci{
183662306a36Sopenharmony_ci	struct dasd_device *device;
183762306a36Sopenharmony_ci	int rc = 0;
183862306a36Sopenharmony_ci
183962306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
184062306a36Sopenharmony_ci	if (IS_ERR(device))
184162306a36Sopenharmony_ci		return -ENODEV;
184262306a36Sopenharmony_ci	if (sysfs_streq("reset", buf))
184362306a36Sopenharmony_ci		clear_bit(DASD_FLAG_LOCK_STOLEN, &device->flags);
184462306a36Sopenharmony_ci	else
184562306a36Sopenharmony_ci		rc = -EINVAL;
184662306a36Sopenharmony_ci	dasd_put_device(device);
184762306a36Sopenharmony_ci
184862306a36Sopenharmony_ci	if (rc)
184962306a36Sopenharmony_ci		return rc;
185062306a36Sopenharmony_ci	else
185162306a36Sopenharmony_ci		return count;
185262306a36Sopenharmony_ci}
185362306a36Sopenharmony_ci
185462306a36Sopenharmony_cistatic DEVICE_ATTR(last_known_reservation_state, 0644,
185562306a36Sopenharmony_ci		   dasd_reservation_state_show, dasd_reservation_state_store);
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_cistatic ssize_t dasd_pm_show(struct device *dev,
185862306a36Sopenharmony_ci			      struct device_attribute *attr, char *buf)
185962306a36Sopenharmony_ci{
186062306a36Sopenharmony_ci	struct dasd_device *device;
186162306a36Sopenharmony_ci	u8 opm, nppm, cablepm, cuirpm, hpfpm, ifccpm;
186262306a36Sopenharmony_ci
186362306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
186462306a36Sopenharmony_ci	if (IS_ERR(device))
186562306a36Sopenharmony_ci		return sprintf(buf, "0\n");
186662306a36Sopenharmony_ci
186762306a36Sopenharmony_ci	opm = dasd_path_get_opm(device);
186862306a36Sopenharmony_ci	nppm = dasd_path_get_nppm(device);
186962306a36Sopenharmony_ci	cablepm = dasd_path_get_cablepm(device);
187062306a36Sopenharmony_ci	cuirpm = dasd_path_get_cuirpm(device);
187162306a36Sopenharmony_ci	hpfpm = dasd_path_get_hpfpm(device);
187262306a36Sopenharmony_ci	ifccpm = dasd_path_get_ifccpm(device);
187362306a36Sopenharmony_ci	dasd_put_device(device);
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_ci	return sprintf(buf, "%02x %02x %02x %02x %02x %02x\n", opm, nppm,
187662306a36Sopenharmony_ci		       cablepm, cuirpm, hpfpm, ifccpm);
187762306a36Sopenharmony_ci}
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_cistatic DEVICE_ATTR(path_masks, 0444, dasd_pm_show, NULL);
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci/*
188262306a36Sopenharmony_ci * threshold value for IFCC/CCC errors
188362306a36Sopenharmony_ci */
188462306a36Sopenharmony_cistatic ssize_t
188562306a36Sopenharmony_cidasd_path_threshold_show(struct device *dev,
188662306a36Sopenharmony_ci			  struct device_attribute *attr, char *buf)
188762306a36Sopenharmony_ci{
188862306a36Sopenharmony_ci	struct dasd_device *device;
188962306a36Sopenharmony_ci	int len;
189062306a36Sopenharmony_ci
189162306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
189262306a36Sopenharmony_ci	if (IS_ERR(device))
189362306a36Sopenharmony_ci		return -ENODEV;
189462306a36Sopenharmony_ci	len = sysfs_emit(buf, "%lu\n", device->path_thrhld);
189562306a36Sopenharmony_ci	dasd_put_device(device);
189662306a36Sopenharmony_ci	return len;
189762306a36Sopenharmony_ci}
189862306a36Sopenharmony_ci
189962306a36Sopenharmony_cistatic ssize_t
190062306a36Sopenharmony_cidasd_path_threshold_store(struct device *dev, struct device_attribute *attr,
190162306a36Sopenharmony_ci			   const char *buf, size_t count)
190262306a36Sopenharmony_ci{
190362306a36Sopenharmony_ci	struct dasd_device *device;
190462306a36Sopenharmony_ci	unsigned long flags;
190562306a36Sopenharmony_ci	unsigned long val;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
190862306a36Sopenharmony_ci	if (IS_ERR(device))
190962306a36Sopenharmony_ci		return -ENODEV;
191062306a36Sopenharmony_ci
191162306a36Sopenharmony_ci	if (kstrtoul(buf, 10, &val) != 0 || val > DASD_THRHLD_MAX) {
191262306a36Sopenharmony_ci		dasd_put_device(device);
191362306a36Sopenharmony_ci		return -EINVAL;
191462306a36Sopenharmony_ci	}
191562306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(to_ccwdev(dev)), flags);
191662306a36Sopenharmony_ci	device->path_thrhld = val;
191762306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(to_ccwdev(dev)), flags);
191862306a36Sopenharmony_ci	dasd_put_device(device);
191962306a36Sopenharmony_ci	return count;
192062306a36Sopenharmony_ci}
192162306a36Sopenharmony_cistatic DEVICE_ATTR(path_threshold, 0644, dasd_path_threshold_show,
192262306a36Sopenharmony_ci		   dasd_path_threshold_store);
192362306a36Sopenharmony_ci
192462306a36Sopenharmony_ci/*
192562306a36Sopenharmony_ci * configure if path is disabled after IFCC/CCC error threshold is
192662306a36Sopenharmony_ci * exceeded
192762306a36Sopenharmony_ci */
192862306a36Sopenharmony_cistatic ssize_t
192962306a36Sopenharmony_cidasd_path_autodisable_show(struct device *dev,
193062306a36Sopenharmony_ci				   struct device_attribute *attr, char *buf)
193162306a36Sopenharmony_ci{
193262306a36Sopenharmony_ci	struct dasd_devmap *devmap;
193362306a36Sopenharmony_ci	int flag;
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
193662306a36Sopenharmony_ci	if (!IS_ERR(devmap))
193762306a36Sopenharmony_ci		flag = (devmap->features & DASD_FEATURE_PATH_AUTODISABLE) != 0;
193862306a36Sopenharmony_ci	else
193962306a36Sopenharmony_ci		flag = (DASD_FEATURE_DEFAULT &
194062306a36Sopenharmony_ci			DASD_FEATURE_PATH_AUTODISABLE) != 0;
194162306a36Sopenharmony_ci	return sysfs_emit(buf, flag ? "1\n" : "0\n");
194262306a36Sopenharmony_ci}
194362306a36Sopenharmony_ci
194462306a36Sopenharmony_cistatic ssize_t
194562306a36Sopenharmony_cidasd_path_autodisable_store(struct device *dev,
194662306a36Sopenharmony_ci				    struct device_attribute *attr,
194762306a36Sopenharmony_ci				    const char *buf, size_t count)
194862306a36Sopenharmony_ci{
194962306a36Sopenharmony_ci	unsigned int val;
195062306a36Sopenharmony_ci	int rc;
195162306a36Sopenharmony_ci
195262306a36Sopenharmony_ci	if (kstrtouint(buf, 0, &val) || val > 1)
195362306a36Sopenharmony_ci		return -EINVAL;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	rc = dasd_set_feature(to_ccwdev(dev),
195662306a36Sopenharmony_ci			      DASD_FEATURE_PATH_AUTODISABLE, val);
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	return rc ? : count;
195962306a36Sopenharmony_ci}
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_cistatic DEVICE_ATTR(path_autodisable, 0644,
196262306a36Sopenharmony_ci		   dasd_path_autodisable_show,
196362306a36Sopenharmony_ci		   dasd_path_autodisable_store);
196462306a36Sopenharmony_ci/*
196562306a36Sopenharmony_ci * interval for IFCC/CCC checks
196662306a36Sopenharmony_ci * meaning time with no IFCC/CCC error before the error counter
196762306a36Sopenharmony_ci * gets reset
196862306a36Sopenharmony_ci */
196962306a36Sopenharmony_cistatic ssize_t
197062306a36Sopenharmony_cidasd_path_interval_show(struct device *dev,
197162306a36Sopenharmony_ci			struct device_attribute *attr, char *buf)
197262306a36Sopenharmony_ci{
197362306a36Sopenharmony_ci	struct dasd_device *device;
197462306a36Sopenharmony_ci	int len;
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
197762306a36Sopenharmony_ci	if (IS_ERR(device))
197862306a36Sopenharmony_ci		return -ENODEV;
197962306a36Sopenharmony_ci	len = sysfs_emit(buf, "%lu\n", device->path_interval);
198062306a36Sopenharmony_ci	dasd_put_device(device);
198162306a36Sopenharmony_ci	return len;
198262306a36Sopenharmony_ci}
198362306a36Sopenharmony_ci
198462306a36Sopenharmony_cistatic ssize_t
198562306a36Sopenharmony_cidasd_path_interval_store(struct device *dev, struct device_attribute *attr,
198662306a36Sopenharmony_ci	       const char *buf, size_t count)
198762306a36Sopenharmony_ci{
198862306a36Sopenharmony_ci	struct dasd_device *device;
198962306a36Sopenharmony_ci	unsigned long flags;
199062306a36Sopenharmony_ci	unsigned long val;
199162306a36Sopenharmony_ci
199262306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
199362306a36Sopenharmony_ci	if (IS_ERR(device))
199462306a36Sopenharmony_ci		return -ENODEV;
199562306a36Sopenharmony_ci
199662306a36Sopenharmony_ci	if ((kstrtoul(buf, 10, &val) != 0) ||
199762306a36Sopenharmony_ci	    (val > DASD_INTERVAL_MAX) || val == 0) {
199862306a36Sopenharmony_ci		dasd_put_device(device);
199962306a36Sopenharmony_ci		return -EINVAL;
200062306a36Sopenharmony_ci	}
200162306a36Sopenharmony_ci	spin_lock_irqsave(get_ccwdev_lock(to_ccwdev(dev)), flags);
200262306a36Sopenharmony_ci	if (val)
200362306a36Sopenharmony_ci		device->path_interval = val;
200462306a36Sopenharmony_ci	spin_unlock_irqrestore(get_ccwdev_lock(to_ccwdev(dev)), flags);
200562306a36Sopenharmony_ci	dasd_put_device(device);
200662306a36Sopenharmony_ci	return count;
200762306a36Sopenharmony_ci}
200862306a36Sopenharmony_ci
200962306a36Sopenharmony_cistatic DEVICE_ATTR(path_interval, 0644, dasd_path_interval_show,
201062306a36Sopenharmony_ci		   dasd_path_interval_store);
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_cistatic ssize_t
201362306a36Sopenharmony_cidasd_device_fcs_show(struct device *dev, struct device_attribute *attr,
201462306a36Sopenharmony_ci		     char *buf)
201562306a36Sopenharmony_ci{
201662306a36Sopenharmony_ci	struct dasd_device *device;
201762306a36Sopenharmony_ci	int fc_sec;
201862306a36Sopenharmony_ci	int rc;
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
202162306a36Sopenharmony_ci	if (IS_ERR(device))
202262306a36Sopenharmony_ci		return -ENODEV;
202362306a36Sopenharmony_ci	fc_sec = dasd_path_get_fcs_device(device);
202462306a36Sopenharmony_ci	if (fc_sec == -EINVAL)
202562306a36Sopenharmony_ci		rc = sysfs_emit(buf, "Inconsistent\n");
202662306a36Sopenharmony_ci	else
202762306a36Sopenharmony_ci		rc = sysfs_emit(buf, "%s\n", dasd_path_get_fcs_str(fc_sec));
202862306a36Sopenharmony_ci	dasd_put_device(device);
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	return rc;
203162306a36Sopenharmony_ci}
203262306a36Sopenharmony_cistatic DEVICE_ATTR(fc_security, 0444, dasd_device_fcs_show, NULL);
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_cistatic ssize_t
203562306a36Sopenharmony_cidasd_path_fcs_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
203662306a36Sopenharmony_ci{
203762306a36Sopenharmony_ci	struct dasd_path *path = to_dasd_path(kobj);
203862306a36Sopenharmony_ci	unsigned int fc_sec = path->fc_security;
203962306a36Sopenharmony_ci
204062306a36Sopenharmony_ci	return sysfs_emit(buf, "%s\n", dasd_path_get_fcs_str(fc_sec));
204162306a36Sopenharmony_ci}
204262306a36Sopenharmony_ci
204362306a36Sopenharmony_cistatic struct kobj_attribute path_fcs_attribute =
204462306a36Sopenharmony_ci	__ATTR(fc_security, 0444, dasd_path_fcs_show, NULL);
204562306a36Sopenharmony_ci
204662306a36Sopenharmony_ci/*
204762306a36Sopenharmony_ci * print copy relation in the form
204862306a36Sopenharmony_ci * primary,secondary[1] primary,secondary[2], ...
204962306a36Sopenharmony_ci */
205062306a36Sopenharmony_cistatic ssize_t
205162306a36Sopenharmony_cidasd_copy_pair_show(struct device *dev,
205262306a36Sopenharmony_ci		    struct device_attribute *attr, char *buf)
205362306a36Sopenharmony_ci{
205462306a36Sopenharmony_ci	char prim_busid[DASD_BUS_ID_SIZE];
205562306a36Sopenharmony_ci	struct dasd_copy_relation *copy;
205662306a36Sopenharmony_ci	struct dasd_devmap *devmap;
205762306a36Sopenharmony_ci	int len = 0;
205862306a36Sopenharmony_ci	int i;
205962306a36Sopenharmony_ci
206062306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(dev));
206162306a36Sopenharmony_ci	if (IS_ERR(devmap))
206262306a36Sopenharmony_ci		return -ENODEV;
206362306a36Sopenharmony_ci
206462306a36Sopenharmony_ci	if (!devmap->copy)
206562306a36Sopenharmony_ci		return -ENODEV;
206662306a36Sopenharmony_ci
206762306a36Sopenharmony_ci	copy = devmap->copy;
206862306a36Sopenharmony_ci	/* find primary */
206962306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
207062306a36Sopenharmony_ci		if (copy->entry[i].configured && copy->entry[i].primary) {
207162306a36Sopenharmony_ci			strscpy(prim_busid, copy->entry[i].busid,
207262306a36Sopenharmony_ci				DASD_BUS_ID_SIZE);
207362306a36Sopenharmony_ci			break;
207462306a36Sopenharmony_ci		}
207562306a36Sopenharmony_ci	}
207662306a36Sopenharmony_ci	if (i == DASD_CP_ENTRIES)
207762306a36Sopenharmony_ci		goto out;
207862306a36Sopenharmony_ci
207962306a36Sopenharmony_ci	/* print all secondary */
208062306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
208162306a36Sopenharmony_ci		if (copy->entry[i].configured && !copy->entry[i].primary)
208262306a36Sopenharmony_ci			len += sysfs_emit_at(buf, len, "%s,%s ", prim_busid,
208362306a36Sopenharmony_ci					     copy->entry[i].busid);
208462306a36Sopenharmony_ci	}
208562306a36Sopenharmony_ci
208662306a36Sopenharmony_ci	len += sysfs_emit_at(buf, len, "\n");
208762306a36Sopenharmony_ciout:
208862306a36Sopenharmony_ci	return len;
208962306a36Sopenharmony_ci}
209062306a36Sopenharmony_ci
209162306a36Sopenharmony_cistatic int dasd_devmap_set_copy_relation(struct dasd_devmap *devmap,
209262306a36Sopenharmony_ci					 struct dasd_copy_relation *copy,
209362306a36Sopenharmony_ci					 char *busid, bool primary)
209462306a36Sopenharmony_ci{
209562306a36Sopenharmony_ci	int i;
209662306a36Sopenharmony_ci
209762306a36Sopenharmony_ci	/* find free entry */
209862306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
209962306a36Sopenharmony_ci		/* current bus_id already included, nothing to do */
210062306a36Sopenharmony_ci		if (copy->entry[i].configured &&
210162306a36Sopenharmony_ci		    strncmp(copy->entry[i].busid, busid, DASD_BUS_ID_SIZE) == 0)
210262306a36Sopenharmony_ci			return 0;
210362306a36Sopenharmony_ci
210462306a36Sopenharmony_ci		if (!copy->entry[i].configured)
210562306a36Sopenharmony_ci			break;
210662306a36Sopenharmony_ci	}
210762306a36Sopenharmony_ci	if (i == DASD_CP_ENTRIES)
210862306a36Sopenharmony_ci		return -EINVAL;
210962306a36Sopenharmony_ci
211062306a36Sopenharmony_ci	copy->entry[i].configured = true;
211162306a36Sopenharmony_ci	strscpy(copy->entry[i].busid, busid, DASD_BUS_ID_SIZE);
211262306a36Sopenharmony_ci	if (primary) {
211362306a36Sopenharmony_ci		copy->active = &copy->entry[i];
211462306a36Sopenharmony_ci		copy->entry[i].primary = true;
211562306a36Sopenharmony_ci	}
211662306a36Sopenharmony_ci	if (!devmap->copy)
211762306a36Sopenharmony_ci		devmap->copy = copy;
211862306a36Sopenharmony_ci
211962306a36Sopenharmony_ci	return 0;
212062306a36Sopenharmony_ci}
212162306a36Sopenharmony_ci
212262306a36Sopenharmony_cistatic void dasd_devmap_del_copy_relation(struct dasd_copy_relation *copy,
212362306a36Sopenharmony_ci					  char *busid)
212462306a36Sopenharmony_ci{
212562306a36Sopenharmony_ci	int i;
212662306a36Sopenharmony_ci
212762306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
212862306a36Sopenharmony_ci	/* find entry */
212962306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
213062306a36Sopenharmony_ci		if (copy->entry[i].configured &&
213162306a36Sopenharmony_ci		    strncmp(copy->entry[i].busid, busid, DASD_BUS_ID_SIZE) == 0)
213262306a36Sopenharmony_ci			break;
213362306a36Sopenharmony_ci	}
213462306a36Sopenharmony_ci	if (i == DASD_CP_ENTRIES || !copy->entry[i].configured) {
213562306a36Sopenharmony_ci		spin_unlock(&dasd_devmap_lock);
213662306a36Sopenharmony_ci		return;
213762306a36Sopenharmony_ci	}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ci	copy->entry[i].configured = false;
214062306a36Sopenharmony_ci	memset(copy->entry[i].busid, 0, DASD_BUS_ID_SIZE);
214162306a36Sopenharmony_ci	if (copy->active == &copy->entry[i]) {
214262306a36Sopenharmony_ci		copy->active = NULL;
214362306a36Sopenharmony_ci		copy->entry[i].primary = false;
214462306a36Sopenharmony_ci	}
214562306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
214662306a36Sopenharmony_ci}
214762306a36Sopenharmony_ci
214862306a36Sopenharmony_cistatic int dasd_devmap_clear_copy_relation(struct device *dev)
214962306a36Sopenharmony_ci{
215062306a36Sopenharmony_ci	struct dasd_copy_relation *copy;
215162306a36Sopenharmony_ci	struct dasd_devmap *devmap;
215262306a36Sopenharmony_ci	int i, rc = 1;
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
215562306a36Sopenharmony_ci	if (IS_ERR(devmap))
215662306a36Sopenharmony_ci		return 1;
215762306a36Sopenharmony_ci
215862306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
215962306a36Sopenharmony_ci	if (!devmap->copy)
216062306a36Sopenharmony_ci		goto out;
216162306a36Sopenharmony_ci
216262306a36Sopenharmony_ci	copy = devmap->copy;
216362306a36Sopenharmony_ci	/* first check if all secondary devices are offline*/
216462306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
216562306a36Sopenharmony_ci		if (!copy->entry[i].configured)
216662306a36Sopenharmony_ci			continue;
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci		if (copy->entry[i].device == copy->active->device)
216962306a36Sopenharmony_ci			continue;
217062306a36Sopenharmony_ci
217162306a36Sopenharmony_ci		if (copy->entry[i].device)
217262306a36Sopenharmony_ci			goto out;
217362306a36Sopenharmony_ci	}
217462306a36Sopenharmony_ci	/* clear all devmap entries */
217562306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
217662306a36Sopenharmony_ci		if (strlen(copy->entry[i].busid) == 0)
217762306a36Sopenharmony_ci			continue;
217862306a36Sopenharmony_ci		if (copy->entry[i].device) {
217962306a36Sopenharmony_ci			dasd_put_device(copy->entry[i].device);
218062306a36Sopenharmony_ci			copy->entry[i].device->copy = NULL;
218162306a36Sopenharmony_ci			copy->entry[i].device = NULL;
218262306a36Sopenharmony_ci		}
218362306a36Sopenharmony_ci		devmap = dasd_find_busid_locked(copy->entry[i].busid);
218462306a36Sopenharmony_ci		devmap->copy = NULL;
218562306a36Sopenharmony_ci		memset(copy->entry[i].busid, 0, DASD_BUS_ID_SIZE);
218662306a36Sopenharmony_ci	}
218762306a36Sopenharmony_ci	kfree(copy);
218862306a36Sopenharmony_ci	rc = 0;
218962306a36Sopenharmony_ciout:
219062306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
219162306a36Sopenharmony_ci	return rc;
219262306a36Sopenharmony_ci}
219362306a36Sopenharmony_ci
219462306a36Sopenharmony_ci/*
219562306a36Sopenharmony_ci * parse BUSIDs from a copy pair
219662306a36Sopenharmony_ci */
219762306a36Sopenharmony_cistatic int dasd_devmap_parse_busid(const char *buf, char *prim_busid,
219862306a36Sopenharmony_ci				   char *sec_busid)
219962306a36Sopenharmony_ci{
220062306a36Sopenharmony_ci	char *primary, *secondary, *tmp, *pt;
220162306a36Sopenharmony_ci	int id0, id1, id2;
220262306a36Sopenharmony_ci
220362306a36Sopenharmony_ci	pt =  kstrdup(buf, GFP_KERNEL);
220462306a36Sopenharmony_ci	tmp = pt;
220562306a36Sopenharmony_ci	if (!tmp)
220662306a36Sopenharmony_ci		return -ENOMEM;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	primary = strsep(&tmp, ",");
220962306a36Sopenharmony_ci	if (!primary) {
221062306a36Sopenharmony_ci		kfree(pt);
221162306a36Sopenharmony_ci		return -EINVAL;
221262306a36Sopenharmony_ci	}
221362306a36Sopenharmony_ci	secondary = strsep(&tmp, ",");
221462306a36Sopenharmony_ci	if (!secondary) {
221562306a36Sopenharmony_ci		kfree(pt);
221662306a36Sopenharmony_ci		return -EINVAL;
221762306a36Sopenharmony_ci	}
221862306a36Sopenharmony_ci	if (dasd_busid(primary, &id0, &id1, &id2)) {
221962306a36Sopenharmony_ci		kfree(pt);
222062306a36Sopenharmony_ci		return -EINVAL;
222162306a36Sopenharmony_ci	}
222262306a36Sopenharmony_ci	sprintf(prim_busid, "%01x.%01x.%04x", id0, id1, id2);
222362306a36Sopenharmony_ci	if (dasd_busid(secondary, &id0, &id1, &id2)) {
222462306a36Sopenharmony_ci		kfree(pt);
222562306a36Sopenharmony_ci		return -EINVAL;
222662306a36Sopenharmony_ci	}
222762306a36Sopenharmony_ci	sprintf(sec_busid, "%01x.%01x.%04x", id0, id1, id2);
222862306a36Sopenharmony_ci	kfree(pt);
222962306a36Sopenharmony_ci
223062306a36Sopenharmony_ci	return 0;
223162306a36Sopenharmony_ci}
223262306a36Sopenharmony_ci
223362306a36Sopenharmony_cistatic ssize_t dasd_copy_pair_store(struct device *dev,
223462306a36Sopenharmony_ci				    struct device_attribute *attr,
223562306a36Sopenharmony_ci				    const char *buf, size_t count)
223662306a36Sopenharmony_ci{
223762306a36Sopenharmony_ci	struct dasd_devmap *prim_devmap, *sec_devmap;
223862306a36Sopenharmony_ci	char prim_busid[DASD_BUS_ID_SIZE];
223962306a36Sopenharmony_ci	char sec_busid[DASD_BUS_ID_SIZE];
224062306a36Sopenharmony_ci	struct dasd_copy_relation *copy;
224162306a36Sopenharmony_ci	struct dasd_device *device;
224262306a36Sopenharmony_ci	bool pprc_enabled;
224362306a36Sopenharmony_ci	int rc;
224462306a36Sopenharmony_ci
224562306a36Sopenharmony_ci	if (strncmp(buf, "clear", strlen("clear")) == 0) {
224662306a36Sopenharmony_ci		if (dasd_devmap_clear_copy_relation(dev))
224762306a36Sopenharmony_ci			return -EINVAL;
224862306a36Sopenharmony_ci		return count;
224962306a36Sopenharmony_ci	}
225062306a36Sopenharmony_ci
225162306a36Sopenharmony_ci	rc = dasd_devmap_parse_busid(buf, prim_busid, sec_busid);
225262306a36Sopenharmony_ci	if (rc)
225362306a36Sopenharmony_ci		return rc;
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	if (strncmp(dev_name(dev), prim_busid, DASD_BUS_ID_SIZE) != 0 &&
225662306a36Sopenharmony_ci	    strncmp(dev_name(dev), sec_busid, DASD_BUS_ID_SIZE) != 0)
225762306a36Sopenharmony_ci		return -EINVAL;
225862306a36Sopenharmony_ci
225962306a36Sopenharmony_ci	/* allocate primary devmap if needed */
226062306a36Sopenharmony_ci	prim_devmap = dasd_find_busid(prim_busid);
226162306a36Sopenharmony_ci	if (IS_ERR(prim_devmap))
226262306a36Sopenharmony_ci		prim_devmap = dasd_add_busid(prim_busid, DASD_FEATURE_DEFAULT);
226362306a36Sopenharmony_ci
226462306a36Sopenharmony_ci	/* allocate secondary devmap if needed */
226562306a36Sopenharmony_ci	sec_devmap = dasd_find_busid(sec_busid);
226662306a36Sopenharmony_ci	if (IS_ERR(sec_devmap))
226762306a36Sopenharmony_ci		sec_devmap = dasd_add_busid(sec_busid, DASD_FEATURE_DEFAULT);
226862306a36Sopenharmony_ci
226962306a36Sopenharmony_ci	/* setting copy relation is only allowed for offline secondary */
227062306a36Sopenharmony_ci	if (sec_devmap->device)
227162306a36Sopenharmony_ci		return -EINVAL;
227262306a36Sopenharmony_ci
227362306a36Sopenharmony_ci	if (prim_devmap->copy) {
227462306a36Sopenharmony_ci		copy = prim_devmap->copy;
227562306a36Sopenharmony_ci	} else if (sec_devmap->copy) {
227662306a36Sopenharmony_ci		copy = sec_devmap->copy;
227762306a36Sopenharmony_ci	} else {
227862306a36Sopenharmony_ci		copy = kzalloc(sizeof(*copy), GFP_KERNEL);
227962306a36Sopenharmony_ci		if (!copy)
228062306a36Sopenharmony_ci			return -ENOMEM;
228162306a36Sopenharmony_ci	}
228262306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
228362306a36Sopenharmony_ci	rc = dasd_devmap_set_copy_relation(prim_devmap, copy, prim_busid, true);
228462306a36Sopenharmony_ci	if (rc) {
228562306a36Sopenharmony_ci		spin_unlock(&dasd_devmap_lock);
228662306a36Sopenharmony_ci		return rc;
228762306a36Sopenharmony_ci	}
228862306a36Sopenharmony_ci	rc = dasd_devmap_set_copy_relation(sec_devmap, copy, sec_busid, false);
228962306a36Sopenharmony_ci	if (rc) {
229062306a36Sopenharmony_ci		spin_unlock(&dasd_devmap_lock);
229162306a36Sopenharmony_ci		return rc;
229262306a36Sopenharmony_ci	}
229362306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
229462306a36Sopenharmony_ci
229562306a36Sopenharmony_ci	/* if primary device is already online call device setup directly */
229662306a36Sopenharmony_ci	if (prim_devmap->device && !prim_devmap->device->copy) {
229762306a36Sopenharmony_ci		device = prim_devmap->device;
229862306a36Sopenharmony_ci		if (device->discipline->pprc_enabled) {
229962306a36Sopenharmony_ci			pprc_enabled = device->discipline->pprc_enabled(device);
230062306a36Sopenharmony_ci			rc = dasd_devmap_set_device_copy_relation(device->cdev,
230162306a36Sopenharmony_ci								  pprc_enabled);
230262306a36Sopenharmony_ci		} else {
230362306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
230462306a36Sopenharmony_ci		}
230562306a36Sopenharmony_ci	}
230662306a36Sopenharmony_ci	if (rc) {
230762306a36Sopenharmony_ci		dasd_devmap_del_copy_relation(copy, prim_busid);
230862306a36Sopenharmony_ci		dasd_devmap_del_copy_relation(copy, sec_busid);
230962306a36Sopenharmony_ci		count = rc;
231062306a36Sopenharmony_ci	}
231162306a36Sopenharmony_ci
231262306a36Sopenharmony_ci	return count;
231362306a36Sopenharmony_ci}
231462306a36Sopenharmony_cistatic DEVICE_ATTR(copy_pair, 0644, dasd_copy_pair_show,
231562306a36Sopenharmony_ci		   dasd_copy_pair_store);
231662306a36Sopenharmony_ci
231762306a36Sopenharmony_cistatic ssize_t
231862306a36Sopenharmony_cidasd_copy_role_show(struct device *dev,
231962306a36Sopenharmony_ci		    struct device_attribute *attr, char *buf)
232062306a36Sopenharmony_ci{
232162306a36Sopenharmony_ci	struct dasd_copy_relation *copy;
232262306a36Sopenharmony_ci	struct dasd_device *device;
232362306a36Sopenharmony_ci	int len, i;
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
232662306a36Sopenharmony_ci	if (IS_ERR(device))
232762306a36Sopenharmony_ci		return -ENODEV;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci	if (!device->copy) {
233062306a36Sopenharmony_ci		len = sysfs_emit(buf, "none\n");
233162306a36Sopenharmony_ci		goto out;
233262306a36Sopenharmony_ci	}
233362306a36Sopenharmony_ci	copy = device->copy;
233462306a36Sopenharmony_ci	/* only the active device is primary */
233562306a36Sopenharmony_ci	if (copy->active->device == device) {
233662306a36Sopenharmony_ci		len = sysfs_emit(buf, "primary\n");
233762306a36Sopenharmony_ci		goto out;
233862306a36Sopenharmony_ci	}
233962306a36Sopenharmony_ci	for (i = 0; i < DASD_CP_ENTRIES; i++) {
234062306a36Sopenharmony_ci		if (copy->entry[i].device == device) {
234162306a36Sopenharmony_ci			len = sysfs_emit(buf, "secondary\n");
234262306a36Sopenharmony_ci			goto out;
234362306a36Sopenharmony_ci		}
234462306a36Sopenharmony_ci	}
234562306a36Sopenharmony_ci	/* not in the list, no COPY role */
234662306a36Sopenharmony_ci	len = sysfs_emit(buf, "none\n");
234762306a36Sopenharmony_ciout:
234862306a36Sopenharmony_ci	dasd_put_device(device);
234962306a36Sopenharmony_ci	return len;
235062306a36Sopenharmony_ci}
235162306a36Sopenharmony_cistatic DEVICE_ATTR(copy_role, 0444, dasd_copy_role_show, NULL);
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_cistatic ssize_t dasd_device_ping(struct device *dev,
235462306a36Sopenharmony_ci				struct device_attribute *attr,
235562306a36Sopenharmony_ci				const char *buf, size_t count)
235662306a36Sopenharmony_ci{
235762306a36Sopenharmony_ci	struct dasd_device *device;
235862306a36Sopenharmony_ci	size_t rc;
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci	device = dasd_device_from_cdev(to_ccwdev(dev));
236162306a36Sopenharmony_ci	if (IS_ERR(device))
236262306a36Sopenharmony_ci		return -ENODEV;
236362306a36Sopenharmony_ci
236462306a36Sopenharmony_ci	/*
236562306a36Sopenharmony_ci	 * do not try during offline processing
236662306a36Sopenharmony_ci	 * early check only
236762306a36Sopenharmony_ci	 * the sleep_on function itself checks for offline
236862306a36Sopenharmony_ci	 * processing again
236962306a36Sopenharmony_ci	 */
237062306a36Sopenharmony_ci	if (test_bit(DASD_FLAG_OFFLINE, &device->flags)) {
237162306a36Sopenharmony_ci		rc = -EBUSY;
237262306a36Sopenharmony_ci		goto out;
237362306a36Sopenharmony_ci	}
237462306a36Sopenharmony_ci	if (!device->discipline || !device->discipline->device_ping) {
237562306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
237662306a36Sopenharmony_ci		goto out;
237762306a36Sopenharmony_ci	}
237862306a36Sopenharmony_ci	rc = device->discipline->device_ping(device);
237962306a36Sopenharmony_ci	if (!rc)
238062306a36Sopenharmony_ci		rc = count;
238162306a36Sopenharmony_ciout:
238262306a36Sopenharmony_ci	dasd_put_device(device);
238362306a36Sopenharmony_ci	return rc;
238462306a36Sopenharmony_ci}
238562306a36Sopenharmony_cistatic DEVICE_ATTR(ping, 0200, NULL, dasd_device_ping);
238662306a36Sopenharmony_ci
238762306a36Sopenharmony_ci#define DASD_DEFINE_ATTR(_name, _func)					\
238862306a36Sopenharmony_cistatic ssize_t dasd_##_name##_show(struct device *dev,			\
238962306a36Sopenharmony_ci				   struct device_attribute *attr,	\
239062306a36Sopenharmony_ci				   char *buf)				\
239162306a36Sopenharmony_ci{									\
239262306a36Sopenharmony_ci	struct ccw_device *cdev = to_ccwdev(dev);			\
239362306a36Sopenharmony_ci	struct dasd_device *device = dasd_device_from_cdev(cdev);	\
239462306a36Sopenharmony_ci	int val = 0;							\
239562306a36Sopenharmony_ci									\
239662306a36Sopenharmony_ci	if (IS_ERR(device))						\
239762306a36Sopenharmony_ci		return -ENODEV;						\
239862306a36Sopenharmony_ci	if (device->discipline && _func)				\
239962306a36Sopenharmony_ci		val = _func(device);					\
240062306a36Sopenharmony_ci	dasd_put_device(device);					\
240162306a36Sopenharmony_ci									\
240262306a36Sopenharmony_ci	return sysfs_emit(buf, "%d\n", val);			\
240362306a36Sopenharmony_ci}									\
240462306a36Sopenharmony_cistatic DEVICE_ATTR(_name, 0444, dasd_##_name##_show, NULL);		\
240562306a36Sopenharmony_ci
240662306a36Sopenharmony_ciDASD_DEFINE_ATTR(ese, device->discipline->is_ese);
240762306a36Sopenharmony_ciDASD_DEFINE_ATTR(extent_size, device->discipline->ext_size);
240862306a36Sopenharmony_ciDASD_DEFINE_ATTR(pool_id, device->discipline->ext_pool_id);
240962306a36Sopenharmony_ciDASD_DEFINE_ATTR(space_configured, device->discipline->space_configured);
241062306a36Sopenharmony_ciDASD_DEFINE_ATTR(space_allocated, device->discipline->space_allocated);
241162306a36Sopenharmony_ciDASD_DEFINE_ATTR(logical_capacity, device->discipline->logical_capacity);
241262306a36Sopenharmony_ciDASD_DEFINE_ATTR(warn_threshold, device->discipline->ext_pool_warn_thrshld);
241362306a36Sopenharmony_ciDASD_DEFINE_ATTR(cap_at_warnlevel, device->discipline->ext_pool_cap_at_warnlevel);
241462306a36Sopenharmony_ciDASD_DEFINE_ATTR(pool_oos, device->discipline->ext_pool_oos);
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_cistatic struct attribute * dasd_attrs[] = {
241762306a36Sopenharmony_ci	&dev_attr_readonly.attr,
241862306a36Sopenharmony_ci	&dev_attr_discipline.attr,
241962306a36Sopenharmony_ci	&dev_attr_status.attr,
242062306a36Sopenharmony_ci	&dev_attr_alias.attr,
242162306a36Sopenharmony_ci	&dev_attr_vendor.attr,
242262306a36Sopenharmony_ci	&dev_attr_uid.attr,
242362306a36Sopenharmony_ci	&dev_attr_use_diag.attr,
242462306a36Sopenharmony_ci	&dev_attr_raw_track_access.attr,
242562306a36Sopenharmony_ci	&dev_attr_eer_enabled.attr,
242662306a36Sopenharmony_ci	&dev_attr_erplog.attr,
242762306a36Sopenharmony_ci	&dev_attr_failfast.attr,
242862306a36Sopenharmony_ci	&dev_attr_expires.attr,
242962306a36Sopenharmony_ci	&dev_attr_retries.attr,
243062306a36Sopenharmony_ci	&dev_attr_timeout.attr,
243162306a36Sopenharmony_ci	&dev_attr_reservation_policy.attr,
243262306a36Sopenharmony_ci	&dev_attr_last_known_reservation_state.attr,
243362306a36Sopenharmony_ci	&dev_attr_safe_offline.attr,
243462306a36Sopenharmony_ci	&dev_attr_host_access_count.attr,
243562306a36Sopenharmony_ci	&dev_attr_path_masks.attr,
243662306a36Sopenharmony_ci	&dev_attr_path_threshold.attr,
243762306a36Sopenharmony_ci	&dev_attr_path_autodisable.attr,
243862306a36Sopenharmony_ci	&dev_attr_path_interval.attr,
243962306a36Sopenharmony_ci	&dev_attr_path_reset.attr,
244062306a36Sopenharmony_ci	&dev_attr_hpf.attr,
244162306a36Sopenharmony_ci	&dev_attr_ese.attr,
244262306a36Sopenharmony_ci	&dev_attr_fc_security.attr,
244362306a36Sopenharmony_ci	&dev_attr_copy_pair.attr,
244462306a36Sopenharmony_ci	&dev_attr_copy_role.attr,
244562306a36Sopenharmony_ci	&dev_attr_ping.attr,
244662306a36Sopenharmony_ci	&dev_attr_aq_mask.attr,
244762306a36Sopenharmony_ci	&dev_attr_aq_requeue.attr,
244862306a36Sopenharmony_ci	&dev_attr_aq_timeouts.attr,
244962306a36Sopenharmony_ci	NULL,
245062306a36Sopenharmony_ci};
245162306a36Sopenharmony_ci
245262306a36Sopenharmony_cistatic const struct attribute_group dasd_attr_group = {
245362306a36Sopenharmony_ci	.attrs = dasd_attrs,
245462306a36Sopenharmony_ci};
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_cistatic struct attribute *capacity_attrs[] = {
245762306a36Sopenharmony_ci	&dev_attr_space_configured.attr,
245862306a36Sopenharmony_ci	&dev_attr_space_allocated.attr,
245962306a36Sopenharmony_ci	&dev_attr_logical_capacity.attr,
246062306a36Sopenharmony_ci	NULL,
246162306a36Sopenharmony_ci};
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_cistatic const struct attribute_group capacity_attr_group = {
246462306a36Sopenharmony_ci	.name = "capacity",
246562306a36Sopenharmony_ci	.attrs = capacity_attrs,
246662306a36Sopenharmony_ci};
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_cistatic struct attribute *ext_pool_attrs[] = {
246962306a36Sopenharmony_ci	&dev_attr_pool_id.attr,
247062306a36Sopenharmony_ci	&dev_attr_extent_size.attr,
247162306a36Sopenharmony_ci	&dev_attr_warn_threshold.attr,
247262306a36Sopenharmony_ci	&dev_attr_cap_at_warnlevel.attr,
247362306a36Sopenharmony_ci	&dev_attr_pool_oos.attr,
247462306a36Sopenharmony_ci	NULL,
247562306a36Sopenharmony_ci};
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_cistatic const struct attribute_group ext_pool_attr_group = {
247862306a36Sopenharmony_ci	.name = "extent_pool",
247962306a36Sopenharmony_ci	.attrs = ext_pool_attrs,
248062306a36Sopenharmony_ci};
248162306a36Sopenharmony_ci
248262306a36Sopenharmony_ciconst struct attribute_group *dasd_dev_groups[] = {
248362306a36Sopenharmony_ci	&dasd_attr_group,
248462306a36Sopenharmony_ci	&capacity_attr_group,
248562306a36Sopenharmony_ci	&ext_pool_attr_group,
248662306a36Sopenharmony_ci	NULL,
248762306a36Sopenharmony_ci};
248862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(dasd_dev_groups);
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci/*
249162306a36Sopenharmony_ci * Return value of the specified feature.
249262306a36Sopenharmony_ci */
249362306a36Sopenharmony_ciint
249462306a36Sopenharmony_cidasd_get_feature(struct ccw_device *cdev, int feature)
249562306a36Sopenharmony_ci{
249662306a36Sopenharmony_ci	struct dasd_devmap *devmap;
249762306a36Sopenharmony_ci
249862306a36Sopenharmony_ci	devmap = dasd_find_busid(dev_name(&cdev->dev));
249962306a36Sopenharmony_ci	if (IS_ERR(devmap))
250062306a36Sopenharmony_ci		return PTR_ERR(devmap);
250162306a36Sopenharmony_ci
250262306a36Sopenharmony_ci	return ((devmap->features & feature) != 0);
250362306a36Sopenharmony_ci}
250462306a36Sopenharmony_ci
250562306a36Sopenharmony_ci/*
250662306a36Sopenharmony_ci * Set / reset given feature.
250762306a36Sopenharmony_ci * Flag indicates whether to set (!=0) or the reset (=0) the feature.
250862306a36Sopenharmony_ci */
250962306a36Sopenharmony_ciint
251062306a36Sopenharmony_cidasd_set_feature(struct ccw_device *cdev, int feature, int flag)
251162306a36Sopenharmony_ci{
251262306a36Sopenharmony_ci	struct dasd_devmap *devmap;
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci	devmap = dasd_devmap_from_cdev(cdev);
251562306a36Sopenharmony_ci	if (IS_ERR(devmap))
251662306a36Sopenharmony_ci		return PTR_ERR(devmap);
251762306a36Sopenharmony_ci
251862306a36Sopenharmony_ci	spin_lock(&dasd_devmap_lock);
251962306a36Sopenharmony_ci	if (flag)
252062306a36Sopenharmony_ci		devmap->features |= feature;
252162306a36Sopenharmony_ci	else
252262306a36Sopenharmony_ci		devmap->features &= ~feature;
252362306a36Sopenharmony_ci	if (devmap->device)
252462306a36Sopenharmony_ci		devmap->device->features = devmap->features;
252562306a36Sopenharmony_ci	spin_unlock(&dasd_devmap_lock);
252662306a36Sopenharmony_ci	return 0;
252762306a36Sopenharmony_ci}
252862306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_set_feature);
252962306a36Sopenharmony_ci
253062306a36Sopenharmony_cistatic struct attribute *paths_info_attrs[] = {
253162306a36Sopenharmony_ci	&path_fcs_attribute.attr,
253262306a36Sopenharmony_ci	NULL,
253362306a36Sopenharmony_ci};
253462306a36Sopenharmony_ciATTRIBUTE_GROUPS(paths_info);
253562306a36Sopenharmony_ci
253662306a36Sopenharmony_cistatic struct kobj_type path_attr_type = {
253762306a36Sopenharmony_ci	.release	= dasd_path_release,
253862306a36Sopenharmony_ci	.default_groups	= paths_info_groups,
253962306a36Sopenharmony_ci	.sysfs_ops	= &kobj_sysfs_ops,
254062306a36Sopenharmony_ci};
254162306a36Sopenharmony_ci
254262306a36Sopenharmony_cistatic void dasd_path_init_kobj(struct dasd_device *device, int chp)
254362306a36Sopenharmony_ci{
254462306a36Sopenharmony_ci	device->path[chp].kobj.kset = device->paths_info;
254562306a36Sopenharmony_ci	kobject_init(&device->path[chp].kobj, &path_attr_type);
254662306a36Sopenharmony_ci}
254762306a36Sopenharmony_ci
254862306a36Sopenharmony_civoid dasd_path_create_kobj(struct dasd_device *device, int chp)
254962306a36Sopenharmony_ci{
255062306a36Sopenharmony_ci	int rc;
255162306a36Sopenharmony_ci
255262306a36Sopenharmony_ci	if (test_bit(DASD_FLAG_OFFLINE, &device->flags))
255362306a36Sopenharmony_ci		return;
255462306a36Sopenharmony_ci	if (!device->paths_info) {
255562306a36Sopenharmony_ci		dev_warn(&device->cdev->dev, "Unable to create paths objects\n");
255662306a36Sopenharmony_ci		return;
255762306a36Sopenharmony_ci	}
255862306a36Sopenharmony_ci	if (device->path[chp].in_sysfs)
255962306a36Sopenharmony_ci		return;
256062306a36Sopenharmony_ci	if (!device->path[chp].conf_data)
256162306a36Sopenharmony_ci		return;
256262306a36Sopenharmony_ci
256362306a36Sopenharmony_ci	dasd_path_init_kobj(device, chp);
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	rc = kobject_add(&device->path[chp].kobj, NULL, "%x.%02x",
256662306a36Sopenharmony_ci			 device->path[chp].cssid, device->path[chp].chpid);
256762306a36Sopenharmony_ci	if (rc)
256862306a36Sopenharmony_ci		kobject_put(&device->path[chp].kobj);
256962306a36Sopenharmony_ci	device->path[chp].in_sysfs = true;
257062306a36Sopenharmony_ci}
257162306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_path_create_kobj);
257262306a36Sopenharmony_ci
257362306a36Sopenharmony_civoid dasd_path_create_kobjects(struct dasd_device *device)
257462306a36Sopenharmony_ci{
257562306a36Sopenharmony_ci	u8 lpm, opm;
257662306a36Sopenharmony_ci
257762306a36Sopenharmony_ci	opm = dasd_path_get_opm(device);
257862306a36Sopenharmony_ci	for (lpm = 0x80; lpm; lpm >>= 1) {
257962306a36Sopenharmony_ci		if (!(lpm & opm))
258062306a36Sopenharmony_ci			continue;
258162306a36Sopenharmony_ci		dasd_path_create_kobj(device, pathmask_to_pos(lpm));
258262306a36Sopenharmony_ci	}
258362306a36Sopenharmony_ci}
258462306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_path_create_kobjects);
258562306a36Sopenharmony_ci
258662306a36Sopenharmony_cistatic void dasd_path_remove_kobj(struct dasd_device *device, int chp)
258762306a36Sopenharmony_ci{
258862306a36Sopenharmony_ci	if (device->path[chp].in_sysfs) {
258962306a36Sopenharmony_ci		kobject_put(&device->path[chp].kobj);
259062306a36Sopenharmony_ci		device->path[chp].in_sysfs = false;
259162306a36Sopenharmony_ci	}
259262306a36Sopenharmony_ci}
259362306a36Sopenharmony_ci
259462306a36Sopenharmony_ci/*
259562306a36Sopenharmony_ci * As we keep kobjects for the lifetime of a device, this function must not be
259662306a36Sopenharmony_ci * called anywhere but in the context of offlining a device.
259762306a36Sopenharmony_ci */
259862306a36Sopenharmony_civoid dasd_path_remove_kobjects(struct dasd_device *device)
259962306a36Sopenharmony_ci{
260062306a36Sopenharmony_ci	int i;
260162306a36Sopenharmony_ci
260262306a36Sopenharmony_ci	for (i = 0; i < 8; i++)
260362306a36Sopenharmony_ci		dasd_path_remove_kobj(device, i);
260462306a36Sopenharmony_ci}
260562306a36Sopenharmony_ciEXPORT_SYMBOL(dasd_path_remove_kobjects);
260662306a36Sopenharmony_ci
260762306a36Sopenharmony_ciint
260862306a36Sopenharmony_cidasd_devmap_init(void)
260962306a36Sopenharmony_ci{
261062306a36Sopenharmony_ci	int i;
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci	/* Initialize devmap structures. */
261362306a36Sopenharmony_ci	dasd_max_devindex = 0;
261462306a36Sopenharmony_ci	for (i = 0; i < 256; i++)
261562306a36Sopenharmony_ci		INIT_LIST_HEAD(&dasd_hashlists[i]);
261662306a36Sopenharmony_ci	return 0;
261762306a36Sopenharmony_ci}
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_civoid
262062306a36Sopenharmony_cidasd_devmap_exit(void)
262162306a36Sopenharmony_ci{
262262306a36Sopenharmony_ci	dasd_forget_ranges();
262362306a36Sopenharmony_ci}
2624