diff mbox series

[v2,2/2] s390x/ap: Wire up the device request notifier interface

Message ID 20230602141125.448833-3-akrowiak@linux.ibm.com (mailing list archive)
State New, archived
Headers show
Series s390x/ap: fix hang when mdev attached to guest is removed | expand

Commit Message

Anthony Krowiak June 2, 2023, 2:11 p.m. UTC
Let's wire up the device request notifier interface to handle device unplug
requests for AP.

Signed-off-by: Tony Krowiak <akrowiak@linux.ibm.com>
Link: https://lore.kernel.org/qemu-devel/20230530225544.280031-1-akrowiak@linux.ibm.com/
---
 hw/vfio/ap.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 113 insertions(+)

Comments

Cédric Le Goater June 2, 2023, 2:28 p.m. UTC | #1
Hello Tony,

On 6/2/23 16:11, Tony Krowiak wrote:
> Let's wire up the device request notifier interface to handle device unplug
> requests for AP.
> 
> Signed-off-by: Tony Krowiak <akrowiak@linux.ibm.com>
> Link: https://lore.kernel.org/qemu-devel/20230530225544.280031-1-akrowiak@linux.ibm.com/
> ---
>   hw/vfio/ap.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 113 insertions(+)
> 
> diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c
> index e0dd561e85a3..6e21d1da5a70 100644
> --- a/hw/vfio/ap.c
> +++ b/hw/vfio/ap.c
> @@ -18,6 +18,8 @@
>   #include "hw/vfio/vfio-common.h"
>   #include "hw/s390x/ap-device.h"
>   #include "qemu/error-report.h"
> +#include "qemu/event_notifier.h"
> +#include "qemu/main-loop.h"
>   #include "qemu/module.h"
>   #include "qemu/option.h"
>   #include "qemu/config-file.h"
> @@ -33,6 +35,7 @@
>   struct VFIOAPDevice {
>       APDevice apdev;
>       VFIODevice vdev;
> +    EventNotifier req_notifier;
>   };
>   
>   OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE)
> @@ -84,10 +87,110 @@ static VFIOGroup *vfio_ap_get_group(VFIOAPDevice *vapdev, Error **errp)
>       return vfio_get_group(groupid, &address_space_memory, errp);
>   }
>   
> +static void vfio_ap_req_notifier_handler(void *opaque)
> +{
> +    VFIOAPDevice *vapdev = opaque;
> +    Error *err = NULL;
> +
> +    if (!event_notifier_test_and_clear(&vapdev->req_notifier)) {
> +        return;
> +    }
> +
> +    qdev_unplug(DEVICE(vapdev), &err);
> +
> +    if (err) {
> +        warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name);
> +    }
> +}
> +
> +static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
> +                                          unsigned int irq, Error **errp)
> +{
> +    int fd;
> +    size_t argsz;
> +    IOHandler *fd_read;
> +    EventNotifier *notifier;
> +    struct vfio_irq_info *irq_info;
> +    VFIODevice *vdev = &vapdev->vdev;
> +
> +    switch (irq) {

Do you have plan for more interrupts ? If not, you could convert the
'switch' statement to a simple 'if' and remove the fd_read variable.

> +    case VFIO_AP_REQ_IRQ_INDEX:
> +        notifier = &vapdev->req_notifier;
> +        fd_read = vfio_ap_req_notifier_handler;
> +        break;
> +    default:
> +        error_setg(errp, "vfio: Unsupported device irq(%d)", irq);
> +        return;
> +    }
> +
> +    if (vdev->num_irqs < irq + 1) {
> +        error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)",
> +                   irq, vdev->num_irqs);
> +        return;
> +    }
> +
> +    argsz = sizeof(*irq_info);
> +    irq_info = g_malloc0(argsz);
> +    irq_info->index = irq;
> +    irq_info->argsz = argsz;
> +
> +    if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO,
> +              irq_info) < 0 || irq_info->count < 1) {
> +        error_setg_errno(errp, errno, "vfio: Error getting irq info");
> +        goto out_free_info;
> +    }
> +
> +    if (event_notifier_init(notifier, 0)) {
> +        error_setg_errno(errp, errno,
> +                         "vfio: Unable to init event notifier for irq (%d)",
> +                         irq);
> +        goto out_free_info;
> +    }
> +
> +    fd = event_notifier_get_fd(notifier);
> +    qemu_set_fd_handler(fd, fd_read, NULL, vapdev);
> +
> +    if (vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd,
> +                               errp)) {
> +        qemu_set_fd_handler(fd, NULL, NULL, vapdev);
> +        event_notifier_cleanup(notifier);
> +    }
> +
> +out_free_info:
> +    g_free(irq_info);
> +
> +}
> +
> +static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev,
> +                                            unsigned int irq)
> +{
> +    Error *err = NULL;
> +    EventNotifier *notifier;
> +
> +    switch (irq) {
> +    case VFIO_AP_REQ_IRQ_INDEX:
> +        notifier = &vapdev->req_notifier;
> +        break;
> +    default:
> +        error_report("vfio: Unsupported device irq(%d)", irq);
> +        return;
> +    }
> +
> +    if (vfio_set_irq_signaling(&vapdev->vdev, irq, 0,
> +                               VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
> +        warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name);
> +    }
> +
> +    qemu_set_fd_handler(event_notifier_get_fd(notifier),
> +                        NULL, NULL, vapdev);
> +    event_notifier_cleanup(notifier);
> +}
> +
>   static void vfio_ap_realize(DeviceState *dev, Error **errp)
>   {
>       int ret;
>       char *mdevid;
> +    Error *err = NULL;
>       VFIOGroup *vfio_group;
>       APDevice *apdev = AP_DEVICE(dev);
>       VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev);
> @@ -116,6 +219,15 @@ static void vfio_ap_realize(DeviceState *dev, Error **errp)
>           goto out_get_dev_err;
>       }
>   
> +    vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err);
> +    if (err) {
> +        /*
> +         * Report this error, but do not make it a failing condition.
> +         * Lack of this IRQ in the host does not prevent normal operation.
> +         */
> +        error_report_err(err);

May be issue a warning instead ?

Thanks,

C.

> +    }
> +
>       return;
>   
>   out_get_dev_err:
> @@ -129,6 +241,7 @@ static void vfio_ap_unrealize(DeviceState *dev)
>       VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev);
>       VFIOGroup *group = vapdev->vdev.group;
>   
> +    vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX);
>       vfio_ap_put_device(vapdev);
>       vfio_put_group(group);
>   }
Anthony Krowiak June 2, 2023, 3 p.m. UTC | #2
On 6/2/23 10:28 AM, Cédric Le Goater wrote:
> Hello Tony,
> 
> On 6/2/23 16:11, Tony Krowiak wrote:
>> Let's wire up the device request notifier interface to handle device 
>> unplug
>> requests for AP.
>>
>> Signed-off-by: Tony Krowiak <akrowiak@linux.ibm.com>
>> Link: 
>> https://lore.kernel.org/qemu-devel/20230530225544.280031-1-akrowiak@linux.ibm.com/
>> ---
>>   hw/vfio/ap.c | 113 +++++++++++++++++++++++++++++++++++++++++++++++++++
>>   1 file changed, 113 insertions(+)
>>
>> diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c
>> index e0dd561e85a3..6e21d1da5a70 100644
>> --- a/hw/vfio/ap.c
>> +++ b/hw/vfio/ap.c
>> @@ -18,6 +18,8 @@
>>   #include "hw/vfio/vfio-common.h"
>>   #include "hw/s390x/ap-device.h"
>>   #include "qemu/error-report.h"
>> +#include "qemu/event_notifier.h"
>> +#include "qemu/main-loop.h"
>>   #include "qemu/module.h"
>>   #include "qemu/option.h"
>>   #include "qemu/config-file.h"
>> @@ -33,6 +35,7 @@
>>   struct VFIOAPDevice {
>>       APDevice apdev;
>>       VFIODevice vdev;
>> +    EventNotifier req_notifier;
>>   };
>>   OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE)
>> @@ -84,10 +87,110 @@ static VFIOGroup *vfio_ap_get_group(VFIOAPDevice 
>> *vapdev, Error **errp)
>>       return vfio_get_group(groupid, &address_space_memory, errp);
>>   }
>> +static void vfio_ap_req_notifier_handler(void *opaque)
>> +{
>> +    VFIOAPDevice *vapdev = opaque;
>> +    Error *err = NULL;
>> +
>> +    if (!event_notifier_test_and_clear(&vapdev->req_notifier)) {
>> +        return;
>> +    }
>> +
>> +    qdev_unplug(DEVICE(vapdev), &err);
>> +
>> +    if (err) {
>> +        warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name);
>> +    }
>> +}
>> +
>> +static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
>> +                                          unsigned int irq, Error 
>> **errp)
>> +{
>> +    int fd;
>> +    size_t argsz;
>> +    IOHandler *fd_read;
>> +    EventNotifier *notifier;
>> +    struct vfio_irq_info *irq_info;
>> +    VFIODevice *vdev = &vapdev->vdev;
>> +
>> +    switch (irq) {
> 
> Do you have plan for more interrupts ? If not, you could convert the
> 'switch' statement to a simple 'if' and remove the fd_read variable.

At this time there are no plans for further interrupts, but the switch 
does make it easy to add them:) On the other hand, I have no problem 
changing this to an 'if' statement. The fd_read variable is used below 
in the call to the qemu_set_fd_handler() function, so I can't get rid of it.

> 
>> +    case VFIO_AP_REQ_IRQ_INDEX:
>> +        notifier = &vapdev->req_notifier;
>> +        fd_read = vfio_ap_req_notifier_handler;
>> +        break;
>> +    default:
>> +        error_setg(errp, "vfio: Unsupported device irq(%d)", irq);
>> +        return;
>> +    }
>> +
>> +    if (vdev->num_irqs < irq + 1) {
>> +        error_setg(errp, "vfio: IRQ %u not available (number of irqs 
>> %u)",
>> +                   irq, vdev->num_irqs);
>> +        return;
>> +    }
>> +
>> +    argsz = sizeof(*irq_info);
>> +    irq_info = g_malloc0(argsz);
>> +    irq_info->index = irq;
>> +    irq_info->argsz = argsz;
>> +
>> +    if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO,
>> +              irq_info) < 0 || irq_info->count < 1) {
>> +        error_setg_errno(errp, errno, "vfio: Error getting irq info");
>> +        goto out_free_info;
>> +    }
>> +
>> +    if (event_notifier_init(notifier, 0)) {
>> +        error_setg_errno(errp, errno,
>> +                         "vfio: Unable to init event notifier for irq 
>> (%d)",
>> +                         irq);
>> +        goto out_free_info;
>> +    }
>> +
>> +    fd = event_notifier_get_fd(notifier);
>> +    qemu_set_fd_handler(fd, fd_read, NULL, vapdev);
>> +
>> +    if (vfio_set_irq_signaling(vdev, irq, 0, 
>> VFIO_IRQ_SET_ACTION_TRIGGER, fd,
>> +                               errp)) {
>> +        qemu_set_fd_handler(fd, NULL, NULL, vapdev);
>> +        event_notifier_cleanup(notifier);
>> +    }
>> +
>> +out_free_info:
>> +    g_free(irq_info);
>> +
>> +}
>> +
>> +static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev,
>> +                                            unsigned int irq)
>> +{
>> +    Error *err = NULL;
>> +    EventNotifier *notifier;
>> +
>> +    switch (irq) {
>> +    case VFIO_AP_REQ_IRQ_INDEX:
>> +        notifier = &vapdev->req_notifier;
>> +        break;
>> +    default:
>> +        error_report("vfio: Unsupported device irq(%d)", irq);
>> +        return;
>> +    }
>> +
>> +    if (vfio_set_irq_signaling(&vapdev->vdev, irq, 0,
>> +                               VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
>> +        warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name);
>> +    }
>> +
>> +    qemu_set_fd_handler(event_notifier_get_fd(notifier),
>> +                        NULL, NULL, vapdev);
>> +    event_notifier_cleanup(notifier);
>> +}
>> +
>>   static void vfio_ap_realize(DeviceState *dev, Error **errp)
>>   {
>>       int ret;
>>       char *mdevid;
>> +    Error *err = NULL;
>>       VFIOGroup *vfio_group;
>>       APDevice *apdev = AP_DEVICE(dev);
>>       VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev);
>> @@ -116,6 +219,15 @@ static void vfio_ap_realize(DeviceState *dev, 
>> Error **errp)
>>           goto out_get_dev_err;
>>       }
>> +    vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err);
>> +    if (err) {
>> +        /*
>> +         * Report this error, but do not make it a failing condition.
>> +         * Lack of this IRQ in the host does not prevent normal 
>> operation.
>> +         */
>> +        error_report_err(err);
> 
> May be issue a warning instead ?

Given the comment above - i.e., not a failing condition - it probably 
makes sense to make it a warning.

> 
> Thanks,
> 
> C.
> 
>> +    }
>> +
>>       return;
>>   out_get_dev_err:
>> @@ -129,6 +241,7 @@ static void vfio_ap_unrealize(DeviceState *dev)
>>       VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev);
>>       VFIOGroup *group = vapdev->vdev.group;
>> +    vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX);
>>       vfio_ap_put_device(vapdev);
>>       vfio_put_group(group);
>>   }
>
diff mbox series

Patch

diff --git a/hw/vfio/ap.c b/hw/vfio/ap.c
index e0dd561e85a3..6e21d1da5a70 100644
--- a/hw/vfio/ap.c
+++ b/hw/vfio/ap.c
@@ -18,6 +18,8 @@ 
 #include "hw/vfio/vfio-common.h"
 #include "hw/s390x/ap-device.h"
 #include "qemu/error-report.h"
+#include "qemu/event_notifier.h"
+#include "qemu/main-loop.h"
 #include "qemu/module.h"
 #include "qemu/option.h"
 #include "qemu/config-file.h"
@@ -33,6 +35,7 @@ 
 struct VFIOAPDevice {
     APDevice apdev;
     VFIODevice vdev;
+    EventNotifier req_notifier;
 };
 
 OBJECT_DECLARE_SIMPLE_TYPE(VFIOAPDevice, VFIO_AP_DEVICE)
@@ -84,10 +87,110 @@  static VFIOGroup *vfio_ap_get_group(VFIOAPDevice *vapdev, Error **errp)
     return vfio_get_group(groupid, &address_space_memory, errp);
 }
 
+static void vfio_ap_req_notifier_handler(void *opaque)
+{
+    VFIOAPDevice *vapdev = opaque;
+    Error *err = NULL;
+
+    if (!event_notifier_test_and_clear(&vapdev->req_notifier)) {
+        return;
+    }
+
+    qdev_unplug(DEVICE(vapdev), &err);
+
+    if (err) {
+        warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name);
+    }
+}
+
+static void vfio_ap_register_irq_notifier(VFIOAPDevice *vapdev,
+                                          unsigned int irq, Error **errp)
+{
+    int fd;
+    size_t argsz;
+    IOHandler *fd_read;
+    EventNotifier *notifier;
+    struct vfio_irq_info *irq_info;
+    VFIODevice *vdev = &vapdev->vdev;
+
+    switch (irq) {
+    case VFIO_AP_REQ_IRQ_INDEX:
+        notifier = &vapdev->req_notifier;
+        fd_read = vfio_ap_req_notifier_handler;
+        break;
+    default:
+        error_setg(errp, "vfio: Unsupported device irq(%d)", irq);
+        return;
+    }
+
+    if (vdev->num_irqs < irq + 1) {
+        error_setg(errp, "vfio: IRQ %u not available (number of irqs %u)",
+                   irq, vdev->num_irqs);
+        return;
+    }
+
+    argsz = sizeof(*irq_info);
+    irq_info = g_malloc0(argsz);
+    irq_info->index = irq;
+    irq_info->argsz = argsz;
+
+    if (ioctl(vdev->fd, VFIO_DEVICE_GET_IRQ_INFO,
+              irq_info) < 0 || irq_info->count < 1) {
+        error_setg_errno(errp, errno, "vfio: Error getting irq info");
+        goto out_free_info;
+    }
+
+    if (event_notifier_init(notifier, 0)) {
+        error_setg_errno(errp, errno,
+                         "vfio: Unable to init event notifier for irq (%d)",
+                         irq);
+        goto out_free_info;
+    }
+
+    fd = event_notifier_get_fd(notifier);
+    qemu_set_fd_handler(fd, fd_read, NULL, vapdev);
+
+    if (vfio_set_irq_signaling(vdev, irq, 0, VFIO_IRQ_SET_ACTION_TRIGGER, fd,
+                               errp)) {
+        qemu_set_fd_handler(fd, NULL, NULL, vapdev);
+        event_notifier_cleanup(notifier);
+    }
+
+out_free_info:
+    g_free(irq_info);
+
+}
+
+static void vfio_ap_unregister_irq_notifier(VFIOAPDevice *vapdev,
+                                            unsigned int irq)
+{
+    Error *err = NULL;
+    EventNotifier *notifier;
+
+    switch (irq) {
+    case VFIO_AP_REQ_IRQ_INDEX:
+        notifier = &vapdev->req_notifier;
+        break;
+    default:
+        error_report("vfio: Unsupported device irq(%d)", irq);
+        return;
+    }
+
+    if (vfio_set_irq_signaling(&vapdev->vdev, irq, 0,
+                               VFIO_IRQ_SET_ACTION_TRIGGER, -1, &err)) {
+        warn_reportf_err(err, VFIO_MSG_PREFIX, vapdev->vdev.name);
+    }
+
+    qemu_set_fd_handler(event_notifier_get_fd(notifier),
+                        NULL, NULL, vapdev);
+    event_notifier_cleanup(notifier);
+}
+
 static void vfio_ap_realize(DeviceState *dev, Error **errp)
 {
     int ret;
     char *mdevid;
+    Error *err = NULL;
     VFIOGroup *vfio_group;
     APDevice *apdev = AP_DEVICE(dev);
     VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev);
@@ -116,6 +219,15 @@  static void vfio_ap_realize(DeviceState *dev, Error **errp)
         goto out_get_dev_err;
     }
 
+    vfio_ap_register_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX, &err);
+    if (err) {
+        /*
+         * Report this error, but do not make it a failing condition.
+         * Lack of this IRQ in the host does not prevent normal operation.
+         */
+        error_report_err(err);
+    }
+
     return;
 
 out_get_dev_err:
@@ -129,6 +241,7 @@  static void vfio_ap_unrealize(DeviceState *dev)
     VFIOAPDevice *vapdev = VFIO_AP_DEVICE(apdev);
     VFIOGroup *group = vapdev->vdev.group;
 
+    vfio_ap_unregister_irq_notifier(vapdev, VFIO_AP_REQ_IRQ_INDEX);
     vfio_ap_put_device(vapdev);
     vfio_put_group(group);
 }