s390: add scm notification
authorSebastian Ott <sebott@linux.vnet.ibm.com>
Tue, 28 Aug 2012 14:47:02 +0000 (16:47 +0200)
committerMartin Schwidefsky <schwidefsky@de.ibm.com>
Wed, 26 Sep 2012 13:44:59 +0000 (15:44 +0200)
Detect an scm change notification in store event information.
Update affected scm devices and notify their drivers.

Signed-off-by: Sebastian Ott <sebott@linux.vnet.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
arch/s390/include/asm/eadm.h
drivers/s390/cio/chsc.c
drivers/s390/cio/chsc.h
drivers/s390/cio/scm.c

index 3922f52..4d6e103 100644 (file)
@@ -102,6 +102,7 @@ struct scm_driver {
        struct device_driver drv;
        int (*probe) (struct scm_device *scmdev);
        int (*remove) (struct scm_device *scmdev);
+       void (*notify) (struct scm_device *scmdev);
        void (*handler) (struct scm_device *scmdev, void *data, int error);
 };
 
index 1218653..4d51a7c 100644 (file)
@@ -398,6 +398,20 @@ static void chsc_process_sei_chp_config(struct chsc_sei_area *sei_area)
        }
 }
 
+static void chsc_process_sei_scm_change(struct chsc_sei_area *sei_area)
+{
+       int ret;
+
+       CIO_CRW_EVENT(4, "chsc: scm change notification\n");
+       if (sei_area->rs != 7)
+               return;
+
+       ret = scm_update_information();
+       if (ret)
+               CIO_CRW_EVENT(0, "chsc: updating change notification"
+                             " failed (rc=%d).\n", ret);
+}
+
 static void chsc_process_sei(struct chsc_sei_area *sei_area)
 {
        /* Check if we might have lost some information. */
@@ -419,6 +433,9 @@ static void chsc_process_sei(struct chsc_sei_area *sei_area)
        case 8: /* channel-path-configuration notification */
                chsc_process_sei_chp_config(sei_area);
                break;
+       case 12: /* scm change notification */
+               chsc_process_sei_scm_change(sei_area);
+               break;
        default: /* other stuff */
                CIO_CRW_EVENT(4, "chsc: unhandled sei content code %d\n",
                              sei_area->cc);
index 3aa94fe..662dab4 100644 (file)
@@ -154,4 +154,11 @@ struct chsc_scm_info {
 
 int chsc_scm_info(struct chsc_scm_info *scm_area, u64 token);
 
+#ifdef CONFIG_SCM_BUS
+int scm_update_information(void);
+#else /* CONFIG_SCM_BUS */
+#define scm_update_information() 0
+#endif /* CONFIG_SCM_BUS */
+
+
 #endif
index 874b64a..aa4476e 100644 (file)
@@ -196,6 +196,47 @@ static void scmdev_setup(struct scm_device *scmdev, struct sale *sale,
        spin_lock_init(&scmdev->lock);
 }
 
+/*
+ * Check for state-changes, notify the driver and userspace.
+ */
+static void scmdev_update(struct scm_device *scmdev, struct sale *sale)
+{
+       struct scm_driver *scmdrv;
+       bool changed;
+
+       device_lock(&scmdev->dev);
+       changed = scmdev->attrs.rank != sale->rank ||
+                 scmdev->attrs.oper_state != sale->op_state;
+       scmdev->attrs.rank = sale->rank;
+       scmdev->attrs.oper_state = sale->op_state;
+       if (!scmdev->dev.driver)
+               goto out;
+       scmdrv = to_scm_drv(scmdev->dev.driver);
+       if (changed && scmdrv->notify)
+               scmdrv->notify(scmdev);
+out:
+       device_unlock(&scmdev->dev);
+       if (changed)
+               kobject_uevent(&scmdev->dev.kobj, KOBJ_CHANGE);
+}
+
+static int check_address(struct device *dev, void *data)
+{
+       struct scm_device *scmdev = to_scm_dev(dev);
+       struct sale *sale = data;
+
+       return scmdev->address == sale->sa;
+}
+
+static struct scm_device *scmdev_find(struct sale *sale)
+{
+       struct device *dev;
+
+       dev = bus_find_device(&scm_bus_type, NULL, sale, check_address);
+
+       return dev ? to_scm_dev(dev) : NULL;
+}
+
 static int scm_add(struct chsc_scm_info *scm_info, size_t num)
 {
        struct sale *sale, *scmal = scm_info->scmal;
@@ -203,6 +244,13 @@ static int scm_add(struct chsc_scm_info *scm_info, size_t num)
        int ret;
 
        for (sale = scmal; sale < scmal + num; sale++) {
+               scmdev = scmdev_find(sale);
+               if (scmdev) {
+                       scmdev_update(scmdev, sale);
+                       /* Release reference from scm_find(). */
+                       put_device(&scmdev->dev);
+                       continue;
+               }
                scmdev = kzalloc(sizeof(*scmdev), GFP_KERNEL);
                if (!scmdev)
                        return -ENODEV;
@@ -218,7 +266,7 @@ static int scm_add(struct chsc_scm_info *scm_info, size_t num)
        return 0;
 }
 
-static int scm_update_information(void)
+int scm_update_information(void)
 {
        struct chsc_scm_info *scm_info;
        u64 token = 0;