Skip to content

Commit

Permalink
vfio-pci/zdev: Add zPCI capabilities to VFIO_DEVICE_GET_INFO
Browse files Browse the repository at this point in the history
Define a new configuration entry VFIO_PCI_ZDEV for VFIO/PCI.

When this s390-only feature is configured we add capabilities to the
VFIO_DEVICE_GET_INFO ioctl that describe features of the associated
zPCI device and its underlying hardware.

This patch is based on work previously done by Pierre Morel.

Signed-off-by: Matthew Rosato <mjrosato@linux.ibm.com>
Reviewed-by: Cornelia Huck <cohuck@redhat.com>
Signed-off-by: Alex Williamson <alex.williamson@redhat.com>
  • Loading branch information
rosatomj authored and awilliam committed Oct 12, 2020
1 parent 0c633f0 commit e6b817d
Show file tree
Hide file tree
Showing 5 changed files with 205 additions and 0 deletions.
12 changes: 12 additions & 0 deletions drivers/vfio/pci/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,15 @@ config VFIO_PCI_NVLINK2
depends on VFIO_PCI && PPC_POWERNV
help
VFIO PCI support for P9 Witherspoon machine with NVIDIA V100 GPUs

config VFIO_PCI_ZDEV
bool "VFIO PCI ZPCI device CLP support"
depends on VFIO_PCI && S390
default y
help
Enabling this option exposes VFIO capabilities containing hardware
configuration for zPCI devices. This enables userspace (e.g. QEMU)
to supply proper configuration values instead of hard-coded defaults
for zPCI devices passed through via VFIO on s390.

Say Y here.
1 change: 1 addition & 0 deletions drivers/vfio/pci/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@
vfio-pci-y := vfio_pci.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o
vfio-pci-$(CONFIG_VFIO_PCI_IGD) += vfio_pci_igd.o
vfio-pci-$(CONFIG_VFIO_PCI_NVLINK2) += vfio_pci_nvlink2.o
vfio-pci-$(CONFIG_VFIO_PCI_ZDEV) += vfio_pci_zdev.o

obj-$(CONFIG_VFIO_PCI) += vfio-pci.o
37 changes: 37 additions & 0 deletions drivers/vfio/pci/vfio_pci.c
Original file line number Diff line number Diff line change
Expand Up @@ -807,15 +807,25 @@ static long vfio_pci_ioctl(void *device_data,

if (cmd == VFIO_DEVICE_GET_INFO) {
struct vfio_device_info info;
struct vfio_info_cap caps = { .buf = NULL, .size = 0 };
unsigned long capsz;

minsz = offsetofend(struct vfio_device_info, num_irqs);

/* For backward compatibility, cannot require this */
capsz = offsetofend(struct vfio_iommu_type1_info, cap_offset);

if (copy_from_user(&info, (void __user *)arg, minsz))
return -EFAULT;

if (info.argsz < minsz)
return -EINVAL;

if (info.argsz >= capsz) {
minsz = capsz;
info.cap_offset = 0;
}

info.flags = VFIO_DEVICE_FLAGS_PCI;

if (vdev->reset_works)
Expand All @@ -824,6 +834,33 @@ static long vfio_pci_ioctl(void *device_data,
info.num_regions = VFIO_PCI_NUM_REGIONS + vdev->num_regions;
info.num_irqs = VFIO_PCI_NUM_IRQS;

if (IS_ENABLED(CONFIG_VFIO_PCI_ZDEV)) {
int ret = vfio_pci_info_zdev_add_caps(vdev, &caps);

if (ret && ret != -ENODEV) {
pci_warn(vdev->pdev, "Failed to setup zPCI info capabilities\n");
return ret;
}
}

if (caps.size) {
info.flags |= VFIO_DEVICE_FLAGS_CAPS;
if (info.argsz < sizeof(info) + caps.size) {
info.argsz = sizeof(info) + caps.size;
} else {
vfio_info_cap_shift(&caps, sizeof(info));
if (copy_to_user((void __user *)arg +
sizeof(info), caps.buf,
caps.size)) {
kfree(caps.buf);
return -EFAULT;
}
info.cap_offset = sizeof(info);
}

kfree(caps.buf);
}

return copy_to_user((void __user *)arg, &info, minsz) ?
-EFAULT : 0;

Expand Down
12 changes: 12 additions & 0 deletions drivers/vfio/pci/vfio_pci_private.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,16 @@ static inline int vfio_pci_ibm_npu2_init(struct vfio_pci_device *vdev)
return -ENODEV;
}
#endif

#ifdef CONFIG_VFIO_PCI_ZDEV
extern int vfio_pci_info_zdev_add_caps(struct vfio_pci_device *vdev,
struct vfio_info_cap *caps);
#else
static inline int vfio_pci_info_zdev_add_caps(struct vfio_pci_device *vdev,
struct vfio_info_cap *caps)
{
return -ENODEV;
}
#endif

#endif /* VFIO_PCI_PRIVATE_H */
143 changes: 143 additions & 0 deletions drivers/vfio/pci/vfio_pci_zdev.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* VFIO ZPCI devices support
*
* Copyright (C) IBM Corp. 2020. All rights reserved.
* Author(s): Pierre Morel <pmorel@linux.ibm.com>
* Matthew Rosato <mjrosato@linux.ibm.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/io.h>
#include <linux/pci.h>
#include <linux/uaccess.h>
#include <linux/vfio.h>
#include <linux/vfio_zdev.h>
#include <asm/pci_clp.h>
#include <asm/pci_io.h>

#include "vfio_pci_private.h"

/*
* Add the Base PCI Function information to the device info region.
*/
static int zpci_base_cap(struct zpci_dev *zdev, struct vfio_pci_device *vdev,
struct vfio_info_cap *caps)
{
struct vfio_device_info_cap_zpci_base cap = {
.header.id = VFIO_DEVICE_INFO_CAP_ZPCI_BASE,
.header.version = 1,
.start_dma = zdev->start_dma,
.end_dma = zdev->end_dma,
.pchid = zdev->pchid,
.vfn = zdev->vfn,
.fmb_length = zdev->fmb_length,
.pft = zdev->pft,
.gid = zdev->pfgid
};

return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
}

/*
* Add the Base PCI Function Group information to the device info region.
*/
static int zpci_group_cap(struct zpci_dev *zdev, struct vfio_pci_device *vdev,
struct vfio_info_cap *caps)
{
struct vfio_device_info_cap_zpci_group cap = {
.header.id = VFIO_DEVICE_INFO_CAP_ZPCI_GROUP,
.header.version = 1,
.dasm = zdev->dma_mask,
.msi_addr = zdev->msi_addr,
.flags = VFIO_DEVICE_INFO_ZPCI_FLAG_REFRESH,
.mui = zdev->fmb_update,
.noi = zdev->max_msi,
.maxstbl = ZPCI_MAX_WRITE_SIZE,
.version = zdev->version
};

return vfio_info_add_capability(caps, &cap.header, sizeof(cap));
}

/*
* Add the device utility string to the device info region.
*/
static int zpci_util_cap(struct zpci_dev *zdev, struct vfio_pci_device *vdev,
struct vfio_info_cap *caps)
{
struct vfio_device_info_cap_zpci_util *cap;
int cap_size = sizeof(*cap) + CLP_UTIL_STR_LEN;
int ret;

cap = kmalloc(cap_size, GFP_KERNEL);

cap->header.id = VFIO_DEVICE_INFO_CAP_ZPCI_UTIL;
cap->header.version = 1;
cap->size = CLP_UTIL_STR_LEN;
memcpy(cap->util_str, zdev->util_str, cap->size);

ret = vfio_info_add_capability(caps, &cap->header, cap_size);

kfree(cap);

return ret;
}

/*
* Add the function path string to the device info region.
*/
static int zpci_pfip_cap(struct zpci_dev *zdev, struct vfio_pci_device *vdev,
struct vfio_info_cap *caps)
{
struct vfio_device_info_cap_zpci_pfip *cap;
int cap_size = sizeof(*cap) + CLP_PFIP_NR_SEGMENTS;
int ret;

cap = kmalloc(cap_size, GFP_KERNEL);

cap->header.id = VFIO_DEVICE_INFO_CAP_ZPCI_PFIP;
cap->header.version = 1;
cap->size = CLP_PFIP_NR_SEGMENTS;
memcpy(cap->pfip, zdev->pfip, cap->size);

ret = vfio_info_add_capability(caps, &cap->header, cap_size);

kfree(cap);

return ret;
}

/*
* Add all supported capabilities to the VFIO_DEVICE_GET_INFO capability chain.
*/
int vfio_pci_info_zdev_add_caps(struct vfio_pci_device *vdev,
struct vfio_info_cap *caps)
{
struct zpci_dev *zdev = to_zpci(vdev->pdev);
int ret;

if (!zdev)
return -ENODEV;

ret = zpci_base_cap(zdev, vdev, caps);
if (ret)
return ret;

ret = zpci_group_cap(zdev, vdev, caps);
if (ret)
return ret;

if (zdev->util_str_avail) {
ret = zpci_util_cap(zdev, vdev, caps);
if (ret)
return ret;
}

ret = zpci_pfip_cap(zdev, vdev, caps);

return ret;
}

0 comments on commit e6b817d

Please sign in to comment.