Hi Diana,

On 9/7/20 4:02 PM, Diana Craciun OSS wrote:
> Hi Eric,
> 
> On 9/4/2020 11:02 AM, Auger Eric wrote:
>> Hi Diana,
>>
>> On 8/26/20 11:33 AM, Diana Craciun wrote:
>>> This patch allows to set an eventfd for fsl-mc device interrupts
>>> and also to trigger the interrupt eventfd from userspace for testing.
>>>
>>> All fsl-mc device interrupts are MSIs. The MSIs are allocated from
>>> the MSI domain only once per DPRC and used by all the DPAA2 objects.
>>> The interrupts are managed by the DPRC in a pool of interrupts. Each
>>> device requests interrupts from this pool. The pool is allocated
>>> when the first virtual device is setting the interrupts.
>>> The pool of interrupts is protected by a lock.
>>>
>>> The DPRC has an interrupt of its own which indicates if the DPRC
>>> contents have changed. However, currently, the contents of a DPRC
>>> assigned to the guest cannot be changed at runtime, so this interrupt
>>> is not configured.
>>>
>>> Signed-off-by: Bharat Bhushan <bharat.bhus...@nxp.com>
>>> Signed-off-by: Diana Craciun <diana.crac...@oss.nxp.com>
>>> ---
>>>   drivers/vfio/fsl-mc/vfio_fsl_mc.c         |  18 ++-
>>>   drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c    | 160 +++++++++++++++++++++-
>>>   drivers/vfio/fsl-mc/vfio_fsl_mc_private.h |  10 ++
>>>   3 files changed, 186 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc.c
>>> b/drivers/vfio/fsl-mc/vfio_fsl_mc.c
>>> index 42014297b484..73834f488a94 100644
>>> --- a/drivers/vfio/fsl-mc/vfio_fsl_mc.c
>>> +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc.c
>>> @@ -147,12 +147,28 @@ static int vfio_fsl_mc_open(void *device_data)
>>>   static void vfio_fsl_mc_release(void *device_data)
>>>   {
>>>       struct vfio_fsl_mc_device *vdev = device_data;
>>> +    int ret;
>>>         mutex_lock(&vdev->reflck->lock);
>>>   -    if (!(--vdev->refcnt))
>>> +    if (!(--vdev->refcnt)) {
>>> +        struct fsl_mc_device *mc_dev = vdev->mc_dev;
>>> +        struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
>>> +        struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
>>> +
>>>           vfio_fsl_mc_regions_cleanup(vdev);
>>>   +        /* reset the device before cleaning up the interrupts */
>>> +        ret = dprc_reset_container(mc_cont->mc_io, 0,
>>> +              mc_cont->mc_handle,
>>> +              mc_cont->obj_desc.id,
>>> +              DPRC_RESET_OPTION_NON_RECURSIVE);
>> shouldn't you test ret?
>>> +
>>> +        vfio_fsl_mc_irqs_cleanup(vdev);
>>> +
>>> +        fsl_mc_cleanup_irq_pool(mc_cont);
>>> +    }
>>> +
>>>       mutex_unlock(&vdev->reflck->lock);
>>>         module_put(THIS_MODULE);
>>> diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c
>>> b/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c
>>> index 058aa97aa54a..409f3507fcf3 100644
>>> --- a/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c
>>> +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_intr.c
>>> @@ -29,12 +29,149 @@ static int vfio_fsl_mc_irq_unmask(struct
>>> vfio_fsl_mc_device *vdev,
>>>       return -EINVAL;
>>>   }
>>>   +int vfio_fsl_mc_irqs_allocate(struct vfio_fsl_mc_device *vdev)
>>> +{
>>> +    struct fsl_mc_device *mc_dev = vdev->mc_dev;
>>> +    struct vfio_fsl_mc_irq *mc_irq;
>>> +    int irq_count;
>>> +    int ret, i;
>>> +
>>> +    /* Device does not support any interrupt */
>> indent needs to be fixed
>>> +    if (mc_dev->obj_desc.irq_count == 0)
>>> +        return 0;
>>> +
>>> +    /* interrupts were already allocated for this device */
>>> +    if (vdev->mc_irqs)
>>> +        return 0;
>>> +
>>> +    irq_count = mc_dev->obj_desc.irq_count;
>>> +
>>> +    mc_irq = kcalloc(irq_count, sizeof(*mc_irq), GFP_KERNEL);
>>> +    if (!mc_irq)
>>> +        return -ENOMEM;
>>> +
>>> +    /* Allocate IRQs */
>>> +    ret = fsl_mc_allocate_irqs(mc_dev);
>>> +    if (ret) {
>>> +        kfree(mc_irq);
>>> +        return ret;
>>> +    }
>>> +
>>> +    for (i = 0; i < irq_count; i++) {
>>> +        mc_irq[i].count = 1;
>>> +        mc_irq[i].flags = VFIO_IRQ_INFO_EVENTFD;
>>> +    }
>>> +
>>> +    vdev->mc_irqs = mc_irq;
>>> +
>>> +    return 0;
>>> +}
>>> +
>>> +static irqreturn_t vfio_fsl_mc_irq_handler(int irq_num, void *arg)
>>> +{
>>> +    struct vfio_fsl_mc_irq *mc_irq = (struct vfio_fsl_mc_irq *)arg;
>>> +
>>> +    eventfd_signal(mc_irq->trigger, 1);
>>> +    return IRQ_HANDLED;
>>> +}
>>> +
>>> +static int vfio_set_trigger(struct vfio_fsl_mc_device *vdev,
>>> +                           int index, int fd)
>>> +{
>>> +    struct vfio_fsl_mc_irq *irq = &vdev->mc_irqs[index];
>>> +    struct eventfd_ctx *trigger;
>>> +    int hwirq;
>>> +    int ret;
>>> +
>>> +    hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq;
>>> +    if (irq->trigger) {
>>> +        free_irq(hwirq, irq);
>>> +        kfree(irq->name);
>>> +        eventfd_ctx_put(irq->trigger);
>>> +        irq->trigger = NULL;
>>> +    }
>>> +
>>> +    if (fd < 0) /* Disable only */
>>> +        return 0;
>>> +
>>> +    irq->name = kasprintf(GFP_KERNEL, "vfio-irq[%d](%s)",
>>> +                hwirq, dev_name(&vdev->mc_dev->dev));
>>> +    if (!irq->name)
>>> +        return -ENOMEM;
>>> +
>>> +    trigger = eventfd_ctx_fdget(fd);
>>> +    if (IS_ERR(trigger)) {
>>> +        kfree(irq->name);
>>> +        return PTR_ERR(trigger);
>>> +    }
>>> +
>>> +    irq->trigger = trigger;
>>> +
>>> +    ret = request_irq(hwirq, vfio_fsl_mc_irq_handler, 0,
>>> +          irq->name, irq);
>>> +    if (ret) {
>>> +        kfree(irq->name);
>>> +        eventfd_ctx_put(trigger);
>>> +        irq->trigger = NULL;
>>> +        return ret;
>>> +    }
>>> +
>>> +    return 0;
>>> +}
>>> +
>>>   static int vfio_fsl_mc_set_irq_trigger(struct vfio_fsl_mc_device
>>> *vdev,
>>>                          unsigned int index, unsigned int start,
>>>                          unsigned int count, u32 flags,
>>>                          void *data)
>>>   {
>>> -    return -EINVAL;
>>> +    struct fsl_mc_device *mc_dev = vdev->mc_dev;
>>> +    int ret, hwirq;
>>> +    struct vfio_fsl_mc_irq *irq;
>>> +    struct device *cont_dev = fsl_mc_cont_dev(&mc_dev->dev);
>>> +    struct fsl_mc_device *mc_cont = to_fsl_mc_device(cont_dev);
>>> +
>>> +    if (start != 0 || count != 1)
>>> +        return -EINVAL;
>>> +
>>> +    mutex_lock(&vdev->reflck->lock);
>>> +    ret = fsl_mc_populate_irq_pool(mc_cont,
>>> +            FSL_MC_IRQ_POOL_MAX_TOTAL_IRQS);
>>> +    if (ret)
>>> +        goto unlock;
>>> +
>>> +    ret = vfio_fsl_mc_irqs_allocate(vdev);
>> any reason the init is done in the set_irq() and not in the open() if
>> !vdev->refcnt?
> 
> Actually yes, there is a reason. It comes from the way the MSIs are
> handled.
> 
> The DPAA2  devices (the devices that can be assigned to the userspace)
> are organized in a hierarchical way. The DPRC is some kind of container
> (parent) for child devices. Only the DPRC is allocating interrupts (it
> allocates MSIs from the MSI domain). The MSIs cannot be allocated in the
> open() because the MSI setup needs an IOMMU cookie which is set when the
> IOMMU is set with VFIO_SET_IOMMU. But iommu is set later, after open(),
> so the MSIs should be allocated afterwards.
> 
> So, the DPRC is allocating a pool of MSIs and each child object will
> request a number of interrupts from that pool. This is what
> vfio_fsl_mc_irqs_allocate does: it requests a number of interrupts from
> the pool.

OK, thank you for the clarifications.

Thanks

Eric
> 
> 
>>> +    if (ret)
>>> +        goto unlock;
>>> +    mutex_unlock(&vdev->reflck->lock);
>>> +
>>> +    if (!count && (flags & VFIO_IRQ_SET_DATA_NONE))
>>> +        return vfio_set_trigger(vdev, index, -1);
>>> +
>>> +    if (flags & VFIO_IRQ_SET_DATA_EVENTFD) {
>>> +        s32 fd = *(s32 *)data;
>>> +
>>> +        return vfio_set_trigger(vdev, index, fd);
>>> +    }
>>> +
>>> +    hwirq = vdev->mc_dev->irqs[index]->msi_desc->irq;
>>> +
>>> +    irq = &vdev->mc_irqs[index];
>>> +
>>> +    if (flags & VFIO_IRQ_SET_DATA_NONE) {
>>> +        vfio_fsl_mc_irq_handler(hwirq, irq);
>>> +
>>> +    } else if (flags & VFIO_IRQ_SET_DATA_BOOL) {
>>> +        u8 trigger = *(u8 *)data;
>>> +
>>> +        if (trigger)
>>> +            vfio_fsl_mc_irq_handler(hwirq, irq);
>>> +    }
>>> +
>>> +    return 0;
>>> +
>>> +unlock:
>>> +    mutex_unlock(&vdev->reflck->lock);
>>> +    return ret;
>>>   }
>>>     int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device *vdev,
>>> @@ -61,3 +198,24 @@ int vfio_fsl_mc_set_irqs_ioctl(struct
>>> vfio_fsl_mc_device *vdev,
>>>         return ret;
>>>   }
>>> +
>>> +/* Free All IRQs for the given MC object */
>>> +void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev)
>>> +{
>>> +    struct fsl_mc_device *mc_dev = vdev->mc_dev;
>>> +    int irq_count = mc_dev->obj_desc.irq_count;
>>> +    int i;
>>> +
>>> +    /* Device does not support any interrupt or the interrupts
>>> +     * were not configured
>>> +     */
>>> +    if (mc_dev->obj_desc.irq_count == 0 || !vdev->mc_irqs)
>>> +        return;
>>> +
>>> +    for (i = 0; i < irq_count; i++)
>>> +        vfio_set_trigger(vdev, i, -1);
>>> +
>>> +    fsl_mc_free_irqs(mc_dev);
>>> +    kfree(vdev->mc_irqs);
>>> +    vdev->mc_irqs = NULL;
>>> +}
>>> diff --git a/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h
>>> b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h
>>> index d5b6fe891a48..bbfca8b55f8a 100644
>>> --- a/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h
>>> +++ b/drivers/vfio/fsl-mc/vfio_fsl_mc_private.h
>>> @@ -15,6 +15,13 @@
>>>   #define VFIO_FSL_MC_INDEX_TO_OFFSET(index)    \
>>>       ((u64)(index) << VFIO_FSL_MC_OFFSET_SHIFT)
>>>   +struct vfio_fsl_mc_irq {
>>> +    u32         flags;
>>> +    u32         count;
>>> +    struct eventfd_ctx  *trigger;
>>> +    char            *name;
>>> +};
>>> +
>>>   struct vfio_fsl_mc_reflck {
>>>       struct kref        kref;
>>>       struct mutex        lock;
>>> @@ -35,6 +42,7 @@ struct vfio_fsl_mc_device {
>>>       struct vfio_fsl_mc_region    *regions;
>>>       struct vfio_fsl_mc_reflck   *reflck;
>>>       struct mutex         igate;
>>> +    struct vfio_fsl_mc_irq      *mc_irqs;
>>>   };
>>>     extern int vfio_fsl_mc_set_irqs_ioctl(struct vfio_fsl_mc_device
>>> *vdev,
>>> @@ -42,4 +50,6 @@ extern int vfio_fsl_mc_set_irqs_ioctl(struct
>>> vfio_fsl_mc_device *vdev,
>>>                      unsigned int start, unsigned int count,
>>>                      void *data);
>>>   +void vfio_fsl_mc_irqs_cleanup(struct vfio_fsl_mc_device *vdev);
>>> +
>>>   #endif /* VFIO_FSL_MC_PRIVATE_H */
>>>
>> Thanks
>>
>> Eric
>>
> 
> Thanks,
> Diana
> 

Reply via email to