VFIO updates for v4.21
- Replace global vfio-pci lock with per bus lock to allow concurrent open and release (Alex Williamson) - Declare mdev function as static (Paolo Cretaro) - Convert char to u8 in mdev/mtty sample driver (Nathan Chancellor) -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.14 (GNU/Linux) iQIcBAABAgAGBQJcHQpWAAoJECObm247sIsi1x0QAIZ+XICmk+cWiYJBOH64IIqS ZjjzKj0QtIBnAtecK9OjCeVSbcaPeL10DPNmg/QF0/TmO4Ch71XZZ2D9DOW1neVv 7FPrK9pXDIy/vDq/5yGXr9d/x5LvqiWFG3DmUp+lmUEbd8wFbdBep1/aSr6HY+78 LGoX8TAGuNfj1+GLhL/jXanFE1Az1+dTh63umI9TptRf+HGxQZmRY5JqfZ8OMWrl vNmiugmkS5quhIKa4PO2ZFBndodgI5hMxezkEDqXzOe0bzQ8uBzs43UVa8/V+Ais 05F4Ya5CbeQYTOkSnwN0DC4Vy6WWUtz6TJBB2x8P57ZYb8PYQg7yJluBxZVYmR4S 0c8/Dqi95fUcVSUmDmW/q46YEqFBcXQZEmHP2lap2BLI4H/It7ft/cGacfUuvIq5 g9H7tZBF8g1MsjYRm4D86kC1AcstbBgZeywmmtNwdTVYpeGbIv7DLvWMlVP0Jk/J gKKAO1RHo/tnpZv/W+ge5pXDSOxZOgHRlXnnlj8jMFjQh0J15dtSmQM13MZhaxnZ 5Q7emt2jv7kvYqsBbkTcS27ii4kmjMlnsX0YPL8OK/QlQbW8jJwIraFoPZvUklkZ FrqU9DIJlaGbtMwB04b9wEvWtfPKl/6ycFWFLu+9rFMVaOVT5oeHUQFV1PWOErw8 /l0qJqiTy9y1K4DJEU1P =HOtX -----END PGP SIGNATURE----- Merge tag 'vfio-v4.21-rc1' of git://github.com/awilliam/linux-vfio Pull VFIO updates from Alex Williamson: - Replace global vfio-pci lock with per bus lock to allow concurrent open and release (Alex Williamson) - Declare mdev function as static (Paolo Cretaro) - Convert char to u8 in mdev/mtty sample driver (Nathan Chancellor) * tag 'vfio-v4.21-rc1' of git://github.com/awilliam/linux-vfio: vfio-mdev/samples: Use u8 instead of char for handle functions vfio/mdev: add static modifier to add_mdev_supported_type vfio/pci: Parallelize device open and release
This commit is contained in:
commit
1984f65c2f
4 changed files with 157 additions and 39 deletions
|
@ -92,8 +92,8 @@ static struct kobj_type mdev_type_ktype = {
|
|||
.release = mdev_type_release,
|
||||
};
|
||||
|
||||
struct mdev_type *add_mdev_supported_type(struct mdev_parent *parent,
|
||||
struct attribute_group *group)
|
||||
static struct mdev_type *add_mdev_supported_type(struct mdev_parent *parent,
|
||||
struct attribute_group *group)
|
||||
{
|
||||
struct mdev_type *type;
|
||||
int ret;
|
||||
|
|
|
@ -56,8 +56,6 @@ module_param(disable_idle_d3, bool, S_IRUGO | S_IWUSR);
|
|||
MODULE_PARM_DESC(disable_idle_d3,
|
||||
"Disable using the PCI D3 low power state for idle, unused devices");
|
||||
|
||||
static DEFINE_MUTEX(driver_lock);
|
||||
|
||||
static inline bool vfio_vga_disabled(void)
|
||||
{
|
||||
#ifdef CONFIG_VFIO_PCI_VGA
|
||||
|
@ -416,14 +414,14 @@ static void vfio_pci_release(void *device_data)
|
|||
{
|
||||
struct vfio_pci_device *vdev = device_data;
|
||||
|
||||
mutex_lock(&driver_lock);
|
||||
mutex_lock(&vdev->reflck->lock);
|
||||
|
||||
if (!(--vdev->refcnt)) {
|
||||
vfio_spapr_pci_eeh_release(vdev->pdev);
|
||||
vfio_pci_disable(vdev);
|
||||
}
|
||||
|
||||
mutex_unlock(&driver_lock);
|
||||
mutex_unlock(&vdev->reflck->lock);
|
||||
|
||||
module_put(THIS_MODULE);
|
||||
}
|
||||
|
@ -436,7 +434,7 @@ static int vfio_pci_open(void *device_data)
|
|||
if (!try_module_get(THIS_MODULE))
|
||||
return -ENODEV;
|
||||
|
||||
mutex_lock(&driver_lock);
|
||||
mutex_lock(&vdev->reflck->lock);
|
||||
|
||||
if (!vdev->refcnt) {
|
||||
ret = vfio_pci_enable(vdev);
|
||||
|
@ -447,7 +445,7 @@ static int vfio_pci_open(void *device_data)
|
|||
}
|
||||
vdev->refcnt++;
|
||||
error:
|
||||
mutex_unlock(&driver_lock);
|
||||
mutex_unlock(&vdev->reflck->lock);
|
||||
if (ret)
|
||||
module_put(THIS_MODULE);
|
||||
return ret;
|
||||
|
@ -1225,6 +1223,9 @@ static const struct vfio_device_ops vfio_pci_ops = {
|
|||
.request = vfio_pci_request,
|
||||
};
|
||||
|
||||
static int vfio_pci_reflck_attach(struct vfio_pci_device *vdev);
|
||||
static void vfio_pci_reflck_put(struct vfio_pci_reflck *reflck);
|
||||
|
||||
static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
{
|
||||
struct vfio_pci_device *vdev;
|
||||
|
@ -1271,6 +1272,14 @@ static int vfio_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
|||
return ret;
|
||||
}
|
||||
|
||||
ret = vfio_pci_reflck_attach(vdev);
|
||||
if (ret) {
|
||||
vfio_del_group_dev(&pdev->dev);
|
||||
vfio_iommu_group_put(group, &pdev->dev);
|
||||
kfree(vdev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (vfio_pci_is_vga(pdev)) {
|
||||
vga_client_register(pdev, vdev, NULL, vfio_pci_set_vga_decode);
|
||||
vga_set_legacy_decoding(pdev,
|
||||
|
@ -1302,6 +1311,8 @@ static void vfio_pci_remove(struct pci_dev *pdev)
|
|||
if (!vdev)
|
||||
return;
|
||||
|
||||
vfio_pci_reflck_put(vdev->reflck);
|
||||
|
||||
vfio_iommu_group_put(pdev->dev.iommu_group, &pdev->dev);
|
||||
kfree(vdev->region);
|
||||
mutex_destroy(&vdev->ioeventfds_lock);
|
||||
|
@ -1358,16 +1369,97 @@ static struct pci_driver vfio_pci_driver = {
|
|||
.err_handler = &vfio_err_handlers,
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(reflck_lock);
|
||||
|
||||
static struct vfio_pci_reflck *vfio_pci_reflck_alloc(void)
|
||||
{
|
||||
struct vfio_pci_reflck *reflck;
|
||||
|
||||
reflck = kzalloc(sizeof(*reflck), GFP_KERNEL);
|
||||
if (!reflck)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
kref_init(&reflck->kref);
|
||||
mutex_init(&reflck->lock);
|
||||
|
||||
return reflck;
|
||||
}
|
||||
|
||||
static void vfio_pci_reflck_get(struct vfio_pci_reflck *reflck)
|
||||
{
|
||||
kref_get(&reflck->kref);
|
||||
}
|
||||
|
||||
static int vfio_pci_reflck_find(struct pci_dev *pdev, void *data)
|
||||
{
|
||||
struct vfio_pci_reflck **preflck = data;
|
||||
struct vfio_device *device;
|
||||
struct vfio_pci_device *vdev;
|
||||
|
||||
device = vfio_device_get_from_dev(&pdev->dev);
|
||||
if (!device)
|
||||
return 0;
|
||||
|
||||
if (pci_dev_driver(pdev) != &vfio_pci_driver) {
|
||||
vfio_device_put(device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
vdev = vfio_device_data(device);
|
||||
|
||||
if (vdev->reflck) {
|
||||
vfio_pci_reflck_get(vdev->reflck);
|
||||
*preflck = vdev->reflck;
|
||||
vfio_device_put(device);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vfio_device_put(device);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfio_pci_reflck_attach(struct vfio_pci_device *vdev)
|
||||
{
|
||||
bool slot = !pci_probe_reset_slot(vdev->pdev->slot);
|
||||
|
||||
mutex_lock(&reflck_lock);
|
||||
|
||||
if (pci_is_root_bus(vdev->pdev->bus) ||
|
||||
vfio_pci_for_each_slot_or_bus(vdev->pdev, vfio_pci_reflck_find,
|
||||
&vdev->reflck, slot) <= 0)
|
||||
vdev->reflck = vfio_pci_reflck_alloc();
|
||||
|
||||
mutex_unlock(&reflck_lock);
|
||||
|
||||
return PTR_ERR_OR_ZERO(vdev->reflck);
|
||||
}
|
||||
|
||||
static void vfio_pci_reflck_release(struct kref *kref)
|
||||
{
|
||||
struct vfio_pci_reflck *reflck = container_of(kref,
|
||||
struct vfio_pci_reflck,
|
||||
kref);
|
||||
|
||||
kfree(reflck);
|
||||
mutex_unlock(&reflck_lock);
|
||||
}
|
||||
|
||||
static void vfio_pci_reflck_put(struct vfio_pci_reflck *reflck)
|
||||
{
|
||||
kref_put_mutex(&reflck->kref, vfio_pci_reflck_release, &reflck_lock);
|
||||
}
|
||||
|
||||
struct vfio_devices {
|
||||
struct vfio_device **devices;
|
||||
int cur_index;
|
||||
int max_index;
|
||||
};
|
||||
|
||||
static int vfio_pci_get_devs(struct pci_dev *pdev, void *data)
|
||||
static int vfio_pci_get_unused_devs(struct pci_dev *pdev, void *data)
|
||||
{
|
||||
struct vfio_devices *devs = data;
|
||||
struct vfio_device *device;
|
||||
struct vfio_pci_device *vdev;
|
||||
|
||||
if (devs->cur_index == devs->max_index)
|
||||
return -ENOSPC;
|
||||
|
@ -1381,16 +1473,28 @@ static int vfio_pci_get_devs(struct pci_dev *pdev, void *data)
|
|||
return -EBUSY;
|
||||
}
|
||||
|
||||
vdev = vfio_device_data(device);
|
||||
|
||||
/* Fault if the device is not unused */
|
||||
if (vdev->refcnt) {
|
||||
vfio_device_put(device);
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
devs->devices[devs->cur_index++] = device;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Attempt to do a bus/slot reset if there are devices affected by a reset for
|
||||
* this device that are needs_reset and all of the affected devices are unused
|
||||
* (!refcnt). Callers are required to hold driver_lock when calling this to
|
||||
* prevent device opens and concurrent bus reset attempts. We prevent device
|
||||
* unbinds by acquiring and holding a reference to the vfio_device.
|
||||
* If a bus or slot reset is available for the provided device and:
|
||||
* - All of the devices affected by that bus or slot reset are unused
|
||||
* (!refcnt)
|
||||
* - At least one of the affected devices is marked dirty via
|
||||
* needs_reset (such as by lack of FLR support)
|
||||
* Then attempt to perform that bus or slot reset. Callers are required
|
||||
* to hold vdev->reflck->lock, protecting the bus/slot reset group from
|
||||
* concurrent opens. A vfio_device reference is acquired for each device
|
||||
* to prevent unbinds during the reset operation.
|
||||
*
|
||||
* NB: vfio-core considers a group to be viable even if some devices are
|
||||
* bound to drivers like pci-stub or pcieport. Here we require all devices
|
||||
|
@ -1401,7 +1505,7 @@ static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev)
|
|||
{
|
||||
struct vfio_devices devs = { .cur_index = 0 };
|
||||
int i = 0, ret = -EINVAL;
|
||||
bool needs_reset = false, slot = false;
|
||||
bool slot = false;
|
||||
struct vfio_pci_device *tmp;
|
||||
|
||||
if (!pci_probe_reset_slot(vdev->pdev->slot))
|
||||
|
@ -1419,28 +1523,36 @@ static void vfio_pci_try_bus_reset(struct vfio_pci_device *vdev)
|
|||
return;
|
||||
|
||||
if (vfio_pci_for_each_slot_or_bus(vdev->pdev,
|
||||
vfio_pci_get_devs, &devs, slot))
|
||||
vfio_pci_get_unused_devs,
|
||||
&devs, slot))
|
||||
goto put_devs;
|
||||
|
||||
/* Does at least one need a reset? */
|
||||
for (i = 0; i < devs.cur_index; i++) {
|
||||
tmp = vfio_device_data(devs.devices[i]);
|
||||
if (tmp->needs_reset)
|
||||
needs_reset = true;
|
||||
if (tmp->refcnt)
|
||||
goto put_devs;
|
||||
if (tmp->needs_reset) {
|
||||
ret = pci_reset_bus(vdev->pdev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_reset)
|
||||
ret = pci_reset_bus(vdev->pdev);
|
||||
|
||||
put_devs:
|
||||
for (i = 0; i < devs.cur_index; i++) {
|
||||
tmp = vfio_device_data(devs.devices[i]);
|
||||
if (!ret)
|
||||
|
||||
/*
|
||||
* If reset was successful, affected devices no longer need
|
||||
* a reset and we should return all the collateral devices
|
||||
* to low power. If not successful, we either didn't reset
|
||||
* the bus or timed out waiting for it, so let's not touch
|
||||
* the power state.
|
||||
*/
|
||||
if (!ret) {
|
||||
tmp->needs_reset = false;
|
||||
|
||||
if (!tmp->refcnt && !disable_idle_d3)
|
||||
pci_set_power_state(tmp->pdev, PCI_D3hot);
|
||||
if (tmp != vdev && !disable_idle_d3)
|
||||
pci_set_power_state(tmp->pdev, PCI_D3hot);
|
||||
}
|
||||
|
||||
vfio_device_put(devs.devices[i]);
|
||||
}
|
||||
|
|
|
@ -82,6 +82,11 @@ struct vfio_pci_dummy_resource {
|
|||
struct list_head res_next;
|
||||
};
|
||||
|
||||
struct vfio_pci_reflck {
|
||||
struct kref kref;
|
||||
struct mutex lock;
|
||||
};
|
||||
|
||||
struct vfio_pci_device {
|
||||
struct pci_dev *pdev;
|
||||
void __iomem *barmap[PCI_STD_RESOURCE_END + 1];
|
||||
|
@ -110,6 +115,7 @@ struct vfio_pci_device {
|
|||
bool needs_reset;
|
||||
bool nointx;
|
||||
struct pci_saved_state *pci_saved_state;
|
||||
struct vfio_pci_reflck *reflck;
|
||||
int refcnt;
|
||||
int ioeventfds_nr;
|
||||
struct eventfd_ctx *err_trigger;
|
||||
|
|
|
@ -171,7 +171,7 @@ static struct mdev_state *find_mdev_state_by_uuid(uuid_le uuid)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void dump_buffer(char *buf, uint32_t count)
|
||||
void dump_buffer(u8 *buf, uint32_t count)
|
||||
{
|
||||
#if defined(DEBUG)
|
||||
int i;
|
||||
|
@ -250,7 +250,7 @@ static void mtty_create_config_space(struct mdev_state *mdev_state)
|
|||
}
|
||||
|
||||
static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset,
|
||||
char *buf, u32 count)
|
||||
u8 *buf, u32 count)
|
||||
{
|
||||
u32 cfg_addr, bar_mask, bar_index = 0;
|
||||
|
||||
|
@ -304,7 +304,7 @@ static void handle_pci_cfg_write(struct mdev_state *mdev_state, u16 offset,
|
|||
}
|
||||
|
||||
static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,
|
||||
u16 offset, char *buf, u32 count)
|
||||
u16 offset, u8 *buf, u32 count)
|
||||
{
|
||||
u8 data = *buf;
|
||||
|
||||
|
@ -475,7 +475,7 @@ static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,
|
|||
}
|
||||
|
||||
static void handle_bar_read(unsigned int index, struct mdev_state *mdev_state,
|
||||
u16 offset, char *buf, u32 count)
|
||||
u16 offset, u8 *buf, u32 count)
|
||||
{
|
||||
/* Handle read requests by guest */
|
||||
switch (offset) {
|
||||
|
@ -650,7 +650,7 @@ static void mdev_read_base(struct mdev_state *mdev_state)
|
|||
}
|
||||
}
|
||||
|
||||
static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count,
|
||||
static ssize_t mdev_access(struct mdev_device *mdev, u8 *buf, size_t count,
|
||||
loff_t pos, bool is_write)
|
||||
{
|
||||
struct mdev_state *mdev_state;
|
||||
|
@ -698,7 +698,7 @@ static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count,
|
|||
#if defined(DEBUG_REGS)
|
||||
pr_info("%s: BAR%d WR @0x%llx %s val:0x%02x dlab:%d\n",
|
||||
__func__, index, offset, wr_reg[offset],
|
||||
(u8)*buf, mdev_state->s[index].dlab);
|
||||
*buf, mdev_state->s[index].dlab);
|
||||
#endif
|
||||
handle_bar_write(index, mdev_state, offset, buf, count);
|
||||
} else {
|
||||
|
@ -708,7 +708,7 @@ static ssize_t mdev_access(struct mdev_device *mdev, char *buf, size_t count,
|
|||
#if defined(DEBUG_REGS)
|
||||
pr_info("%s: BAR%d RD @0x%llx %s val:0x%02x dlab:%d\n",
|
||||
__func__, index, offset, rd_reg[offset],
|
||||
(u8)*buf, mdev_state->s[index].dlab);
|
||||
*buf, mdev_state->s[index].dlab);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
@ -827,7 +827,7 @@ ssize_t mtty_read(struct mdev_device *mdev, char __user *buf, size_t count,
|
|||
if (count >= 4 && !(*ppos % 4)) {
|
||||
u32 val;
|
||||
|
||||
ret = mdev_access(mdev, (char *)&val, sizeof(val),
|
||||
ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
|
||||
*ppos, false);
|
||||
if (ret <= 0)
|
||||
goto read_err;
|
||||
|
@ -839,7 +839,7 @@ ssize_t mtty_read(struct mdev_device *mdev, char __user *buf, size_t count,
|
|||
} else if (count >= 2 && !(*ppos % 2)) {
|
||||
u16 val;
|
||||
|
||||
ret = mdev_access(mdev, (char *)&val, sizeof(val),
|
||||
ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
|
||||
*ppos, false);
|
||||
if (ret <= 0)
|
||||
goto read_err;
|
||||
|
@ -851,7 +851,7 @@ ssize_t mtty_read(struct mdev_device *mdev, char __user *buf, size_t count,
|
|||
} else {
|
||||
u8 val;
|
||||
|
||||
ret = mdev_access(mdev, (char *)&val, sizeof(val),
|
||||
ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
|
||||
*ppos, false);
|
||||
if (ret <= 0)
|
||||
goto read_err;
|
||||
|
@ -889,7 +889,7 @@ ssize_t mtty_write(struct mdev_device *mdev, const char __user *buf,
|
|||
if (copy_from_user(&val, buf, sizeof(val)))
|
||||
goto write_err;
|
||||
|
||||
ret = mdev_access(mdev, (char *)&val, sizeof(val),
|
||||
ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
|
||||
*ppos, true);
|
||||
if (ret <= 0)
|
||||
goto write_err;
|
||||
|
@ -901,7 +901,7 @@ ssize_t mtty_write(struct mdev_device *mdev, const char __user *buf,
|
|||
if (copy_from_user(&val, buf, sizeof(val)))
|
||||
goto write_err;
|
||||
|
||||
ret = mdev_access(mdev, (char *)&val, sizeof(val),
|
||||
ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
|
||||
*ppos, true);
|
||||
if (ret <= 0)
|
||||
goto write_err;
|
||||
|
@ -913,7 +913,7 @@ ssize_t mtty_write(struct mdev_device *mdev, const char __user *buf,
|
|||
if (copy_from_user(&val, buf, sizeof(val)))
|
||||
goto write_err;
|
||||
|
||||
ret = mdev_access(mdev, (char *)&val, sizeof(val),
|
||||
ret = mdev_access(mdev, (u8 *)&val, sizeof(val),
|
||||
*ppos, true);
|
||||
if (ret <= 0)
|
||||
goto write_err;
|
||||
|
|
Loading…
Reference in a new issue