mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-09-22 09:34:56 +02:00
s390/vfio_ccw: Implement a crw lock
commit16b0798024upstream. Unlike the channel_program struct, which covers synchronous I/O submissions and asynchronous interrupts, the CRW region relies exclusively on asynchronous events coming from hardware. Implement a lock to manage the list of those payloads, to ensure they are read cohesively. Fixes:3f02cb2fd9("vfio-ccw: Wire up the CRW irq and CRW region") Cc: stable@vger.kernel.org Reviewed-by: Matthew Rosato <mjrosato@linux.ibm.com> Reviewed-by: Farhan Ali <alifm@linux.ibm.com> Signed-off-by: Eric Farman <farman@linux.ibm.com> Signed-off-by: Christian Borntraeger <borntraeger@linux.ibm.com> [farman@linux.ibm.com: resolved merge conflict] Signed-off-by: Eric Farman <farman@linux.ibm.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
f0d189d95e
commit
a3d60ae241
@@ -93,18 +93,13 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
|
||||
loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
|
||||
struct ccw_crw_region *region;
|
||||
struct vfio_ccw_crw *crw;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
if (pos + count > sizeof(*region))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
crw = list_first_entry_or_null(&private->crw,
|
||||
struct vfio_ccw_crw, next);
|
||||
|
||||
if (crw)
|
||||
list_del(&crw->next);
|
||||
|
||||
if (i >= private->num_regions) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
@@ -113,6 +108,16 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
|
||||
i = array_index_nospec(i, private->num_regions);
|
||||
region = private->region[i].data;
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
crw = list_first_entry_or_null(&private->crw,
|
||||
struct vfio_ccw_crw, next);
|
||||
|
||||
if (crw)
|
||||
list_del(&crw->next);
|
||||
|
||||
/* Drop CRW lock while copying to userspace */
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
|
||||
if (crw)
|
||||
memcpy(®ion->crw, &crw->crw, sizeof(region->crw));
|
||||
|
||||
@@ -122,15 +127,16 @@ static ssize_t vfio_ccw_crw_region_read(struct vfio_ccw_private *private,
|
||||
ret = count;
|
||||
|
||||
region->crw = 0;
|
||||
|
||||
out:
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
kfree(crw);
|
||||
|
||||
/* Notify the guest if more CRWs are on our queue */
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
if (!list_empty(&private->crw) && private->crw_trigger)
|
||||
eventfd_signal(private->crw_trigger, 1);
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
|
||||
out:
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -118,11 +118,14 @@ void vfio_ccw_sch_io_todo(struct work_struct *work)
|
||||
void vfio_ccw_crw_todo(struct work_struct *work)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
unsigned long flags;
|
||||
|
||||
private = container_of(work, struct vfio_ccw_private, crw_work);
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
if (!list_empty(&private->crw) && private->crw_trigger)
|
||||
eventfd_signal(private->crw_trigger, 1);
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
}
|
||||
|
||||
void vfio_ccw_notoper_todo(struct work_struct *work)
|
||||
@@ -286,6 +289,7 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
|
||||
unsigned int rsid)
|
||||
{
|
||||
struct vfio_ccw_crw *crw;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* If unable to allocate a CRW, just drop the event and
|
||||
@@ -303,7 +307,9 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
|
||||
crw->crw.erc = erc;
|
||||
crw->crw.rsid = rsid;
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
list_add_tail(&crw->next, &private->crw);
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
queue_work(vfio_ccw_work_q, &private->crw_work);
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev)
|
||||
INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
|
||||
INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
|
||||
INIT_WORK(&private->notoper_work, vfio_ccw_notoper_todo);
|
||||
spin_lock_init(&private->crw_lock);
|
||||
|
||||
private->cp.guest_cp = kcalloc(CCWCHAIN_LEN_MAX, sizeof(struct ccw1),
|
||||
GFP_KERNEL);
|
||||
@@ -132,6 +133,7 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev)
|
||||
struct vfio_ccw_private *private =
|
||||
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||
struct vfio_ccw_crw *crw, *temp;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Ensure these work items are fully drained, so none can
|
||||
@@ -147,10 +149,12 @@ static void vfio_ccw_mdev_release_dev(struct vfio_device *vdev)
|
||||
cancel_work_sync(&private->crw_work);
|
||||
flush_work(&private->notoper_work);
|
||||
|
||||
spin_lock_irqsave(&private->crw_lock, flags);
|
||||
list_for_each_entry_safe(crw, temp, &private->crw, next) {
|
||||
list_del(&crw->next);
|
||||
kfree(crw);
|
||||
}
|
||||
spin_unlock_irqrestore(&private->crw_lock, flags);
|
||||
|
||||
kmem_cache_free(vfio_ccw_crw_region, private->crw_region);
|
||||
kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
|
||||
|
||||
@@ -98,6 +98,8 @@ struct vfio_ccw_parent {
|
||||
* @cp: channel program for the current I/O operation
|
||||
* @irb: irb info received from interrupt
|
||||
* @scsw: scsw info
|
||||
* @crw_lock: serialization of CRW list information
|
||||
* @crw: list of Channel Report Word elements
|
||||
* @io_trigger: eventfd ctx for signaling userspace I/O results
|
||||
* @crw_trigger: eventfd ctx for signaling userspace CRW information
|
||||
* @req_trigger: eventfd ctx for signaling userspace to return device
|
||||
@@ -120,6 +122,8 @@ struct vfio_ccw_private {
|
||||
struct channel_program cp;
|
||||
struct irb irb;
|
||||
union scsw scsw;
|
||||
|
||||
spinlock_t crw_lock;
|
||||
struct list_head crw;
|
||||
|
||||
struct eventfd_ctx *io_trigger;
|
||||
|
||||
Reference in New Issue
Block a user