VFIO fixes for v4.10-rc3

- Add mtty sample driver properly into build system (Alex Williamson)
  - Restore type1 mapping performance after mdev (Alex Williamson)
  - Fix mdev device race (Alex Williamson)
  - Cleanups to the mdev ABI used by vendor drivers (Alex Williamson)
  - Build fix for old compilers (Arnd Bergmann)
  - Fix sample driver error path (Dan Carpenter)
  - Handle pci_iomap() error (Arvind Yadav)
  - Fix mdev ioctl return type (Paul Gortmaker)
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.14 (GNU/Linux)
 
 iQIcBAABAgAGBQJYb73JAAoJECObm247sIsi8UoP/22ad8PE1kFxRndZBZ0cAAeR
 KN/m1fpxRj0cLYC/VG4CseU9qMDKCa6WT/QY/kbR2Mm4CzUdoF1pXyWZkqs1PCuY
 tOeGk11KR+OM1AhqmJxn1+5A/9Bp0pLUYC+bBujJ5Pg955ePzv8hp6eD1xedjN+H
 mq4HAWCL8ac0mxNicAo/EUmsdwpUtXN9ruzDnEqMy9u33U4+p9hlWMzKz1xBch1h
 uIEwSZu2K5Qb7LmN1faCTWHuE2brN3rsIYTE0K4hQn71cksHk2CjURi7YSbaDXRM
 KUhHO6BS3Wbrr5ljXB8ih08+vesYKfU8nHFNWssQiQtg3SiIy5u7zVXJbDSiOQ3F
 f7HqZFm8Kpjw15TkpLOWfg03D8LDxBrael8w92Xi1joFQSLw6ei4OFz44lx5f5+1
 hfWadBJv7dfATTOaH8jz85ClFzZ+eEI1D5k4ehv9Rt+FL6KQh31AK+LRx98gDKc2
 vDVxqfJegMLmAAYcwrmpiv/KytRWveXBGNVmarbXCZ1AhwGSjpV/1PJw2GXY47Fh
 0pgjTdVg2X7mB6ljMlB3KioVTzRvXgG0gURiIQC0kEs93bS9aJqywZEjgJJQdcy3
 bh+K2vKX0O+V4ekx1fMvHFJM/7l27d8GleWWSnxE6yKW9X3+nkgsZHj9/JayuWF/
 nw1fHMl4U7M8dCHmy9mV
 =EHct
 -----END PGP SIGNATURE-----

Merge tag 'vfio-v4.10-rc3' of git://github.com/awilliam/linux-vfio

Pull VFIO fixes from Alex Williamson:
 - Add mtty sample driver properly into build system (Alex Williamson)
 - Restore type1 mapping performance after mdev (Alex Williamson)
 - Fix mdev device race (Alex Williamson)
 - Cleanups to the mdev ABI used by vendor drivers (Alex Williamson)
 - Build fix for old compilers (Arnd Bergmann)
 - Fix sample driver error path (Dan Carpenter)
 - Handle pci_iomap() error (Arvind Yadav)
 - Fix mdev ioctl return type (Paul Gortmaker)

* tag 'vfio-v4.10-rc3' of git://github.com/awilliam/linux-vfio:
  vfio-mdev: fix non-standard ioctl return val causing i386 build fail
  vfio-pci: Handle error from pci_iomap
  vfio-mdev: fix some error codes in the sample code
  vfio-pci: use 32-bit comparisons for register address for gcc-4.5
  vfio-mdev: Make mdev_device private and abstract interfaces
  vfio-mdev: Make mdev_parent private
  vfio-mdev: de-polute the namespace, rename parent_device & parent_ops
  vfio-mdev: Fix remove race
  vfio/type1: Restore mapping performance with mdev support
  vfio-mdev: Fix mtty sample driver building
This commit is contained in:
Linus Torvalds 2017-01-06 11:19:03 -08:00
commit 5824f92463
14 changed files with 247 additions and 172 deletions

View File

@ -127,22 +127,22 @@ the VFIO when devices are unbound from the driver.
Physical Device Driver Interface
--------------------------------
The physical device driver interface provides the parent_ops[3] structure to
define the APIs to manage work in the mediated core driver that is related to
the physical device.
The physical device driver interface provides the mdev_parent_ops[3] structure
to define the APIs to manage work in the mediated core driver that is related
to the physical device.
The structures in the parent_ops structure are as follows:
The structures in the mdev_parent_ops structure are as follows:
* dev_attr_groups: attributes of the parent device
* mdev_attr_groups: attributes of the mediated device
* supported_config: attributes to define supported configurations
The functions in the parent_ops structure are as follows:
The functions in the mdev_parent_ops structure are as follows:
* create: allocate basic resources in a driver for a mediated device
* remove: free resources in a driver when a mediated device is destroyed
The callbacks in the parent_ops structure are as follows:
The callbacks in the mdev_parent_ops structure are as follows:
* open: open callback of mediated device
* close: close callback of mediated device
@ -151,14 +151,14 @@ The callbacks in the parent_ops structure are as follows:
* write: write emulation callback
* mmap: mmap emulation callback
A driver should use the parent_ops structure in the function call to register
itself with the mdev core driver:
A driver should use the mdev_parent_ops structure in the function call to
register itself with the mdev core driver:
extern int mdev_register_device(struct device *dev,
const struct parent_ops *ops);
const struct mdev_parent_ops *ops);
However, the parent_ops structure is not required in the function call that a
driver should use to unregister itself with the mdev core driver:
However, the mdev_parent_ops structure is not required in the function call
that a driver should use to unregister itself with the mdev core driver:
extern void mdev_unregister_device(struct device *dev);
@ -223,6 +223,9 @@ Directories and files under the sysfs for Each Physical Device
sprintf(buf, "%s-%s", dev_driver_string(parent->dev), group->name);
(or using mdev_parent_dev(mdev) to arrive at the parent device outside
of the core mdev code)
* device_api
This attribute should show which device API is being created, for example,
@ -394,5 +397,5 @@ References
[1] See Documentation/vfio.txt for more information on VFIO.
[2] struct mdev_driver in include/linux/mdev.h
[3] struct parent_ops in include/linux/mdev.h
[3] struct mdev_parent_ops in include/linux/mdev.h
[4] struct vfio_iommu_driver_ops in include/linux/vfio.h

View File

@ -169,7 +169,7 @@ static void __gvt_cache_remove_entry(struct intel_vgpu *vgpu,
static void gvt_cache_remove(struct intel_vgpu *vgpu, gfn_t gfn)
{
struct device *dev = &vgpu->vdev.mdev->dev;
struct device *dev = mdev_dev(vgpu->vdev.mdev);
struct gvt_dma *this;
unsigned long g1;
int rc;
@ -198,7 +198,7 @@ static void gvt_cache_destroy(struct intel_vgpu *vgpu)
{
struct gvt_dma *dma;
struct rb_node *node = NULL;
struct device *dev = &vgpu->vdev.mdev->dev;
struct device *dev = mdev_dev(vgpu->vdev.mdev);
unsigned long gfn;
mutex_lock(&vgpu->vdev.cache_lock);
@ -399,7 +399,7 @@ static int intel_vgpu_create(struct kobject *kobj, struct mdev_device *mdev)
struct device *pdev;
void *gvt;
pdev = mdev->parent->dev;
pdev = mdev_parent_dev(mdev);
gvt = kdev_to_i915(pdev)->gvt;
type = intel_gvt_find_vgpu_type(gvt, kobject_name(kobj));
@ -421,7 +421,7 @@ static int intel_vgpu_create(struct kobject *kobj, struct mdev_device *mdev)
mdev_set_drvdata(mdev, vgpu);
gvt_dbg_core("intel_vgpu_create succeeded for mdev: %s\n",
dev_name(&mdev->dev));
dev_name(mdev_dev(mdev)));
return 0;
}
@ -485,7 +485,7 @@ static int intel_vgpu_open(struct mdev_device *mdev)
vgpu->vdev.group_notifier.notifier_call = intel_vgpu_group_notifier;
events = VFIO_IOMMU_NOTIFY_DMA_UNMAP;
ret = vfio_register_notifier(&mdev->dev, VFIO_IOMMU_NOTIFY, &events,
ret = vfio_register_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY, &events,
&vgpu->vdev.iommu_notifier);
if (ret != 0) {
gvt_err("vfio_register_notifier for iommu failed: %d\n", ret);
@ -493,7 +493,7 @@ static int intel_vgpu_open(struct mdev_device *mdev)
}
events = VFIO_GROUP_NOTIFY_SET_KVM;
ret = vfio_register_notifier(&mdev->dev, VFIO_GROUP_NOTIFY, &events,
ret = vfio_register_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY, &events,
&vgpu->vdev.group_notifier);
if (ret != 0) {
gvt_err("vfio_register_notifier for group failed: %d\n", ret);
@ -508,11 +508,11 @@ static int intel_vgpu_open(struct mdev_device *mdev)
return ret;
undo_group:
vfio_unregister_notifier(&mdev->dev, VFIO_GROUP_NOTIFY,
vfio_unregister_notifier(mdev_dev(mdev), VFIO_GROUP_NOTIFY,
&vgpu->vdev.group_notifier);
undo_iommu:
vfio_unregister_notifier(&mdev->dev, VFIO_IOMMU_NOTIFY,
vfio_unregister_notifier(mdev_dev(mdev), VFIO_IOMMU_NOTIFY,
&vgpu->vdev.iommu_notifier);
out:
return ret;
@ -529,11 +529,11 @@ static void __intel_vgpu_release(struct intel_vgpu *vgpu)
if (atomic_cmpxchg(&vgpu->vdev.released, 0, 1))
return;
ret = vfio_unregister_notifier(&vgpu->vdev.mdev->dev, VFIO_IOMMU_NOTIFY,
ret = vfio_unregister_notifier(mdev_dev(vgpu->vdev.mdev), VFIO_IOMMU_NOTIFY,
&vgpu->vdev.iommu_notifier);
WARN(ret, "vfio_unregister_notifier for iommu failed: %d\n", ret);
ret = vfio_unregister_notifier(&vgpu->vdev.mdev->dev, VFIO_GROUP_NOTIFY,
ret = vfio_unregister_notifier(mdev_dev(vgpu->vdev.mdev), VFIO_GROUP_NOTIFY,
&vgpu->vdev.group_notifier);
WARN(ret, "vfio_unregister_notifier for group failed: %d\n", ret);
@ -1111,7 +1111,7 @@ static long intel_vgpu_ioctl(struct mdev_device *mdev, unsigned int cmd,
return 0;
}
static const struct parent_ops intel_vgpu_ops = {
static const struct mdev_parent_ops intel_vgpu_ops = {
.supported_type_groups = intel_vgpu_type_groups,
.create = intel_vgpu_create,
.remove = intel_vgpu_remove,
@ -1398,7 +1398,7 @@ static unsigned long kvmgt_gfn_to_pfn(unsigned long handle, unsigned long gfn)
return pfn;
pfn = INTEL_GVT_INVALID_ADDR;
dev = &info->vgpu->vdev.mdev->dev;
dev = mdev_dev(info->vgpu->vdev.mdev);
rc = vfio_pin_pages(dev, &gfn, 1, IOMMU_READ | IOMMU_WRITE, &pfn);
if (rc != 1) {
gvt_err("vfio_pin_pages failed for gfn 0x%lx: %d\n", gfn, rc);

View File

@ -27,6 +27,45 @@ static LIST_HEAD(parent_list);
static DEFINE_MUTEX(parent_list_lock);
static struct class_compat *mdev_bus_compat_class;
static LIST_HEAD(mdev_list);
static DEFINE_MUTEX(mdev_list_lock);
struct device *mdev_parent_dev(struct mdev_device *mdev)
{
return mdev->parent->dev;
}
EXPORT_SYMBOL(mdev_parent_dev);
void *mdev_get_drvdata(struct mdev_device *mdev)
{
return mdev->driver_data;
}
EXPORT_SYMBOL(mdev_get_drvdata);
void mdev_set_drvdata(struct mdev_device *mdev, void *data)
{
mdev->driver_data = data;
}
EXPORT_SYMBOL(mdev_set_drvdata);
struct device *mdev_dev(struct mdev_device *mdev)
{
return &mdev->dev;
}
EXPORT_SYMBOL(mdev_dev);
struct mdev_device *mdev_from_dev(struct device *dev)
{
return dev_is_mdev(dev) ? to_mdev_device(dev) : NULL;
}
EXPORT_SYMBOL(mdev_from_dev);
uuid_le mdev_uuid(struct mdev_device *mdev)
{
return mdev->uuid;
}
EXPORT_SYMBOL(mdev_uuid);
static int _find_mdev_device(struct device *dev, void *data)
{
struct mdev_device *mdev;
@ -42,7 +81,7 @@ static int _find_mdev_device(struct device *dev, void *data)
return 0;
}
static bool mdev_device_exist(struct parent_device *parent, uuid_le uuid)
static bool mdev_device_exist(struct mdev_parent *parent, uuid_le uuid)
{
struct device *dev;
@ -56,9 +95,9 @@ static bool mdev_device_exist(struct parent_device *parent, uuid_le uuid)
}
/* Should be called holding parent_list_lock */
static struct parent_device *__find_parent_device(struct device *dev)
static struct mdev_parent *__find_parent_device(struct device *dev)
{
struct parent_device *parent;
struct mdev_parent *parent;
list_for_each_entry(parent, &parent_list, next) {
if (parent->dev == dev)
@ -69,8 +108,8 @@ static struct parent_device *__find_parent_device(struct device *dev)
static void mdev_release_parent(struct kref *kref)
{
struct parent_device *parent = container_of(kref, struct parent_device,
ref);
struct mdev_parent *parent = container_of(kref, struct mdev_parent,
ref);
struct device *dev = parent->dev;
kfree(parent);
@ -78,7 +117,7 @@ static void mdev_release_parent(struct kref *kref)
}
static
inline struct parent_device *mdev_get_parent(struct parent_device *parent)
inline struct mdev_parent *mdev_get_parent(struct mdev_parent *parent)
{
if (parent)
kref_get(&parent->ref);
@ -86,7 +125,7 @@ inline struct parent_device *mdev_get_parent(struct parent_device *parent)
return parent;
}
static inline void mdev_put_parent(struct parent_device *parent)
static inline void mdev_put_parent(struct mdev_parent *parent)
{
if (parent)
kref_put(&parent->ref, mdev_release_parent);
@ -95,7 +134,7 @@ static inline void mdev_put_parent(struct parent_device *parent)
static int mdev_device_create_ops(struct kobject *kobj,
struct mdev_device *mdev)
{
struct parent_device *parent = mdev->parent;
struct mdev_parent *parent = mdev->parent;
int ret;
ret = parent->ops->create(kobj, mdev);
@ -122,7 +161,7 @@ static int mdev_device_create_ops(struct kobject *kobj,
*/
static int mdev_device_remove_ops(struct mdev_device *mdev, bool force_remove)
{
struct parent_device *parent = mdev->parent;
struct mdev_parent *parent = mdev->parent;
int ret;
/*
@ -153,10 +192,10 @@ static int mdev_device_remove_cb(struct device *dev, void *data)
* Add device to list of registered parent devices.
* Returns a negative value on error, otherwise 0.
*/
int mdev_register_device(struct device *dev, const struct parent_ops *ops)
int mdev_register_device(struct device *dev, const struct mdev_parent_ops *ops)
{
int ret;
struct parent_device *parent;
struct mdev_parent *parent;
/* check for mandatory ops */
if (!ops || !ops->create || !ops->remove || !ops->supported_type_groups)
@ -229,7 +268,7 @@ EXPORT_SYMBOL(mdev_register_device);
void mdev_unregister_device(struct device *dev)
{
struct parent_device *parent;
struct mdev_parent *parent;
bool force_remove = true;
mutex_lock(&parent_list_lock);
@ -266,7 +305,7 @@ int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid)
{
int ret;
struct mdev_device *mdev;
struct parent_device *parent;
struct mdev_parent *parent;
struct mdev_type *type = to_mdev_type(kobj);
parent = mdev_get_parent(type->parent);
@ -316,6 +355,11 @@ int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid)
dev_dbg(&mdev->dev, "MDEV: created\n");
mutex_unlock(&parent->lock);
mutex_lock(&mdev_list_lock);
list_add(&mdev->next, &mdev_list);
mutex_unlock(&mdev_list_lock);
return ret;
create_failed:
@ -329,12 +373,30 @@ int mdev_device_create(struct kobject *kobj, struct device *dev, uuid_le uuid)
int mdev_device_remove(struct device *dev, bool force_remove)
{
struct mdev_device *mdev;
struct parent_device *parent;
struct mdev_device *mdev, *tmp;
struct mdev_parent *parent;
struct mdev_type *type;
int ret;
bool found = false;
mdev = to_mdev_device(dev);
mutex_lock(&mdev_list_lock);
list_for_each_entry(tmp, &mdev_list, next) {
if (tmp == mdev) {
found = true;
break;
}
}
if (found)
list_del(&mdev->next);
mutex_unlock(&mdev_list_lock);
if (!found)
return -ENODEV;
type = to_mdev_type(mdev->type_kobj);
parent = mdev->parent;
mutex_lock(&parent->lock);
@ -342,6 +404,11 @@ int mdev_device_remove(struct device *dev, bool force_remove)
ret = mdev_device_remove_ops(mdev, force_remove);
if (ret) {
mutex_unlock(&parent->lock);
mutex_lock(&mdev_list_lock);
list_add(&mdev->next, &mdev_list);
mutex_unlock(&mdev_list_lock);
return ret;
}
@ -349,7 +416,8 @@ int mdev_device_remove(struct device *dev, bool force_remove)
device_unregister(dev);
mutex_unlock(&parent->lock);
mdev_put_parent(parent);
return ret;
return 0;
}
static int __init mdev_init(void)

View File

@ -16,10 +16,33 @@
int mdev_bus_register(void);
void mdev_bus_unregister(void);
struct mdev_parent {
struct device *dev;
const struct mdev_parent_ops *ops;
struct kref ref;
struct mutex lock;
struct list_head next;
struct kset *mdev_types_kset;
struct list_head type_list;
};
struct mdev_device {
struct device dev;
struct mdev_parent *parent;
uuid_le uuid;
void *driver_data;
struct kref ref;
struct list_head next;
struct kobject *type_kobj;
};
#define to_mdev_device(dev) container_of(dev, struct mdev_device, dev)
#define dev_is_mdev(d) ((d)->bus == &mdev_bus_type)
struct mdev_type {
struct kobject kobj;
struct kobject *devices_kobj;
struct parent_device *parent;
struct mdev_parent *parent;
struct list_head next;
struct attribute_group *group;
};
@ -29,8 +52,8 @@ struct mdev_type {
#define to_mdev_type(_kobj) \
container_of(_kobj, struct mdev_type, kobj)
int parent_create_sysfs_files(struct parent_device *parent);
void parent_remove_sysfs_files(struct parent_device *parent);
int parent_create_sysfs_files(struct mdev_parent *parent);
void parent_remove_sysfs_files(struct mdev_parent *parent);
int mdev_create_sysfs_files(struct device *dev, struct mdev_type *type);
void mdev_remove_sysfs_files(struct device *dev, struct mdev_type *type);

View File

@ -92,7 +92,7 @@ static struct kobj_type mdev_type_ktype = {
.release = mdev_type_release,
};
struct mdev_type *add_mdev_supported_type(struct parent_device *parent,
struct mdev_type *add_mdev_supported_type(struct mdev_parent *parent,
struct attribute_group *group)
{
struct mdev_type *type;
@ -158,7 +158,7 @@ static void remove_mdev_supported_type(struct mdev_type *type)
kobject_put(&type->kobj);
}
static int add_mdev_supported_type_groups(struct parent_device *parent)
static int add_mdev_supported_type_groups(struct mdev_parent *parent)
{
int i;
@ -183,7 +183,7 @@ static int add_mdev_supported_type_groups(struct parent_device *parent)
}
/* mdev sysfs functions */
void parent_remove_sysfs_files(struct parent_device *parent)
void parent_remove_sysfs_files(struct mdev_parent *parent)
{
struct mdev_type *type, *tmp;
@ -196,7 +196,7 @@ void parent_remove_sysfs_files(struct parent_device *parent)
kset_unregister(parent->mdev_types_kset);
}
int parent_create_sysfs_files(struct parent_device *parent)
int parent_create_sysfs_files(struct mdev_parent *parent)
{
int ret;

View File

@ -27,7 +27,7 @@
static int vfio_mdev_open(void *device_data)
{
struct mdev_device *mdev = device_data;
struct parent_device *parent = mdev->parent;
struct mdev_parent *parent = mdev->parent;
int ret;
if (unlikely(!parent->ops->open))
@ -46,7 +46,7 @@ static int vfio_mdev_open(void *device_data)
static void vfio_mdev_release(void *device_data)
{
struct mdev_device *mdev = device_data;
struct parent_device *parent = mdev->parent;
struct mdev_parent *parent = mdev->parent;
if (likely(parent->ops->release))
parent->ops->release(mdev);
@ -58,7 +58,7 @@ static long vfio_mdev_unlocked_ioctl(void *device_data,
unsigned int cmd, unsigned long arg)
{
struct mdev_device *mdev = device_data;
struct parent_device *parent = mdev->parent;
struct mdev_parent *parent = mdev->parent;
if (unlikely(!parent->ops->ioctl))
return -EINVAL;
@ -70,7 +70,7 @@ static ssize_t vfio_mdev_read(void *device_data, char __user *buf,
size_t count, loff_t *ppos)
{
struct mdev_device *mdev = device_data;
struct parent_device *parent = mdev->parent;
struct mdev_parent *parent = mdev->parent;
if (unlikely(!parent->ops->read))
return -EINVAL;
@ -82,7 +82,7 @@ static ssize_t vfio_mdev_write(void *device_data, const char __user *buf,
size_t count, loff_t *ppos)
{
struct mdev_device *mdev = device_data;
struct parent_device *parent = mdev->parent;
struct mdev_parent *parent = mdev->parent;
if (unlikely(!parent->ops->write))
return -EINVAL;
@ -93,7 +93,7 @@ static ssize_t vfio_mdev_write(void *device_data, const char __user *buf,
static int vfio_mdev_mmap(void *device_data, struct vm_area_struct *vma)
{
struct mdev_device *mdev = device_data;
struct parent_device *parent = mdev->parent;
struct mdev_parent *parent = mdev->parent;
if (unlikely(!parent->ops->mmap))
return -EINVAL;

View File

@ -1142,6 +1142,10 @@ static int vfio_pci_mmap(void *device_data, struct vm_area_struct *vma)
return ret;
vdev->barmap[index] = pci_iomap(pdev, index, 0);
if (!vdev->barmap[index]) {
pci_release_selected_regions(pdev, 1 << index);
return -ENOMEM;
}
}
vma->vm_private_data = vdev;

View File

@ -193,7 +193,10 @@ ssize_t vfio_pci_vga_rw(struct vfio_pci_device *vdev, char __user *buf,
if (!vdev->has_vga)
return -EINVAL;
switch (pos) {
if (pos > 0xbfffful)
return -EINVAL;
switch ((u32)pos) {
case 0xa0000 ... 0xbffff:
count = min(count, (size_t)(0xc0000 - pos));
iomem = ioremap_nocache(0xa0000, 0xbffff - 0xa0000 + 1);

View File

@ -268,28 +268,38 @@ static void vfio_lock_acct(struct task_struct *task, long npage)
{
struct vwork *vwork;
struct mm_struct *mm;
bool is_current;
if (!npage)
return;
mm = get_task_mm(task);
is_current = (task->mm == current->mm);
mm = is_current ? task->mm : get_task_mm(task);
if (!mm)
return; /* process exited or nothing to do */
return; /* process exited */
if (down_write_trylock(&mm->mmap_sem)) {
mm->locked_vm += npage;
up_write(&mm->mmap_sem);
mmput(mm);
if (!is_current)
mmput(mm);
return;
}
if (is_current) {
mm = get_task_mm(task);
if (!mm)
return;
}
/*
* Couldn't get mmap_sem lock, so must setup to update
* mm->locked_vm later. If locked_vm were atomic, we
* wouldn't need this silliness
*/
vwork = kmalloc(sizeof(struct vwork), GFP_KERNEL);
if (!vwork) {
if (WARN_ON(!vwork)) {
mmput(mm);
return;
}
@ -393,77 +403,71 @@ static int vaddr_get_pfn(struct mm_struct *mm, unsigned long vaddr,
static long vfio_pin_pages_remote(struct vfio_dma *dma, unsigned long vaddr,
long npage, unsigned long *pfn_base)
{
unsigned long limit;
bool lock_cap = ns_capable(task_active_pid_ns(dma->task)->user_ns,
CAP_IPC_LOCK);
struct mm_struct *mm;
long ret, i = 0, lock_acct = 0;
unsigned long limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT;
bool lock_cap = capable(CAP_IPC_LOCK);
long ret, pinned = 0, lock_acct = 0;
bool rsvd;
dma_addr_t iova = vaddr - dma->vaddr + dma->iova;
mm = get_task_mm(dma->task);
if (!mm)
/* This code path is only user initiated */
if (!current->mm)
return -ENODEV;
ret = vaddr_get_pfn(mm, vaddr, dma->prot, pfn_base);
ret = vaddr_get_pfn(current->mm, vaddr, dma->prot, pfn_base);
if (ret)
goto pin_pg_remote_exit;
return ret;
pinned++;
rsvd = is_invalid_reserved_pfn(*pfn_base);
limit = task_rlimit(dma->task, RLIMIT_MEMLOCK) >> PAGE_SHIFT;
/*
* Reserved pages aren't counted against the user, externally pinned
* pages are already counted against the user.
*/
if (!rsvd && !vfio_find_vpfn(dma, iova)) {
if (!lock_cap && mm->locked_vm + 1 > limit) {
if (!lock_cap && current->mm->locked_vm + 1 > limit) {
put_pfn(*pfn_base, dma->prot);
pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n", __func__,
limit << PAGE_SHIFT);
ret = -ENOMEM;
goto pin_pg_remote_exit;
return -ENOMEM;
}
lock_acct++;
}
i++;
if (likely(!disable_hugepages)) {
/* Lock all the consecutive pages from pfn_base */
for (vaddr += PAGE_SIZE, iova += PAGE_SIZE; i < npage;
i++, vaddr += PAGE_SIZE, iova += PAGE_SIZE) {
unsigned long pfn = 0;
if (unlikely(disable_hugepages))
goto out;
ret = vaddr_get_pfn(mm, vaddr, dma->prot, &pfn);
if (ret)
break;
/* Lock all the consecutive pages from pfn_base */
for (vaddr += PAGE_SIZE, iova += PAGE_SIZE; pinned < npage;
pinned++, vaddr += PAGE_SIZE, iova += PAGE_SIZE) {
unsigned long pfn = 0;
if (pfn != *pfn_base + i ||
rsvd != is_invalid_reserved_pfn(pfn)) {
ret = vaddr_get_pfn(current->mm, vaddr, dma->prot, &pfn);
if (ret)
break;
if (pfn != *pfn_base + pinned ||
rsvd != is_invalid_reserved_pfn(pfn)) {
put_pfn(pfn, dma->prot);
break;
}
if (!rsvd && !vfio_find_vpfn(dma, iova)) {
if (!lock_cap &&
current->mm->locked_vm + lock_acct + 1 > limit) {
put_pfn(pfn, dma->prot);
pr_warn("%s: RLIMIT_MEMLOCK (%ld) exceeded\n",
__func__, limit << PAGE_SHIFT);
break;
}
if (!rsvd && !vfio_find_vpfn(dma, iova)) {
if (!lock_cap &&
mm->locked_vm + lock_acct + 1 > limit) {
put_pfn(pfn, dma->prot);
pr_warn("%s: RLIMIT_MEMLOCK (%ld) "
"exceeded\n", __func__,
limit << PAGE_SHIFT);
break;
}
lock_acct++;
}
lock_acct++;
}
}
vfio_lock_acct(dma->task, lock_acct);
ret = i;
out:
vfio_lock_acct(current, lock_acct);
pin_pg_remote_exit:
mmput(mm);
return ret;
return pinned;
}
static long vfio_unpin_pages_remote(struct vfio_dma *dma, dma_addr_t iova,
@ -473,10 +477,10 @@ static long vfio_unpin_pages_remote(struct vfio_dma *dma, dma_addr_t iova,
long unlocked = 0, locked = 0;
long i;
for (i = 0; i < npage; i++) {
for (i = 0; i < npage; i++, iova += PAGE_SIZE) {
if (put_pfn(pfn++, dma->prot)) {
unlocked++;
if (vfio_find_vpfn(dma, iova + (i << PAGE_SHIFT)))
if (vfio_find_vpfn(dma, iova))
locked++;
}
}

View File

@ -13,34 +13,10 @@
#ifndef MDEV_H
#define MDEV_H
/* Parent device */
struct parent_device {
struct device *dev;
const struct parent_ops *ops;
/* internal */
struct kref ref;
struct mutex lock;
struct list_head next;
struct kset *mdev_types_kset;
struct list_head type_list;
};
/* Mediated device */
struct mdev_device {
struct device dev;
struct parent_device *parent;
uuid_le uuid;
void *driver_data;
/* internal */
struct kref ref;
struct list_head next;
struct kobject *type_kobj;
};
struct mdev_device;
/**
* struct parent_ops - Structure to be registered for each parent device to
* struct mdev_parent_ops - Structure to be registered for each parent device to
* register the device to mdev module.
*
* @owner: The module owner.
@ -86,10 +62,9 @@ struct mdev_device {
* @mdev: mediated device structure
* @vma: vma structure
* Parent device that support mediated device should be registered with mdev
* module with parent_ops structure.
* module with mdev_parent_ops structure.
**/
struct parent_ops {
struct mdev_parent_ops {
struct module *owner;
const struct attribute_group **dev_attr_groups;
const struct attribute_group **mdev_attr_groups;
@ -103,7 +78,7 @@ struct parent_ops {
size_t count, loff_t *ppos);
ssize_t (*write)(struct mdev_device *mdev, const char __user *buf,
size_t count, loff_t *ppos);
ssize_t (*ioctl)(struct mdev_device *mdev, unsigned int cmd,
long (*ioctl)(struct mdev_device *mdev, unsigned int cmd,
unsigned long arg);
int (*mmap)(struct mdev_device *mdev, struct vm_area_struct *vma);
};
@ -142,27 +117,22 @@ struct mdev_driver {
};
#define to_mdev_driver(drv) container_of(drv, struct mdev_driver, driver)
#define to_mdev_device(dev) container_of(dev, struct mdev_device, dev)
static inline void *mdev_get_drvdata(struct mdev_device *mdev)
{
return mdev->driver_data;
}
static inline void mdev_set_drvdata(struct mdev_device *mdev, void *data)
{
mdev->driver_data = data;
}
extern void *mdev_get_drvdata(struct mdev_device *mdev);
extern void mdev_set_drvdata(struct mdev_device *mdev, void *data);
extern uuid_le mdev_uuid(struct mdev_device *mdev);
extern struct bus_type mdev_bus_type;
#define dev_is_mdev(d) ((d)->bus == &mdev_bus_type)
extern int mdev_register_device(struct device *dev,
const struct parent_ops *ops);
const struct mdev_parent_ops *ops);
extern void mdev_unregister_device(struct device *dev);
extern int mdev_register_driver(struct mdev_driver *drv, struct module *owner);
extern void mdev_unregister_driver(struct mdev_driver *drv);
extern struct device *mdev_parent_dev(struct mdev_device *mdev);
extern struct device *mdev_dev(struct mdev_device *mdev);
extern struct mdev_device *mdev_from_dev(struct device *dev);
#endif /* MDEV_H */

View File

@ -105,4 +105,11 @@ config SAMPLE_BLACKFIN_GPTIMERS
help
Build samples of blackfin gptimers sample module.
config SAMPLE_VFIO_MDEV_MTTY
tristate "Build VFIO mtty example mediated device sample code -- loadable modules only"
depends on VFIO_MDEV_DEVICE && m
help
Build a virtual tty sample driver for use as a VFIO
mediated device
endif # SAMPLES

View File

@ -2,4 +2,5 @@
obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ trace_events/ livepatch/ \
hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \
configfs/ connector/ v4l/ trace_printk/ blackfin/
configfs/ connector/ v4l/ trace_printk/ blackfin/ \
vfio-mdev/

View File

@ -1,13 +1 @@
#
# Makefile for mtty.c file
#
KERNEL_DIR:=/lib/modules/$(shell uname -r)/build
obj-m:=mtty.o
modules clean modules_install:
$(MAKE) -C $(KERNEL_DIR) SUBDIRS=$(PWD) $@
default: modules
module: modules
obj-$(CONFIG_SAMPLE_VFIO_MDEV_MTTY) += mtty.o

View File

@ -164,7 +164,7 @@ static struct mdev_state *find_mdev_state_by_uuid(uuid_le uuid)
struct mdev_state *mds;
list_for_each_entry(mds, &mdev_devices_list, next) {
if (uuid_le_cmp(mds->mdev->uuid, uuid) == 0)
if (uuid_le_cmp(mdev_uuid(mds->mdev), uuid) == 0)
return mds;
}
@ -341,7 +341,8 @@ static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,
pr_err("Serial port %d: Fifo level trigger\n",
index);
#endif
mtty_trigger_interrupt(mdev_state->mdev->uuid);
mtty_trigger_interrupt(
mdev_uuid(mdev_state->mdev));
}
} else {
#if defined(DEBUG_INTR)
@ -355,7 +356,8 @@ static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,
*/
if (mdev_state->s[index].uart_reg[UART_IER] &
UART_IER_RLSI)
mtty_trigger_interrupt(mdev_state->mdev->uuid);
mtty_trigger_interrupt(
mdev_uuid(mdev_state->mdev));
}
mutex_unlock(&mdev_state->rxtx_lock);
break;
@ -374,7 +376,8 @@ static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,
pr_err("Serial port %d: IER_THRI write\n",
index);
#endif
mtty_trigger_interrupt(mdev_state->mdev->uuid);
mtty_trigger_interrupt(
mdev_uuid(mdev_state->mdev));
}
mutex_unlock(&mdev_state->rxtx_lock);
@ -445,7 +448,7 @@ static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,
#if defined(DEBUG_INTR)
pr_err("Serial port %d: MCR_OUT2 write\n", index);
#endif
mtty_trigger_interrupt(mdev_state->mdev->uuid);
mtty_trigger_interrupt(mdev_uuid(mdev_state->mdev));
}
if ((mdev_state->s[index].uart_reg[UART_IER] & UART_IER_MSI) &&
@ -453,7 +456,7 @@ static void handle_bar_write(unsigned int index, struct mdev_state *mdev_state,
#if defined(DEBUG_INTR)
pr_err("Serial port %d: MCR RTS/DTR write\n", index);
#endif
mtty_trigger_interrupt(mdev_state->mdev->uuid);
mtty_trigger_interrupt(mdev_uuid(mdev_state->mdev));
}
break;
@ -504,7 +507,8 @@ static void handle_bar_read(unsigned int index, struct mdev_state *mdev_state,
#endif
if (mdev_state->s[index].uart_reg[UART_IER] &
UART_IER_THRI)
mtty_trigger_interrupt(mdev_state->mdev->uuid);
mtty_trigger_interrupt(
mdev_uuid(mdev_state->mdev));
}
mutex_unlock(&mdev_state->rxtx_lock);
@ -734,7 +738,7 @@ int mtty_create(struct kobject *kobj, struct mdev_device *mdev)
for (i = 0; i < 2; i++) {
snprintf(name, MTTY_STRING_LEN, "%s-%d",
dev_driver_string(mdev->parent->dev), i + 1);
dev_driver_string(mdev_parent_dev(mdev)), i + 1);
if (!strcmp(kobj->name, name)) {
nr_ports = i + 1;
break;
@ -1298,10 +1302,8 @@ static ssize_t
sample_mdev_dev_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct mdev_device *mdev = to_mdev_device(dev);
if (mdev)
return sprintf(buf, "This is MDEV %s\n", dev_name(&mdev->dev));
if (mdev_from_dev(dev))
return sprintf(buf, "This is MDEV %s\n", dev_name(dev));
return sprintf(buf, "\n");
}
@ -1402,7 +1404,7 @@ struct attribute_group *mdev_type_groups[] = {
NULL,
};
struct parent_ops mdev_fops = {
struct mdev_parent_ops mdev_fops = {
.owner = THIS_MODULE,
.dev_attr_groups = mtty_dev_groups,
.mdev_attr_groups = mdev_dev_groups,
@ -1447,6 +1449,7 @@ static int __init mtty_dev_init(void)
if (IS_ERR(mtty_dev.vd_class)) {
pr_err("Error: failed to register mtty_dev class\n");
ret = PTR_ERR(mtty_dev.vd_class);
goto failed1;
}
@ -1458,7 +1461,8 @@ static int __init mtty_dev_init(void)
if (ret)
goto failed2;
if (mdev_register_device(&mtty_dev.dev, &mdev_fops) != 0)
ret = mdev_register_device(&mtty_dev.dev, &mdev_fops);
if (ret)
goto failed3;
mutex_init(&mdev_list_lock);