mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
Merge tag 'vfio-v6.2-rc1' of https://github.com/awilliam/linux-vfio
Pull VFIO updates from Alex Williamson: - Replace deprecated git://github.com link in MAINTAINERS (Palmer Dabbelt) - Simplify vfio/mlx5 with module_pci_driver() helper (Shang XiaoJing) - Drop unnecessary buffer from ACPI call (Rafael Mendonca) - Correct latent missing include issue in iova-bitmap and fix support for unaligned bitmaps. Follow-up with better fix through refactor (Joao Martins) - Rework ccw mdev driver to split private data from parent structure, better aligning with the mdev lifecycle and allowing us to remove a temporary workaround (Eric Farman) - Add an interface to get an estimated migration data size for a device, allowing userspace to make informed decisions, ex. more accurately predicting VM downtime (Yishai Hadas) - Fix minor typo in vfio/mlx5 array declaration (Yishai Hadas) - Simplify module and Kconfig through consolidating SPAPR/EEH code and config options and folding virqfd module into main vfio module (Jason Gunthorpe) - Fix error path from device_register() across all vfio mdev and sample drivers (Alex Williamson) - Define migration pre-copy interface and implement for vfio/mlx5 devices, allowing portions of the device state to be saved while the device continues operation, towards reducing the stop-copy state size (Jason Gunthorpe, Yishai Hadas, Shay Drory) - Implement pre-copy for hisi_acc devices (Shameer Kolothum) - Fixes to mdpy mdev driver remove path and error path on probe (Shang XiaoJing) - vfio/mlx5 fixes for incorrect return after copy_to_user() fault and incorrect buffer freeing (Dan Carpenter) * tag 'vfio-v6.2-rc1' of https://github.com/awilliam/linux-vfio: (42 commits) vfio/mlx5: error pointer dereference in error handling vfio/mlx5: fix error code in mlx5vf_precopy_ioctl() samples: vfio-mdev: Fix missing pci_disable_device() in mdpy_fb_probe() hisi_acc_vfio_pci: Enable PRE_COPY flag hisi_acc_vfio_pci: Move the dev compatibility tests for early check hisi_acc_vfio_pci: Introduce support for PRE_COPY state transitions hisi_acc_vfio_pci: Add support for precopy IOCTL vfio/mlx5: Enable MIGRATION_PRE_COPY flag vfio/mlx5: Fallback to STOP_COPY upon specific PRE_COPY error vfio/mlx5: Introduce multiple loads vfio/mlx5: Consider temporary end of stream as part of PRE_COPY vfio/mlx5: Introduce vfio precopy ioctl implementation vfio/mlx5: Introduce SW headers for migration states vfio/mlx5: Introduce device transitions of PRE_COPY vfio/mlx5: Refactor to use queue based data chunks vfio/mlx5: Refactor migration file state vfio/mlx5: Refactor MKEY usage vfio/mlx5: Refactor PD usage vfio/mlx5: Enforce a single SAVE command at a time vfio: Extend the device migration protocol with PRE_COPY ...
This commit is contained in:
+1
-1
@@ -21781,7 +21781,7 @@ M: Alex Williamson <alex.williamson@redhat.com>
|
||||
R: Cornelia Huck <cohuck@redhat.com>
|
||||
L: kvm@vger.kernel.org
|
||||
S: Maintained
|
||||
T: git git://github.com/awilliam/linux-vfio.git
|
||||
T: git https://github.com/awilliam/linux-vfio.git
|
||||
F: Documentation/ABI/testing/sysfs-devices-vfio-dev
|
||||
F: Documentation/driver-api/vfio.rst
|
||||
F: drivers/vfio/
|
||||
|
||||
@@ -1465,7 +1465,6 @@ static void intel_vgpu_release_dev(struct vfio_device *vfio_dev)
|
||||
struct intel_vgpu *vgpu = vfio_dev_to_vgpu(vfio_dev);
|
||||
|
||||
intel_gvt_destroy_vgpu(vgpu);
|
||||
vfio_free_device(vfio_dev);
|
||||
}
|
||||
|
||||
static const struct vfio_device_ops intel_vgpu_dev_ops = {
|
||||
|
||||
@@ -16,6 +16,7 @@ static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private,
|
||||
char __user *buf, size_t count,
|
||||
loff_t *ppos)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
unsigned int i = VFIO_CCW_OFFSET_TO_INDEX(*ppos) - VFIO_CCW_NUM_REGIONS;
|
||||
loff_t pos = *ppos & VFIO_CCW_OFFSET_MASK;
|
||||
struct ccw_schib_region *region;
|
||||
@@ -27,12 +28,12 @@ static ssize_t vfio_ccw_schib_region_read(struct vfio_ccw_private *private,
|
||||
mutex_lock(&private->io_mutex);
|
||||
region = private->region[i].data;
|
||||
|
||||
if (cio_update_schib(private->sch)) {
|
||||
if (cio_update_schib(sch)) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy(region, &private->sch->schib, sizeof(*region));
|
||||
memcpy(region, &sch->schib, sizeof(*region));
|
||||
|
||||
if (copy_to_user(buf, (void *)region + pos, count)) {
|
||||
ret = -EFAULT;
|
||||
|
||||
+75
-101
@@ -23,10 +23,10 @@
|
||||
#include "vfio_ccw_private.h"
|
||||
|
||||
struct workqueue_struct *vfio_ccw_work_q;
|
||||
static struct kmem_cache *vfio_ccw_io_region;
|
||||
static struct kmem_cache *vfio_ccw_cmd_region;
|
||||
static struct kmem_cache *vfio_ccw_schib_region;
|
||||
static struct kmem_cache *vfio_ccw_crw_region;
|
||||
struct kmem_cache *vfio_ccw_io_region;
|
||||
struct kmem_cache *vfio_ccw_cmd_region;
|
||||
struct kmem_cache *vfio_ccw_schib_region;
|
||||
struct kmem_cache *vfio_ccw_crw_region;
|
||||
|
||||
debug_info_t *vfio_ccw_debug_msg_id;
|
||||
debug_info_t *vfio_ccw_debug_trace_id;
|
||||
@@ -36,10 +36,19 @@ debug_info_t *vfio_ccw_debug_trace_id;
|
||||
*/
|
||||
int vfio_ccw_sch_quiesce(struct subchannel *sch)
|
||||
{
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
|
||||
DECLARE_COMPLETION_ONSTACK(completion);
|
||||
int iretry, ret = 0;
|
||||
|
||||
/*
|
||||
* Probably an impossible situation, after being called through
|
||||
* FSM callbacks. But in the event it did, register a warning
|
||||
* and return as if things were fine.
|
||||
*/
|
||||
if (WARN_ON(!private))
|
||||
return 0;
|
||||
|
||||
iretry = 255;
|
||||
do {
|
||||
|
||||
@@ -70,7 +79,7 @@ int vfio_ccw_sch_quiesce(struct subchannel *sch)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vfio_ccw_sch_io_todo(struct work_struct *work)
|
||||
void vfio_ccw_sch_io_todo(struct work_struct *work)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
struct irb *irb;
|
||||
@@ -106,7 +115,7 @@ static void vfio_ccw_sch_io_todo(struct work_struct *work)
|
||||
eventfd_signal(private->io_trigger, 1);
|
||||
}
|
||||
|
||||
static void vfio_ccw_crw_todo(struct work_struct *work)
|
||||
void vfio_ccw_crw_todo(struct work_struct *work)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
|
||||
@@ -121,90 +130,39 @@ static void vfio_ccw_crw_todo(struct work_struct *work)
|
||||
*/
|
||||
static void vfio_ccw_sch_irq(struct subchannel *sch)
|
||||
{
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
|
||||
|
||||
/*
|
||||
* The subchannel should still be disabled at this point,
|
||||
* so an interrupt would be quite surprising. As with an
|
||||
* interrupt while the FSM is closed, let's attempt to
|
||||
* disable the subchannel again.
|
||||
*/
|
||||
if (!private) {
|
||||
VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: unexpected interrupt\n",
|
||||
sch->schid.cssid, sch->schid.ssid,
|
||||
sch->schid.sch_no);
|
||||
|
||||
cio_disable_subchannel(sch);
|
||||
return;
|
||||
}
|
||||
|
||||
inc_irq_stat(IRQIO_CIO);
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_INTERRUPT);
|
||||
}
|
||||
|
||||
static struct vfio_ccw_private *vfio_ccw_alloc_private(struct subchannel *sch)
|
||||
static void vfio_ccw_free_parent(struct device *dev)
|
||||
{
|
||||
struct vfio_ccw_private *private;
|
||||
struct vfio_ccw_parent *parent = container_of(dev, struct vfio_ccw_parent, dev);
|
||||
|
||||
private = kzalloc(sizeof(*private), GFP_KERNEL);
|
||||
if (!private)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
private->sch = sch;
|
||||
mutex_init(&private->io_mutex);
|
||||
private->state = VFIO_CCW_STATE_STANDBY;
|
||||
INIT_LIST_HEAD(&private->crw);
|
||||
INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
|
||||
INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
|
||||
|
||||
private->cp.guest_cp = kcalloc(CCWCHAIN_LEN_MAX, sizeof(struct ccw1),
|
||||
GFP_KERNEL);
|
||||
if (!private->cp.guest_cp)
|
||||
goto out_free_private;
|
||||
|
||||
private->io_region = kmem_cache_zalloc(vfio_ccw_io_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!private->io_region)
|
||||
goto out_free_cp;
|
||||
|
||||
private->cmd_region = kmem_cache_zalloc(vfio_ccw_cmd_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!private->cmd_region)
|
||||
goto out_free_io;
|
||||
|
||||
private->schib_region = kmem_cache_zalloc(vfio_ccw_schib_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
|
||||
if (!private->schib_region)
|
||||
goto out_free_cmd;
|
||||
|
||||
private->crw_region = kmem_cache_zalloc(vfio_ccw_crw_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
|
||||
if (!private->crw_region)
|
||||
goto out_free_schib;
|
||||
return private;
|
||||
|
||||
out_free_schib:
|
||||
kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
|
||||
out_free_cmd:
|
||||
kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
|
||||
out_free_io:
|
||||
kmem_cache_free(vfio_ccw_io_region, private->io_region);
|
||||
out_free_cp:
|
||||
kfree(private->cp.guest_cp);
|
||||
out_free_private:
|
||||
mutex_destroy(&private->io_mutex);
|
||||
kfree(private);
|
||||
return ERR_PTR(-ENOMEM);
|
||||
kfree(parent);
|
||||
}
|
||||
|
||||
static void vfio_ccw_free_private(struct vfio_ccw_private *private)
|
||||
{
|
||||
struct vfio_ccw_crw *crw, *temp;
|
||||
|
||||
list_for_each_entry_safe(crw, temp, &private->crw, next) {
|
||||
list_del(&crw->next);
|
||||
kfree(crw);
|
||||
}
|
||||
|
||||
kmem_cache_free(vfio_ccw_crw_region, private->crw_region);
|
||||
kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
|
||||
kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
|
||||
kmem_cache_free(vfio_ccw_io_region, private->io_region);
|
||||
kfree(private->cp.guest_cp);
|
||||
mutex_destroy(&private->io_mutex);
|
||||
kfree(private);
|
||||
}
|
||||
static int vfio_ccw_sch_probe(struct subchannel *sch)
|
||||
{
|
||||
struct pmcw *pmcw = &sch->schib.pmcw;
|
||||
struct vfio_ccw_private *private;
|
||||
struct vfio_ccw_parent *parent;
|
||||
int ret = -ENOMEM;
|
||||
|
||||
if (pmcw->qf) {
|
||||
@@ -213,42 +171,50 @@ static int vfio_ccw_sch_probe(struct subchannel *sch)
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
private = vfio_ccw_alloc_private(sch);
|
||||
if (IS_ERR(private))
|
||||
return PTR_ERR(private);
|
||||
parent = kzalloc(sizeof(*parent), GFP_KERNEL);
|
||||
if (!parent)
|
||||
return -ENOMEM;
|
||||
|
||||
dev_set_drvdata(&sch->dev, private);
|
||||
|
||||
private->mdev_type.sysfs_name = "io";
|
||||
private->mdev_type.pretty_name = "I/O subchannel (Non-QDIO)";
|
||||
private->mdev_types[0] = &private->mdev_type;
|
||||
ret = mdev_register_parent(&private->parent, &sch->dev,
|
||||
&vfio_ccw_mdev_driver,
|
||||
private->mdev_types, 1);
|
||||
dev_set_name(&parent->dev, "parent");
|
||||
parent->dev.parent = &sch->dev;
|
||||
parent->dev.release = &vfio_ccw_free_parent;
|
||||
ret = device_register(&parent->dev);
|
||||
if (ret)
|
||||
goto out_free;
|
||||
|
||||
dev_set_drvdata(&sch->dev, parent);
|
||||
|
||||
parent->mdev_type.sysfs_name = "io";
|
||||
parent->mdev_type.pretty_name = "I/O subchannel (Non-QDIO)";
|
||||
parent->mdev_types[0] = &parent->mdev_type;
|
||||
ret = mdev_register_parent(&parent->parent, &sch->dev,
|
||||
&vfio_ccw_mdev_driver,
|
||||
parent->mdev_types, 1);
|
||||
if (ret)
|
||||
goto out_unreg;
|
||||
|
||||
VFIO_CCW_MSG_EVENT(4, "bound to subchannel %x.%x.%04x\n",
|
||||
sch->schid.cssid, sch->schid.ssid,
|
||||
sch->schid.sch_no);
|
||||
return 0;
|
||||
|
||||
out_unreg:
|
||||
device_del(&parent->dev);
|
||||
out_free:
|
||||
put_device(&parent->dev);
|
||||
dev_set_drvdata(&sch->dev, NULL);
|
||||
vfio_ccw_free_private(private);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void vfio_ccw_sch_remove(struct subchannel *sch)
|
||||
{
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
|
||||
|
||||
mdev_unregister_parent(&private->parent);
|
||||
mdev_unregister_parent(&parent->parent);
|
||||
|
||||
device_unregister(&parent->dev);
|
||||
dev_set_drvdata(&sch->dev, NULL);
|
||||
|
||||
vfio_ccw_free_private(private);
|
||||
|
||||
VFIO_CCW_MSG_EVENT(4, "unbound from subchannel %x.%x.%04x\n",
|
||||
sch->schid.cssid, sch->schid.ssid,
|
||||
sch->schid.sch_no);
|
||||
@@ -256,7 +222,11 @@ static void vfio_ccw_sch_remove(struct subchannel *sch)
|
||||
|
||||
static void vfio_ccw_sch_shutdown(struct subchannel *sch)
|
||||
{
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
|
||||
|
||||
if (WARN_ON(!private))
|
||||
return;
|
||||
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_CLOSE);
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
|
||||
@@ -274,7 +244,8 @@ static void vfio_ccw_sch_shutdown(struct subchannel *sch)
|
||||
*/
|
||||
static int vfio_ccw_sch_event(struct subchannel *sch, int process)
|
||||
{
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
|
||||
unsigned long flags;
|
||||
int rc = -EAGAIN;
|
||||
|
||||
@@ -287,8 +258,10 @@ static int vfio_ccw_sch_event(struct subchannel *sch, int process)
|
||||
|
||||
rc = 0;
|
||||
|
||||
if (cio_update_schib(sch))
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
|
||||
if (cio_update_schib(sch)) {
|
||||
if (private)
|
||||
vfio_ccw_fsm_event(private, VFIO_CCW_EVENT_NOT_OPER);
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
spin_unlock_irqrestore(sch->lock, flags);
|
||||
@@ -326,14 +299,15 @@ static void vfio_ccw_queue_crw(struct vfio_ccw_private *private,
|
||||
static int vfio_ccw_chp_event(struct subchannel *sch,
|
||||
struct chp_link *link, int event)
|
||||
{
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
|
||||
int mask = chp_ssd_get_mask(&sch->ssd_info, link);
|
||||
int retry = 255;
|
||||
|
||||
if (!private || !mask)
|
||||
return 0;
|
||||
|
||||
trace_vfio_ccw_chp_event(private->sch->schid, mask, event);
|
||||
trace_vfio_ccw_chp_event(sch->schid, mask, event);
|
||||
VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: mask=0x%x event=%d\n",
|
||||
sch->schid.cssid,
|
||||
sch->schid.ssid, sch->schid.sch_no,
|
||||
|
||||
@@ -18,15 +18,13 @@
|
||||
|
||||
static int fsm_io_helper(struct vfio_ccw_private *private)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
union orb *orb;
|
||||
int ccode;
|
||||
__u8 lpm;
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
|
||||
sch = private->sch;
|
||||
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
|
||||
orb = cp_get_orb(&private->cp, (u32)virt_to_phys(sch), sch->lpm);
|
||||
@@ -80,13 +78,11 @@ out:
|
||||
|
||||
static int fsm_do_halt(struct vfio_ccw_private *private)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
unsigned long flags;
|
||||
int ccode;
|
||||
int ret;
|
||||
|
||||
sch = private->sch;
|
||||
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
|
||||
VFIO_CCW_TRACE_EVENT(2, "haltIO");
|
||||
@@ -121,13 +117,11 @@ static int fsm_do_halt(struct vfio_ccw_private *private)
|
||||
|
||||
static int fsm_do_clear(struct vfio_ccw_private *private)
|
||||
{
|
||||
struct subchannel *sch;
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
unsigned long flags;
|
||||
int ccode;
|
||||
int ret;
|
||||
|
||||
sch = private->sch;
|
||||
|
||||
spin_lock_irqsave(sch->lock, flags);
|
||||
|
||||
VFIO_CCW_TRACE_EVENT(2, "clearIO");
|
||||
@@ -160,7 +154,7 @@ static int fsm_do_clear(struct vfio_ccw_private *private)
|
||||
static void fsm_notoper(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
struct subchannel *sch = private->sch;
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
|
||||
VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: notoper event %x state %x\n",
|
||||
sch->schid.cssid,
|
||||
@@ -228,7 +222,7 @@ static void fsm_async_retry(struct vfio_ccw_private *private,
|
||||
static void fsm_disabled_irq(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
struct subchannel *sch = private->sch;
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
|
||||
/*
|
||||
* An interrupt in a disabled state means a previous disable was not
|
||||
@@ -238,7 +232,9 @@ static void fsm_disabled_irq(struct vfio_ccw_private *private,
|
||||
}
|
||||
inline struct subchannel_id get_schid(struct vfio_ccw_private *p)
|
||||
{
|
||||
return p->sch->schid;
|
||||
struct subchannel *sch = to_subchannel(p->vdev.dev->parent);
|
||||
|
||||
return sch->schid;
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -360,10 +356,11 @@ static void fsm_async_request(struct vfio_ccw_private *private,
|
||||
static void fsm_irq(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
struct irb *irb = this_cpu_ptr(&cio_irb);
|
||||
|
||||
VFIO_CCW_TRACE_EVENT(6, "IRQ");
|
||||
VFIO_CCW_TRACE_EVENT(6, dev_name(&private->sch->dev));
|
||||
VFIO_CCW_TRACE_EVENT(6, dev_name(&sch->dev));
|
||||
|
||||
memcpy(&private->irb, irb, sizeof(*irb));
|
||||
|
||||
@@ -376,7 +373,7 @@ static void fsm_irq(struct vfio_ccw_private *private,
|
||||
static void fsm_open(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
struct subchannel *sch = private->sch;
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(sch->lock);
|
||||
@@ -397,7 +394,7 @@ err_unlock:
|
||||
static void fsm_close(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
struct subchannel *sch = private->sch;
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
int ret;
|
||||
|
||||
spin_lock_irq(sch->lock);
|
||||
|
||||
@@ -49,26 +49,70 @@ static int vfio_ccw_mdev_init_dev(struct vfio_device *vdev)
|
||||
struct vfio_ccw_private *private =
|
||||
container_of(vdev, struct vfio_ccw_private, vdev);
|
||||
|
||||
init_completion(&private->release_comp);
|
||||
mutex_init(&private->io_mutex);
|
||||
private->state = VFIO_CCW_STATE_STANDBY;
|
||||
INIT_LIST_HEAD(&private->crw);
|
||||
INIT_WORK(&private->io_work, vfio_ccw_sch_io_todo);
|
||||
INIT_WORK(&private->crw_work, vfio_ccw_crw_todo);
|
||||
|
||||
private->cp.guest_cp = kcalloc(CCWCHAIN_LEN_MAX, sizeof(struct ccw1),
|
||||
GFP_KERNEL);
|
||||
if (!private->cp.guest_cp)
|
||||
goto out_free_private;
|
||||
|
||||
private->io_region = kmem_cache_zalloc(vfio_ccw_io_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!private->io_region)
|
||||
goto out_free_cp;
|
||||
|
||||
private->cmd_region = kmem_cache_zalloc(vfio_ccw_cmd_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!private->cmd_region)
|
||||
goto out_free_io;
|
||||
|
||||
private->schib_region = kmem_cache_zalloc(vfio_ccw_schib_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!private->schib_region)
|
||||
goto out_free_cmd;
|
||||
|
||||
private->crw_region = kmem_cache_zalloc(vfio_ccw_crw_region,
|
||||
GFP_KERNEL | GFP_DMA);
|
||||
if (!private->crw_region)
|
||||
goto out_free_schib;
|
||||
|
||||
return 0;
|
||||
|
||||
out_free_schib:
|
||||
kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
|
||||
out_free_cmd:
|
||||
kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
|
||||
out_free_io:
|
||||
kmem_cache_free(vfio_ccw_io_region, private->io_region);
|
||||
out_free_cp:
|
||||
kfree(private->cp.guest_cp);
|
||||
out_free_private:
|
||||
mutex_destroy(&private->io_mutex);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
static int vfio_ccw_mdev_probe(struct mdev_device *mdev)
|
||||
{
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(mdev->dev.parent);
|
||||
struct subchannel *sch = to_subchannel(mdev->dev.parent);
|
||||
struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_private *private;
|
||||
int ret;
|
||||
|
||||
if (private->state == VFIO_CCW_STATE_NOT_OPER)
|
||||
return -ENODEV;
|
||||
private = vfio_alloc_device(vfio_ccw_private, vdev, &mdev->dev,
|
||||
&vfio_ccw_dev_ops);
|
||||
if (IS_ERR(private))
|
||||
return PTR_ERR(private);
|
||||
|
||||
ret = vfio_init_device(&private->vdev, &mdev->dev, &vfio_ccw_dev_ops);
|
||||
if (ret)
|
||||
return ret;
|
||||
dev_set_drvdata(&parent->dev, private);
|
||||
|
||||
VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: create\n",
|
||||
private->sch->schid.cssid,
|
||||
private->sch->schid.ssid,
|
||||
private->sch->schid.sch_no);
|
||||
sch->schid.cssid,
|
||||
sch->schid.ssid,
|
||||
sch->schid.sch_no);
|
||||
|
||||
ret = vfio_register_emulated_iommu_dev(&private->vdev);
|
||||
if (ret)
|
||||
@@ -77,6 +121,7 @@ static int vfio_ccw_mdev_probe(struct mdev_device *mdev)
|
||||
return 0;
|
||||
|
||||
err_put_vdev:
|
||||
dev_set_drvdata(&parent->dev, NULL);
|
||||
vfio_put_device(&private->vdev);
|
||||
return ret;
|
||||
}
|
||||
@@ -85,40 +130,36 @@ 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;
|
||||
|
||||
/*
|
||||
* We cannot free vfio_ccw_private here because it includes
|
||||
* parent info which must be free'ed by css driver.
|
||||
*
|
||||
* Use a workaround by memset'ing the core device part and
|
||||
* then notifying the remove path that all active references
|
||||
* to this device have been released.
|
||||
*/
|
||||
memset(vdev, 0, sizeof(*vdev));
|
||||
complete(&private->release_comp);
|
||||
list_for_each_entry_safe(crw, temp, &private->crw, next) {
|
||||
list_del(&crw->next);
|
||||
kfree(crw);
|
||||
}
|
||||
|
||||
kmem_cache_free(vfio_ccw_crw_region, private->crw_region);
|
||||
kmem_cache_free(vfio_ccw_schib_region, private->schib_region);
|
||||
kmem_cache_free(vfio_ccw_cmd_region, private->cmd_region);
|
||||
kmem_cache_free(vfio_ccw_io_region, private->io_region);
|
||||
kfree(private->cp.guest_cp);
|
||||
mutex_destroy(&private->io_mutex);
|
||||
}
|
||||
|
||||
static void vfio_ccw_mdev_remove(struct mdev_device *mdev)
|
||||
{
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(mdev->dev.parent);
|
||||
struct subchannel *sch = to_subchannel(mdev->dev.parent);
|
||||
struct vfio_ccw_parent *parent = dev_get_drvdata(&sch->dev);
|
||||
struct vfio_ccw_private *private = dev_get_drvdata(&parent->dev);
|
||||
|
||||
VFIO_CCW_MSG_EVENT(2, "sch %x.%x.%04x: remove\n",
|
||||
private->sch->schid.cssid,
|
||||
private->sch->schid.ssid,
|
||||
private->sch->schid.sch_no);
|
||||
sch->schid.cssid,
|
||||
sch->schid.ssid,
|
||||
sch->schid.sch_no);
|
||||
|
||||
vfio_unregister_group_dev(&private->vdev);
|
||||
|
||||
dev_set_drvdata(&parent->dev, NULL);
|
||||
vfio_put_device(&private->vdev);
|
||||
/*
|
||||
* Wait for all active references on mdev are released so it
|
||||
* is safe to defer kfree() to a later point.
|
||||
*
|
||||
* TODO: the clean fix is to split parent/mdev info from ccw
|
||||
* private structure so each can be managed in its own life
|
||||
* cycle.
|
||||
*/
|
||||
wait_for_completion(&private->release_comp);
|
||||
}
|
||||
|
||||
static int vfio_ccw_mdev_open_device(struct vfio_device *vdev)
|
||||
|
||||
@@ -67,10 +67,24 @@ struct vfio_ccw_crw {
|
||||
struct crw crw;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vfio_ccw_parent
|
||||
*
|
||||
* @dev: embedded device struct
|
||||
* @parent: parent data structures for mdevs created
|
||||
* @mdev_type(s): identifying information for mdevs created
|
||||
*/
|
||||
struct vfio_ccw_parent {
|
||||
struct device dev;
|
||||
|
||||
struct mdev_parent parent;
|
||||
struct mdev_type mdev_type;
|
||||
struct mdev_type *mdev_types[1];
|
||||
};
|
||||
|
||||
/**
|
||||
* struct vfio_ccw_private
|
||||
* @vdev: Embedded VFIO device
|
||||
* @sch: pointer to the subchannel
|
||||
* @state: internal state of the device
|
||||
* @completion: synchronization helper of the I/O completion
|
||||
* @io_region: MMIO region to input/output I/O arguments/results
|
||||
@@ -88,12 +102,9 @@ struct vfio_ccw_crw {
|
||||
* @req_trigger: eventfd ctx for signaling userspace to return device
|
||||
* @io_work: work for deferral process of I/O handling
|
||||
* @crw_work: work for deferral process of CRW handling
|
||||
* @release_comp: synchronization helper for vfio device release
|
||||
* @parent: parent data structures for mdevs created
|
||||
*/
|
||||
struct vfio_ccw_private {
|
||||
struct vfio_device vdev;
|
||||
struct subchannel *sch;
|
||||
int state;
|
||||
struct completion *completion;
|
||||
struct ccw_io_region *io_region;
|
||||
@@ -114,15 +125,11 @@ struct vfio_ccw_private {
|
||||
struct eventfd_ctx *req_trigger;
|
||||
struct work_struct io_work;
|
||||
struct work_struct crw_work;
|
||||
|
||||
struct completion release_comp;
|
||||
|
||||
struct mdev_parent parent;
|
||||
struct mdev_type mdev_type;
|
||||
struct mdev_type *mdev_types[1];
|
||||
} __aligned(8);
|
||||
|
||||
int vfio_ccw_sch_quiesce(struct subchannel *sch);
|
||||
void vfio_ccw_sch_io_todo(struct work_struct *work);
|
||||
void vfio_ccw_crw_todo(struct work_struct *work);
|
||||
|
||||
extern struct mdev_driver vfio_ccw_mdev_driver;
|
||||
|
||||
@@ -162,12 +169,18 @@ extern fsm_func_t *vfio_ccw_jumptable[NR_VFIO_CCW_STATES][NR_VFIO_CCW_EVENTS];
|
||||
static inline void vfio_ccw_fsm_event(struct vfio_ccw_private *private,
|
||||
enum vfio_ccw_event event)
|
||||
{
|
||||
trace_vfio_ccw_fsm_event(private->sch->schid, private->state, event);
|
||||
struct subchannel *sch = to_subchannel(private->vdev.dev->parent);
|
||||
|
||||
if (sch)
|
||||
trace_vfio_ccw_fsm_event(sch->schid, private->state, event);
|
||||
vfio_ccw_jumptable[private->state][event](private, event);
|
||||
}
|
||||
|
||||
extern struct workqueue_struct *vfio_ccw_work_q;
|
||||
|
||||
extern struct kmem_cache *vfio_ccw_io_region;
|
||||
extern struct kmem_cache *vfio_ccw_cmd_region;
|
||||
extern struct kmem_cache *vfio_ccw_schib_region;
|
||||
extern struct kmem_cache *vfio_ccw_crw_region;
|
||||
|
||||
/* s390 debug feature, similar to base cio */
|
||||
extern debug_info_t *vfio_ccw_debug_msg_id;
|
||||
|
||||
@@ -122,7 +122,7 @@ static int vfio_ap_matrix_dev_create(void)
|
||||
return 0;
|
||||
|
||||
matrix_drv_err:
|
||||
device_unregister(&matrix_dev->device);
|
||||
device_del(&matrix_dev->device);
|
||||
matrix_reg_err:
|
||||
put_device(&matrix_dev->device);
|
||||
matrix_alloc_err:
|
||||
|
||||
@@ -765,11 +765,6 @@ static void vfio_ap_mdev_unlink_fr_queues(struct ap_matrix_mdev *matrix_mdev)
|
||||
}
|
||||
}
|
||||
|
||||
static void vfio_ap_mdev_release_dev(struct vfio_device *vdev)
|
||||
{
|
||||
vfio_free_device(vdev);
|
||||
}
|
||||
|
||||
static void vfio_ap_mdev_remove(struct mdev_device *mdev)
|
||||
{
|
||||
struct ap_matrix_mdev *matrix_mdev = dev_get_drvdata(&mdev->dev);
|
||||
@@ -1800,7 +1795,6 @@ static const struct attribute_group vfio_queue_attr_group = {
|
||||
|
||||
static const struct vfio_device_ops vfio_ap_matrix_dev_ops = {
|
||||
.init = vfio_ap_mdev_init_dev,
|
||||
.release = vfio_ap_mdev_release_dev,
|
||||
.open_device = vfio_ap_mdev_open_device,
|
||||
.close_device = vfio_ap_mdev_close_device,
|
||||
.ioctl = vfio_ap_mdev_ioctl,
|
||||
|
||||
@@ -48,13 +48,8 @@ config VFIO_NOIOMMU
|
||||
If you don't know what to do here, say N.
|
||||
endif
|
||||
|
||||
config VFIO_SPAPR_EEH
|
||||
tristate
|
||||
depends on EEH && VFIO_IOMMU_SPAPR_TCE
|
||||
default VFIO
|
||||
|
||||
config VFIO_VIRQFD
|
||||
tristate
|
||||
bool
|
||||
select EVENTFD
|
||||
default n
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
vfio_virqfd-y := virqfd.o
|
||||
|
||||
obj-$(CONFIG_VFIO) += vfio.o
|
||||
|
||||
vfio-y += vfio_main.o \
|
||||
@@ -8,11 +6,10 @@ vfio-y += vfio_main.o \
|
||||
iova_bitmap.o
|
||||
vfio-$(CONFIG_IOMMUFD) += iommufd.o
|
||||
vfio-$(CONFIG_VFIO_CONTAINER) += container.o
|
||||
vfio-$(CONFIG_VFIO_VIRQFD) += virqfd.o
|
||||
|
||||
obj-$(CONFIG_VFIO_VIRQFD) += vfio_virqfd.o
|
||||
obj-$(CONFIG_VFIO_IOMMU_TYPE1) += vfio_iommu_type1.o
|
||||
obj-$(CONFIG_VFIO_IOMMU_SPAPR_TCE) += vfio_iommu_spapr_tce.o
|
||||
obj-$(CONFIG_VFIO_SPAPR_EEH) += vfio_spapr_eeh.o
|
||||
obj-$(CONFIG_VFIO_PCI) += pci/
|
||||
obj-$(CONFIG_VFIO_PLATFORM) += platform/
|
||||
obj-$(CONFIG_VFIO_MDEV) += mdev/
|
||||
|
||||
@@ -568,7 +568,6 @@ static void vfio_fsl_mc_release_dev(struct vfio_device *core_vdev)
|
||||
|
||||
vfio_fsl_uninit_device(vdev);
|
||||
mutex_destroy(&vdev->igate);
|
||||
vfio_free_device(core_vdev);
|
||||
}
|
||||
|
||||
static int vfio_fsl_mc_remove(struct fsl_mc_device *mc_dev)
|
||||
|
||||
+17
-16
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
#include <linux/iova_bitmap.h>
|
||||
#include <linux/mm.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/highmem.h>
|
||||
|
||||
#define BITS_PER_PAGE (PAGE_SIZE * BITS_PER_BYTE)
|
||||
@@ -295,11 +296,13 @@ void iova_bitmap_free(struct iova_bitmap *bitmap)
|
||||
*/
|
||||
static unsigned long iova_bitmap_mapped_remaining(struct iova_bitmap *bitmap)
|
||||
{
|
||||
unsigned long remaining;
|
||||
unsigned long remaining, bytes;
|
||||
|
||||
bytes = (bitmap->mapped.npages << PAGE_SHIFT) - bitmap->mapped.pgoff;
|
||||
|
||||
remaining = bitmap->mapped_total_index - bitmap->mapped_base_index;
|
||||
remaining = min_t(unsigned long, remaining,
|
||||
(bitmap->mapped.npages << PAGE_SHIFT) / sizeof(*bitmap->bitmap));
|
||||
bytes / sizeof(*bitmap->bitmap));
|
||||
|
||||
return remaining;
|
||||
}
|
||||
@@ -394,29 +397,27 @@ int iova_bitmap_for_each(struct iova_bitmap *bitmap, void *opaque,
|
||||
* Set the bits corresponding to the range [iova .. iova+length-1] in
|
||||
* the user bitmap.
|
||||
*
|
||||
* Return: The number of bits set.
|
||||
*/
|
||||
void iova_bitmap_set(struct iova_bitmap *bitmap,
|
||||
unsigned long iova, size_t length)
|
||||
{
|
||||
struct iova_bitmap_map *mapped = &bitmap->mapped;
|
||||
unsigned long offset = (iova - mapped->iova) >> mapped->pgshift;
|
||||
unsigned long nbits = max_t(unsigned long, 1, length >> mapped->pgshift);
|
||||
unsigned long page_idx = offset / BITS_PER_PAGE;
|
||||
unsigned long page_offset = mapped->pgoff;
|
||||
void *kaddr;
|
||||
|
||||
offset = offset % BITS_PER_PAGE;
|
||||
unsigned long cur_bit = ((iova - mapped->iova) >>
|
||||
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
|
||||
unsigned long last_bit = (((iova + length - 1) - mapped->iova) >>
|
||||
mapped->pgshift) + mapped->pgoff * BITS_PER_BYTE;
|
||||
|
||||
do {
|
||||
unsigned long size = min(BITS_PER_PAGE - offset, nbits);
|
||||
unsigned int page_idx = cur_bit / BITS_PER_PAGE;
|
||||
unsigned int offset = cur_bit % BITS_PER_PAGE;
|
||||
unsigned int nbits = min(BITS_PER_PAGE - offset,
|
||||
last_bit - cur_bit + 1);
|
||||
void *kaddr;
|
||||
|
||||
kaddr = kmap_local_page(mapped->pages[page_idx]);
|
||||
bitmap_set(kaddr + page_offset, offset, size);
|
||||
bitmap_set(kaddr, offset, nbits);
|
||||
kunmap_local(kaddr);
|
||||
page_offset = offset = 0;
|
||||
nbits -= size;
|
||||
page_idx++;
|
||||
} while (nbits > 0);
|
||||
cur_bit += nbits;
|
||||
} while (cur_bit <= last_bit);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(iova_bitmap_set);
|
||||
|
||||
@@ -360,8 +360,8 @@ static int vf_qm_check_match(struct hisi_acc_vf_core_device *hisi_acc_vdev,
|
||||
u32 que_iso_state;
|
||||
int ret;
|
||||
|
||||
if (migf->total_length < QM_MATCH_SIZE)
|
||||
return -EINVAL;
|
||||
if (migf->total_length < QM_MATCH_SIZE || hisi_acc_vdev->match_done)
|
||||
return 0;
|
||||
|
||||
if (vf_data->acc_magic != ACC_DEV_MAGIC) {
|
||||
dev_err(dev, "failed to match ACC_DEV_MAGIC\n");
|
||||
@@ -406,6 +406,7 @@ static int vf_qm_check_match(struct hisi_acc_vf_core_device *hisi_acc_vdev,
|
||||
}
|
||||
|
||||
hisi_acc_vdev->vf_qm_state = vf_data->vf_qm_state;
|
||||
hisi_acc_vdev->match_done = true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -493,10 +494,6 @@ static int vf_qm_state_save(struct hisi_acc_vf_core_device *hisi_acc_vdev,
|
||||
struct device *dev = &vf_qm->pdev->dev;
|
||||
int ret;
|
||||
|
||||
ret = vf_qm_get_match_data(hisi_acc_vdev, vf_data);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (unlikely(qm_wait_dev_not_ready(vf_qm))) {
|
||||
/* Update state and return with match data */
|
||||
vf_data->vf_qm_state = QM_NOT_READY;
|
||||
@@ -673,12 +670,6 @@ static int hisi_acc_vf_load_state(struct hisi_acc_vf_core_device *hisi_acc_vdev)
|
||||
struct hisi_acc_vf_migration_file *migf = hisi_acc_vdev->resuming_migf;
|
||||
int ret;
|
||||
|
||||
/* Check dev compatibility */
|
||||
ret = vf_qm_check_match(hisi_acc_vdev, migf);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to match the VF!\n");
|
||||
return ret;
|
||||
}
|
||||
/* Recover data to VF */
|
||||
ret = vf_qm_load_data(hisi_acc_vdev, migf);
|
||||
if (ret) {
|
||||
@@ -732,6 +723,10 @@ static ssize_t hisi_acc_vf_resume_write(struct file *filp, const char __user *bu
|
||||
*pos += len;
|
||||
done = len;
|
||||
migf->total_length += len;
|
||||
|
||||
ret = vf_qm_check_match(migf->hisi_acc_vdev, migf);
|
||||
if (ret)
|
||||
done = -EFAULT;
|
||||
out_unlock:
|
||||
mutex_unlock(&migf->lock);
|
||||
return done;
|
||||
@@ -764,9 +759,58 @@ hisi_acc_vf_pci_resume(struct hisi_acc_vf_core_device *hisi_acc_vdev)
|
||||
|
||||
stream_open(migf->filp->f_inode, migf->filp);
|
||||
mutex_init(&migf->lock);
|
||||
migf->hisi_acc_vdev = hisi_acc_vdev;
|
||||
return migf;
|
||||
}
|
||||
|
||||
static long hisi_acc_vf_precopy_ioctl(struct file *filp,
|
||||
unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct hisi_acc_vf_migration_file *migf = filp->private_data;
|
||||
struct hisi_acc_vf_core_device *hisi_acc_vdev = migf->hisi_acc_vdev;
|
||||
loff_t *pos = &filp->f_pos;
|
||||
struct vfio_precopy_info info;
|
||||
unsigned long minsz;
|
||||
int ret;
|
||||
|
||||
if (cmd != VFIO_MIG_GET_PRECOPY_INFO)
|
||||
return -ENOTTY;
|
||||
|
||||
minsz = offsetofend(struct vfio_precopy_info, dirty_bytes);
|
||||
|
||||
if (copy_from_user(&info, (void __user *)arg, minsz))
|
||||
return -EFAULT;
|
||||
if (info.argsz < minsz)
|
||||
return -EINVAL;
|
||||
|
||||
mutex_lock(&hisi_acc_vdev->state_mutex);
|
||||
if (hisi_acc_vdev->mig_state != VFIO_DEVICE_STATE_PRE_COPY) {
|
||||
mutex_unlock(&hisi_acc_vdev->state_mutex);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
mutex_lock(&migf->lock);
|
||||
|
||||
if (migf->disabled) {
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (*pos > migf->total_length) {
|
||||
ret = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
info.dirty_bytes = 0;
|
||||
info.initial_bytes = migf->total_length - *pos;
|
||||
|
||||
ret = copy_to_user((void __user *)arg, &info, minsz) ? -EFAULT : 0;
|
||||
out:
|
||||
mutex_unlock(&migf->lock);
|
||||
mutex_unlock(&hisi_acc_vdev->state_mutex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static ssize_t hisi_acc_vf_save_read(struct file *filp, char __user *buf, size_t len,
|
||||
loff_t *pos)
|
||||
{
|
||||
@@ -807,12 +851,14 @@ out_unlock:
|
||||
static const struct file_operations hisi_acc_vf_save_fops = {
|
||||
.owner = THIS_MODULE,
|
||||
.read = hisi_acc_vf_save_read,
|
||||
.unlocked_ioctl = hisi_acc_vf_precopy_ioctl,
|
||||
.compat_ioctl = compat_ptr_ioctl,
|
||||
.release = hisi_acc_vf_release_file,
|
||||
.llseek = no_llseek,
|
||||
};
|
||||
|
||||
static struct hisi_acc_vf_migration_file *
|
||||
hisi_acc_vf_stop_copy(struct hisi_acc_vf_core_device *hisi_acc_vdev)
|
||||
hisi_acc_open_saving_migf(struct hisi_acc_vf_core_device *hisi_acc_vdev)
|
||||
{
|
||||
struct hisi_acc_vf_migration_file *migf;
|
||||
int ret;
|
||||
@@ -832,8 +878,9 @@ hisi_acc_vf_stop_copy(struct hisi_acc_vf_core_device *hisi_acc_vdev)
|
||||
|
||||
stream_open(migf->filp->f_inode, migf->filp);
|
||||
mutex_init(&migf->lock);
|
||||
migf->hisi_acc_vdev = hisi_acc_vdev;
|
||||
|
||||
ret = vf_qm_state_save(hisi_acc_vdev, migf);
|
||||
ret = vf_qm_get_match_data(hisi_acc_vdev, &migf->vf_data);
|
||||
if (ret) {
|
||||
fput(migf->filp);
|
||||
return ERR_PTR(ret);
|
||||
@@ -842,6 +889,44 @@ hisi_acc_vf_stop_copy(struct hisi_acc_vf_core_device *hisi_acc_vdev)
|
||||
return migf;
|
||||
}
|
||||
|
||||
static struct hisi_acc_vf_migration_file *
|
||||
hisi_acc_vf_pre_copy(struct hisi_acc_vf_core_device *hisi_acc_vdev)
|
||||
{
|
||||
struct hisi_acc_vf_migration_file *migf;
|
||||
|
||||
migf = hisi_acc_open_saving_migf(hisi_acc_vdev);
|
||||
if (IS_ERR(migf))
|
||||
return migf;
|
||||
|
||||
migf->total_length = QM_MATCH_SIZE;
|
||||
return migf;
|
||||
}
|
||||
|
||||
static struct hisi_acc_vf_migration_file *
|
||||
hisi_acc_vf_stop_copy(struct hisi_acc_vf_core_device *hisi_acc_vdev, bool open)
|
||||
{
|
||||
int ret;
|
||||
struct hisi_acc_vf_migration_file *migf = NULL;
|
||||
|
||||
if (open) {
|
||||
/*
|
||||
* Userspace didn't use PRECOPY support. Hence saving_migf
|
||||
* is not opened yet.
|
||||
*/
|
||||
migf = hisi_acc_open_saving_migf(hisi_acc_vdev);
|
||||
if (IS_ERR(migf))
|
||||
return migf;
|
||||
} else {
|
||||
migf = hisi_acc_vdev->saving_migf;
|
||||
}
|
||||
|
||||
ret = vf_qm_state_save(hisi_acc_vdev, migf);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
return open ? migf : NULL;
|
||||
}
|
||||
|
||||
static int hisi_acc_vf_stop_device(struct hisi_acc_vf_core_device *hisi_acc_vdev)
|
||||
{
|
||||
struct device *dev = &hisi_acc_vdev->vf_dev->dev;
|
||||
@@ -869,6 +954,31 @@ hisi_acc_vf_set_device_state(struct hisi_acc_vf_core_device *hisi_acc_vdev,
|
||||
u32 cur = hisi_acc_vdev->mig_state;
|
||||
int ret;
|
||||
|
||||
if (cur == VFIO_DEVICE_STATE_RUNNING && new == VFIO_DEVICE_STATE_PRE_COPY) {
|
||||
struct hisi_acc_vf_migration_file *migf;
|
||||
|
||||
migf = hisi_acc_vf_pre_copy(hisi_acc_vdev);
|
||||
if (IS_ERR(migf))
|
||||
return ERR_CAST(migf);
|
||||
get_file(migf->filp);
|
||||
hisi_acc_vdev->saving_migf = migf;
|
||||
return migf->filp;
|
||||
}
|
||||
|
||||
if (cur == VFIO_DEVICE_STATE_PRE_COPY && new == VFIO_DEVICE_STATE_STOP_COPY) {
|
||||
struct hisi_acc_vf_migration_file *migf;
|
||||
|
||||
ret = hisi_acc_vf_stop_device(hisi_acc_vdev);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
migf = hisi_acc_vf_stop_copy(hisi_acc_vdev, false);
|
||||
if (IS_ERR(migf))
|
||||
return ERR_CAST(migf);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cur == VFIO_DEVICE_STATE_RUNNING && new == VFIO_DEVICE_STATE_STOP) {
|
||||
ret = hisi_acc_vf_stop_device(hisi_acc_vdev);
|
||||
if (ret)
|
||||
@@ -879,7 +989,7 @@ hisi_acc_vf_set_device_state(struct hisi_acc_vf_core_device *hisi_acc_vdev,
|
||||
if (cur == VFIO_DEVICE_STATE_STOP && new == VFIO_DEVICE_STATE_STOP_COPY) {
|
||||
struct hisi_acc_vf_migration_file *migf;
|
||||
|
||||
migf = hisi_acc_vf_stop_copy(hisi_acc_vdev);
|
||||
migf = hisi_acc_vf_stop_copy(hisi_acc_vdev, true);
|
||||
if (IS_ERR(migf))
|
||||
return ERR_CAST(migf);
|
||||
get_file(migf->filp);
|
||||
@@ -911,6 +1021,11 @@ hisi_acc_vf_set_device_state(struct hisi_acc_vf_core_device *hisi_acc_vdev,
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cur == VFIO_DEVICE_STATE_PRE_COPY && new == VFIO_DEVICE_STATE_RUNNING) {
|
||||
hisi_acc_vf_disable_fds(hisi_acc_vdev);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (cur == VFIO_DEVICE_STATE_STOP && new == VFIO_DEVICE_STATE_RUNNING) {
|
||||
hisi_acc_vf_start_device(hisi_acc_vdev);
|
||||
return NULL;
|
||||
@@ -957,6 +1072,14 @@ hisi_acc_vfio_pci_set_device_state(struct vfio_device *vdev,
|
||||
return res;
|
||||
}
|
||||
|
||||
static int
|
||||
hisi_acc_vfio_pci_get_data_size(struct vfio_device *vdev,
|
||||
unsigned long *stop_copy_length)
|
||||
{
|
||||
*stop_copy_length = sizeof(struct acc_vf_data);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
hisi_acc_vfio_pci_get_device_state(struct vfio_device *vdev,
|
||||
enum vfio_device_mig_state *curr_state)
|
||||
@@ -1213,6 +1336,7 @@ static void hisi_acc_vfio_pci_close_device(struct vfio_device *core_vdev)
|
||||
static const struct vfio_migration_ops hisi_acc_vfio_pci_migrn_state_ops = {
|
||||
.migration_set_state = hisi_acc_vfio_pci_set_device_state,
|
||||
.migration_get_state = hisi_acc_vfio_pci_get_device_state,
|
||||
.migration_get_data_size = hisi_acc_vfio_pci_get_data_size,
|
||||
};
|
||||
|
||||
static int hisi_acc_vfio_pci_migrn_init_dev(struct vfio_device *core_vdev)
|
||||
@@ -1227,7 +1351,7 @@ static int hisi_acc_vfio_pci_migrn_init_dev(struct vfio_device *core_vdev)
|
||||
hisi_acc_vdev->vf_dev = pdev;
|
||||
mutex_init(&hisi_acc_vdev->state_mutex);
|
||||
|
||||
core_vdev->migration_flags = VFIO_MIGRATION_STOP_COPY;
|
||||
core_vdev->migration_flags = VFIO_MIGRATION_STOP_COPY | VFIO_MIGRATION_PRE_COPY;
|
||||
core_vdev->mig_ops = &hisi_acc_vfio_pci_migrn_state_ops;
|
||||
|
||||
return vfio_pci_core_init_dev(core_vdev);
|
||||
|
||||
@@ -91,12 +91,14 @@ struct hisi_acc_vf_migration_file {
|
||||
struct mutex lock;
|
||||
bool disabled;
|
||||
|
||||
struct hisi_acc_vf_core_device *hisi_acc_vdev;
|
||||
struct acc_vf_data vf_data;
|
||||
size_t total_length;
|
||||
};
|
||||
|
||||
struct hisi_acc_vf_core_device {
|
||||
struct vfio_pci_core_device core_device;
|
||||
u8 match_done:1;
|
||||
u8 deferred_reset:1;
|
||||
/* For migration state */
|
||||
struct mutex state_mutex;
|
||||
|
||||
+340
-73
File diff suppressed because it is too large
Load Diff
+81
-15
@@ -12,31 +12,74 @@
|
||||
#include <linux/mlx5/cq.h>
|
||||
#include <linux/mlx5/qp.h>
|
||||
|
||||
#define MLX5VF_PRE_COPY_SUPP(mvdev) \
|
||||
((mvdev)->core_device.vdev.migration_flags & VFIO_MIGRATION_PRE_COPY)
|
||||
|
||||
enum mlx5_vf_migf_state {
|
||||
MLX5_MIGF_STATE_ERROR = 1,
|
||||
MLX5_MIGF_STATE_PRE_COPY_ERROR,
|
||||
MLX5_MIGF_STATE_PRE_COPY,
|
||||
MLX5_MIGF_STATE_SAVE_LAST,
|
||||
MLX5_MIGF_STATE_COMPLETE,
|
||||
};
|
||||
|
||||
enum mlx5_vf_load_state {
|
||||
MLX5_VF_LOAD_STATE_READ_IMAGE_NO_HEADER,
|
||||
MLX5_VF_LOAD_STATE_READ_HEADER,
|
||||
MLX5_VF_LOAD_STATE_PREP_IMAGE,
|
||||
MLX5_VF_LOAD_STATE_READ_IMAGE,
|
||||
MLX5_VF_LOAD_STATE_LOAD_IMAGE,
|
||||
};
|
||||
|
||||
struct mlx5_vf_migration_header {
|
||||
__le64 image_size;
|
||||
/* For future use in case we may need to change the kernel protocol */
|
||||
__le64 flags;
|
||||
};
|
||||
|
||||
struct mlx5_vhca_data_buffer {
|
||||
struct sg_append_table table;
|
||||
loff_t start_pos;
|
||||
u64 length;
|
||||
u64 allocated_length;
|
||||
u64 header_image_size;
|
||||
u32 mkey;
|
||||
enum dma_data_direction dma_dir;
|
||||
u8 dmaed:1;
|
||||
struct list_head buf_elm;
|
||||
struct mlx5_vf_migration_file *migf;
|
||||
/* Optimize mlx5vf_get_migration_page() for sequential access */
|
||||
struct scatterlist *last_offset_sg;
|
||||
unsigned int sg_last_entry;
|
||||
unsigned long last_offset;
|
||||
};
|
||||
|
||||
struct mlx5vf_async_data {
|
||||
struct mlx5_async_work cb_work;
|
||||
struct work_struct work;
|
||||
struct mlx5_vhca_data_buffer *buf;
|
||||
struct mlx5_vhca_data_buffer *header_buf;
|
||||
int status;
|
||||
u32 pdn;
|
||||
u32 mkey;
|
||||
u8 last_chunk:1;
|
||||
void *out;
|
||||
};
|
||||
|
||||
struct mlx5_vf_migration_file {
|
||||
struct file *filp;
|
||||
struct mutex lock;
|
||||
u8 disabled:1;
|
||||
u8 is_err:1;
|
||||
enum mlx5_vf_migf_state state;
|
||||
|
||||
struct sg_append_table table;
|
||||
size_t total_length;
|
||||
size_t allocated_length;
|
||||
|
||||
/* Optimize mlx5vf_get_migration_page() for sequential access */
|
||||
struct scatterlist *last_offset_sg;
|
||||
unsigned int sg_last_entry;
|
||||
unsigned long last_offset;
|
||||
enum mlx5_vf_load_state load_state;
|
||||
u32 pdn;
|
||||
loff_t max_pos;
|
||||
struct mlx5_vhca_data_buffer *buf;
|
||||
struct mlx5_vhca_data_buffer *buf_header;
|
||||
spinlock_t list_lock;
|
||||
struct list_head buf_list;
|
||||
struct list_head avail_list;
|
||||
struct mlx5vf_pci_core_device *mvdev;
|
||||
wait_queue_head_t poll_wait;
|
||||
struct completion save_comp;
|
||||
struct mlx5_async_ctx async_ctx;
|
||||
struct mlx5vf_async_data async_data;
|
||||
};
|
||||
@@ -113,19 +156,42 @@ struct mlx5vf_pci_core_device {
|
||||
struct mlx5_core_dev *mdev;
|
||||
};
|
||||
|
||||
enum {
|
||||
MLX5VF_QUERY_INC = (1UL << 0),
|
||||
MLX5VF_QUERY_FINAL = (1UL << 1),
|
||||
};
|
||||
|
||||
int mlx5vf_cmd_suspend_vhca(struct mlx5vf_pci_core_device *mvdev, u16 op_mod);
|
||||
int mlx5vf_cmd_resume_vhca(struct mlx5vf_pci_core_device *mvdev, u16 op_mod);
|
||||
int mlx5vf_cmd_query_vhca_migration_state(struct mlx5vf_pci_core_device *mvdev,
|
||||
size_t *state_size);
|
||||
size_t *state_size, u8 query_flags);
|
||||
void mlx5vf_cmd_set_migratable(struct mlx5vf_pci_core_device *mvdev,
|
||||
const struct vfio_migration_ops *mig_ops,
|
||||
const struct vfio_log_ops *log_ops);
|
||||
void mlx5vf_cmd_remove_migratable(struct mlx5vf_pci_core_device *mvdev);
|
||||
void mlx5vf_cmd_close_migratable(struct mlx5vf_pci_core_device *mvdev);
|
||||
int mlx5vf_cmd_save_vhca_state(struct mlx5vf_pci_core_device *mvdev,
|
||||
struct mlx5_vf_migration_file *migf);
|
||||
struct mlx5_vf_migration_file *migf,
|
||||
struct mlx5_vhca_data_buffer *buf, bool inc,
|
||||
bool track);
|
||||
int mlx5vf_cmd_load_vhca_state(struct mlx5vf_pci_core_device *mvdev,
|
||||
struct mlx5_vf_migration_file *migf);
|
||||
struct mlx5_vf_migration_file *migf,
|
||||
struct mlx5_vhca_data_buffer *buf);
|
||||
int mlx5vf_cmd_alloc_pd(struct mlx5_vf_migration_file *migf);
|
||||
void mlx5vf_cmd_dealloc_pd(struct mlx5_vf_migration_file *migf);
|
||||
void mlx5fv_cmd_clean_migf_resources(struct mlx5_vf_migration_file *migf);
|
||||
struct mlx5_vhca_data_buffer *
|
||||
mlx5vf_alloc_data_buffer(struct mlx5_vf_migration_file *migf,
|
||||
size_t length, enum dma_data_direction dma_dir);
|
||||
void mlx5vf_free_data_buffer(struct mlx5_vhca_data_buffer *buf);
|
||||
struct mlx5_vhca_data_buffer *
|
||||
mlx5vf_get_data_buffer(struct mlx5_vf_migration_file *migf,
|
||||
size_t length, enum dma_data_direction dma_dir);
|
||||
void mlx5vf_put_data_buffer(struct mlx5_vhca_data_buffer *buf);
|
||||
int mlx5vf_add_migration_pages(struct mlx5_vhca_data_buffer *buf,
|
||||
unsigned int npages);
|
||||
struct page *mlx5vf_get_migration_page(struct mlx5_vhca_data_buffer *buf,
|
||||
unsigned long offset);
|
||||
void mlx5vf_state_mutex_unlock(struct mlx5vf_pci_core_device *mvdev);
|
||||
void mlx5vf_disable_fds(struct mlx5vf_pci_core_device *mvdev);
|
||||
void mlx5vf_mig_file_cleanup_cb(struct work_struct *_work);
|
||||
|
||||
+651
-139
File diff suppressed because it is too large
Load Diff
@@ -27,6 +27,9 @@
|
||||
#include <linux/vgaarb.h>
|
||||
#include <linux/nospec.h>
|
||||
#include <linux/sched/mm.h>
|
||||
#if IS_ENABLED(CONFIG_EEH)
|
||||
#include <asm/eeh.h>
|
||||
#endif
|
||||
|
||||
#include "vfio_pci_priv.h"
|
||||
|
||||
@@ -686,7 +689,9 @@ void vfio_pci_core_close_device(struct vfio_device *core_vdev)
|
||||
vdev->sriov_pf_core_dev->vf_token->users--;
|
||||
mutex_unlock(&vdev->sriov_pf_core_dev->vf_token->lock);
|
||||
}
|
||||
vfio_spapr_pci_eeh_release(vdev->pdev);
|
||||
#if IS_ENABLED(CONFIG_EEH)
|
||||
eeh_dev_release(vdev->pdev);
|
||||
#endif
|
||||
vfio_pci_core_disable(vdev);
|
||||
|
||||
mutex_lock(&vdev->igate);
|
||||
@@ -705,7 +710,9 @@ EXPORT_SYMBOL_GPL(vfio_pci_core_close_device);
|
||||
void vfio_pci_core_finish_enable(struct vfio_pci_core_device *vdev)
|
||||
{
|
||||
vfio_pci_probe_mmaps(vdev);
|
||||
vfio_spapr_pci_eeh_open(vdev->pdev);
|
||||
#if IS_ENABLED(CONFIG_EEH)
|
||||
eeh_dev_open(vdev->pdev);
|
||||
#endif
|
||||
|
||||
if (vdev->sriov_pf_core_dev) {
|
||||
mutex_lock(&vdev->sriov_pf_core_dev->vf_token->lock);
|
||||
@@ -2109,7 +2116,6 @@ void vfio_pci_core_release_dev(struct vfio_device *core_vdev)
|
||||
mutex_destroy(&vdev->vma_lock);
|
||||
kfree(vdev->region);
|
||||
kfree(vdev->pm_save);
|
||||
vfio_free_device(core_vdev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(vfio_pci_core_release_dev);
|
||||
|
||||
@@ -2128,7 +2134,8 @@ int vfio_pci_core_register_device(struct vfio_pci_core_device *vdev)
|
||||
|
||||
if (vdev->vdev.mig_ops) {
|
||||
if (!(vdev->vdev.mig_ops->migration_get_state &&
|
||||
vdev->vdev.mig_ops->migration_set_state) ||
|
||||
vdev->vdev.mig_ops->migration_set_state &&
|
||||
vdev->vdev.mig_ops->migration_get_data_size) ||
|
||||
!(vdev->vdev.migration_flags & VFIO_MIGRATION_STOP_COPY))
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
@@ -95,7 +95,6 @@ static void vfio_amba_release_dev(struct vfio_device *core_vdev)
|
||||
|
||||
vfio_platform_release_common(vdev);
|
||||
kfree(vdev->name);
|
||||
vfio_free_device(core_vdev);
|
||||
}
|
||||
|
||||
static void vfio_amba_remove(struct amba_device *adev)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user