Re: [PATCH v2 3/7] vfio/pci: Introduce VF token

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, 9 Mar 2020 01:33:48 +0000
"Tian, Kevin" <kevin.tian@xxxxxxxxx> wrote:

> > From: Tian, Kevin
> > Sent: Monday, March 9, 2020 9:22 AM
> >   
> > > From: Alex Williamson <alex.williamson@xxxxxxxxxx>
> > > Sent: Monday, March 9, 2020 8:46 AM
> > >
> > > On Sat, 7 Mar 2020 01:04:41 +0000
> > > "Tian, Kevin" <kevin.tian@xxxxxxxxx> wrote:
> > >  
> > > > > From: Alex Williamson <alex.williamson@xxxxxxxxxx>
> > > > > Sent: Friday, March 6, 2020 11:39 PM
> > > > >
> > > > > On Fri, 6 Mar 2020 08:32:40 +0000
> > > > > "Tian, Kevin" <kevin.tian@xxxxxxxxx> wrote:
> > > > >  
> > > > > > > From: Alex Williamson <alex.williamson@xxxxxxxxxx>
> > > > > > > Sent: Friday, March 6, 2020 2:18 AM
> > > > > > >
> > > > > > > On Tue, 25 Feb 2020 02:59:37 +0000
> > > > > > > "Tian, Kevin" <kevin.tian@xxxxxxxxx> wrote:
> > > > > > >  
> > > > > > > > > From: Alex Williamson
> > > > > > > > > Sent: Thursday, February 20, 2020 2:54 AM
> > > > > > > > >
> > > > > > > > > If we enable SR-IOV on a vfio-pci owned PF, the resulting VFs are  
> > > not  
> > > > > > > > > fully isolated from the PF.  The PF can always cause a denial of  
> > > service  
> > > > > > > > > to the VF, even if by simply resetting itself.  The degree to which  
> > a  
> > > PF  
> > > > > > > > > can access the data passed through a VF or interfere with its  
> > > > > operation  
> > > > > > > > > is dependent on a given SR-IOV implementation.  Therefore we  
> > > want  
> > > > > to  
> > > > > > > > > avoid a scenario where an existing vfio-pci based userspace  
> > driver  
> > > > > might  
> > > > > > > > > assume the PF driver is trusted, for example assigning a PF to  
> > one  
> > > VM  
> > > > > > > > > and VF to another with some expectation of isolation.  IOMMU  
> > > > > grouping  
> > > > > > > > > could be a solution to this, but imposes an unnecessarily strong
> > > > > > > > > relationship between PF and VF drivers if they need to operate  
> > > with  
> > > > > the  
> > > > > > > > > same IOMMU context.  Instead we introduce a "VF token", which  
> > > is  
> > > > > > > > > essentially just a shared secret between PF and VF drivers,  
> > > > > implemented  
> > > > > > > > > as a UUID.
> > > > > > > > >
> > > > > > > > > The VF token can be set by a vfio-pci based PF driver and must  
> > be  
> > > > > known  
> > > > > > > > > by the vfio-pci based VF driver in order to gain access to the  
> > device.  
> > > > > > > > > This allows the degree to which this VF token is considered  
> > secret  
> > > to  
> > > > > be  
> > > > > > > > > determined by the applications and environment.  For example a  
> > > VM  
> > > > > > > might  
> > > > > > > > > generate a random UUID known only internally to the hypervisor  
> > > > > while a  
> > > > > > > > > userspace networking appliance might use a shared, or even well  
> > > > > know,  
> > > > > > > > > UUID among the application drivers.
> > > > > > > > >
> > > > > > > > > To incorporate this VF token, the VFIO_GROUP_GET_DEVICE_FD  
> > > > > interface  
> > > > > > > is  
> > > > > > > > > extended to accept key=value pairs in addition to the device  
> > name.  
> > > > > This  
> > > > > > > > > allows us to most easily deny user access to the device without  
> > risk  
> > > > > > > > > that existing userspace drivers assume region offsets, IRQs, and  
> > > other  
> > > > > > > > > device features, leading to more elaborate error paths.  The  
> > > format of  
> > > > > > > > > these options are expected to take the form:
> > > > > > > > >
> > > > > > > > > "$DEVICE_NAME $OPTION1=$VALUE1 $OPTION2=$VALUE2"
> > > > > > > > >
> > > > > > > > > Where the device name is always provided first for compatibility  
> > > and  
> > > > > > > > > additional options are specified in a space separated list.  The
> > > > > > > > > relation between and requirements for the additional options  
> > will  
> > > be  
> > > > > > > > > vfio bus driver dependent, however unknown or unused option  
> > > > > within  
> > > > > > > this  
> > > > > > > > > schema should return error.  This allow for future use of  
> > unknown  
> > > > > > > > > options as well as a positive indication to the user that an option  
> > is  
> > > > > > > > > used.
> > > > > > > > >
> > > > > > > > > An example VF token option would take this form:
> > > > > > > > >
> > > > > > > > > "0000:03:00.0 vf_token=2ab74924-c335-45f4-9b16-  
> > 8569e5b08258"  
> > > > > > > > >
> > > > > > > > > When accessing a VF where the PF is making use of vfio-pci, the  
> > > user  
> > > > > > > > > MUST provide the current vf_token.  When accessing a PF, the  
> > > user  
> > > > > MUST  
> > > > > > > > > provide the current vf_token IF there are active VF users or MAY  
> > > > > provide  
> > > > > > > > > a vf_token in order to set the current VF token when no VF users  
> > > are  
> > > > > > > > > active.  The former requirement assures VF users that an  
> > > > > unassociated  
> > > > > > > > > driver cannot usurp the PF device.  These semantics also imply  
> > that  
> > > a  
> > > > > > > > > VF token MUST be set by a PF driver before VF drivers can access  
> > > their  
> > > > > > > > > device, the default token is random and mechanisms to read the  
> > > > > token  
> > > > > > > are  
> > > > > > > > > not provided in order to protect the VF token of previous users.  
> > > Use  
> > > > > of  
> > > > > > > > > the vf_token option outside of these cases will return an error,  
> > as  
> > > > > > > > > discussed above.
> > > > > > > > >
> > > > > > > > > Signed-off-by: Alex Williamson <alex.williamson@xxxxxxxxxx>
> > > > > > > > > ---
> > > > > > > > >  drivers/vfio/pci/vfio_pci.c         |  198
> > > > > > > > > +++++++++++++++++++++++++++++++++++
> > > > > > > > >  drivers/vfio/pci/vfio_pci_private.h |    8 +
> > > > > > > > >  2 files changed, 205 insertions(+), 1 deletion(-)
> > > > > > > > >
> > > > > > > > > diff --git a/drivers/vfio/pci/vfio_pci.c  
> > b/drivers/vfio/pci/vfio_pci.c  
> > > > > > > > > index 2ec6c31d0ab0..8dd6ef9543ca 100644
> > > > > > > > > --- a/drivers/vfio/pci/vfio_pci.c
> > > > > > > > > +++ b/drivers/vfio/pci/vfio_pci.c
> > > > > > > > > @@ -466,6 +466,44 @@ static void vfio_pci_disable(struct  
> > > > > > > vfio_pci_device  
> > > > > > > > > *vdev)
> > > > > > > > >  		vfio_pci_set_power_state(vdev, PCI_D3hot);
> > > > > > > > >  }
> > > > > > > > >
> > > > > > > > > +static struct pci_driver vfio_pci_driver;
> > > > > > > > > +
> > > > > > > > > +static struct vfio_pci_device *get_pf_vdev(struct vfio_pci_device  
> > > > > *vdev,  
> > > > > > > > > +					   struct vfio_device **pf_dev)
> > > > > > > > > +{
> > > > > > > > > +	struct pci_dev *physfn = pci_physfn(vdev->pdev);
> > > > > > > > > +
> > > > > > > > > +	if (!vdev->pdev->is_virtfn)
> > > > > > > > > +		return NULL;
> > > > > > > > > +
> > > > > > > > > +	*pf_dev = vfio_device_get_from_dev(&physfn->dev);
> > > > > > > > > +	if (!*pf_dev)
> > > > > > > > > +		return NULL;
> > > > > > > > > +
> > > > > > > > > +	if (pci_dev_driver(physfn) != &vfio_pci_driver) {
> > > > > > > > > +		vfio_device_put(*pf_dev);
> > > > > > > > > +		return NULL;
> > > > > > > > > +	}
> > > > > > > > > +
> > > > > > > > > +	return vfio_device_data(*pf_dev);
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > > +static void vfio_pci_vf_token_user_add(struct vfio_pci_device  
> > > *vdev,  
> > > > > int  
> > > > > > > val)  
> > > > > > > > > +{
> > > > > > > > > +	struct vfio_device *pf_dev;
> > > > > > > > > +	struct vfio_pci_device *pf_vdev = get_pf_vdev(vdev,  
> > > > > &pf_dev);  
> > > > > > > > > +
> > > > > > > > > +	if (!pf_vdev)
> > > > > > > > > +		return;
> > > > > > > > > +
> > > > > > > > > +	mutex_lock(&pf_vdev->vf_token->lock);
> > > > > > > > > +	pf_vdev->vf_token->users += val;
> > > > > > > > > +	WARN_ON(pf_vdev->vf_token->users < 0);
> > > > > > > > > +	mutex_unlock(&pf_vdev->vf_token->lock);
> > > > > > > > > +
> > > > > > > > > +	vfio_device_put(pf_dev);
> > > > > > > > > +}
> > > > > > > > > +
> > > > > > > > >  static void vfio_pci_release(void *device_data)
> > > > > > > > >  {
> > > > > > > > >  	struct vfio_pci_device *vdev = device_data;
> > > > > > > > > @@ -473,6 +511,7 @@ static void vfio_pci_release(void  
> > > *device_data)  
> > > > > > > > >  	mutex_lock(&vdev->reflck->lock);
> > > > > > > > >
> > > > > > > > >  	if (!(--vdev->refcnt)) {
> > > > > > > > > +		vfio_pci_vf_token_user_add(vdev, -1);
> > > > > > > > >  		vfio_spapr_pci_eeh_release(vdev->pdev);
> > > > > > > > >  		vfio_pci_disable(vdev);
> > > > > > > > >  	}
> > > > > > > > > @@ -498,6 +537,7 @@ static int vfio_pci_open(void  
> > *device_data)  
> > > > > > > > >  			goto error;
> > > > > > > > >
> > > > > > > > >  		vfio_spapr_pci_eeh_open(vdev->pdev);
> > > > > > > > > +		vfio_pci_vf_token_user_add(vdev, 1);
> > > > > > > > >  	}
> > > > > > > > >  	vdev->refcnt++;
> > > > > > > > >  error:
> > > > > > > > > @@ -1278,11 +1318,148 @@ static void vfio_pci_request(void  
> > > > > > > *device_data,  
> > > > > > > > > unsigned int count)
> > > > > > > > >  	mutex_unlock(&vdev->igate);
> > > > > > > > >  }
> > > > > > > > >
> > > > > > > > > +static int vfio_pci_validate_vf_token(struct vfio_pci_device  
> > *vdev,  
> > > > > > > > > +				      bool vf_token, uuid_t *uuid)
> > > > > > > > > +{
> > > > > > > > > +	/*
> > > > > > > > > +	 * There's always some degree of trust or collaboration  
> > > > > between SR-  
> > > > > > > > > IOV
> > > > > > > > > +	 * PF and VFs, even if just that the PF hosts the SR-IOV  
> > > > > capability and  
> > > > > > > > > +	 * can disrupt VFs with a reset, but often the PF has more  
> > > > > explicit  
> > > > > > > > > +	 * access to deny service to the VF or access data passed  
> > > > > through the  
> > > > > > > > > +	 * VF.  We therefore require an opt-in via a shared VF token  
> > > > > (UUID)  
> > > > > > > > > to
> > > > > > > > > +	 * represent this trust.  This both prevents that a VF driver  
> > > > > might  
> > > > > > > > > +	 * assume the PF driver is a trusted, in-kernel driver, and also  
> > > > > that  
> > > > > > > > > +	 * a PF driver might be replaced with a rogue driver, unknown  
> > > > > to in-  
> > > > > > > > > use
> > > > > > > > > +	 * VF drivers.
> > > > > > > > > +	 *
> > > > > > > > > +	 * Therefore when presented with a VF, if the PF is a vfio  
> > > > > device and  
> > > > > > > > > +	 * it is bound to the vfio-pci driver, the user needs to provide  
> > > > > a VF  
> > > > > > > > > +	 * token to access the device, in the form of appending a  
> > > > > vf_token to  
> > > > > > > > > +	 * the device name, for example:
> > > > > > > > > +	 *
> > > > > > > > > +	 * "0000:04:10.0 vf_token=bd8d9d2b-5a5f-4f5a-a211-  
> > > > > f591514ba1f3"  
> > > > > > > > > +	 *
> > > > > > > > > +	 * When presented with a PF which has VFs in use, the user  
> > > > > must also  
> > > > > > > > > +	 * provide the current VF token to prove collaboration with  
> > > > > existing  
> > > > > > > > > +	 * VF users.  If VFs are not in use, the VF token provided for  
> > > > > the PF  
> > > > > > > > > +	 * device will act to set the VF token.
> > > > > > > > > +	 *
> > > > > > > > > +	 * If the VF token is provided but unused, a fault is generated.  
> > > > > > > >
> > > > > > > > fault->error, otherwise it is easy to consider a CPU fault. 😊  
> > > > > > >
> > > > > > > Ok, I can make that change, but I think you might have a unique
> > > > > > > background to make a leap that a userspace ioctl can trigger a CPU
> > > > > > > fault ;)
> > > > > > >  
> > > > > > > > > +	 */
> > > > > > > > > +	if (!vdev->pdev->is_virtfn && !vdev->vf_token && !vf_token)
> > > > > > > > > +		return 0; /* No VF token provided or required */
> > > > > > > > > +
> > > > > > > > > +	if (vdev->pdev->is_virtfn) {
> > > > > > > > > +		struct vfio_device *pf_dev;
> > > > > > > > > +		struct vfio_pci_device *pf_vdev = get_pf_vdev(vdev,
> > > > > > > > > &pf_dev);
> > > > > > > > > +		bool match;
> > > > > > > > > +
> > > > > > > > > +		if (!pf_vdev) {
> > > > > > > > > +			if (!vf_token)
> > > > > > > > > +				return 0; /* PF is not vfio-pci, no VF  
> > > > > token */  
> > > > > > > > > +
> > > > > > > > > +			pci_info_ratelimited(vdev->pdev,
> > > > > > > > > +				"VF token incorrectly provided, PF not  
> > > > > bound  
> > > > > > > > > to vfio-pci\n");
> > > > > > > > > +			return -EINVAL;
> > > > > > > > > +		}
> > > > > > > > > +
> > > > > > > > > +		if (!vf_token) {
> > > > > > > > > +			vfio_device_put(pf_dev);
> > > > > > > > > +			pci_info_ratelimited(vdev->pdev,
> > > > > > > > > +				"VF token required to access  
> > > > > device\n");  
> > > > > > > > > +			return -EACCES;
> > > > > > > > > +		}
> > > > > > > > > +
> > > > > > > > > +		mutex_lock(&pf_vdev->vf_token->lock);
> > > > > > > > > +		match = uuid_equal(uuid, &pf_vdev->vf_token-  
> > > > > >uuid);  
> > > > > > > > > +		mutex_unlock(&pf_vdev->vf_token->lock);
> > > > > > > > > +
> > > > > > > > > +		vfio_device_put(pf_dev);
> > > > > > > > > +
> > > > > > > > > +		if (!match) {
> > > > > > > > > +			pci_info_ratelimited(vdev->pdev,
> > > > > > > > > +				"Incorrect VF token provided for  
> > > > > device\n");  
> > > > > > > > > +			return -EACCES;
> > > > > > > > > +		}
> > > > > > > > > +	} else if (vdev->vf_token) {
> > > > > > > > > +		mutex_lock(&vdev->vf_token->lock);
> > > > > > > > > +		if (vdev->vf_token->users) {
> > > > > > > > > +			if (!vf_token) {
> > > > > > > > > +				mutex_unlock(&vdev->vf_token-  
> > > > > >lock);  
> > > > > > > > > +				pci_info_ratelimited(vdev->pdev,
> > > > > > > > > +					"VF token required to access
> > > > > > > > > device\n");
> > > > > > > > > +				return -EACCES;
> > > > > > > > > +			}
> > > > > > > > > +
> > > > > > > > > +			if (!uuid_equal(uuid, &vdev->vf_token->uuid))  
> > > > > {  
> > > > > > > > > +				mutex_unlock(&vdev->vf_token-  
> > > > > >lock);  
> > > > > > > > > +				pci_info_ratelimited(vdev->pdev,
> > > > > > > > > +					"Incorrect VF token provided  
> > > > > for  
> > > > > > > > > device\n");
> > > > > > > > > +				return -EACCES;
> > > > > > > > > +			}
> > > > > > > > > +		} else if (vf_token) {
> > > > > > > > > +			uuid_copy(&vdev->vf_token->uuid, uuid);
> > > > > > > > > +		}  
> > > > > > > >
> > > > > > > > It implies that we allow PF to be accessed w/o providing a VF token,
> > > > > > > > as long as no VF is currently in-use, which further means no VF can
> > > > > > > > be further assigned since no one knows the random uuid allocated
> > > > > > > > by vfio. Just want to confirm whether it is the desired flavor. If an
> > > > > > > > user really wants to use PF-only, possibly he should disable SR-IOV
> > > > > > > > instead...  
> > > > > > >
> > > > > > > Yes, this is the behavior I'm intending.  Are you suggesting that we
> > > > > > > should require a VF token in order to access a PF that has SR-IOV
> > > > > > > already enabled?  This introduces an inconsistency that SR-IOV can  
> > be  
> > > > > >
> > > > > > yes. I felt that it's meaningless otherwise if an user has no attempt to
> > > > > > manage SR-IOV but still leaving it enabled. In many cases, enabling of
> > > > > > SR-IOV may reserve some resource in the hardware, thus simply  
> > hurting  
> > > > > > PF performance.  
> > > > >
> > > > > But a user needs to be granted access to a device by a privileged
> > > > > entity and the privileged entity may also enable SR-IOV, so it seems
> > > > > you're assuming the privileged entity is operating independently and
> > > > > not in the best interest of enabling the specific user case.  
> > > >
> > > > what about throwing out a warning for such situation? so the userspace
> > > > knows some collaboration is missing before its access to the device.  
> > >
> > > This seems arbitrary.  pci-pf-stub proves to us that there are devices
> > > that need no special setup for SR-IOV, we don't know that we don't have
> > > such a device.  Enabling SR-IOV after the user opens the device also  
> 
> btw no special setup doesn't mean that a PF driver cannot do bad thing to 
> VFs. In such case, I think the whole token idea should be still applied.

pci-pf-stub is a native host driver that does not provide access to the
device to userspace.  We trust native host drivers, whether they be
pci-pf-stub, igb, ixgbe, i40e, etc.  We don't require a token to attach
a userspace driver to a VF of one of these PF drivers because we trust
them.  The VF token idea is exclusively for cases where the PF driver
is an untrusted userspace driver.

> > > doesn't indicate there's necessarily collaboration between the two, so
> > > if we generate a warning on one, how do we assume the other is ok?  I
> > > don't really understand why this is generating such concern.  Thanks,  
> 
> specifically I feel we should warn both:
> 
> 1) userspace driver GET_DEVICE_FD w/o providing a token on a PF
> which has SR-IOV already enabled
> 2) admin writes non-zero numvfs to a PF which has already bound to
> userspace driver which doesn't provide a token
> 
> in both cases VFs are enabled but cannot be used (if you agree that
> the token idea should be also applied to 'no special setup' case)

Both of these seem to be imposing an ordering requirement simply for the
sake of generating a warning.  Nothing else requires that ordering.  In
case 1), it's just as legitimate for the user to call
VFIO_DEVICE_FEATURE to set the token after they've opened the device.
Perhaps the user would even look at config space via the vfio-pci API
in order to determine that SR-IOV is enabled and set a token.  In case
2), the user driver may choose not to set a token until VFs have been
successfully created.

> > I meant to warn the suboptimal case where the userspace driver doesn't
> > provide a token when accessing a PF which has SR-IOV already enabled.
> > I don't think a sane configuration/coordination should do this since all
> > VFs are simply wasted and instead may hurt the PF performance...

*May* hurt performance, but we don't know.  Some designs might have
resources dedicated to VFs that aren't used by the PF at all.  As I've
experimented with this patch series, I find that an igb PF with SR-IOV
enabled assigned to a VM doesn't work at all, it's not simply a
performance issue.  I suspect that's going to be a clue to the user
that their configuration is invalid.  I'm sure we'll take some support
overhead as a result of that, but I don't see that we can generate an
arbitrary advisement warning when it very well might be supported on
other devices.  This is the nature of a meta driver that supports any
device bound to it.  Thanks,

Alex





[Index of Archives]     [DMA Engine]     [Linux Coverity]     [Linux USB]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Greybus]

  Powered by Linux