kexec: add dmesg log symbols to /proc/vmcoreinfo lists
[safe/jmp/linux-2.6] / drivers / s390 / block / dasd_devmap.c
index 672e503..e77666c 100644 (file)
  *
  */
 
-#include <linux/config.h>
+#define KMSG_COMPONENT "dasd"
+
 #include <linux/ctype.h>
 #include <linux/init.h>
 #include <linux/module.h>
 
 #include <asm/debug.h>
 #include <asm/uaccess.h>
+#include <asm/ipl.h>
 
 /* This is ugly... */
 #define PRINTK_HEADER "dasd_devmap:"
+#define DASD_BUS_ID_SIZE 20
 
 #include "dasd_int.h"
 
-kmem_cache_t *dasd_page_cache;
-EXPORT_SYMBOL(dasd_page_cache);
+struct kmem_cache *dasd_page_cache;
+EXPORT_SYMBOL_GPL(dasd_page_cache);
 
 /*
  * dasd_devmap_t is used to store the features and the relation
@@ -41,7 +44,7 @@ EXPORT_SYMBOL(dasd_page_cache);
  */
 struct dasd_devmap {
        struct list_head list;
-       char bus_id[BUS_ID_SIZE];
+       char bus_id[DASD_BUS_ID_SIZE];
         unsigned int devindex;
         unsigned short features;
        struct dasd_device *device;
@@ -64,6 +67,10 @@ 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);
+int dasd_nofcx;                        /* disable High Performance Ficon */
+EXPORT_SYMBOL_GPL(dasd_nofcx);
 
 /*
  * char *dasd[] is intended to hold the ranges supplied by the dasd= statement
@@ -74,7 +81,7 @@ static char *dasd[256];
 module_param_array(dasd, charp, NULL, 0);
 
 /*
- * Single spinlock to protect devmap structures and lists.
+ * Single spinlock to protect devmap and servermap structures and lists.
  */
 static DEFINE_SPINLOCK(dasd_devmap_lock);
 
@@ -84,15 +91,15 @@ static DEFINE_SPINLOCK(dasd_devmap_lock);
 static struct list_head dasd_hashlists[256];
 int dasd_max_devindex;
 
-static struct dasd_devmap *dasd_add_busid(char *, int);
+static struct dasd_devmap *dasd_add_busid(const char *, int);
 
 static inline int
-dasd_hash_busid(char *bus_id)
+dasd_hash_busid(const char *bus_id)
 {
        int hash, i;
 
        hash = 0;
-       for (i = 0; (i < BUS_ID_SIZE) && *bus_id; i++, bus_id++)
+       for (i = 0; (i < DASD_BUS_ID_SIZE) && *bus_id; i++, bus_id++)
                hash += *bus_id;
        return hash & 0xff;
 }
@@ -116,14 +123,30 @@ dasd_call_setup(char *str)
 __setup ("dasd=", dasd_call_setup);
 #endif /* #ifndef MODULE */
 
+#define        DASD_IPLDEV     "ipldev"
+
 /*
  * Read a device busid/devno from a string.
  */
-static inline int
+static int
+
 dasd_busid(char **str, int *id0, int *id1, int *devno)
 {
        int val, old_style;
 
+       /* Interpret ipldev busid */
+       if (strncmp(DASD_IPLDEV, *str, strlen(DASD_IPLDEV)) == 0) {
+               if (ipl_info.type != IPL_TYPE_CCW) {
+                       pr_err("The IPL device is not a CCW device\n");
+                       return -EINVAL;
+               }
+               *id0 = 0;
+               *id1 = ipl_info.data.ccw.dev_id.ssid;
+               *devno = ipl_info.data.ccw.dev_id.devno;
+               *str += strlen(DASD_IPLDEV);
+
+               return 0;
+       }
        /* check for leading '0x' */
        old_style = 0;
        if ((*str)[0] == '0' && (*str)[1] == 'x') {
@@ -165,7 +188,7 @@ dasd_busid(char **str, int *id0, int *id1, int *devno)
  * only one: "ro" for read-only devices. The default feature set
  * is empty (value 0).
  */
-static inline int
+static int
 dasd_feature_list(char *str, char **endp)
 {
        int features, len, rc;
@@ -185,10 +208,13 @@ dasd_feature_list(char *str, char **endp)
                        features |= DASD_FEATURE_READONLY;
                else if (len == 4 && !strncmp(str, "diag", 4))
                        features |= DASD_FEATURE_USEDIAG;
+               else if (len == 6 && !strncmp(str, "erplog", 6))
+                       features |= DASD_FEATURE_ERPLOG;
+               else if (len == 8 && !strncmp(str, "failfast", 8))
+                       features |= DASD_FEATURE_FAILFAST;
                else {
-                       MESSAGE(KERN_WARNING,
-                               "unsupported feature: %*s, "
-                               "ignoring setting", len, str);
+                       pr_warning("%*s is not a supported device option\n",
+                                  len, str);
                        rc = -EINVAL;
                }
                str += len;
@@ -197,8 +223,8 @@ dasd_feature_list(char *str, char **endp)
                str++;
        }
        if (*str != ')') {
-               MESSAGE(KERN_WARNING, "%s",
-                       "missing ')' in dasd parameter string\n");
+               pr_warning("A closing parenthesis ')' is missing in the "
+                          "dasd= parameter\n");
                rc = -EINVAL;
        } else
                str++;
@@ -228,29 +254,43 @@ dasd_parse_keyword( char *parsestring ) {
                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");
+               pr_info("The autodetection mode has been activated\n");
                 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");
+               pr_info("The probeonly mode has been activated\n");
                 return residual_str;
         }
-        if (strncmp ("fixedbuffers", parsestring, length) == 0) {
+       if (strncmp("nopav", parsestring, length) == 0) {
+               if (MACHINE_IS_VM)
+                       pr_info("'nopav' is not supported on z/VM\n");
+               else {
+                       dasd_nopav = 1;
+                       pr_info("PAV support has be deactivated\n");
+               }
+               return residual_str;
+       }
+       if (strncmp("nofcx", parsestring, length) == 0) {
+               dasd_nofcx = 1;
+               pr_info("High Performance FICON support has been "
+                       "deactivated\n");
+               return residual_str;
+       }
+       if (strncmp("fixedbuffers", parsestring, length) == 0) {
                if (dasd_page_cache)
                        return residual_str;
                dasd_page_cache =
-                       kmem_cache_create("dasd_page_cache", PAGE_SIZE, 0,
-                                         SLAB_CACHE_DMA, NULL, NULL );
+                       kmem_cache_create("dasd_page_cache", PAGE_SIZE,
+                                         PAGE_SIZE, SLAB_CACHE_DMA,
+                                         NULL);
                if (!dasd_page_cache)
-                       MESSAGE(KERN_WARNING, "%s", "Failed to create slab, "
+                       DBF_EVENT(DBF_WARNING, "%s", "Failed to create slab, "
                                "fixed buffer mode disabled.");
                else
-                       MESSAGE (KERN_INFO, "%s",
+                       DBF_EVENT(DBF_INFO, "%s",
                                 "turning on fixed buffer mode");
                 return residual_str;
         }
@@ -271,7 +311,7 @@ dasd_parse_range( char *parsestring ) {
        int from, from_id0, from_id1;
        int to, to_id0, to_id1;
        int features, rc;
-       char bus_id[BUS_ID_SIZE+1], *str;
+       char bus_id[DASD_BUS_ID_SIZE+1], *str;
 
        str = parsestring;
        rc = dasd_busid(&str, &from_id0, &from_id1, &from);
@@ -288,12 +328,14 @@ dasd_parse_range( char *parsestring ) {
            (from_id0 != to_id0 || from_id1 != to_id1 || from > to))
                rc = -EINVAL;
        if (rc) {
-               MESSAGE(KERN_ERR, "Invalid device range %s", parsestring);
+               pr_err("%s is not a valid device range\n", parsestring);
                return ERR_PTR(rc);
        }
        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++);
@@ -305,12 +347,12 @@ dasd_parse_range( char *parsestring ) {
                return str + 1;
        if (*str == '\0')
                return str;
-       MESSAGE(KERN_WARNING,
-               "junk at end of dasd parameter string: %s\n", str);
+       pr_warning("The dasd= parameter value %s has an invalid ending\n",
+                  str);
        return ERR_PTR(-EINVAL);
 }
 
-static inline char *
+static char *
 dasd_parse_next_element( char *parsestring ) {
        char * residual_str;
        residual_str = dasd_parse_keyword(parsestring);
@@ -362,7 +404,7 @@ dasd_parse(void)
  * devices.
  */
 static struct dasd_devmap *
-dasd_add_busid(char *bus_id, int features)
+dasd_add_busid(const char *bus_id, int features)
 {
        struct dasd_devmap *devmap, *new, *tmp;
        int hash;
@@ -372,22 +414,22 @@ dasd_add_busid(char *bus_id, int features)
        if (!new)
                return ERR_PTR(-ENOMEM);
        spin_lock(&dasd_devmap_lock);
-       devmap = 0;
+       devmap = NULL;
        hash = dasd_hash_busid(bus_id);
        list_for_each_entry(tmp, &dasd_hashlists[hash], list)
-               if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
+               if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) {
                        devmap = tmp;
                        break;
                }
        if (!devmap) {
                /* This bus_id is new. */
                new->devindex = dasd_max_devindex++;
-               strncpy(new->bus_id, bus_id, BUS_ID_SIZE);
+               strncpy(new->bus_id, bus_id, DASD_BUS_ID_SIZE);
                new->features = features;
-               new->device = 0;
+               new->device = NULL;
                list_add(&new->list, &dasd_hashlists[hash]);
                devmap = new;
-               new = 0;
+               new = NULL;
        }
        spin_unlock(&dasd_devmap_lock);
        kfree(new);
@@ -398,7 +440,7 @@ dasd_add_busid(char *bus_id, int features)
  * Find devmap for device with given bus_id.
  */
 static struct dasd_devmap *
-dasd_find_busid(char *bus_id)
+dasd_find_busid(const char *bus_id)
 {
        struct dasd_devmap *devmap, *tmp;
        int hash;
@@ -407,7 +449,7 @@ dasd_find_busid(char *bus_id)
        devmap = ERR_PTR(-ENODEV);
        hash = dasd_hash_busid(bus_id);
        list_for_each_entry(tmp, &dasd_hashlists[hash], list) {
-               if (strncmp(tmp->bus_id, bus_id, BUS_ID_SIZE) == 0) {
+               if (strncmp(tmp->bus_id, bus_id, DASD_BUS_ID_SIZE) == 0) {
                        devmap = tmp;
                        break;
                }
@@ -420,7 +462,7 @@ dasd_find_busid(char *bus_id)
  * Check if busid has been added to the list of dasd ranges.
  */
 int
-dasd_busid_known(char *bus_id)
+dasd_busid_known(const char *bus_id)
 {
        return IS_ERR(dasd_find_busid(bus_id)) ? -ENOENT : 0;
 }
@@ -457,7 +499,7 @@ dasd_device_from_devindex(int devindex)
        int i;
 
        spin_lock(&dasd_devmap_lock);
-       devmap = 0;
+       devmap = NULL;
        for (i = 0; (i < 256) && !devmap; i++)
                list_for_each_entry(tmp, &dasd_hashlists[i], list)
                        if (tmp->devindex == devindex) {
@@ -483,9 +525,9 @@ dasd_devmap_from_cdev(struct ccw_device *cdev)
 {
        struct dasd_devmap *devmap;
 
-       devmap = dasd_find_busid(cdev->dev.bus_id);
+       devmap = dasd_find_busid(dev_name(&cdev->dev));
        if (IS_ERR(devmap))
-               devmap = dasd_add_busid(cdev->dev.bus_id,
+               devmap = dasd_add_busid(dev_name(&cdev->dev),
                                        DASD_FEATURE_DEFAULT);
        return devmap;
 }
@@ -498,17 +540,17 @@ dasd_create_device(struct ccw_device *cdev)
 {
        struct dasd_devmap *devmap;
        struct dasd_device *device;
+       unsigned long flags;
        int rc;
 
        devmap = dasd_devmap_from_cdev(cdev);
        if (IS_ERR(devmap))
                return (void *) devmap;
-       cdev->dev.driver_data = devmap;
 
        device = dasd_alloc_device();
        if (IS_ERR(device))
                return device;
-       atomic_set(&device->ref_count, 2);
+       atomic_set(&device->ref_count, 3);
 
        spin_lock(&dasd_devmap_lock);
        if (!devmap->device) {
@@ -527,6 +569,11 @@ dasd_create_device(struct ccw_device *cdev)
                dasd_free_device(device);
                return ERR_PTR(rc);
        }
+
+       spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+       dev_set_drvdata(&cdev->dev, device);
+       spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
+
        return device;
 }
 
@@ -544,9 +591,10 @@ dasd_delete_device(struct dasd_device *device)
 {
        struct ccw_device *cdev;
        struct dasd_devmap *devmap;
+       unsigned long flags;
 
        /* First remove device pointer from devmap. */
-       devmap = dasd_find_busid(device->cdev->dev.bus_id);
+       devmap = dasd_find_busid(dev_name(&device->cdev->dev));
        BUG_ON(IS_ERR(devmap));
        spin_lock(&dasd_devmap_lock);
        if (devmap->device != device) {
@@ -557,9 +605,16 @@ dasd_delete_device(struct dasd_device *device)
        devmap->device = NULL;
        spin_unlock(&dasd_devmap_lock);
 
-       /* Drop ref_count by 2, one for the devmap reference and
-        * one for the passed reference. */
-       atomic_sub(2, &device->ref_count);
+       /* Disconnect dasd_device structure from ccw_device structure. */
+       spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
+       dev_set_drvdata(&device->cdev->dev, NULL);
+       spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
+
+       /*
+        * Drop ref_count by 3, one for the devmap reference, one for
+        * the cdev reference and one for the passed reference.
+        */
+       atomic_sub(3, &device->ref_count);
 
        /* Wait for reference counter to drop to zero. */
        wait_event(dasd_delete_wq, atomic_read(&device->ref_count) == 0);
@@ -568,9 +623,6 @@ dasd_delete_device(struct dasd_device *device)
        cdev = device->cdev;
        device->cdev = NULL;
 
-       /* Disconnect dasd_devmap structure from ccw_device structure. */
-       cdev->dev.driver_data = NULL;
-
        /* Put ccw_device structure. */
        put_device(&cdev->dev);
 
@@ -590,21 +642,32 @@ dasd_put_device_wake(struct dasd_device *device)
 
 /*
  * Return dasd_device structure associated with cdev.
+ * This function needs to be called with the ccw device
+ * lock held. It can be used from interrupt context.
+ */
+struct dasd_device *
+dasd_device_from_cdev_locked(struct ccw_device *cdev)
+{
+       struct dasd_device *device = dev_get_drvdata(&cdev->dev);
+
+       if (!device)
+               return ERR_PTR(-ENODEV);
+       dasd_get_device(device);
+       return device;
+}
+
+/*
+ * Return dasd_device structure associated with cdev.
  */
 struct dasd_device *
 dasd_device_from_cdev(struct ccw_device *cdev)
 {
-       struct dasd_devmap *devmap;
        struct dasd_device *device;
+       unsigned long flags;
 
-       device = ERR_PTR(-ENODEV);
-       spin_lock(&dasd_devmap_lock);
-       devmap = cdev->dev.driver_data;
-       if (devmap && devmap->device) {
-               device = devmap->device;
-               dasd_get_device(device);
-       }
-       spin_unlock(&dasd_devmap_lock);
+       spin_lock_irqsave(get_ccwdev_lock(cdev), flags);
+       device = dasd_device_from_cdev_locked(cdev);
+       spin_unlock_irqrestore(get_ccwdev_lock(cdev), flags);
        return device;
 }
 
@@ -613,6 +676,51 @@ dasd_device_from_cdev(struct ccw_device *cdev)
  */
 
 /*
+ * failfast controls the behaviour, if no path is available
+ */
+static ssize_t dasd_ff_show(struct device *dev, struct device_attribute *attr,
+                           char *buf)
+{
+       struct dasd_devmap *devmap;
+       int ff_flag;
+
+       devmap = dasd_find_busid(dev_name(dev));
+       if (!IS_ERR(devmap))
+               ff_flag = (devmap->features & DASD_FEATURE_FAILFAST) != 0;
+       else
+               ff_flag = (DASD_FEATURE_DEFAULT & DASD_FEATURE_FAILFAST) != 0;
+       return snprintf(buf, PAGE_SIZE, ff_flag ? "1\n" : "0\n");
+}
+
+static ssize_t dasd_ff_store(struct device *dev, struct device_attribute *attr,
+             const char *buf, size_t count)
+{
+       struct dasd_devmap *devmap;
+       int val;
+       char *endp;
+
+       devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
+       if (IS_ERR(devmap))
+               return PTR_ERR(devmap);
+
+       val = simple_strtoul(buf, &endp, 0);
+       if (((endp + 1) < (buf + count)) || (val > 1))
+               return -EINVAL;
+
+       spin_lock(&dasd_devmap_lock);
+       if (val)
+               devmap->features |= DASD_FEATURE_FAILFAST;
+       else
+               devmap->features &= ~DASD_FEATURE_FAILFAST;
+       if (devmap->device)
+               devmap->device->features = devmap->features;
+       spin_unlock(&dasd_devmap_lock);
+       return count;
+}
+
+static DEVICE_ATTR(failfast, 0644, dasd_ff_show, dasd_ff_store);
+
+/*
  * readonly controls the readonly status of a dasd
  */
 static ssize_t
@@ -621,7 +729,7 @@ dasd_ro_show(struct device *dev, struct device_attribute *attr, char *buf)
        struct dasd_devmap *devmap;
        int ro_flag;
 
-       devmap = dasd_find_busid(dev->bus_id);
+       devmap = dasd_find_busid(dev_name(dev));
        if (!IS_ERR(devmap))
                ro_flag = (devmap->features & DASD_FEATURE_READONLY) != 0;
        else
@@ -634,26 +742,78 @@ dasd_ro_store(struct device *dev, struct device_attribute *attr,
              const char *buf, size_t count)
 {
        struct dasd_devmap *devmap;
-       int ro_flag;
+       int val;
+       char *endp;
 
        devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
        if (IS_ERR(devmap))
                return PTR_ERR(devmap);
-       ro_flag = buf[0] == '1';
+
+       val = simple_strtoul(buf, &endp, 0);
+       if (((endp + 1) < (buf + count)) || (val > 1))
+               return -EINVAL;
+
        spin_lock(&dasd_devmap_lock);
-       if (ro_flag)
+       if (val)
                devmap->features |= DASD_FEATURE_READONLY;
        else
                devmap->features &= ~DASD_FEATURE_READONLY;
        if (devmap->device)
                devmap->device->features = devmap->features;
-       if (devmap->device && devmap->device->gdp)
-               set_disk_ro(devmap->device->gdp, ro_flag);
+       if (devmap->device && devmap->device->block
+           && devmap->device->block->gdp)
+               set_disk_ro(devmap->device->block->gdp, val);
        spin_unlock(&dasd_devmap_lock);
        return count;
 }
 
 static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
+/*
+ * erplog controls the logging of ERP related data
+ * (e.g. failing channel programs).
+ */
+static ssize_t
+dasd_erplog_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+       struct dasd_devmap *devmap;
+       int erplog;
+
+       devmap = dasd_find_busid(dev_name(dev));
+       if (!IS_ERR(devmap))
+               erplog = (devmap->features & DASD_FEATURE_ERPLOG) != 0;
+       else
+               erplog = (DASD_FEATURE_DEFAULT & DASD_FEATURE_ERPLOG) != 0;
+       return snprintf(buf, PAGE_SIZE, erplog ? "1\n" : "0\n");
+}
+
+static ssize_t
+dasd_erplog_store(struct device *dev, struct device_attribute *attr,
+             const char *buf, size_t count)
+{
+       struct dasd_devmap *devmap;
+       int val;
+       char *endp;
+
+       devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
+       if (IS_ERR(devmap))
+               return PTR_ERR(devmap);
+
+       val = simple_strtoul(buf, &endp, 0);
+       if (((endp + 1) < (buf + count)) || (val > 1))
+               return -EINVAL;
+
+       spin_lock(&dasd_devmap_lock);
+       if (val)
+               devmap->features |= DASD_FEATURE_ERPLOG;
+       else
+               devmap->features &= ~DASD_FEATURE_ERPLOG;
+       if (devmap->device)
+               devmap->device->features = devmap->features;
+       spin_unlock(&dasd_devmap_lock);
+       return count;
+}
+
+static DEVICE_ATTR(erplog, 0644, dasd_erplog_show, dasd_erplog_store);
 
 /*
  * use_diag controls whether the driver should use diag rather than ssch
@@ -665,7 +825,7 @@ dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
        struct dasd_devmap *devmap;
        int use_diag;
 
-       devmap = dasd_find_busid(dev->bus_id);
+       devmap = dasd_find_busid(dev_name(dev));
        if (!IS_ERR(devmap))
                use_diag = (devmap->features & DASD_FEATURE_USEDIAG) != 0;
        else
@@ -679,17 +839,22 @@ dasd_use_diag_store(struct device *dev, struct device_attribute *attr,
 {
        struct dasd_devmap *devmap;
        ssize_t rc;
-       int use_diag;
+       int val;
+       char *endp;
 
        devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
        if (IS_ERR(devmap))
                return PTR_ERR(devmap);
-       use_diag = buf[0] == '1';
+
+       val = simple_strtoul(buf, &endp, 0);
+       if (((endp + 1) < (buf + count)) || (val > 1))
+               return -EINVAL;
+
        spin_lock(&dasd_devmap_lock);
        /* Changing diag discipline flag is only allowed in offline state. */
        rc = count;
        if (!devmap->device) {
-               if (use_diag)
+               if (val)
                        devmap->features |= DASD_FEATURE_USEDIAG;
                else
                        devmap->features &= ~DASD_FEATURE_USEDIAG;
@@ -705,34 +870,79 @@ static ssize_t
 dasd_discipline_show(struct device *dev, struct device_attribute *attr,
                     char *buf)
 {
-       struct dasd_devmap *devmap;
-       char *dname;
+       struct dasd_device *device;
+       ssize_t len;
 
-       spin_lock(&dasd_devmap_lock);
-       dname = "none";
-       devmap = dev->driver_data;
-       if (devmap && devmap->device && devmap->device->discipline)
-               dname = devmap->device->discipline->name;
-       spin_unlock(&dasd_devmap_lock);
-       return snprintf(buf, PAGE_SIZE, "%s\n", dname);
+       device = dasd_device_from_cdev(to_ccwdev(dev));
+       if (!IS_ERR(device) && device->discipline) {
+               len = snprintf(buf, PAGE_SIZE, "%s\n",
+                              device->discipline->name);
+               dasd_put_device(device);
+       } else
+               len = snprintf(buf, PAGE_SIZE, "none\n");
+       return len;
 }
 
 static DEVICE_ATTR(discipline, 0444, dasd_discipline_show, NULL);
 
 static ssize_t
+dasd_device_status_show(struct device *dev, struct device_attribute *attr,
+                    char *buf)
+{
+       struct dasd_device *device;
+       ssize_t len;
+
+       device = dasd_device_from_cdev(to_ccwdev(dev));
+       if (!IS_ERR(device)) {
+               switch (device->state) {
+               case DASD_STATE_NEW:
+                       len = snprintf(buf, PAGE_SIZE, "new\n");
+                       break;
+               case DASD_STATE_KNOWN:
+                       len = snprintf(buf, PAGE_SIZE, "detected\n");
+                       break;
+               case DASD_STATE_BASIC:
+                       len = snprintf(buf, PAGE_SIZE, "basic\n");
+                       break;
+               case DASD_STATE_UNFMT:
+                       len = snprintf(buf, PAGE_SIZE, "unformatted\n");
+                       break;
+               case DASD_STATE_READY:
+                       len = snprintf(buf, PAGE_SIZE, "ready\n");
+                       break;
+               case DASD_STATE_ONLINE:
+                       len = snprintf(buf, PAGE_SIZE, "online\n");
+                       break;
+               default:
+                       len = snprintf(buf, PAGE_SIZE, "no stat\n");
+                       break;
+               }
+               dasd_put_device(device);
+       } else
+               len = snprintf(buf, PAGE_SIZE, "unknown\n");
+       return len;
+}
+
+static DEVICE_ATTR(status, 0444, dasd_device_status_show, NULL);
+
+static ssize_t
 dasd_alias_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct dasd_devmap *devmap;
        int alias;
 
-       devmap = dasd_find_busid(dev->bus_id);
+       devmap = dasd_find_busid(dev_name(dev));
        spin_lock(&dasd_devmap_lock);
-       if (!IS_ERR(devmap))
-               alias = devmap->uid.alias;
+       if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
+               spin_unlock(&dasd_devmap_lock);
+               return sprintf(buf, "0\n");
+       }
+       if (devmap->uid.type == UA_BASE_PAV_ALIAS ||
+           devmap->uid.type == UA_HYPER_PAV_ALIAS)
+               alias = 1;
        else
                alias = 0;
        spin_unlock(&dasd_devmap_lock);
-
        return sprintf(buf, alias ? "1\n" : "0\n");
 }
 
@@ -744,7 +954,7 @@ dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
        struct dasd_devmap *devmap;
        char *vendor;
 
-       devmap = dasd_find_busid(dev->bus_id);
+       devmap = dasd_find_busid(dev_name(dev));
        spin_lock(&dasd_devmap_lock);
        if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0)
                vendor = devmap->uid.vendor;
@@ -758,25 +968,52 @@ dasd_vendor_show(struct device *dev, struct device_attribute *attr, char *buf)
 static DEVICE_ATTR(vendor, 0444, dasd_vendor_show, NULL);
 
 #define UID_STRLEN ( /* vendor */ 3 + 1 + /* serial    */ 14 + 1 +\
-                    /* SSID   */ 4 + 1 + /* unit addr */ 2 + 1)
+                    /* SSID   */ 4 + 1 + /* unit addr */ 2 + 1 +\
+                    /* vduit */ 32 + 1)
 
 static ssize_t
 dasd_uid_show(struct device *dev, struct device_attribute *attr, char *buf)
 {
        struct dasd_devmap *devmap;
-       char uid[UID_STRLEN];
+       char uid_string[UID_STRLEN];
+       char ua_string[3];
+       struct dasd_uid *uid;
 
-       devmap = dasd_find_busid(dev->bus_id);
+       devmap = dasd_find_busid(dev_name(dev));
        spin_lock(&dasd_devmap_lock);
-       if (!IS_ERR(devmap) && strlen(devmap->uid.vendor) > 0)
-               snprintf(uid, sizeof(uid), "%s.%s.%04x.%02x",
-                        devmap->uid.vendor, devmap->uid.serial,
-                        devmap->uid.ssid, devmap->uid.unit_addr);
+       if (IS_ERR(devmap) || strlen(devmap->uid.vendor) == 0) {
+               spin_unlock(&dasd_devmap_lock);
+               return sprintf(buf, "\n");
+       }
+       uid = &devmap->uid;
+       switch (uid->type) {
+       case UA_BASE_DEVICE:
+               sprintf(ua_string, "%02x", uid->real_unit_addr);
+               break;
+       case UA_BASE_PAV_ALIAS:
+               sprintf(ua_string, "%02x", uid->base_unit_addr);
+               break;
+       case UA_HYPER_PAV_ALIAS:
+               sprintf(ua_string, "xx");
+               break;
+       default:
+               /* should not happen, treat like base device */
+               sprintf(ua_string, "%02x", uid->real_unit_addr);
+               break;
+       }
+       if (strlen(uid->vduit) > 0)
+               snprintf(uid_string, sizeof(uid_string),
+                        "%s.%s.%04x.%s.%s",
+                        uid->vendor, uid->serial,
+                        uid->ssid, ua_string,
+                        uid->vduit);
        else
-               uid[0] = 0;
+               snprintf(uid_string, sizeof(uid_string),
+                        "%s.%s.%04x.%s",
+                        uid->vendor, uid->serial,
+                        uid->ssid, ua_string);
        spin_unlock(&dasd_devmap_lock);
-
-       return snprintf(buf, PAGE_SIZE, "%s\n", uid);
+       return snprintf(buf, PAGE_SIZE, "%s\n", uid_string);
 }
 
 static DEVICE_ATTR(uid, 0444, dasd_uid_show, NULL);
@@ -790,7 +1027,7 @@ dasd_eer_show(struct device *dev, struct device_attribute *attr, char *buf)
        struct dasd_devmap *devmap;
        int eer_flag;
 
-       devmap = dasd_find_busid(dev->bus_id);
+       devmap = dasd_find_busid(dev_name(dev));
        if (!IS_ERR(devmap) && devmap->device)
                eer_flag = dasd_eer_enabled(devmap->device);
        else
@@ -803,14 +1040,20 @@ dasd_eer_store(struct device *dev, struct device_attribute *attr,
               const char *buf, size_t count)
 {
        struct dasd_devmap *devmap;
-       int rc;
+       int val, rc;
+       char *endp;
 
        devmap = dasd_devmap_from_cdev(to_ccwdev(dev));
        if (IS_ERR(devmap))
                return PTR_ERR(devmap);
        if (!devmap->device)
-               return count;
-       if (buf[0] == '1') {
+               return -ENODEV;
+
+       val = simple_strtoul(buf, &endp, 0);
+       if (((endp + 1) < (buf + count)) || (val > 1))
+               return -EINVAL;
+
+       if (val) {
                rc = dasd_eer_enable(devmap->device);
                if (rc)
                        return rc;
@@ -824,11 +1067,14 @@ static DEVICE_ATTR(eer_enabled, 0644, dasd_eer_show, dasd_eer_store);
 static struct attribute * dasd_attrs[] = {
        &dev_attr_readonly.attr,
        &dev_attr_discipline.attr,
+       &dev_attr_status.attr,
        &dev_attr_alias.attr,
        &dev_attr_vendor.attr,
        &dev_attr_uid.attr,
        &dev_attr_use_diag.attr,
        &dev_attr_eer_enabled.attr,
+       &dev_attr_erplog.attr,
+       &dev_attr_failfast.attr,
        NULL,
 };
 
@@ -836,7 +1082,6 @@ static struct attribute_group dasd_attr_group = {
        .attrs = dasd_attrs,
 };
 
-
 /*
  * Return copy of the device unique identifier.
  */
@@ -845,7 +1090,7 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid)
 {
        struct dasd_devmap *devmap;
 
-       devmap = dasd_find_busid(cdev->dev.bus_id);
+       devmap = dasd_find_busid(dev_name(&cdev->dev));
        if (IS_ERR(devmap))
                return PTR_ERR(devmap);
        spin_lock(&dasd_devmap_lock);
@@ -856,21 +1101,29 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid)
 
 /*
  * Register the given device unique identifier into devmap struct.
+ * In addition check if the related storage server subsystem ID is already
+ * contained in the dasd_server_ssid_list. If subsystem ID is not contained,
+ * create new entry.
+ * 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;
 
-       devmap = dasd_find_busid(cdev->dev.bus_id);
+       devmap = dasd_find_busid(dev_name(&cdev->dev));
        if (IS_ERR(devmap))
                return PTR_ERR(devmap);
+
        spin_lock(&dasd_devmap_lock);
        devmap->uid = *uid;
        spin_unlock(&dasd_devmap_lock);
+
        return 0;
 }
-EXPORT_SYMBOL(dasd_set_uid);
+EXPORT_SYMBOL_GPL(dasd_set_uid);
 
 /*
  * Return value of the specified feature.
@@ -880,9 +1133,9 @@ dasd_get_feature(struct ccw_device *cdev, int feature)
 {
        struct dasd_devmap *devmap;
 
-       devmap = dasd_find_busid(cdev->dev.bus_id);
+       devmap = dasd_find_busid(dev_name(&cdev->dev));
        if (IS_ERR(devmap))
-               return (int) PTR_ERR(devmap);
+               return PTR_ERR(devmap);
 
        return ((devmap->features & feature) != 0);
 }
@@ -896,9 +1149,9 @@ dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
 {
        struct dasd_devmap *devmap;
 
-       devmap = dasd_find_busid(cdev->dev.bus_id);
+       devmap = dasd_find_busid(dev_name(&cdev->dev));
        if (IS_ERR(devmap))
-               return (int) PTR_ERR(devmap);
+               return PTR_ERR(devmap);
 
        spin_lock(&dasd_devmap_lock);
        if (flag)
@@ -935,7 +1188,6 @@ dasd_devmap_init(void)
        for (i = 0; i < 256; i++)
                INIT_LIST_HEAD(&dasd_hashlists[i]);
        return 0;
-
 }
 
 void