Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 40545573 authored by Horst Hummel's avatar Horst Hummel Committed by Martin Schwidefsky
Browse files

[S390] add PAV support to the dasd driver.



Add support for parallel-access-volumes to the dasd driver. This
allows concurrent access to dasd devices with multiple channel
programs.

Signed-off-by: default avatarHorst Hummel <horst.hummel@de.ibm.com>
Signed-off-by: default avatarMartin Schwidefsky <schwidefsky@de.ibm.com>
parent 8f27766a
Loading
Loading
Loading
Loading
+23 −28
Original line number Diff line number Diff line
@@ -1855,15 +1855,34 @@ dasd_generic_probe (struct ccw_device *cdev,
{
	int ret;

	ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
	if (ret) {
		printk(KERN_WARNING
		       "dasd_generic_probe: could not set ccw-device options "
		       "for %s\n", cdev->dev.bus_id);
		return ret;
	}
	ret = dasd_add_sysfs_files(cdev);
	if (ret) {
		printk(KERN_WARNING
		       "dasd_generic_probe: could not add sysfs entries "
		       "for %s\n", cdev->dev.bus_id);
	} else {
		cdev->handler = &dasd_int_handler;
		return ret;
	}
	cdev->handler = &dasd_int_handler;

	/*
	 * Automatically online either all dasd devices (dasd_autodetect)
	 * or all devices specified with dasd= parameters during
	 * initial probe.
	 */
	if ((dasd_get_feature(cdev, DASD_FEATURE_INITIAL_ONLINE) > 0 ) ||
	    (dasd_autodetect && dasd_busid_known(cdev->dev.bus_id) != 0))
		ret = ccw_device_set_online(cdev);
	if (ret)
		printk(KERN_WARNING
		       "dasd_generic_probe: could not initially online "
		       "ccw-device %s\n", cdev->dev.bus_id);
	return ret;
}

@@ -1911,6 +1930,8 @@ dasd_generic_set_online (struct ccw_device *cdev,
	struct dasd_device *device;
	int rc;

	/* first online clears initial online feature flag */
	dasd_set_feature(cdev, DASD_FEATURE_INITIAL_ONLINE, 0);
	device = dasd_create_device(cdev);
	if (IS_ERR(device))
		return PTR_ERR(device);
@@ -2065,31 +2086,6 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
	return ret;
}

/*
 * Automatically online either all dasd devices (dasd_autodetect) or
 * all devices specified with dasd= parameters.
 */
static int
__dasd_auto_online(struct device *dev, void *data)
{
	struct ccw_device *cdev;

	cdev = to_ccwdev(dev);
	if (dasd_autodetect || dasd_busid_known(cdev->dev.bus_id) == 0)
		ccw_device_set_online(cdev);
	return 0;
}

void
dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver)
{
	struct device_driver *drv;

	drv = get_driver(&dasd_discipline_driver->driver);
	driver_for_each_device(drv, NULL, NULL, __dasd_auto_online);
	put_driver(drv);
}


static int __init
dasd_init(void)
@@ -2170,5 +2166,4 @@ EXPORT_SYMBOL_GPL(dasd_generic_remove);
EXPORT_SYMBOL_GPL(dasd_generic_notify);
EXPORT_SYMBOL_GPL(dasd_generic_set_online);
EXPORT_SYMBOL_GPL(dasd_generic_set_offline);
EXPORT_SYMBOL_GPL(dasd_generic_auto_online);
+71 −9
Original line number Diff line number Diff line
@@ -27,7 +27,7 @@
#include "dasd_int.h"

kmem_cache_t *dasd_page_cache;
EXPORT_SYMBOL(dasd_page_cache);
EXPORT_SYMBOL_GPL(dasd_page_cache);

/*
 * dasd_devmap_t is used to store the features and the relation
@@ -48,6 +48,20 @@ struct dasd_devmap {
	struct dasd_uid uid;
};

/*
 * dasd_servermap is used to store the server_id of all storage servers
 * accessed by DASD device driver.
 */
struct dasd_servermap {
	struct list_head list;
	struct server_id {
		char vendor[4];
		char serial[15];
	} sid;
};

static struct list_head dasd_serverlist;

/*
 * Parameter parsing functions for dasd= parameter. The syntax is:
 *   <devno>		: (0x)?[0-9a-fA-F]+
@@ -64,6 +78,8 @@ struct dasd_devmap {

int dasd_probeonly =  0;	/* is true, when probeonly mode is active */
int dasd_autodetect = 0;	/* is true, when autodetection is active */
int dasd_nopav = 0;		/* is true, when PAV is disabled */
EXPORT_SYMBOL_GPL(dasd_nopav);

/*
 * char *dasd[] is intended to hold the ranges supplied by the dasd= statement
@@ -240,6 +256,11 @@ dasd_parse_keyword( char *parsestring ) {
			"turning to probeonly mode");
                return residual_str;
        }
	if (strncmp("nopav", parsestring, length) == 0) {
		dasd_nopav = 1;
		MESSAGE(KERN_INFO, "%s", "disable PAV mode");
		return residual_str;
	}
	if (strncmp("fixedbuffers", parsestring, length) == 0) {
		if (dasd_page_cache)
			return residual_str;
@@ -294,6 +315,8 @@ dasd_parse_range( char *parsestring ) {
	features = dasd_feature_list(str, &str);
	if (features < 0)
		return ERR_PTR(-EINVAL);
	/* each device in dasd= parameter should be set initially online */
	features |= DASD_FEATURE_INITIAL_ONLINE;
	while (from <= to) {
		sprintf(bus_id, "%01x.%01x.%04x",
			from_id0, from_id1, from++);
@@ -836,6 +859,38 @@ static struct attribute_group dasd_attr_group = {
	.attrs = dasd_attrs,
};

/*
 * Check if the related storage server is already contained in the
 * dasd_serverlist. If server is not contained, create new entry.
 * Return 0 if server was already in serverlist,
 *	  1 if the server was added successfully
 *	 <0 in case of error.
 */
static int
dasd_add_server(struct dasd_uid *uid)
{
	struct dasd_servermap *new, *tmp;

	/* check if server is already contained */
	list_for_each_entry(tmp, &dasd_serverlist, list)
	  // normale cmp?
		if (strncmp(tmp->sid.vendor, uid->vendor,
			    sizeof(tmp->sid.vendor)) == 0
		    && strncmp(tmp->sid.serial, uid->serial,
			       sizeof(tmp->sid.serial)) == 0)
			return 0;

	new = (struct dasd_servermap *)
		kzalloc(sizeof(struct dasd_servermap), GFP_KERNEL);
	if (!new)
		return -ENOMEM;

	strncpy(new->sid.vendor, uid->vendor, sizeof(new->sid.vendor));
	strncpy(new->sid.serial, uid->serial, sizeof(new->sid.serial));
	list_add(&new->list, &dasd_serverlist);
	return 1;
}


/*
 * Return copy of the device unique identifier.
@@ -856,21 +911,26 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid)

/*
 * Register the given device unique identifier into devmap struct.
 * Return 0 if server was already in serverlist,
 *	  1 if the server was added successful
 *	 <0 in case of error.
 */
int
dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{
	struct dasd_devmap *devmap;
	int rc;

	devmap = dasd_find_busid(cdev->dev.bus_id);
	if (IS_ERR(devmap))
		return PTR_ERR(devmap);
	spin_lock(&dasd_devmap_lock);
	devmap->uid = *uid;
	rc = dasd_add_server(uid);
	spin_unlock(&dasd_devmap_lock);
	return 0;
	return rc;
}
EXPORT_SYMBOL(dasd_set_uid);
EXPORT_SYMBOL_GPL(dasd_set_uid);

/*
 * Return value of the specified feature.
@@ -882,7 +942,7 @@ dasd_get_feature(struct ccw_device *cdev, int feature)

	devmap = dasd_find_busid(cdev->dev.bus_id);
	if (IS_ERR(devmap))
		return (int) PTR_ERR(devmap);
		return PTR_ERR(devmap);

	return ((devmap->features & feature) != 0);
}
@@ -898,7 +958,7 @@ dasd_set_feature(struct ccw_device *cdev, int feature, int flag)

	devmap = dasd_find_busid(cdev->dev.bus_id);
	if (IS_ERR(devmap))
		return (int) PTR_ERR(devmap);
		return PTR_ERR(devmap);

	spin_lock(&dasd_devmap_lock);
	if (flag)
@@ -934,8 +994,10 @@ dasd_devmap_init(void)
	dasd_max_devindex = 0;
	for (i = 0; i < 256; i++)
		INIT_LIST_HEAD(&dasd_hashlists[i]);
	return 0;

	/* Initialize servermap structure. */
	INIT_LIST_HEAD(&dasd_serverlist);
	return 0;
}

void
+110 −30
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@
#include <asm/io.h>
#include <asm/todclk.h>
#include <asm/uaccess.h>
#include <asm/cio.h>
#include <asm/ccwdev.h>

#include "dasd_int.h"
@@ -89,11 +90,16 @@ dasd_eckd_probe (struct ccw_device *cdev)
{
	int ret;

	/* set ECKD specific ccw-device options */
	ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);
	if (ret) {
		printk(KERN_WARNING
		       "dasd_eckd_probe: could not set ccw-device options "
		       "for %s\n", cdev->dev.bus_id);
		return ret;
	}
	ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
	if (ret)
	return ret;
	ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | CCWDEV_ALLOW_FORCE);
	return 0;
}

static int
@@ -540,6 +546,86 @@ dasd_eckd_read_conf(struct dasd_device *device)
	return 0;
}

/*
 * Build CP for Perform Subsystem Function - SSC.
 */
struct dasd_ccw_req *
dasd_eckd_build_psf_ssc(struct dasd_device *device)
{
       struct dasd_ccw_req *cqr;
       struct dasd_psf_ssc_data *psf_ssc_data;
       struct ccw1 *ccw;

       cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
				  sizeof(struct dasd_psf_ssc_data),
				  device);

       if (IS_ERR(cqr)) {
	       DEV_MESSAGE(KERN_WARNING, device, "%s",
			   "Could not allocate PSF-SSC request");
	       return cqr;
       }
       psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
       psf_ssc_data->order = PSF_ORDER_SSC;
       psf_ssc_data->suborder = 0x08;

       ccw = cqr->cpaddr;
       ccw->cmd_code = DASD_ECKD_CCW_PSF;
       ccw->cda = (__u32)(addr_t)psf_ssc_data;
       ccw->count = 66;

       cqr->device = device;
       cqr->expires = 10*HZ;
       cqr->buildclk = get_clock();
       cqr->status = DASD_CQR_FILLED;
       return cqr;
}

/*
 * Perform Subsystem Function.
 * It is necessary to trigger CIO for channel revalidation since this
 * call might change behaviour of DASD devices.
 */
static int
dasd_eckd_psf_ssc(struct dasd_device *device)
{
       struct dasd_ccw_req *cqr;
       int rc;

       cqr = dasd_eckd_build_psf_ssc(device);
       if (IS_ERR(cqr))
	       return PTR_ERR(cqr);

       rc = dasd_sleep_on(cqr);
       if (!rc)
	       /* trigger CIO to reprobe devices */
	       css_schedule_reprobe();
       dasd_sfree_request(cqr, cqr->device);
       return rc;
}

/*
 * Valide storage server of current device.
 */
static int
dasd_eckd_validate_server(struct dasd_device *device)
{
	int rc;

	/* Currently PAV is the only reason to 'validate' server on LPAR */
	if (dasd_nopav || MACHINE_IS_VM)
		return 0;

	rc = dasd_eckd_psf_ssc(device);
	if (rc)
		/* may be requested feature is not available on server,
		 * therefore just report error and go ahead */
		DEV_MESSAGE(KERN_INFO, device,
			    "Perform Subsystem Function returned rc=%d", rc);
	/* RE-Read Configuration Data */
	return dasd_eckd_read_conf(device);
}

/*
 * Check device characteristics.
 * If the device is accessible using ECKD discipline, the device is enabled.
@@ -570,16 +656,29 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
	private->attrib.operation = DASD_NORMAL_CACHE;
	private->attrib.nr_cyl = 0;

	/* Read Configuration Data */
	rc = dasd_eckd_read_conf(device);
	if (rc)
		return rc;

	/* Generate device unique id and register in devmap */
	rc = dasd_eckd_generate_uid(device, &uid);
	if (rc)
		return rc;
	rc = dasd_set_uid(device->cdev, &uid);
	if (rc == 1)	/* new server found */
		rc = dasd_eckd_validate_server(device);
	if (rc)
		return rc;

	/* Read Device Characteristics */
	rdc_data = (void *) &(private->rdc_data);
	memset(rdc_data, 0, sizeof(rdc_data));
	rc = read_dev_chars(device->cdev, &rdc_data, 64);
	if (rc) {
	if (rc)
		DEV_MESSAGE(KERN_WARNING, device,
			    "Read device characteristics returned error %d",
			    rc);
		return rc;
	}
			    "Read device characteristics returned "
			    "rc=%d", rc);

	DEV_MESSAGE(KERN_INFO, device,
		    "%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
@@ -590,19 +689,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
		    private->rdc_data.no_cyl,
		    private->rdc_data.trk_per_cyl,
		    private->rdc_data.sec_per_trk);

	/* Read Configuration Data */
	rc = dasd_eckd_read_conf (device);
	if (rc)
		return rc;

	/* Generate device unique id and register in devmap */
	rc = dasd_eckd_generate_uid(device, &uid);
	if (rc)
		return rc;

	rc = dasd_set_uid(device->cdev, &uid);

	return rc;
}

@@ -1687,14 +1773,8 @@ static struct dasd_discipline dasd_eckd_discipline = {
static int __init
dasd_eckd_init(void)
{
	int ret;

	ASCEBC(dasd_eckd_discipline.ebcname, 4);

	ret = ccw_driver_register(&dasd_eckd_driver);
	if (!ret)
		dasd_generic_auto_online(&dasd_eckd_driver);
	return ret;
	return ccw_driver_register(&dasd_eckd_driver);
}

static void __exit
+14 −2
Original line number Diff line number Diff line
@@ -44,6 +44,7 @@
 * Perform Subsystem Function / Sub-Orders
 */
#define PSF_ORDER_PRSSD 0x18
#define PSF_ORDER_SSC	0x1D

/*****************************************************************************
 * SECTION: Type Definitions
@@ -353,4 +354,15 @@ struct dasd_psf_prssd_data {
	unsigned char varies[9];
} __attribute__ ((packed));

/*
 * Perform Subsystem Function - Set Subsystem Characteristics
 */
struct dasd_psf_ssc_data {
	unsigned char order;
	unsigned char flags;
	unsigned char cu_type[4];
	unsigned char suborder;
	unsigned char reserved[59];
} __attribute__((packed));

#endif				/* DASD_ECKD_H */
+3 −17
Original line number Diff line number Diff line
@@ -56,13 +56,7 @@ static struct ccw_driver dasd_fba_driver; /* see below */
static int
dasd_fba_probe(struct ccw_device *cdev)
{
	int ret;

	ret = dasd_generic_probe (cdev, &dasd_fba_discipline);
	if (ret)
		return ret;
	ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
	return 0;
	return dasd_generic_probe(cdev, &dasd_fba_discipline);
}

static int
@@ -569,16 +563,8 @@ static struct dasd_discipline dasd_fba_discipline = {
static int __init
dasd_fba_init(void)
{
	int ret;

	ASCEBC(dasd_fba_discipline.ebcname, 4);

	ret = ccw_driver_register(&dasd_fba_driver);
	if (ret)
		return ret;

	dasd_generic_auto_online(&dasd_fba_driver);
	return 0;
	return ccw_driver_register(&dasd_fba_driver);
}

static void __exit
Loading