@@ -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,50 @@ 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 +1902,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 +1955,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 =
@@ -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)
@@ -87,7 +87,7 @@ static int vfio_ap_matrix_dev_create(void)
/* Fill in config info via PQAP(QCI), if available */
if (test_facility(12)) {
- ret = ap_qci(&matrix_dev->info);
+ ret = ap_qci(&matrix_dev->config_info);
if (ret)
goto matrix_alloc_err;
}
@@ -148,6 +148,8 @@ static int __init vfio_ap_init(void)
vfio_ap_drv.probe = vfio_ap_mdev_probe_queue;
vfio_ap_drv.remove = vfio_ap_mdev_remove_queue;
vfio_ap_drv.in_use = vfio_ap_mdev_resource_in_use;
+ vfio_ap_drv.on_config_changed = vfio_ap_on_cfg_changed;
+ vfio_ap_drv.on_scan_complete = vfio_ap_on_scan_complete;
vfio_ap_drv.ids = ap_queue_ids;
ret = ap_driver_register(&vfio_ap_drv, THIS_MODULE, VFIO_AP_DRV_NAME);
@@ -335,24 +335,14 @@ static void vfio_ap_mdev_commit_apcb(struct ap_matrix_mdev *matrix_mdev)
static void vfio_ap_mdev_filter_apcb(struct ap_matrix_mdev *matrix_mdev,
struct ap_matrix *shadow_apcb)
{
- int ret;
unsigned long apid, apqi, apqn;
- ret = ap_qci(&matrix_dev->info);
- if (ret)
- return;
-
- /*
- * Copy the adapters, domains and control domains to the shadow_apcb
- * from the matrix mdev, but only those that are assigned to the host's
- * AP configuration.
- */
bitmap_and(shadow_apcb->apm, matrix_mdev->matrix.apm,
- (unsigned long *)matrix_dev->info.apm, AP_DEVICES);
+ (unsigned long *)matrix_dev->config_info.apm, AP_DEVICES);
bitmap_and(shadow_apcb->aqm, matrix_mdev->matrix.aqm,
- (unsigned long *)matrix_dev->info.aqm, AP_DOMAINS);
+ (unsigned long *)matrix_dev->config_info.aqm, AP_DOMAINS);
bitmap_and(shadow_apcb->adm, matrix_mdev->matrix.adm,
- (unsigned long *)matrix_dev->info.adm, AP_DOMAINS);
+ (unsigned long *)matrix_dev->config_info.adm, AP_DOMAINS);
for_each_set_bit_inv(apid, shadow_apcb->apm, AP_DEVICES) {
for_each_set_bit_inv(apqi, shadow_apcb->aqm, AP_DOMAINS) {
@@ -385,7 +375,7 @@ static void vfio_ap_mdev_refresh_apcb(struct ap_matrix_mdev *matrix_mdev)
{
struct ap_matrix shadow_apcb;
- vfio_ap_matrix_init(&matrix_dev->info, &shadow_apcb);
+ vfio_ap_matrix_init(&matrix_dev->config_info, &shadow_apcb);
vfio_ap_mdev_filter_apcb(matrix_mdev, &shadow_apcb);
if (memcmp(&shadow_apcb, &matrix_mdev->shadow_apcb,
@@ -410,9 +400,9 @@ static int vfio_ap_mdev_create(struct kobject *kobj, struct mdev_device *mdev)
}
matrix_mdev->mdev = mdev;
- vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->matrix);
+ vfio_ap_matrix_init(&matrix_dev->config_info, &matrix_mdev->matrix);
init_waitqueue_head(&matrix_mdev->wait_for_kvm);
- vfio_ap_matrix_init(&matrix_dev->info, &matrix_mdev->shadow_apcb);
+ vfio_ap_matrix_init(&matrix_dev->config_info, &matrix_mdev->shadow_apcb);
hash_init(matrix_mdev->qtable);
mdev_set_drvdata(mdev, matrix_mdev);
matrix_mdev->pqap_hook.hook = handle_pqap;
@@ -952,7 +942,8 @@ static void vfio_ap_mdev_hot_plug_cdom(struct ap_matrix_mdev *matrix_mdev,
unsigned long domid)
{
if (!test_bit_inv(domid, matrix_mdev->shadow_apcb.adm) &&
- test_bit_inv(domid, (unsigned long *)matrix_dev->info.adm)) {
+ test_bit_inv(domid,
+ (unsigned long *)matrix_dev->config_info.adm)) {
set_bit_inv(domid, matrix_mdev->shadow_apcb.adm);
vfio_ap_mdev_commit_apcb(matrix_mdev);
}
@@ -1596,15 +1587,27 @@ int vfio_ap_mdev_probe_queue(struct ap_device *apdev)
vfio_ap_queue_link_mdev(q);
if (q->matrix_mdev) {
/*
- * If the KVM pointer is in the process of being set, wait until the
- * process has completed.
+ * If we are in the process of adding adapters and/or domains
+ * due to a change to the host's AP configuration, it is more
+ * efficient to refresh the guest's APCB in a single operation
+ * after the AP bus scan is complete (see the
+ * vfio_ap_on_scan_complete function) rather than to do the
+ * refresh for each queue added, so if that is not the case,
+ * let's go ahead and refresh the guest's APCB here.
*/
- wait_event_cmd(q->matrix_mdev->wait_for_kvm,
- !q->matrix_mdev->kvm_busy,
- mutex_unlock(&matrix_dev->lock),
- mutex_lock(&matrix_dev->lock));
+ if (bitmap_empty(matrix_dev->ap_add, AP_DEVICES) &&
+ bitmap_empty(matrix_dev->aq_add, AP_DOMAINS)) {
+ /*
+ * If the KVM pointer is in the process of being set, wait until the
+ * process has completed.
+ */
+ wait_event_cmd(q->matrix_mdev->wait_for_kvm,
+ !q->matrix_mdev->kvm_busy,
+ mutex_unlock(&matrix_dev->lock),
+ mutex_lock(&matrix_dev->lock));
- vfio_ap_mdev_refresh_apcb(q->matrix_mdev);
+ vfio_ap_mdev_refresh_apcb(q->matrix_mdev);
+ }
}
dev_set_drvdata(&apdev->device, q);
mutex_unlock(&matrix_dev->lock);
@@ -1646,8 +1649,172 @@ int vfio_ap_mdev_resource_in_use(unsigned long *apm, unsigned long *aqm)
if (!mutex_trylock(&matrix_dev->lock))
return -EBUSY;
+
ret = vfio_ap_mdev_verify_no_sharing(apm, aqm);
mutex_unlock(&matrix_dev->lock);
return ret;
}
+
+/*
+ * vfio_ap_mdev_unlink_apids
+ *
+ * @matrix_mdev: The matrix mediated device
+ *
+ * @apid_rem: The bitmap specifying the APIDs of the adapters removed from
+ * the host's AP configuration
+ *
+ * Unlinks @matrix_mdev from each queue assigned to @matrix_mdev whose APQN
+ * contains an APID specified in @apid_rem.
+ *
+ * Returns true if one or more AP queue devices were unlinked; otherwise,
+ * returns false.
+ */
+static bool vfio_ap_mdev_unlink_apids(struct ap_matrix_mdev *matrix_mdev,
+ unsigned long *apid_rem)
+{
+ int bkt, apid;
+ bool q_unlinked = false;
+ struct vfio_ap_queue *q;
+
+ hash_for_each(matrix_mdev->qtable, bkt, q, mdev_qnode) {
+ apid = AP_QID_CARD(q->apqn);
+ if (test_bit_inv(apid, apid_rem)) {
+ vfio_ap_mdev_reset_queue(q, 1);
+ vfio_ap_mdev_unlink_queue(q);
+ q_unlinked = true;
+ }
+ }
+
+ return q_unlinked;
+}
+
+/*
+ * vfio_ap_mdev_unlink_apqis
+ *
+ * @matrix_mdev: The matrix mediated device
+ *
+ * @apqi_rem: The bitmap specifying the APQIs of the domains removed from
+ * the host's AP configuration
+ *
+ * Unlinks @matrix_mdev from each queue assigned to @matrix_mdev whose APQN
+ * contains an APQI specified in @apqi_rem.
+ *
+ * Returns true if one or more AP queue devices were unlinked; otherwise,
+ * returns false.
+ */
+static bool vfio_ap_mdev_unlink_apqis(struct ap_matrix_mdev *matrix_mdev,
+ unsigned long *apqi_rem)
+{
+ int bkt, apqi;
+ bool q_unlinked = false;
+ struct vfio_ap_queue *q;
+
+ hash_for_each(matrix_mdev->qtable, bkt, q, mdev_qnode) {
+ apqi = AP_QID_QUEUE(q->apqn);
+ if (test_bit_inv(apqi, apqi_rem)) {
+ vfio_ap_mdev_reset_queue(q, 1);
+ vfio_ap_mdev_unlink_queue(q);
+ q_unlinked = true;
+ }
+ }
+
+ return q_unlinked;
+}
+
+static void vfio_ap_mdev_on_cfg_remove(void)
+{
+ bool refresh_apcb = false;
+ int ap_remove, aq_remove;
+ struct ap_matrix_mdev *matrix_mdev;
+ DECLARE_BITMAP(aprem, AP_DEVICES);
+ DECLARE_BITMAP(aqrem, AP_DOMAINS);
+ unsigned long *cur_apm, *cur_aqm, *prev_apm, *prev_aqm;
+
+ cur_apm = (unsigned long *)matrix_dev->config_info.apm;
+ cur_aqm = (unsigned long *)matrix_dev->config_info.aqm;
+ prev_apm = (unsigned long *)matrix_dev->config_info_prev.apm;
+ prev_aqm = (unsigned long *)matrix_dev->config_info_prev.aqm;
+
+ ap_remove = bitmap_andnot(aprem, prev_apm, cur_apm, AP_DEVICES);
+ aq_remove = bitmap_andnot(aqrem, prev_aqm, cur_aqm, AP_DOMAINS);
+
+ if (!ap_remove && !aq_remove)
+ return;
+
+ list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
+ if (ap_remove)
+ refresh_apcb = vfio_ap_mdev_unlink_apids(matrix_mdev,
+ aprem);
+
+ if (aq_remove)
+ refresh_apcb = vfio_ap_mdev_unlink_apqis(matrix_mdev,
+ aqrem);
+
+ if (refresh_apcb)
+ vfio_ap_mdev_refresh_apcb(matrix_mdev);
+ }
+}
+
+static void vfio_ap_mdev_on_cfg_add(void)
+{
+ unsigned long *cur_apm, *cur_aqm, *cur_adm;
+ unsigned long *prev_apm, *prev_aqm, *prev_adm;
+
+ cur_apm = (unsigned long *)matrix_dev->config_info.apm;
+ cur_aqm = (unsigned long *)matrix_dev->config_info.aqm;
+ cur_adm = (unsigned long *)matrix_dev->config_info.adm;
+
+ prev_apm = (unsigned long *)matrix_dev->config_info_prev.apm;
+ prev_aqm = (unsigned long *)matrix_dev->config_info_prev.aqm;
+ prev_adm = (unsigned long *)matrix_dev->config_info_prev.adm;
+
+ bitmap_andnot(matrix_dev->ap_add, cur_apm, prev_apm, AP_DEVICES);
+ bitmap_andnot(matrix_dev->aq_add, cur_aqm, prev_aqm, AP_DOMAINS);
+ bitmap_andnot(matrix_dev->ad_add, cur_adm, prev_adm, AP_DOMAINS);
+}
+
+void vfio_ap_init_mdev_matrixes(struct ap_config_info *config_info)
+{
+ struct ap_matrix_mdev *matrix_mdev;
+
+ list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
+ vfio_ap_matrix_init(config_info, &matrix_mdev->matrix);
+ vfio_ap_matrix_init(config_info, &matrix_mdev->shadow_apcb);
+ }
+}
+
+void vfio_ap_on_cfg_changed(struct ap_config_info *new_config_info,
+ struct ap_config_info *old_config_info)
+{
+ mutex_lock(&matrix_dev->lock);
+ memcpy(&matrix_dev->config_info, new_config_info,
+ sizeof(struct ap_config_info));
+ memcpy(&matrix_dev->config_info_prev, old_config_info,
+ sizeof(struct ap_config_info));
+ vfio_ap_init_mdev_matrixes(new_config_info);
+ vfio_ap_mdev_on_cfg_remove();
+ vfio_ap_mdev_on_cfg_add();
+ mutex_unlock(&matrix_dev->lock);
+}
+
+void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
+ struct ap_config_info *old_config_info)
+{
+ struct ap_matrix_mdev *matrix_mdev;
+
+ mutex_lock(&matrix_dev->lock);
+ list_for_each_entry(matrix_mdev, &matrix_dev->mdev_list, node) {
+ if (bitmap_intersects(matrix_mdev->matrix.apm,
+ matrix_dev->ap_add, AP_DEVICES) ||
+ bitmap_intersects(matrix_mdev->matrix.aqm,
+ matrix_dev->aq_add, AP_DOMAINS) ||
+ bitmap_intersects(matrix_mdev->matrix.adm,
+ matrix_dev->ad_add, AP_DOMAINS))
+ vfio_ap_mdev_refresh_apcb(matrix_mdev);
+ }
+
+ bitmap_clear(matrix_dev->ap_add, 0, AP_DEVICES);
+ bitmap_clear(matrix_dev->aq_add, 0, AP_DOMAINS);
+ mutex_unlock(&matrix_dev->lock);
+}
@@ -29,7 +29,9 @@
* ap_matrix_dev - the AP matrix device structure
* @device: generic device structure associated with the AP matrix device
* @available_instances: number of mediated matrix devices that can be created
- * @info: the struct containing the output from the PQAP(QCI) instruction
+ * @config_info: the current host AP configuration information
+ * @config_info_prev: the host AP configuration information from the previous
+ * configuration changed notification
* mdev_list: the list of mediated matrix devices created
* lock: mutex for locking the AP matrix device. This lock will be
* taken every time we fiddle with state managed by the vfio_ap
@@ -40,10 +42,14 @@
struct ap_matrix_dev {
struct device device;
atomic_t available_instances;
- struct ap_config_info info;
+ struct ap_config_info config_info;
+ struct ap_config_info config_info_prev;
struct list_head mdev_list;
struct mutex lock;
struct ap_driver *vfio_ap_drv;
+ DECLARE_BITMAP(ap_add, AP_DEVICES);
+ DECLARE_BITMAP(aq_add, AP_DOMAINS);
+ DECLARE_BITMAP(ad_add, AP_DOMAINS);
};
extern struct ap_matrix_dev *matrix_dev;
@@ -111,4 +117,9 @@ void vfio_ap_mdev_remove_queue(struct ap_device *queue);
int vfio_ap_mdev_resource_in_use(unsigned long *apm, unsigned long *aqm);
+void vfio_ap_on_cfg_changed(struct ap_config_info *new_config_info,
+ struct ap_config_info *old_config_info);
+void vfio_ap_on_scan_complete(struct ap_config_info *new_config_info,
+ struct ap_config_info *old_config_info);
+
#endif /* _VFIO_AP_PRIVATE_H_ */