This patch intruduces an extension to the ap bus to notify device drivers
when the host AP configuration changes - i.e., adapters, domains or
control domains are added or removed. To that end, two new callbacks are
introduced for AP device drivers:

  void (*on_config_changed)(struct ap_config_info *new_config_info,
                            struct ap_config_info *old_config_info);

     This callback is invoked at the start of the AP bus scan
     function when it determines that the host AP configuration information
     has changed since the previous scan. This is done by storing
     an old and current QCI info struct and comparing them. If there is any
     difference, the callback is invoked.

     Note that when the AP bus scan detects that AP adapters, domains or
     control domains have been removed from the host's AP configuration, it
     will remove the associated devices from the AP bus subsystem's device
     model. This callback gives the device driver a chance to respond to
     the removal of the AP devices from the host configuration prior to
     calling the device driver's remove callback. The primary purpose of
     this callback is to allow the vfio_ap driver to do a bulk unplug of
     all affected adapters, domains and control domains from affected
     guests rather than unplugging them one at a time when the remove
     callback is invoked.

  void (*on_scan_complete)(struct ap_config_info *new_config_info,
                           struct ap_config_info *old_config_info);

     The on_scan_complete callback is invoked after the ap bus scan is
     complete if the host AP configuration data has changed.

     Note that when the AP bus scan detects that adapters, domains or
     control domains have been added to the host's configuration, it will
     create new devices in the AP bus subsystem's device model. The primary
     purpose of this callback is to allow the vfio_ap driver to do a bulk
     plug of all affected adapters, domains and control domains into
     affected guests rather than plugging them one at a time when the
     probe callback is invoked.

Please note that changes to the apmask and aqmask do not trigger
these two callbacks since the bus scan function is not invoked by changes
to those masks.

Signed-off-by: Harald Freudenberger <fre...@linux.ibm.com>
Signed-off-by: Tony Krowiak <akrow...@linux.ibm.com>
---
 drivers/s390/crypto/ap_bus.c | 91 +++++++++++++++++++++++++++++++++++-
 drivers/s390/crypto/ap_bus.h | 12 +++++
 2 files changed, 101 insertions(+), 2 deletions(-)

diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 7d8add952dd6..788bfdaadafd 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -82,6 +82,7 @@ static atomic64_t ap_scan_bus_count;
 static DECLARE_COMPLETION(ap_init_apqn_bindings_complete);
 
 static struct ap_config_info *ap_qci_info;
+static struct ap_config_info *ap_qci_info_old;
 
 /*
  * AP bus related debug feature things.
@@ -1579,6 +1580,52 @@ static int __match_queue_device_with_queue_id(struct 
device *dev, const void *da
                && AP_QID_QUEUE(to_ap_queue(dev)->qid) == (int)(long) data;
 }
 
+/* Helper function for notify_config_changed */
+static int __drv_notify_config_changed(struct device_driver *drv, void *data)
+{
+       struct ap_driver *ap_drv = to_ap_drv(drv);
+
+       if (try_module_get(drv->owner)) {
+               if (ap_drv->on_config_changed)
+                       ap_drv->on_config_changed(ap_qci_info,
+                                                 ap_qci_info_old);
+               module_put(drv->owner);
+       }
+
+       return 0;
+}
+
+/* Notify all drivers about an qci config change */
+static inline void notify_config_changed(void)
+{
+       bus_for_each_drv(&ap_bus_type, NULL, NULL,
+                        __drv_notify_config_changed);
+}
+
+/* Helper function for notify_scan_complete */
+static int __drv_notify_scan_complete(struct device_driver *drv, void *data)
+{
+       struct ap_driver *ap_drv = to_ap_drv(drv);
+
+       if (try_module_get(drv->owner)) {
+               if (ap_drv->on_scan_complete)
+                       ap_drv->on_scan_complete(ap_qci_info,
+                                                ap_qci_info_old);
+               module_put(drv->owner);
+       }
+
+       return 0;
+}
+
+/* Notify all drivers about bus scan complete */
+static inline void notify_scan_complete(void)
+{
+       bus_for_each_drv(&ap_bus_type, NULL, NULL,
+                        __drv_notify_scan_complete);
+}
+
+
+
 /*
  * Helper function for ap_scan_bus().
  * Remove card device and associated queue devices.
@@ -1857,15 +1904,51 @@ static inline void ap_scan_adapter(int ap)
        put_device(&ac->ap_dev.device);
 }
 
+/*
+ * ap_get_configuration
+ *
+ * Stores the host AP configuration information returned from the previous call
+ * to Query Configuration Information (QCI), then retrieves and stores the
+ * current AP configuration returned from QCI.
+ *
+ * Returns true if the host AP configuration changed between calls to QCI;
+ * otherwise, returns false.
+ */
+static bool ap_get_configuration(void)
+{
+       bool cfg_chg = false;
+
+       if (ap_qci_info) {
+               if (!ap_qci_info_old) {
+                       ap_qci_info_old = kzalloc(sizeof(*ap_qci_info_old),
+                                                 GFP_KERNEL);
+                       if (!ap_qci_info_old)
+                               return false;
+               } else {
+                       memcpy(ap_qci_info_old, ap_qci_info,
+                              sizeof(struct ap_config_info));
+               }
+               ap_fetch_qci_info(ap_qci_info);
+               cfg_chg = memcmp(ap_qci_info,
+                                ap_qci_info_old,
+                                sizeof(struct ap_config_info)) != 0;
+       }
+
+       return cfg_chg;
+}
+
 /**
  * ap_scan_bus(): Scan the AP bus for new devices
  * Runs periodically, workqueue timer (ap_config_time)
  */
 static void ap_scan_bus(struct work_struct *unused)
 {
-       int ap;
+       int ap, config_changed = 0;
 
-       ap_fetch_qci_info(ap_qci_info);
+       /* config change notify */
+       config_changed = ap_get_configuration();
+       if (config_changed)
+               notify_config_changed();
        ap_select_domain();
 
        AP_DBF_DBG("%s running\n", __func__);
@@ -1874,6 +1957,10 @@ static void ap_scan_bus(struct work_struct *unused)
        for (ap = 0; ap <= ap_max_adapter_id; ap++)
                ap_scan_adapter(ap);
 
+       /* scan complete notify */
+       if (config_changed)
+               notify_scan_complete();
+
        /* check if there is at least one queue available with default domain */
        if (ap_domain_index >= 0) {
                struct device *dev =
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index 95c9da072f81..e91082bd159c 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -146,6 +146,18 @@ struct ap_driver {
        int (*probe)(struct ap_device *);
        void (*remove)(struct ap_device *);
        int (*in_use)(unsigned long *apm, unsigned long *aqm);
+       /*
+        * Called at the start of the ap bus scan function when
+        * the crypto config information (qci) has changed.
+        */
+       void (*on_config_changed)(struct ap_config_info *new_config_info,
+                                 struct ap_config_info *old_config_info);
+       /*
+        * Called at the end of the ap bus scan function when
+        * the crypto config information (qci) has changed.
+        */
+       void (*on_scan_complete)(struct ap_config_info *new_config_info,
+                                struct ap_config_info *old_config_info);
 };
 
 #define to_ap_drv(x) container_of((x), struct ap_driver, driver)
-- 
2.21.1

Reply via email to