[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: Horst Hummel <horst.hummel@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 672e503..9e9ae71 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -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
@@ -49,6 +49,20 @@
 };
 
 /*
+ * 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]+
  *   <busid>		: [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+
@@ -64,6 +78,8 @@
 
 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
@@ -228,19 +244,24 @@
 		length = strlen(parsestring);
 		residual_str = parsestring + length;
         }
-	if (strncmp ("autodetect", parsestring, length) == 0) {
+	if (strncmp("autodetect", parsestring, length) == 0) {
 		dasd_autodetect = 1;
 		MESSAGE (KERN_INFO, "%s",
 			 "turning to autodetection mode");
                 return residual_str;
         }
-        if (strncmp ("probeonly", parsestring, length) == 0) {
+	if (strncmp("probeonly", parsestring, length) == 0) {
 		dasd_probeonly = 1;
 		MESSAGE(KERN_INFO, "%s",
 			"turning to probeonly mode");
                 return residual_str;
         }
-        if (strncmp ("fixedbuffers", parsestring, length) == 0) {
+	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;
 		dasd_page_cache =
@@ -294,6 +315,8 @@
 	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 @@
 	.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 @@
 
 /*
  * 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 @@
 
 	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 @@
 
 	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_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