Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e5f84dba authored by Dong Jia Shi's avatar Dong Jia Shi Committed by Cornelia Huck
Browse files

vfio: ccw: return I/O results asynchronously



Introduce a singlethreaded workqueue to handle the I/O interrupts.
With the work added to this queue, we store the I/O results to the
io_region of the subchannel, then signal the userspace program to
handle the results.

Signed-off-by: default avatarDong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
Message-Id: <20170317031743.40128-13-bjsdjshi@linux.vnet.ibm.com>
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
parent 120e214e
Loading
Loading
Loading
Loading
+34 −24
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@
#include "css.h"
#include "vfio_ccw_private.h"

struct workqueue_struct *vfio_ccw_work_q;

/*
 * Helpers
 */
@@ -52,6 +54,7 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch)

			spin_lock_irq(sch->lock);
			private->completion = NULL;
			flush_workqueue(vfio_ccw_work_q);
			ret = cio_cancel_halt_clear(sch, &iretry);
		};

@@ -63,18 +66,12 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch)
	return ret;
}

static int doing_io(struct vfio_ccw_private *private, u32 intparm)
{
	return (private->intparm == intparm);
}

static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private)
{
	struct subchannel *sch;
	union orb *orb;
	int ccode;
	__u8 lpm;
	u32 intparm;

	sch = private->sch;

@@ -89,7 +86,7 @@ static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private)
		 * Initialize device status information
		 */
		sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
		break;
		return 0;
	case 1:		/* Status pending */
	case 2:		/* Busy */
		return -EBUSY;
@@ -109,15 +106,26 @@ static int vfio_ccw_sch_io_helper(struct vfio_ccw_private *private)
	default:
		return ccode;
	}
}

	intparm = (u32)(addr_t)sch;
	private->intparm = 0;
	wait_event(private->wait_q, doing_io(private, intparm));
static void vfio_ccw_sch_io_todo(struct work_struct *work)
{
	struct vfio_ccw_private *private;
	struct subchannel *sch;
	struct irb *irb;

	if (scsw_is_solicited(&private->irb.scsw))
		cp_update_scsw(&private->cp, &private->irb.scsw);
	private = container_of(work, struct vfio_ccw_private, io_work);
	irb = &private->irb;
	sch = private->sch;

	return 0;
	if (scsw_is_solicited(&irb->scsw)) {
		cp_update_scsw(&private->cp, &irb->scsw);
		cp_free(&private->cp);
	}
	memcpy(private->io_region.irb_area, irb, sizeof(*irb));

	if (private->io_trigger)
		eventfd_signal(private->io_trigger, 1);
}

/* Deal with the ccw command request from the userspace. */
@@ -126,7 +134,6 @@ int vfio_ccw_sch_cmd_request(struct vfio_ccw_private *private)
	struct mdev_device *mdev = private->mdev;
	union orb *orb;
	union scsw *scsw = &private->scsw;
	struct irb *irb = &private->irb;
	struct ccw_io_region *io_region = &private->io_region;
	int ret;

@@ -147,11 +154,7 @@ int vfio_ccw_sch_cmd_request(struct vfio_ccw_private *private)

		/* Start channel program and wait for I/O interrupt. */
		ret = vfio_ccw_sch_io_helper(private);
		if (!ret) {
			/* Get irb info and copy it to irb_area. */
			memcpy(io_region->irb_area, irb, sizeof(*irb));
		}

		if (!ret)
			cp_free(&private->cp);
	} else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
		/* XXX: Handle halt. */
@@ -229,8 +232,8 @@ static void vfio_ccw_sch_irq(struct subchannel *sch)

	irb = this_cpu_ptr(&cio_irb);
	memcpy(&private->irb, irb, sizeof(*irb));
	private->intparm = (u32)(addr_t)sch;
	wake_up(&private->wait_q);

	queue_work(vfio_ccw_work_q, &private->io_work);

	if (private->completion)
		complete(private->completion);
@@ -269,7 +272,7 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
	if (ret)
		goto out_rm_group;

	init_waitqueue_head(&private->wait_q);
	INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
	atomic_set(&private->avail, 1);

	return 0;
@@ -367,10 +370,16 @@ static int __init vfio_ccw_sch_init(void)
{
	int ret;

	vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw");
	if (!vfio_ccw_work_q)
		return -ENOMEM;

	isc_register(VFIO_CCW_ISC);
	ret = css_driver_register(&vfio_ccw_sch_driver);
	if (ret)
	if (ret) {
		isc_unregister(VFIO_CCW_ISC);
		destroy_workqueue(vfio_ccw_work_q);
	}

	return ret;
}
@@ -379,6 +388,7 @@ static void __exit vfio_ccw_sch_exit(void)
{
	css_driver_unregister(&vfio_ccw_sch_driver);
	isc_unregister(VFIO_CCW_ISC);
	destroy_workqueue(vfio_ccw_work_q);
}
module_init(vfio_ccw_sch_init);
module_exit(vfio_ccw_sch_exit);
+0 −3
Original line number Diff line number Diff line
@@ -202,9 +202,6 @@ static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
	if (region->ret_code != 0)
		return region->ret_code;

	if (private->io_trigger)
		eventfd_signal(private->io_trigger, 1);

	return count;
}

+3 −4
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@

#include <linux/completion.h>
#include <linux/eventfd.h>
#include <linux/workqueue.h>
#include <linux/vfio_ccw.h>

#include "css.h"
@@ -25,12 +26,11 @@
 * @mdev: pointer to the mediated device
 * @nb: notifier for vfio events
 * @io_region: MMIO region to input/output I/O arguments/results
 * @wait_q: wait for interrupt
 * @intparm: record current interrupt parameter, used for wait interrupt
 * @cp: channel program for the current I/O operation
 * @irb: irb info received from interrupt
 * @scsw: scsw info
 * @io_trigger: eventfd ctx for signaling userspace I/O results
 * @io_work: work for deferral process of I/O handling
 */
struct vfio_ccw_private {
	struct subchannel	*sch;
@@ -40,13 +40,12 @@ struct vfio_ccw_private {
	struct notifier_block	nb;
	struct ccw_io_region	io_region;

	wait_queue_head_t	wait_q;
	u32			intparm;
	struct channel_program	cp;
	struct irb		irb;
	union scsw		scsw;

	struct eventfd_ctx	*io_trigger;
	struct work_struct	io_work;
} __aligned(8);

extern int vfio_ccw_mdev_reg(struct subchannel *sch);