[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <87tucgiouf.fsf@redhat.com>
Date: Wed, 02 Mar 2022 12:19:20 +0100
From: Cornelia Huck <cohuck@...hat.com>
To: Yishai Hadas <yishaih@...dia.com>, alex.williamson@...hat.com,
bhelgaas@...gle.com, jgg@...dia.com, saeedm@...dia.com
Cc: linux-pci@...r.kernel.org, kvm@...r.kernel.org,
netdev@...r.kernel.org, kuba@...nel.org, leonro@...dia.com,
kwankhede@...dia.com, mgurtovoy@...dia.com, yishaih@...dia.com,
maorg@...dia.com, ashok.raj@...el.com, kevin.tian@...el.com,
shameerali.kolothum.thodi@...wei.com
Subject: Re: [PATCH V9 mlx5-next 09/15] vfio: Define device migration
protocol v2
On Thu, Feb 24 2022, Yishai Hadas <yishaih@...dia.com> wrote:
> From: Jason Gunthorpe <jgg@...dia.com>
>
> Replace the existing region based migration protocol with an ioctl based
> protocol. The two protocols have the same general semantic behaviors, but
> the way the data is transported is changed.
>
> This is the STOP_COPY portion of the new protocol, it defines the 5 states
> for basic stop and copy migration and the protocol to move the migration
> data in/out of the kernel.
>
> Compared to the clarification of the v1 protocol Alex proposed:
>
> https://lore.kernel.org/r/163909282574.728533.7460416142511440919.stgit@omen
>
> This has a few deliberate functional differences:
>
> - ERROR arcs allow the device function to remain unchanged.
>
> - The protocol is not required to return to the original state on
> transition failure. Instead userspace can execute an unwind back to
> the original state, reset, or do something else without needing kernel
> support. This simplifies the kernel design and should userspace choose
> a policy like always reset, avoids doing useless work in the kernel
> on error handling paths.
>
> - PRE_COPY is made optional, userspace must discover it before using it.
> This reflects the fact that the majority of drivers we are aware of
> right now will not implement PRE_COPY.
>
> - segmentation is not part of the data stream protocol, the receiver
> does not have to reproduce the framing boundaries.
>
> The hybrid FSM for the device_state is described as a Mealy machine by
> documenting each of the arcs the driver is required to implement. Defining
> the remaining set of old/new device_state transitions as 'combination
> transitions' which are naturally defined as taking multiple FSM arcs along
> the shortest path within the FSM's digraph allows a complete matrix of
> transitions.
>
> A new VFIO_DEVICE_FEATURE of VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE is
> defined to replace writing to the device_state field in the region. This
> allows returning a brand new FD whenever the requested transition opens
> a data transfer session.
>
> The VFIO core code implements the new feature and provides a helper
> function to the driver. Using the helper the driver only has to
> implement 6 of the FSM arcs and the other combination transitions are
> elaborated consistently from those arcs.
>
> A new VFIO_DEVICE_FEATURE of VFIO_DEVICE_FEATURE_MIGRATION is defined to
> report the capability for migration and indicate which set of states and
> arcs are supported by the device. The FSM provides a lot of flexibility to
> make backwards compatible extensions but the VFIO_DEVICE_FEATURE also
> allows for future breaking extensions for scenarios that cannot support
> even the basic STOP_COPY requirements.
>
> The VFIO_DEVICE_FEATURE_MIG_DEVICE_STATE with the GET option (i.e.
> VFIO_DEVICE_FEATURE_GET) can be used to read the current migration state
> of the VFIO device.
>
> Data transfer sessions are now carried over a file descriptor, instead of
> the region. The FD functions for the lifetime of the data transfer
> session. read() and write() transfer the data with normal Linux stream FD
> semantics. This design allows future expansion to support poll(),
> io_uring, and other performance optimizations.
>
> The complicated mmap mode for data transfer is discarded as current qemu
> doesn't take meaningful advantage of it, and the new qemu implementation
> avoids substantially all the performance penalty of using a read() on the
> region.
>
> Signed-off-by: Jason Gunthorpe <jgg@...dia.com>
> Tested-by: Shameer Kolothum <shameerali.kolothum.thodi@...wei.com>
> Reviewed-by: Kevin Tian <kevin.tian@...el.com>
> Signed-off-by: Yishai Hadas <yishaih@...dia.com>
> ---
> drivers/vfio/vfio.c | 199 ++++++++++++++++++++++++++++++++++++++
> include/linux/vfio.h | 20 ++++
> include/uapi/linux/vfio.h | 174 ++++++++++++++++++++++++++++++---
> 3 files changed, 380 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/vfio/vfio.c b/drivers/vfio/vfio.c
> index 71763e2ac561..b37ab27b511f 100644
> --- a/drivers/vfio/vfio.c
> +++ b/drivers/vfio/vfio.c
> @@ -1557,6 +1557,197 @@ static int vfio_device_fops_release(struct inode *inode, struct file *filep)
> return 0;
> }
>
> +/*
> + * vfio_mig_get_next_state - Compute the next step in the FSM
> + * @cur_fsm - The current state the device is in
> + * @new_fsm - The target state to reach
> + * @next_fsm - Pointer to the next step to get to new_fsm
> + *
> + * Return 0 upon success, otherwise -errno
> + * Upon success the next step in the state progression between cur_fsm and
> + * new_fsm will be set in next_fsm.
What about non-success? Can the caller make any assumption about
next_fsm in that case? Because...
> + *
> + * This breaks down requests for combination transitions into smaller steps and
> + * returns the next step to get to new_fsm. The function may need to be called
> + * multiple times before reaching new_fsm.
> + *
> + */
> +int vfio_mig_get_next_state(struct vfio_device *device,
> + enum vfio_device_mig_state cur_fsm,
> + enum vfio_device_mig_state new_fsm,
> + enum vfio_device_mig_state *next_fsm)
> +{
> + enum { VFIO_DEVICE_NUM_STATES = VFIO_DEVICE_STATE_RESUMING + 1 };
> + /*
> + * The coding in this table requires the driver to implement 6
> + * FSM arcs:
> + * RESUMING -> STOP
> + * RUNNING -> STOP
> + * STOP -> RESUMING
> + * STOP -> RUNNING
> + * STOP -> STOP_COPY
> + * STOP_COPY -> STOP
> + *
> + * The coding will step through multiple states for these combination
> + * transitions:
> + * RESUMING -> STOP -> RUNNING
> + * RESUMING -> STOP -> STOP_COPY
> + * RUNNING -> STOP -> RESUMING
> + * RUNNING -> STOP -> STOP_COPY
> + * STOP_COPY -> STOP -> RESUMING
> + * STOP_COPY -> STOP -> RUNNING
> + */
> + static const u8 vfio_from_fsm_table[VFIO_DEVICE_NUM_STATES][VFIO_DEVICE_NUM_STATES] = {
> + [VFIO_DEVICE_STATE_STOP] = {
> + [VFIO_DEVICE_STATE_STOP] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_RUNNING] = VFIO_DEVICE_STATE_RUNNING,
> + [VFIO_DEVICE_STATE_STOP_COPY] = VFIO_DEVICE_STATE_STOP_COPY,
> + [VFIO_DEVICE_STATE_RESUMING] = VFIO_DEVICE_STATE_RESUMING,
> + [VFIO_DEVICE_STATE_ERROR] = VFIO_DEVICE_STATE_ERROR,
> + },
> + [VFIO_DEVICE_STATE_RUNNING] = {
> + [VFIO_DEVICE_STATE_STOP] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_RUNNING] = VFIO_DEVICE_STATE_RUNNING,
> + [VFIO_DEVICE_STATE_STOP_COPY] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_RESUMING] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_ERROR] = VFIO_DEVICE_STATE_ERROR,
> + },
> + [VFIO_DEVICE_STATE_STOP_COPY] = {
> + [VFIO_DEVICE_STATE_STOP] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_RUNNING] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_STOP_COPY] = VFIO_DEVICE_STATE_STOP_COPY,
> + [VFIO_DEVICE_STATE_RESUMING] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_ERROR] = VFIO_DEVICE_STATE_ERROR,
> + },
> + [VFIO_DEVICE_STATE_RESUMING] = {
> + [VFIO_DEVICE_STATE_STOP] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_RUNNING] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_STOP_COPY] = VFIO_DEVICE_STATE_STOP,
> + [VFIO_DEVICE_STATE_RESUMING] = VFIO_DEVICE_STATE_RESUMING,
> + [VFIO_DEVICE_STATE_ERROR] = VFIO_DEVICE_STATE_ERROR,
> + },
> + [VFIO_DEVICE_STATE_ERROR] = {
> + [VFIO_DEVICE_STATE_STOP] = VFIO_DEVICE_STATE_ERROR,
> + [VFIO_DEVICE_STATE_RUNNING] = VFIO_DEVICE_STATE_ERROR,
> + [VFIO_DEVICE_STATE_STOP_COPY] = VFIO_DEVICE_STATE_ERROR,
> + [VFIO_DEVICE_STATE_RESUMING] = VFIO_DEVICE_STATE_ERROR,
> + [VFIO_DEVICE_STATE_ERROR] = VFIO_DEVICE_STATE_ERROR,
> + },
> + };
> +
> + if (WARN_ON(cur_fsm >= ARRAY_SIZE(vfio_from_fsm_table)))
> + return -EINVAL;
> +
> + if (new_fsm >= ARRAY_SIZE(vfio_from_fsm_table))
> + return -EINVAL;
> +
> + *next_fsm = vfio_from_fsm_table[cur_fsm][new_fsm];
> + return (*next_fsm != VFIO_DEVICE_STATE_ERROR) ? 0 : -EINVAL;
...next_fsm will contain STATE_ERROR if we try to transition from or to
STATE_ERROR, but it remains unchanged if the input states are out of
range, yet in both cases the return value is -EINVAL. Looking further, ...
> + * any -> ERROR
> + * ERROR cannot be specified as a device state, however any transition request
> + * can be failed with an errno return and may then move the device_state into
> + * ERROR. In this case the device was unable to execute the requested arc and
> + * was also unable to restore the device to any valid device_state.
> + * To recover from ERROR VFIO_DEVICE_RESET must be used to return the
> + * device_state back to RUNNING.
...this seems to indicate that not moving into STATE_ERROR is an
option anyway. Do we need any extra guidance in the description for
vfio_mig_get_next_state()?
Powered by blists - more mailing lists