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

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

vfio: ccw: handle ccw command request



We implement the basic ccw command handling infrastructure
here:
1. Translate the ccw commands.
2. Issue the translated ccw commands to the device.
3. Once we get the execution result, update the guest SCSW
   with it.

Acked-by: default avatarPierre Morel <pmorel@linux.vnet.ibm.com>
Signed-off-by: default avatarDong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
Message-Id: <20170317031743.40128-9-bjsdjshi@linux.vnet.ibm.com>
Signed-off-by: default avatarCornelia Huck <cornelia.huck@de.ibm.com>
parent 060d2b5a
Loading
Loading
Loading
Loading
+114 −0
Original line number Diff line number Diff line
@@ -11,9 +11,13 @@
#include <linux/init.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/uuid.h>
#include <linux/mdev.h>

#include <asm/isc.h>

#include "ioasm.h"
#include "css.h"
#include "vfio_ccw_private.h"

/*
@@ -59,6 +63,109 @@ 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;

	orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm);

	/* Issue "Start Subchannel" */
	ccode = ssch(sch->schid, orb);

	switch (ccode) {
	case 0:
		/*
		 * Initialize device status information
		 */
		sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
		break;
	case 1:		/* Status pending */
	case 2:		/* Busy */
		return -EBUSY;
	case 3:		/* Device/path not operational */
	{
		lpm = orb->cmd.lpm;
		if (lpm != 0)
			sch->lpm &= ~lpm;
		else
			sch->lpm = 0;

		if (cio_update_schib(sch))
			return -ENODEV;

		return sch->lpm ? -EACCES : -ENODEV;
	}
	default:
		return ccode;
	}

	intparm = (u32)(addr_t)sch;
	private->intparm = 0;
	wait_event(private->wait_q, doing_io(private, intparm));

	if (scsw_is_solicited(&private->irb.scsw))
		cp_update_scsw(&private->cp, &private->irb.scsw);

	return 0;
}

/* Deal with the ccw command request from the userspace. */
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;

	memcpy(scsw, io_region->scsw_area, sizeof(*scsw));

	if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
		orb = (union orb *)io_region->orb_area;

		ret = cp_init(&private->cp, mdev_dev(mdev), orb);
		if (ret)
			return ret;

		ret = cp_prefetch(&private->cp);
		if (ret) {
			cp_free(&private->cp);
			return ret;
		}

		/* 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));
		}

		cp_free(&private->cp);
	} else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
		/* XXX: Handle halt. */
		ret = -EOPNOTSUPP;
	} else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
		/* XXX: Handle clear. */
		ret = -EOPNOTSUPP;
	} else {
		ret = -EOPNOTSUPP;
	}

	return ret;
}

/*
 * Sysfs interfaces
 */
@@ -113,12 +220,18 @@ static struct attribute_group vfio_subchannel_attr_group = {
static void vfio_ccw_sch_irq(struct subchannel *sch)
{
	struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
	struct irb *irb;

	inc_irq_stat(IRQIO_CIO);

	if (!private)
		return;

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

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

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

	return 0;
+20 −4
Original line number Diff line number Diff line
@@ -23,13 +23,26 @@ static int vfio_ccw_mdev_notifier(struct notifier_block *nb,
		return NOTIFY_STOP;

	/*
	 * TODO:
	 * Vendor drivers MUST unpin pages in response to an
	 * invalidation.
	 */
	if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP)
	if (action == VFIO_IOMMU_NOTIFY_DMA_UNMAP) {
		struct vfio_iommu_type1_dma_unmap *unmap = data;
		struct subchannel *sch = private->sch;

		if (!cp_iova_pinned(&private->cp, unmap->iova))
			return NOTIFY_OK;

		if (vfio_ccw_sch_quiesce(sch))
			return NOTIFY_BAD;

		if (cio_enable_subchannel(sch, (u32)(unsigned long)sch))
			return NOTIFY_BAD;

		cp_free(&private->cp);
		return NOTIFY_OK;
	}

	return NOTIFY_DONE;
}

@@ -167,7 +180,10 @@ static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
	region = &private->io_region;
	if (copy_from_user((void *)region + *ppos, buf, count))
		return -EFAULT;
	region->ret_code = 0;

	region->ret_code = vfio_ccw_sch_cmd_request(private);
	if (region->ret_code != 0)
		return region->ret_code;

	return count;
}
+14 −0
Original line number Diff line number Diff line
@@ -10,9 +10,11 @@
#ifndef _VFIO_CCW_PRIVATE_H_
#define _VFIO_CCW_PRIVATE_H_

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

#include "css.h"
#include "vfio_ccw_cp.h"

/**
 * struct vfio_ccw_private
@@ -22,6 +24,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
 */
struct vfio_ccw_private {
	struct subchannel	*sch;
@@ -30,11 +37,18 @@ struct vfio_ccw_private {
	struct mdev_device	*mdev;
	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;
} __aligned(8);

extern int vfio_ccw_mdev_reg(struct subchannel *sch);
extern void vfio_ccw_mdev_unreg(struct subchannel *sch);

extern int vfio_ccw_sch_quiesce(struct subchannel *sch);
extern int vfio_ccw_sch_cmd_request(struct vfio_ccw_private *private);

#endif