- support for sending halt/clear requests to the device
- various bug fixes -----BEGIN PGP SIGNATURE----- iQJGBAABCAAwFiEEw9DWbcNiT/aowBjO3s9rk8bwL68FAlzBdxkSHGNvaHVja0By ZWRoYXQuY29tAAoJEN7Pa5PG8C+v4LgP/ihL2qHmn++nvQ8N7vP2FPyf8UPtpmrZ YZ6uZGLgBVy8GnlJK8M3IexyJmrmJeUqbPXrkrjG64lkVnZSRVI5xRp5oWZA7XLo DZgaj0Q8jsPlWzFr8oMTXtjzsGLnSjzRUiIfVNDXOBNUZKrrWaL53ObS7Mwh0Iju MyS7bskYDMp5fP4NTITHgiKmVN4J0r3xuTgCKx5UZbb/ulAVkrHT2kI0MNsIWSl/ eUannzh3Gh+lEfyjlyIxtjxmNMBMdP15O6kyX9MrGyqEEVzmv7o3YkCRv/dFhiTL pTUBytrwQB7vcanZtIfHuh7NJH1RLGK1tOtK1VMrizZXDMqqMkT6m4UZ621ngvhf Mmv0D7Swn8VGTLf+UhmWQtVxfDHVacGqVoJLcLdEbK8M0d1EjRMieLXmisrU29qB KerQCFHqvyPUbeqKLN645EUm06VdFs137psHDe5Qc/0U+xoWQkMoyHeJhuJploMT UXfvKddsOM53flkL7yAeKEeFTaTLpYrnNSEUw2FdIlO8L0Gpv/sk7eN6X6ZNnAOu zMeat/hCwrBHyqN9I+VwquFPsD6D6+FjcwSPO/xAkTWqTcMz23AAF4i0lpBfx9Xz Puq3nABhNoywBMwCrX5oFolj49wG1vEEoN6sZpuKm3dqC5EAr3PDt/6R171p7za5 VE5bvMAeqhX0 =SjP/ -----END PGP SIGNATURE----- Merge tag 'vfio-ccw-20190425' of https://git.kernel.org/pub/scm/linux/kernel/git/kvms390/vfio-ccw into features Pull vfio-ccw from Cornelia Huck with the following changes: - support for sending halt/clear requests to the device - various bug fixes
This commit is contained in:
commit
5513bc8eb4
|
@ -20,5 +20,6 @@ obj-$(CONFIG_CCWGROUP) += ccwgroup.o
|
|||
qdio-objs := qdio_main.o qdio_thinint.o qdio_debug.o qdio_setup.o
|
||||
obj-$(CONFIG_QDIO) += qdio.o
|
||||
|
||||
vfio_ccw-objs += vfio_ccw_drv.o vfio_ccw_cp.o vfio_ccw_ops.o vfio_ccw_fsm.o
|
||||
vfio_ccw-objs += vfio_ccw_drv.o vfio_ccw_cp.o vfio_ccw_ops.o vfio_ccw_fsm.o \
|
||||
vfio_ccw_async.o
|
||||
obj-$(CONFIG_VFIO_CCW) += vfio_ccw.o
|
||||
|
|
|
@ -233,6 +233,7 @@ int hsch(struct subchannel_id schid)
|
|||
|
||||
return ccode;
|
||||
}
|
||||
EXPORT_SYMBOL(hsch);
|
||||
|
||||
static inline int __xsch(struct subchannel_id schid)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,88 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* Async I/O region for vfio_ccw
|
||||
*
|
||||
* Copyright Red Hat, Inc. 2019
|
||||
*
|
||||
* Author(s): Cornelia Huck <cohuck@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/vfio.h>
|
||||
#include <linux/mdev.h>
|
||||
|
||||
#include "vfio_ccw_private.h"
|
||||
|
||||
static ssize_t vfio_ccw_async_region_read(struct vfio_ccw_private *private,
|
||||
char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS;
|
||||
loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
|
||||
struct ccw_cmd_region *region;
|
||||
int ret;
|
||||
|
||||
if (pos + count > sizeof(*region))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
region = private->region[i].data;
|
||||
if (copy_to_user(buf, (void *)region + pos, count))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = count;
|
||||
mutex_unlock(&private->io_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vfio_ccw_async_region_write(struct vfio_ccw_private *private,
|
||||
const char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS;
|
||||
loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
|
||||
struct ccw_cmd_region *region;
|
||||
int ret;
|
||||
|
||||
if (pos + count > sizeof(*region))
|
||||
return -EINVAL;
|
||||
|
||||
if (!mutex_trylock(&private->io_mutex))
|
||||
return -EAGAIN;
|
||||
|
||||
region = private->region[i].data;
|
||||
if (copy_from_user((void *)region + pos, buf, count)) {
|
||||
ret = -EFAULT;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_ASYNC_REQ);
|
||||
|
||||
ret = region->ret_code ? region->ret_code : count;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&private->io_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vfio_ccw_async_region_release(struct vfio_ccw_private *private,
|
||||
struct vfio_ccw_region *region)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
const struct vfio_ccw_regops vfio_ccw_async_region_ops = {
|
||||
.read = vfio_ccw_async_region_read,
|
||||
.write = vfio_ccw_async_region_write,
|
||||
.release = vfio_ccw_async_region_release,
|
||||
};
|
||||
|
||||
int vfio_ccw_register_async_dev_regions(struct vfio_ccw_private *private)
|
||||
{
|
||||
return vfio_ccw_register_dev_region(private,
|
||||
VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD,
|
||||
&vfio_ccw_async_region_ops,
|
||||
sizeof(struct ccw_cmd_region),
|
||||
VFIO_REGION_INFO_FLAG_READ |
|
||||
VFIO_REGION_INFO_FLAG_WRITE,
|
||||
private->cmd_region);
|
||||
}
|
|
@ -362,6 +362,7 @@ static void cp_unpin_free(struct channel_program *cp)
|
|||
struct ccwchain *chain, *temp;
|
||||
int i;
|
||||
|
||||
cp->initialized = false;
|
||||
list_for_each_entry_safe(chain, temp, &cp->ccwchain_list, next) {
|
||||
for (i = 0; i < chain->ch_len; i++) {
|
||||
pfn_array_table_unpin_free(chain->ch_pat + i,
|
||||
|
@ -732,6 +733,9 @@ int cp_init(struct channel_program *cp, struct device *mdev, union orb *orb)
|
|||
*/
|
||||
cp->orb.cmd.c64 = 1;
|
||||
|
||||
if (!ret)
|
||||
cp->initialized = true;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -746,7 +750,8 @@ int cp_init(struct channel_program *cp, struct device *mdev, union orb *orb)
|
|||
*/
|
||||
void cp_free(struct channel_program *cp)
|
||||
{
|
||||
cp_unpin_free(cp);
|
||||
if (cp->initialized)
|
||||
cp_unpin_free(cp);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -791,6 +796,10 @@ int cp_prefetch(struct channel_program *cp)
|
|||
struct ccwchain *chain;
|
||||
int len, idx, ret;
|
||||
|
||||
/* this is an error in the caller */
|
||||
if (!cp->initialized)
|
||||
return -EINVAL;
|
||||
|
||||
list_for_each_entry(chain, &cp->ccwchain_list, next) {
|
||||
len = chain->ch_len;
|
||||
for (idx = 0; idx < len; idx++) {
|
||||
|
@ -826,6 +835,10 @@ union orb *cp_get_orb(struct channel_program *cp, u32 intparm, u8 lpm)
|
|||
struct ccwchain *chain;
|
||||
struct ccw1 *cpa;
|
||||
|
||||
/* this is an error in the caller */
|
||||
if (!cp->initialized)
|
||||
return NULL;
|
||||
|
||||
orb = &cp->orb;
|
||||
|
||||
orb->cmd.intparm = intparm;
|
||||
|
@ -862,6 +875,9 @@ void cp_update_scsw(struct channel_program *cp, union scsw *scsw)
|
|||
u32 cpa = scsw->cmd.cpa;
|
||||
u32 ccw_head;
|
||||
|
||||
if (!cp->initialized)
|
||||
return;
|
||||
|
||||
/*
|
||||
* LATER:
|
||||
* For now, only update the cmd.cpa part. We may need to deal with
|
||||
|
@ -898,6 +914,9 @@ bool cp_iova_pinned(struct channel_program *cp, u64 iova)
|
|||
struct ccwchain *chain;
|
||||
int i;
|
||||
|
||||
if (!cp->initialized)
|
||||
return false;
|
||||
|
||||
list_for_each_entry(chain, &cp->ccwchain_list, next) {
|
||||
for (i = 0; i < chain->ch_len; i++)
|
||||
if (pfn_array_table_iova_pinned(chain->ch_pat + i,
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
* @ccwchain_list: list head of ccwchains
|
||||
* @orb: orb for the currently processed ssch request
|
||||
* @mdev: the mediated device to perform page pinning/unpinning
|
||||
* @initialized: whether this instance is actually initialized
|
||||
*
|
||||
* @ccwchain_list is the head of a ccwchain list, that contents the
|
||||
* translated result of the guest channel program that pointed out by
|
||||
|
@ -30,6 +31,7 @@ struct channel_program {
|
|||
struct list_head ccwchain_list;
|
||||
union orb orb;
|
||||
struct device *mdev;
|
||||
bool initialized;
|
||||
};
|
||||
|
||||
extern int cp_init(struct channel_program *cp, struct device *mdev,
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
* VFIO based Physical Subchannel device driver
|
||||
*
|
||||
* Copyright IBM Corp. 2017
|
||||
* Copyright Red Hat, Inc. 2019
|
||||
*
|
||||
* Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
|
||||
* Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cohuck@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
|
@ -23,6 +25,7 @@
|
|||
|
||||
struct workqueue_struct *vfio_ccw_work_q;
|
||||
static struct kmem_cache *vfio_ccw_io_region;
|
||||
static struct kmem_cache *vfio_ccw_cmd_region;
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
|
@ -40,26 +43,30 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch)
|
|||
if (ret != -EBUSY)
|
||||
goto out_unlock;
|
||||
|
||||
iretry = 255;
|
||||
do {
|
||||
iretry = 255;
|
||||
|
||||
ret = cio_cancel_halt_clear(sch, &iretry);
|
||||
while (ret == -EBUSY) {
|
||||
/*
|
||||
* Flush all I/O and wait for
|
||||
* cancel/halt/clear completion.
|
||||
*/
|
||||
private->completion = &completion;
|
||||
spin_unlock_irq(sch->lock);
|
||||
|
||||
if (ret == -EIO) {
|
||||
pr_err("vfio_ccw: could not quiesce subchannel 0.%x.%04x!\n",
|
||||
sch->schid.ssid, sch->schid.sch_no);
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Flush all I/O and wait for
|
||||
* cancel/halt/clear completion.
|
||||
*/
|
||||
private->completion = &completion;
|
||||
spin_unlock_irq(sch->lock);
|
||||
|
||||
if (ret == -EBUSY)
|
||||
wait_for_completion_timeout(&completion, 3*HZ);
|
||||
|
||||
spin_lock_irq(sch->lock);
|
||||
private->completion = NULL;
|
||||
flush_workqueue(vfio_ccw_work_q);
|
||||
ret = cio_cancel_halt_clear(sch, &iretry);
|
||||
};
|
||||
|
||||
private->completion = NULL;
|
||||
flush_workqueue(vfio_ccw_work_q);
|
||||
spin_lock_irq(sch->lock);
|
||||
ret = cio_disable_subchannel(sch);
|
||||
} while (ret == -EBUSY);
|
||||
out_unlock:
|
||||
|
@ -84,7 +91,9 @@ static void vfio_ccw_sch_io_todo(struct work_struct *work)
|
|||
if (is_final)
|
||||
cp_free(&private->cp);
|
||||
}
|
||||
mutex_lock(&private->io_mutex);
|
||||
memcpy(private->io_region->irb_area, irb, sizeof(*irb));
|
||||
mutex_unlock(&private->io_mutex);
|
||||
|
||||
if (private->io_trigger)
|
||||
eventfd_signal(private->io_trigger, 1);
|
||||
|
@ -108,7 +117,7 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
|
|||
{
|
||||
struct pmcw *pmcw = &sch->schib.pmcw;
|
||||
struct vfio_ccw_private *private;
|
||||
int ret;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (pmcw->qf) {
|
||||
dev_warn(&sch->dev, "vfio: ccw: does not support QDIO: %s\n",
|
||||
|
@ -122,13 +131,17 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
|
|||
|
||||
private->io_region = kmem_cache_zalloc(vfio_ccw_io_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!private->io_region) {
|
||||
kfree(private);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!private->io_region)
|
||||
goto out_free;
|
||||
|
||||
private->cmd_region = kmem_cache_zalloc(vfio_ccw_cmd_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!private->cmd_region)
|
||||
goto out_free;
|
||||
|
||||
private->sch = sch;
|
||||
dev_set_drvdata(&sch->dev, private);
|
||||
mutex_init(&private->io_mutex);
|
||||
|
||||
spin_lock_irq(sch->lock);
|
||||
private->state = VFIO_CCW_STATE_NOT_OPER;
|
||||
|
@ -152,7 +165,10 @@ out_disable:
|
|||
cio_disable_subchannel(sch);
|
||||
out_free:
|
||||
dev_set_drvdata(&sch->dev, NULL);
|
||||
kmem_cache_free(vfio_ccw_io_region, private->io_region);
|
||||
if (private->cmd_region)
|
||||
kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
|
||||
if (private->io_region)
|
||||
kmem_cache_free(vfio_ccw_io_region, private->io_region);
|
||||
kfree(private);
|
||||
return ret;
|
||||
}
|
||||
|
@ -167,6 +183,7 @@ static int vfio_ccw_sch_remove(struct subchannel *sch)
|
|||
|
||||
dev_set_drvdata(&sch->dev, NULL);
|
||||
|
||||
kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
|
||||
kmem_cache_free(vfio_ccw_io_region, private->io_region);
|
||||
kfree(private);
|
||||
|
||||
|
@ -241,7 +258,7 @@ static struct css_driver vfio_ccw_sch_driver = {
|
|||
|
||||
static int __init vfio_ccw_sch_init(void)
|
||||
{
|
||||
int ret;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
vfio_ccw_work_q = create_singlethread_workqueue("vfio-ccw");
|
||||
if (!vfio_ccw_work_q)
|
||||
|
@ -251,20 +268,30 @@ static int __init vfio_ccw_sch_init(void)
|
|||
sizeof(struct ccw_io_region), 0,
|
||||
SLAB_ACCOUNT, 0,
|
||||
sizeof(struct ccw_io_region), NULL);
|
||||
if (!vfio_ccw_io_region) {
|
||||
destroy_workqueue(vfio_ccw_work_q);
|
||||
return -ENOMEM;
|
||||
}
|
||||
if (!vfio_ccw_io_region)
|
||||
goto out_err;
|
||||
|
||||
vfio_ccw_cmd_region = kmem_cache_create_usercopy("vfio_ccw_cmd_region",
|
||||
sizeof(struct ccw_cmd_region), 0,
|
||||
SLAB_ACCOUNT, 0,
|
||||
sizeof(struct ccw_cmd_region), NULL);
|
||||
if (!vfio_ccw_cmd_region)
|
||||
goto out_err;
|
||||
|
||||
isc_register(VFIO_CCW_ISC);
|
||||
ret = css_driver_register(&vfio_ccw_sch_driver);
|
||||
if (ret) {
|
||||
isc_unregister(VFIO_CCW_ISC);
|
||||
kmem_cache_destroy(vfio_ccw_io_region);
|
||||
destroy_workqueue(vfio_ccw_work_q);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
return ret;
|
||||
|
||||
out_err:
|
||||
kmem_cache_destroy(vfio_ccw_cmd_region);
|
||||
kmem_cache_destroy(vfio_ccw_io_region);
|
||||
destroy_workqueue(vfio_ccw_work_q);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void __exit vfio_ccw_sch_exit(void)
|
||||
|
|
|
@ -3,8 +3,10 @@
|
|||
* Finite state machine for vfio-ccw device handling
|
||||
*
|
||||
* Copyright IBM Corp. 2017
|
||||
* Copyright Red Hat, Inc. 2019
|
||||
*
|
||||
* Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cohuck@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/vfio.h>
|
||||
|
@ -28,9 +30,12 @@ static int fsm_io_helper(struct vfio_ccw_private *private)
|
|||
sch = private->sch;
|
||||
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
private->state = VFIO_CCW_STATE_BUSY;
|
||||
|
||||
orb = cp_get_orb(&private->cp, (u32)(addr_t)sch, sch->lpm);
|
||||
if (!orb) {
|
||||
ret = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Issue "Start Subchannel" */
|
||||
ccode = ssch(sch->schid, orb);
|
||||
|
@ -42,6 +47,7 @@ static int fsm_io_helper(struct vfio_ccw_private *private)
|
|||
*/
|
||||
sch->schib.scsw.cmd.actl |= SCSW_ACTL_START_PEND;
|
||||
ret = 0;
|
||||
private->state = VFIO_CCW_STATE_CP_PENDING;
|
||||
break;
|
||||
case 1: /* Status pending */
|
||||
case 2: /* Busy */
|
||||
|
@ -64,6 +70,76 @@ static int fsm_io_helper(struct vfio_ccw_private *private)
|
|||
default:
|
||||
ret = ccode;
|
||||
}
|
||||
out:
|
||||
spin_unlock_irqrestore(sch->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsm_do_halt(struct vfio_ccw_private *private)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
unsigned long flags;
|
||||
int ccode;
|
||||
int ret;
|
||||
|
||||
sch = private->sch;
|
||||
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
|
||||
/* Issue "Halt Subchannel" */
|
||||
ccode = hsch(sch->schid);
|
||||
|
||||
switch (ccode) {
|
||||
case 0:
|
||||
/*
|
||||
* Initialize device status information
|
||||
*/
|
||||
sch->schib.scsw.cmd.actl |= SCSW_ACTL_HALT_PEND;
|
||||
ret = 0;
|
||||
break;
|
||||
case 1: /* Status pending */
|
||||
case 2: /* Busy */
|
||||
ret = -EBUSY;
|
||||
break;
|
||||
case 3: /* Device not operational */
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
default:
|
||||
ret = ccode;
|
||||
}
|
||||
spin_unlock_irqrestore(sch->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int fsm_do_clear(struct vfio_ccw_private *private)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
unsigned long flags;
|
||||
int ccode;
|
||||
int ret;
|
||||
|
||||
sch = private->sch;
|
||||
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
|
||||
/* Issue "Clear Subchannel" */
|
||||
ccode = csch(sch->schid);
|
||||
|
||||
switch (ccode) {
|
||||
case 0:
|
||||
/*
|
||||
* Initialize device status information
|
||||
*/
|
||||
sch->schib.scsw.cmd.actl = SCSW_ACTL_CLEAR_PEND;
|
||||
/* TODO: check what else we might need to clear */
|
||||
ret = 0;
|
||||
break;
|
||||
case 3: /* Device not operational */
|
||||
ret = -ENODEV;
|
||||
break;
|
||||
default:
|
||||
ret = ccode;
|
||||
}
|
||||
spin_unlock_irqrestore(sch->lock, flags);
|
||||
return ret;
|
||||
}
|
||||
|
@ -102,6 +178,30 @@ static void fsm_io_busy(struct vfio_ccw_private *private,
|
|||
private->io_region->ret_code = -EBUSY;
|
||||
}
|
||||
|
||||
static void fsm_io_retry(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
private->io_region->ret_code = -EAGAIN;
|
||||
}
|
||||
|
||||
static void fsm_async_error(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
struct ccw_cmd_region *cmd_region = private->cmd_region;
|
||||
|
||||
pr_err("vfio-ccw: FSM: %s request from state:%d\n",
|
||||
cmd_region->command == VFIO_CCW_ASYNC_CMD_HSCH ? "halt" :
|
||||
cmd_region->command == VFIO_CCW_ASYNC_CMD_CSCH ? "clear" :
|
||||
"<unknown>", private->state);
|
||||
cmd_region->ret_code = -EIO;
|
||||
}
|
||||
|
||||
static void fsm_async_retry(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
private->cmd_region->ret_code = -EAGAIN;
|
||||
}
|
||||
|
||||
static void fsm_disabled_irq(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
|
@ -130,8 +230,7 @@ static void fsm_io_request(struct vfio_ccw_private *private,
|
|||
struct mdev_device *mdev = private->mdev;
|
||||
char *errstr = "request";
|
||||
|
||||
private->state = VFIO_CCW_STATE_BUSY;
|
||||
|
||||
private->state = VFIO_CCW_STATE_CP_PROCESSING;
|
||||
memcpy(scsw, io_region->scsw_area, sizeof(*scsw));
|
||||
|
||||
if (scsw->cmd.fctl & SCSW_FCTL_START_FUNC) {
|
||||
|
@ -166,21 +265,41 @@ static void fsm_io_request(struct vfio_ccw_private *private,
|
|||
}
|
||||
return;
|
||||
} else if (scsw->cmd.fctl & SCSW_FCTL_HALT_FUNC) {
|
||||
/* XXX: Handle halt. */
|
||||
/* halt is handled via the async cmd region */
|
||||
io_region->ret_code = -EOPNOTSUPP;
|
||||
goto err_out;
|
||||
} else if (scsw->cmd.fctl & SCSW_FCTL_CLEAR_FUNC) {
|
||||
/* XXX: Handle clear. */
|
||||
/* clear is handled via the async cmd region */
|
||||
io_region->ret_code = -EOPNOTSUPP;
|
||||
goto err_out;
|
||||
}
|
||||
|
||||
err_out:
|
||||
private->state = VFIO_CCW_STATE_IDLE;
|
||||
trace_vfio_ccw_io_fctl(scsw->cmd.fctl, get_schid(private),
|
||||
io_region->ret_code, errstr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Deal with an async request from userspace.
|
||||
*/
|
||||
static void fsm_async_request(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
struct ccw_cmd_region *cmd_region = private->cmd_region;
|
||||
|
||||
switch (cmd_region->command) {
|
||||
case VFIO_CCW_ASYNC_CMD_HSCH:
|
||||
cmd_region->ret_code = fsm_do_halt(private);
|
||||
break;
|
||||
case VFIO_CCW_ASYNC_CMD_CSCH:
|
||||
cmd_region->ret_code = fsm_do_clear(private);
|
||||
break;
|
||||
default:
|
||||
/* should not happen? */
|
||||
cmd_region->ret_code = -EINVAL;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Got an interrupt for a normal io (state busy).
|
||||
*/
|
||||
|
@ -204,21 +323,31 @@ fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS] = {
|
|||
[VFIO_CCW_STATE_NOT_OPER] = {
|
||||
[VFIO_CCW_EVENT_NOT_OPER] = fsm_nop,
|
||||
[VFIO_CCW_EVENT_IO_REQ] = fsm_io_error,
|
||||
[VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_error,
|
||||
[VFIO_CCW_EVENT_INTERRUPT] = fsm_disabled_irq,
|
||||
},
|
||||
[VFIO_CCW_STATE_STANDBY] = {
|
||||
[VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper,
|
||||
[VFIO_CCW_EVENT_IO_REQ] = fsm_io_error,
|
||||
[VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_error,
|
||||
[VFIO_CCW_EVENT_INTERRUPT] = fsm_irq,
|
||||
},
|
||||
[VFIO_CCW_STATE_IDLE] = {
|
||||
[VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper,
|
||||
[VFIO_CCW_EVENT_IO_REQ] = fsm_io_request,
|
||||
[VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_request,
|
||||
[VFIO_CCW_EVENT_INTERRUPT] = fsm_irq,
|
||||
},
|
||||
[VFIO_CCW_STATE_BUSY] = {
|
||||
[VFIO_CCW_STATE_CP_PROCESSING] = {
|
||||
[VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper,
|
||||
[VFIO_CCW_EVENT_IO_REQ] = fsm_io_retry,
|
||||
[VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_retry,
|
||||
[VFIO_CCW_EVENT_INTERRUPT] = fsm_irq,
|
||||
},
|
||||
[VFIO_CCW_STATE_CP_PENDING] = {
|
||||
[VFIO_CCW_EVENT_NOT_OPER] = fsm_notoper,
|
||||
[VFIO_CCW_EVENT_IO_REQ] = fsm_io_busy,
|
||||
[VFIO_CCW_EVENT_ASYNC_REQ] = fsm_async_request,
|
||||
[VFIO_CCW_EVENT_INTERRUPT] = fsm_irq,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -3,13 +3,17 @@
|
|||
* Physical device callbacks for vfio_ccw
|
||||
*
|
||||
* Copyright IBM Corp. 2017
|
||||
* Copyright Red Hat, Inc. 2019
|
||||
*
|
||||
* Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
|
||||
* Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cohuck@redhat.com>
|
||||
*/
|
||||
|
||||
#include <linux/vfio.h>
|
||||
#include <linux/mdev.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include "vfio_ccw_private.h"
|
||||
|
||||
|
@ -130,11 +134,12 @@ static int vfio_ccw_mdev_remove(struct mdev_device *mdev)
|
|||
|
||||
if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
|
||||
(private->state != VFIO_CCW_STATE_STANDBY)) {
|
||||
if (!vfio_ccw_mdev_reset(mdev))
|
||||
if (!vfio_ccw_sch_quiesce(private->sch))
|
||||
private->state = VFIO_CCW_STATE_STANDBY;
|
||||
/* The state will be NOT_OPER on error. */
|
||||
}
|
||||
|
||||
cp_free(&private->cp);
|
||||
private->mdev = NULL;
|
||||
atomic_inc(&private->avail);
|
||||
|
||||
|
@ -146,20 +151,66 @@ static int vfio_ccw_mdev_open(struct mdev_device *mdev)
|
|||
struct vfio_ccw_private *private =
|
||||
dev_get_drvdata(mdev_parent_dev(mdev));
|
||||
unsigned long events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
|
||||
int ret;
|
||||
|
||||
private->nb.notifier_call = vfio_ccw_mdev_notifier;
|
||||
|
||||
return vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
|
||||
&events, &private->nb);
|
||||
ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
|
||||
&events, &private->nb);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = vfio_ccw_register_async_dev_regions(private);
|
||||
if (ret)
|
||||
vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
|
||||
&private->nb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vfio_ccw_mdev_release(struct mdev_device *mdev)
|
||||
{
|
||||
struct vfio_ccw_private *private =
|
||||
dev_get_drvdata(mdev_parent_dev(mdev));
|
||||
int i;
|
||||
|
||||
if ((private->state != VFIO_CCW_STATE_NOT_OPER) &&
|
||||
(private->state != VFIO_CCW_STATE_STANDBY)) {
|
||||
if (!vfio_ccw_mdev_reset(mdev))
|
||||
private->state = VFIO_CCW_STATE_STANDBY;
|
||||
/* The state will be NOT_OPER on error. */
|
||||
}
|
||||
|
||||
cp_free(&private->cp);
|
||||
vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
|
||||
&private->nb);
|
||||
|
||||
for (i = 0; i < private->num_regions; i++)
|
||||
private->region[i].ops->release(private, &private->region[i]);
|
||||
|
||||
private->num_regions = 0;
|
||||
kfree(private->region);
|
||||
private->region = NULL;
|
||||
}
|
||||
|
||||
static ssize_t vfio_ccw_mdev_read_io_region(struct vfio_ccw_private *private,
|
||||
char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
|
||||
struct ccw_io_region *region;
|
||||
int ret;
|
||||
|
||||
if (pos + count > sizeof(*region))
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&private->io_mutex);
|
||||
region = private->io_region;
|
||||
if (copy_to_user(buf, (void *)region + pos, count))
|
||||
ret = -EFAULT;
|
||||
else
|
||||
ret = count;
|
||||
mutex_unlock(&private->io_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev,
|
||||
|
@ -167,18 +218,54 @@ static ssize_t vfio_ccw_mdev_read(struct mdev_device *mdev,
|
|||
size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
|
||||
struct vfio_ccw_private *private;
|
||||
struct ccw_io_region *region;
|
||||
|
||||
if (*ppos + count > sizeof(*region))
|
||||
return -EINVAL;
|
||||
|
||||
private = dev_get_drvdata(mdev_parent_dev(mdev));
|
||||
region = private->io_region;
|
||||
if (copy_to_user(buf, (void *)region + *ppos, count))
|
||||
return -EFAULT;
|
||||
|
||||
return count;
|
||||
if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
|
||||
return -EINVAL;
|
||||
|
||||
switch (index) {
|
||||
case VFIO_CCW_CONFIG_REGION_INDEX:
|
||||
return vfio_ccw_mdev_read_io_region(private, buf, count, ppos);
|
||||
default:
|
||||
index -= VFIO_CCW_NUM_REGIONS;
|
||||
return private->region[index].ops->read(private, buf, count,
|
||||
ppos);
|
||||
}
|
||||
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static ssize_t vfio_ccw_mdev_write_io_region(struct vfio_ccw_private *private,
|
||||
const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
|
||||
struct ccw_io_region *region;
|
||||
int ret;
|
||||
|
||||
if (pos + count > sizeof(*region))
|
||||
return -EINVAL;
|
||||
|
||||
if (!mutex_trylock(&private->io_mutex))
|
||||
return -EAGAIN;
|
||||
|
||||
region = private->io_region;
|
||||
if (copy_from_user((void *)region + pos, buf, count)) {
|
||||
ret = -EFAULT;
|
||||
goto out_unlock;
|
||||
}
|
||||
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ);
|
||||
if (region->ret_code != 0)
|
||||
private->state = VFIO_CCW_STATE_IDLE;
|
||||
ret = (region->ret_code != 0) ? region->ret_code : count;
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&private->io_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
|
||||
|
@ -186,42 +273,47 @@ static ssize_t vfio_ccw_mdev_write(struct mdev_device *mdev,
|
|||
size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
unsigned int index = VFIO_CCW_OFFSET_TO_INDEX(*ppos);
|
||||
struct vfio_ccw_private *private;
|
||||
struct ccw_io_region *region;
|
||||
|
||||
if (*ppos + count > sizeof(*region))
|
||||
return -EINVAL;
|
||||
|
||||
private = dev_get_drvdata(mdev_parent_dev(mdev));
|
||||
if (private->state != VFIO_CCW_STATE_IDLE)
|
||||
return -EACCES;
|
||||
|
||||
region = private->io_region;
|
||||
if (copy_from_user((void *)region + *ppos, buf, count))
|
||||
return -EFAULT;
|
||||
if (index >= VFIO_CCW_NUM_REGIONS + private->num_regions)
|
||||
return -EINVAL;
|
||||
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_IO_REQ);
|
||||
if (region->ret_code != 0) {
|
||||
private->state = VFIO_CCW_STATE_IDLE;
|
||||
return region->ret_code;
|
||||
switch (index) {
|
||||
case VFIO_CCW_CONFIG_REGION_INDEX:
|
||||
return vfio_ccw_mdev_write_io_region(private, buf, count, ppos);
|
||||
default:
|
||||
index -= VFIO_CCW_NUM_REGIONS;
|
||||
return private->region[index].ops->write(private, buf, count,
|
||||
ppos);
|
||||
}
|
||||
|
||||
return count;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info)
|
||||
static int vfio_ccw_mdev_get_device_info(struct vfio_device_info *info,
|
||||
struct mdev_device *mdev)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
|
||||
private = dev_get_drvdata(mdev_parent_dev(mdev));
|
||||
info->flags = VFIO_DEVICE_FLAGS_CCW | VFIO_DEVICE_FLAGS_RESET;
|
||||
info->num_regions = VFIO_CCW_NUM_REGIONS;
|
||||
info->num_regions = VFIO_CCW_NUM_REGIONS + private->num_regions;
|
||||
info->num_irqs = VFIO_CCW_NUM_IRQS;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfio_ccw_mdev_get_region_info(struct vfio_region_info *info,
|
||||
u16 *cap_type_id,
|
||||
void **cap_type)
|
||||
struct mdev_device *mdev,
|
||||
unsigned long arg)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
int i;
|
||||
|
||||
private = dev_get_drvdata(mdev_parent_dev(mdev));
|
||||
switch (info->index) {
|
||||
case VFIO_CCW_CONFIG_REGION_INDEX:
|
||||
info->offset = 0;
|
||||
|
@ -229,9 +321,55 @@ static int vfio_ccw_mdev_get_region_info(struct vfio_region_info *info,
|
|||
info->flags = VFIO_REGION_INFO_FLAG_READ
|
||||
| VFIO_REGION_INFO_FLAG_WRITE;
|
||||
return 0;
|
||||
default:
|
||||
return -EINVAL;
|
||||
default: /* all other regions are handled via capability chain */
|
||||
{
|
||||
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
|
||||
struct vfio_region_info_cap_type cap_type = {
|
||||
.header.id = VFIO_REGION_INFO_CAP_TYPE,
|
||||
.header.version = 1 };
|
||||
int ret;
|
||||
|
||||
if (info->index >=
|
||||
VFIO_CCW_NUM_REGIONS + private->num_regions)
|
||||
return -EINVAL;
|
||||
|
||||
info->index = array_index_nospec(info->index,
|
||||
VFIO_CCW_NUM_REGIONS +
|
||||
private->num_regions);
|
||||
|
||||
i = info->index - VFIO_CCW_NUM_REGIONS;
|
||||
|
||||
info->offset = VFIO_CCW_INDEX_TO_OFFSET(info->index);
|
||||
info->size = private->region[i].size;
|
||||
info->flags = private->region[i].flags;
|
||||
|
||||
cap_type.type = private->region[i].type;
|
||||
cap_type.subtype = private->region[i].subtype;
|
||||
|
||||
ret = vfio_info_add_capability(&caps, &cap_type.header,
|
||||
sizeof(cap_type));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
info->flags |= VFIO_REGION_INFO_FLAG_CAPS;
|
||||
if (info->argsz < sizeof(*info) + caps.size) {
|
||||
info->argsz = sizeof(*info) + caps.size;
|
||||
info->cap_offset = 0;
|
||||
} else {
|
||||
vfio_info_cap_shift(&caps, sizeof(*info));
|
||||
if (copy_to_user((void __user *)arg + sizeof(*info),
|
||||
caps.buf, caps.size)) {
|
||||
kfree(caps.buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
info->cap_offset = sizeof(*info);
|
||||
}
|
||||
|
||||
kfree(caps.buf);
|
||||
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfio_ccw_mdev_get_irq_info(struct vfio_irq_info *info)
|
||||
|
@ -308,6 +446,32 @@ static int vfio_ccw_mdev_set_irqs(struct mdev_device *mdev,
|
|||
}
|
||||
}
|
||||
|
||||
int vfio_ccw_register_dev_region(struct vfio_ccw_private *private,
|
||||
unsigned int subtype,
|
||||
const struct vfio_ccw_regops *ops,
|
||||
size_t size, u32 flags, void *data)
|
||||
{
|
||||
struct vfio_ccw_region *region;
|
||||
|
||||
region = krealloc(private->region,
|
||||
(private->num_regions + 1) * sizeof(*region),
|
||||
GFP_KERNEL);
|
||||
if (!region)
|
||||
return -ENOMEM;
|
||||
|
||||
private->region = region;
|
||||
private->region[private->num_regions].type = VFIO_REGION_TYPE_CCW;
|
||||
private->region[private->num_regions].subtype = subtype;
|
||||
private->region[private->num_regions].ops = ops;
|
||||
private->region[private->num_regions].size = size;
|
||||
private->region[private->num_regions].flags = flags;
|
||||
private->region[private->num_regions].data = data;
|
||||
|
||||
private->num_regions++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
|
||||
unsigned int cmd,
|
||||
unsigned long arg)
|
||||
|
@ -328,7 +492,7 @@ static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
|
|||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
ret = vfio_ccw_mdev_get_device_info(&info);
|
||||
ret = vfio_ccw_mdev_get_device_info(&info, mdev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
@ -337,8 +501,6 @@ static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
|
|||
case VFIO_DEVICE_GET_REGION_INFO:
|
||||
{
|
||||
struct vfio_region_info info;
|
||||
u16 cap_type_id = 0;
|
||||
void *cap_type = NULL;
|
||||
|
||||
minsz = offsetofend(struct vfio_region_info, offset);
|
||||
|
||||
|
@ -348,8 +510,7 @@ static ssize_t vfio_ccw_mdev_ioctl(struct mdev_device *mdev,
|
|||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
ret = vfio_ccw_mdev_get_region_info(&info, &cap_type_id,
|
||||
&cap_type);
|
||||
ret = vfio_ccw_mdev_get_region_info(&info, mdev, arg);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
|
|
|
@ -3,9 +3,11 @@
|
|||
* Private stuff for vfio_ccw driver
|
||||
*
|
||||
* Copyright IBM Corp. 2017
|
||||
* Copyright Red Hat, Inc. 2019
|
||||
*
|
||||
* Author(s): Dong Jia Shi <bjsdjshi@linux.vnet.ibm.com>
|
||||
* Xiao Feng Ren <renxiaof@linux.vnet.ibm.com>
|
||||
* Cornelia Huck <cohuck@redhat.com>
|
||||
*/
|
||||
|
||||
#ifndef _VFIO_CCW_PRIVATE_H_
|
||||
|
@ -19,6 +21,40 @@
|
|||
#include "css.h"
|
||||
#include "vfio_ccw_cp.h"
|
||||
|
||||
#define VFIO_CCW_OFFSET_SHIFT 10
|
||||
#define VFIO_CCW_OFFSET_TO_INDEX(off) (off >> VFIO_CCW_OFFSET_SHIFT)
|
||||
#define VFIO_CCW_INDEX_TO_OFFSET(index) ((u64)(index) << VFIO_CCW_OFFSET_SHIFT)
|
||||
#define VFIO_CCW_OFFSET_MASK (((u64)(1) << VFIO_CCW_OFFSET_SHIFT) - 1)
|
||||
|
||||
/* capability chain handling similar to vfio-pci */
|
||||
struct vfio_ccw_private;
|
||||
struct vfio_ccw_region;
|
||||
|
||||
struct vfio_ccw_regops {
|
||||
ssize_t (*read)(struct vfio_ccw_private *private, char __user *buf,
|
||||
size_t count, loff_t *ppos);
|
||||
ssize_t (*write)(struct vfio_ccw_private *private,
|
||||
const char __user *buf, size_t count, loff_t *ppos);
|
||||
void (*release)(struct vfio_ccw_private *private,
|
||||
struct vfio_ccw_region *region);
|
||||
};
|
||||
|
||||
struct vfio_ccw_region {
|
||||
u32 type;
|
||||
u32 subtype;
|
||||
const struct vfio_ccw_regops *ops;
|
||||
void *data;
|
||||
size_t size;
|
||||
u32 flags;
|
||||
};
|
||||
|
||||
int vfio_ccw_register_dev_region(struct vfio_ccw_private *private,
|
||||
unsigned int subtype,
|
||||
const struct vfio_ccw_regops *ops,
|
||||
size_t size, u32 flags, void *data);
|
||||
|
||||
int vfio_ccw_register_async_dev_regions(struct vfio_ccw_private *private);
|
||||
|
||||
/**
|
||||
* struct vfio_ccw_private
|
||||
* @sch: pointer to the subchannel
|
||||
|
@ -28,6 +64,10 @@
|
|||
* @mdev: pointer to the mediated device
|
||||
* @nb: notifier for vfio events
|
||||
* @io_region: MMIO region to input/output I/O arguments/results
|
||||
* @io_mutex: protect against concurrent update of I/O regions
|
||||
* @region: additional regions for other subchannel operations
|
||||
* @cmd_region: MMIO region for asynchronous I/O commands other than START
|
||||
* @num_regions: number of additional regions
|
||||
* @cp: channel program for the current I/O operation
|
||||
* @irb: irb info received from interrupt
|
||||
* @scsw: scsw info
|
||||
|
@ -42,6 +82,10 @@ struct vfio_ccw_private {
|
|||
struct mdev_device *mdev;
|
||||
struct notifier_block nb;
|
||||
struct ccw_io_region *io_region;
|
||||
struct mutex io_mutex;
|
||||
struct vfio_ccw_region *region;
|
||||
struct ccw_cmd_region *cmd_region;
|
||||
int num_regions;
|
||||
|
||||
struct channel_program cp;
|
||||
struct irb irb;
|
||||
|
@ -63,7 +107,8 @@ enum vfio_ccw_state {
|
|||
VFIO_CCW_STATE_NOT_OPER,
|
||||
VFIO_CCW_STATE_STANDBY,
|
||||
VFIO_CCW_STATE_IDLE,
|
||||
VFIO_CCW_STATE_BUSY,
|
||||
VFIO_CCW_STATE_CP_PROCESSING,
|
||||
VFIO_CCW_STATE_CP_PENDING,
|
||||
/* last element! */
|
||||
NR_VFIO_CCW_STATES
|
||||
};
|
||||
|
@ -75,6 +120,7 @@ enum vfio_ccw_event {
|
|||
VFIO_CCW_EVENT_NOT_OPER,
|
||||
VFIO_CCW_EVENT_IO_REQ,
|
||||
VFIO_CCW_EVENT_INTERRUPT,
|
||||
VFIO_CCW_EVENT_ASYNC_REQ,
|
||||
/* last element! */
|
||||
NR_VFIO_CCW_EVENTS
|
||||
};
|
||||
|
|
|
@ -353,6 +353,10 @@ struct vfio_region_gfx_edid {
|
|||
#define VFIO_DEVICE_GFX_LINK_STATE_DOWN 2
|
||||
};
|
||||
|
||||
#define VFIO_REGION_TYPE_CCW (2)
|
||||
/* ccw sub-types */
|
||||
#define VFIO_REGION_SUBTYPE_CCW_ASYNC_CMD (1)
|
||||
|
||||
/*
|
||||
* 10de vendor sub-type
|
||||
*
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include <linux/types.h>
|
||||
|
||||
/* used for START SUBCHANNEL, always present */
|
||||
struct ccw_io_region {
|
||||
#define ORB_AREA_SIZE 12
|
||||
__u8 orb_area[ORB_AREA_SIZE];
|
||||
|
@ -22,4 +23,15 @@ struct ccw_io_region {
|
|||
__u32 ret_code;
|
||||
} __packed;
|
||||
|
||||
/*
|
||||
* used for processing commands that trigger asynchronous actions
|
||||
* Note: this is controlled by a capability
|
||||
*/
|
||||
#define VFIO_CCW_ASYNC_CMD_HSCH (1 << 0)
|
||||
#define VFIO_CCW_ASYNC_CMD_CSCH (1 << 1)
|
||||
struct ccw_cmd_region {
|
||||
__u32 command;
|
||||
__u32 ret_code;
|
||||
} __packed;
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue