Commit ef39830c authored by Sudeep Dutt's avatar Sudeep Dutt Committed by Greg Kroah-Hartman

misc: mic: Remove MIC X100 host virtio functionality

This patch deletes the virtio functionality from the MIC X100 host
driver. A subsequent patch will re-enable this functionality by
consolidating the hardware independent logic in a new Virtio over PCIe
(VOP) driver.
Reviewed-by: default avatarAshutosh Dixit <ashutosh.dixit@intel.com>
Signed-off-by: default avatarSudeep Dutt <sudeep.dutt@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 4ddbdbb9
......@@ -9,5 +9,3 @@ mic_host-objs += mic_smpt.o
mic_host-objs += mic_intr.o
mic_host-objs += mic_boot.o
mic_host-objs += mic_debugfs.o
mic_host-objs += mic_fops.o
mic_host-objs += mic_virtio.o
......@@ -28,7 +28,6 @@
#include "../common/mic_dev.h"
#include "mic_device.h"
#include "mic_smpt.h"
#include "mic_virtio.h"
static inline struct mic_device *scdev_to_mdev(struct scif_hw_dev *scdev)
{
......@@ -423,7 +422,6 @@ static void _mic_stop(struct cosm_device *cdev, bool force)
* will be the first to be registered and the last to be
* unregistered.
*/
mic_virtio_reset_devices(mdev);
scif_unregister_device(mdev->scdev);
mic_free_dma_chans(mdev);
mbus_unregister_device(mdev->dma_mbdev);
......
......@@ -26,7 +26,6 @@
#include "../common/mic_dev.h"
#include "mic_device.h"
#include "mic_smpt.h"
#include "mic_virtio.h"
/* Debugfs parent dir */
static struct dentry *mic_dbg;
......@@ -100,190 +99,6 @@ static const struct file_operations post_code_ops = {
.release = mic_post_code_debug_release
};
static int mic_dp_show(struct seq_file *s, void *pos)
{
struct mic_device *mdev = s->private;
struct mic_device_desc *d;
struct mic_device_ctrl *dc;
struct mic_vqconfig *vqconfig;
__u32 *features;
__u8 *config;
struct mic_bootparam *bootparam = mdev->dp;
int i, j;
seq_printf(s, "Bootparam: magic 0x%x\n",
bootparam->magic);
seq_printf(s, "Bootparam: h2c_config_db %d\n",
bootparam->h2c_config_db);
seq_printf(s, "Bootparam: node_id %d\n",
bootparam->node_id);
seq_printf(s, "Bootparam: c2h_scif_db %d\n",
bootparam->c2h_scif_db);
seq_printf(s, "Bootparam: h2c_scif_db %d\n",
bootparam->h2c_scif_db);
seq_printf(s, "Bootparam: scif_host_dma_addr 0x%llx\n",
bootparam->scif_host_dma_addr);
seq_printf(s, "Bootparam: scif_card_dma_addr 0x%llx\n",
bootparam->scif_card_dma_addr);
for (i = sizeof(*bootparam); i < MIC_DP_SIZE;
i += mic_total_desc_size(d)) {
d = mdev->dp + i;
dc = (void *)d + mic_aligned_desc_size(d);
/* end of list */
if (d->type == 0)
break;
if (d->type == -1)
continue;
seq_printf(s, "Type %d ", d->type);
seq_printf(s, "Num VQ %d ", d->num_vq);
seq_printf(s, "Feature Len %d\n", d->feature_len);
seq_printf(s, "Config Len %d ", d->config_len);
seq_printf(s, "Shutdown Status %d\n", d->status);
for (j = 0; j < d->num_vq; j++) {
vqconfig = mic_vq_config(d) + j;
seq_printf(s, "vqconfig[%d]: ", j);
seq_printf(s, "address 0x%llx ", vqconfig->address);
seq_printf(s, "num %d ", vqconfig->num);
seq_printf(s, "used address 0x%llx\n",
vqconfig->used_address);
}
features = (__u32 *)mic_vq_features(d);
seq_printf(s, "Features: Host 0x%x ", features[0]);
seq_printf(s, "Guest 0x%x\n", features[1]);
config = mic_vq_configspace(d);
for (j = 0; j < d->config_len; j++)
seq_printf(s, "config[%d]=%d\n", j, config[j]);
seq_puts(s, "Device control:\n");
seq_printf(s, "Config Change %d ", dc->config_change);
seq_printf(s, "Vdev reset %d\n", dc->vdev_reset);
seq_printf(s, "Guest Ack %d ", dc->guest_ack);
seq_printf(s, "Host ack %d\n", dc->host_ack);
seq_printf(s, "Used address updated %d ",
dc->used_address_updated);
seq_printf(s, "Vdev 0x%llx\n", dc->vdev);
seq_printf(s, "c2h doorbell %d ", dc->c2h_vdev_db);
seq_printf(s, "h2c doorbell %d\n", dc->h2c_vdev_db);
}
return 0;
}
static int mic_dp_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, mic_dp_show, inode->i_private);
}
static int mic_dp_debug_release(struct inode *inode, struct file *file)
{
return single_release(inode, file);
}
static const struct file_operations dp_ops = {
.owner = THIS_MODULE,
.open = mic_dp_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = mic_dp_debug_release
};
static int mic_vdev_info_show(struct seq_file *s, void *unused)
{
struct mic_device *mdev = s->private;
struct list_head *pos, *tmp;
struct mic_vdev *mvdev;
int i, j;
mutex_lock(&mdev->mic_mutex);
list_for_each_safe(pos, tmp, &mdev->vdev_list) {
mvdev = list_entry(pos, struct mic_vdev, list);
seq_printf(s, "VDEV type %d state %s in %ld out %ld\n",
mvdev->virtio_id,
mic_vdevup(mvdev) ? "UP" : "DOWN",
mvdev->in_bytes,
mvdev->out_bytes);
for (i = 0; i < MIC_MAX_VRINGS; i++) {
struct vring_desc *desc;
struct vring_avail *avail;
struct vring_used *used;
struct mic_vringh *mvr = &mvdev->mvr[i];
struct vringh *vrh = &mvr->vrh;
int num = vrh->vring.num;
if (!num)
continue;
desc = vrh->vring.desc;
seq_printf(s, "vring i %d avail_idx %d",
i, mvr->vring.info->avail_idx & (num - 1));
seq_printf(s, " vring i %d avail_idx %d\n",
i, mvr->vring.info->avail_idx);
seq_printf(s, "vrh i %d weak_barriers %d",
i, vrh->weak_barriers);
seq_printf(s, " last_avail_idx %d last_used_idx %d",
vrh->last_avail_idx, vrh->last_used_idx);
seq_printf(s, " completed %d\n", vrh->completed);
for (j = 0; j < num; j++) {
seq_printf(s, "desc[%d] addr 0x%llx len %d",
j, desc->addr, desc->len);
seq_printf(s, " flags 0x%x next %d\n",
desc->flags, desc->next);
desc++;
}
avail = vrh->vring.avail;
seq_printf(s, "avail flags 0x%x idx %d\n",
vringh16_to_cpu(vrh, avail->flags),
vringh16_to_cpu(vrh, avail->idx) & (num - 1));
seq_printf(s, "avail flags 0x%x idx %d\n",
vringh16_to_cpu(vrh, avail->flags),
vringh16_to_cpu(vrh, avail->idx));
for (j = 0; j < num; j++)
seq_printf(s, "avail ring[%d] %d\n",
j, avail->ring[j]);
used = vrh->vring.used;
seq_printf(s, "used flags 0x%x idx %d\n",
vringh16_to_cpu(vrh, used->flags),
vringh16_to_cpu(vrh, used->idx) & (num - 1));
seq_printf(s, "used flags 0x%x idx %d\n",
vringh16_to_cpu(vrh, used->flags),
vringh16_to_cpu(vrh, used->idx));
for (j = 0; j < num; j++)
seq_printf(s, "used ring[%d] id %d len %d\n",
j, vringh32_to_cpu(vrh,
used->ring[j].id),
vringh32_to_cpu(vrh,
used->ring[j].len));
}
}
mutex_unlock(&mdev->mic_mutex);
return 0;
}
static int mic_vdev_info_debug_open(struct inode *inode, struct file *file)
{
return single_open(file, mic_vdev_info_show, inode->i_private);
}
static int mic_vdev_info_debug_release(struct inode *inode, struct file *file)
{
return single_release(inode, file);
}
static const struct file_operations vdev_info_ops = {
.owner = THIS_MODULE,
.open = mic_vdev_info_debug_open,
.read = seq_read,
.llseek = seq_lseek,
.release = mic_vdev_info_debug_release
};
static int mic_msi_irq_info_show(struct seq_file *s, void *pos)
{
struct mic_device *mdev = s->private;
......@@ -367,11 +182,6 @@ void mic_create_debug_dir(struct mic_device *mdev)
debugfs_create_file("post_code", 0444, mdev->dbg_dir, mdev,
&post_code_ops);
debugfs_create_file("dp", 0444, mdev->dbg_dir, mdev, &dp_ops);
debugfs_create_file("vdev_info", 0444, mdev->dbg_dir, mdev,
&vdev_info_ops);
debugfs_create_file("msi_irq_info", 0444, mdev->dbg_dir, mdev,
&msi_irq_info_ops);
}
......
......@@ -64,9 +64,6 @@ extern struct cosm_hw_ops cosm_hw_ops;
* @bootaddr: MIC boot address.
* @dp: virtio device page
* @dp_dma_addr: virtio device page DMA address.
* @name: name for the misc char device
* @miscdev: registered misc char device
* @vdev_list: list of virtio devices.
* @dma_mbdev: MIC BUS DMA device.
* @dma_ch - Array of DMA channels
* @num_dma_ch - Number of DMA channels available
......@@ -91,9 +88,6 @@ struct mic_device {
u32 bootaddr;
void *dp;
dma_addr_t dp_dma_addr;
char name[16];
struct miscdevice miscdev;
struct list_head vdev_list;
struct mbus_device *dma_mbdev;
struct dma_chan *dma_ch[MIC_MAX_DMA_CHAN];
int num_dma_ch;
......
/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2013 Intel Corporation.
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Intel MIC Host driver.
*
*/
#include <linux/poll.h>
#include <linux/pci.h>
#include <linux/mic_common.h>
#include "../common/mic_dev.h"
#include "mic_device.h"
#include "mic_fops.h"
#include "mic_virtio.h"
int mic_open(struct inode *inode, struct file *f)
{
struct mic_vdev *mvdev;
struct mic_device *mdev = container_of(f->private_data,
struct mic_device, miscdev);
mvdev = kzalloc(sizeof(*mvdev), GFP_KERNEL);
if (!mvdev)
return -ENOMEM;
init_waitqueue_head(&mvdev->waitq);
INIT_LIST_HEAD(&mvdev->list);
mvdev->mdev = mdev;
mvdev->virtio_id = -1;
f->private_data = mvdev;
return 0;
}
int mic_release(struct inode *inode, struct file *f)
{
struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
if (-1 != mvdev->virtio_id)
mic_virtio_del_device(mvdev);
f->private_data = NULL;
kfree(mvdev);
return 0;
}
long mic_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
{
struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
void __user *argp = (void __user *)arg;
int ret;
switch (cmd) {
case MIC_VIRTIO_ADD_DEVICE:
{
ret = mic_virtio_add_device(mvdev, argp);
if (ret < 0) {
dev_err(mic_dev(mvdev),
"%s %d errno ret %d\n",
__func__, __LINE__, ret);
return ret;
}
break;
}
case MIC_VIRTIO_COPY_DESC:
{
struct mic_copy_desc copy;
ret = mic_vdev_inited(mvdev);
if (ret)
return ret;
if (copy_from_user(&copy, argp, sizeof(copy)))
return -EFAULT;
dev_dbg(mic_dev(mvdev),
"%s %d === iovcnt 0x%x vr_idx 0x%x update_used %d\n",
__func__, __LINE__, copy.iovcnt, copy.vr_idx,
copy.update_used);
ret = mic_virtio_copy_desc(mvdev, &copy);
if (ret < 0) {
dev_err(mic_dev(mvdev),
"%s %d errno ret %d\n",
__func__, __LINE__, ret);
return ret;
}
if (copy_to_user(
&((struct mic_copy_desc __user *)argp)->out_len,
&copy.out_len, sizeof(copy.out_len))) {
dev_err(mic_dev(mvdev), "%s %d errno ret %d\n",
__func__, __LINE__, -EFAULT);
return -EFAULT;
}
break;
}
case MIC_VIRTIO_CONFIG_CHANGE:
{
ret = mic_vdev_inited(mvdev);
if (ret)
return ret;
ret = mic_virtio_config_change(mvdev, argp);
if (ret < 0) {
dev_err(mic_dev(mvdev),
"%s %d errno ret %d\n",
__func__, __LINE__, ret);
return ret;
}
break;
}
default:
return -ENOIOCTLCMD;
};
return 0;
}
/*
* We return POLLIN | POLLOUT from poll when new buffers are enqueued, and
* not when previously enqueued buffers may be available. This means that
* in the card->host (TX) path, when userspace is unblocked by poll it
* must drain all available descriptors or it can stall.
*/
unsigned int mic_poll(struct file *f, poll_table *wait)
{
struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
int mask = 0;
poll_wait(f, &mvdev->waitq, wait);
if (mic_vdev_inited(mvdev)) {
mask = POLLERR;
} else if (mvdev->poll_wake) {
mvdev->poll_wake = 0;
mask = POLLIN | POLLOUT;
}
return mask;
}
static inline int
mic_query_offset(struct mic_vdev *mvdev, unsigned long offset,
unsigned long *size, unsigned long *pa)
{
struct mic_device *mdev = mvdev->mdev;
unsigned long start = MIC_DP_SIZE;
int i;
/*
* MMAP interface is as follows:
* offset region
* 0x0 virtio device_page
* 0x1000 first vring
* 0x1000 + size of 1st vring second vring
* ....
*/
if (!offset) {
*pa = virt_to_phys(mdev->dp);
*size = MIC_DP_SIZE;
return 0;
}
for (i = 0; i < mvdev->dd->num_vq; i++) {
struct mic_vringh *mvr = &mvdev->mvr[i];
if (offset == start) {
*pa = virt_to_phys(mvr->vring.va);
*size = mvr->vring.len;
return 0;
}
start += mvr->vring.len;
}
return -1;
}
/*
* Maps the device page and virtio rings to user space for readonly access.
*/
int
mic_mmap(struct file *f, struct vm_area_struct *vma)
{
struct mic_vdev *mvdev = (struct mic_vdev *)f->private_data;
unsigned long offset = vma->vm_pgoff << PAGE_SHIFT;
unsigned long pa, size = vma->vm_end - vma->vm_start, size_rem = size;
int i, err;
err = mic_vdev_inited(mvdev);
if (err)
return err;
if (vma->vm_flags & VM_WRITE)
return -EACCES;
while (size_rem) {
i = mic_query_offset(mvdev, offset, &size, &pa);
if (i < 0)
return -EINVAL;
err = remap_pfn_range(vma, vma->vm_start + offset,
pa >> PAGE_SHIFT, size, vma->vm_page_prot);
if (err)
return err;
dev_dbg(mic_dev(mvdev),
"%s %d type %d size 0x%lx off 0x%lx pa 0x%lx vma 0x%lx\n",
__func__, __LINE__, mvdev->virtio_id, size, offset,
pa, vma->vm_start + offset);
size_rem -= size;
offset += size;
}
return 0;
}
/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2013 Intel Corporation.
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Intel MIC Host driver.
*
*/
#ifndef _MIC_FOPS_H_
#define _MIC_FOPS_H_
int mic_open(struct inode *inode, struct file *filp);
int mic_release(struct inode *inode, struct file *filp);
ssize_t mic_read(struct file *filp, char __user *buf,
size_t count, loff_t *pos);
long mic_ioctl(struct file *filp, unsigned int cmd, unsigned long arg);
int mic_mmap(struct file *f, struct vm_area_struct *vma);
unsigned int mic_poll(struct file *f, poll_table *wait);
#endif
......@@ -27,8 +27,6 @@
#include "mic_device.h"
#include "mic_x100.h"
#include "mic_smpt.h"
#include "mic_fops.h"
#include "mic_virtio.h"
static const char mic_driver_name[] = "mic";
......@@ -57,17 +55,6 @@ MODULE_DEVICE_TABLE(pci, mic_pci_tbl);
/* ID allocator for MIC devices */
static struct ida g_mic_ida;
/* Base device node number for MIC devices */
static dev_t g_mic_devno;
static const struct file_operations mic_fops = {
.open = mic_open,
.release = mic_release,
.unlocked_ioctl = mic_ioctl,
.poll = mic_poll,
.mmap = mic_mmap,
.owner = THIS_MODULE,
};
/* Initialize the device page */
static int mic_dp_init(struct mic_device *mdev)
......@@ -169,7 +156,6 @@ mic_device_init(struct mic_device *mdev, struct pci_dev *pdev)
mic_ops_init(mdev);
mutex_init(&mdev->mic_mutex);
mdev->irq_info.next_avail_src = 0;
INIT_LIST_HEAD(&mdev->vdev_list);
}
/**
......@@ -259,30 +245,15 @@ static int mic_probe(struct pci_dev *pdev,
goto smpt_uninit;
}
mic_bootparam_init(mdev);
mic_create_debug_dir(mdev);
mdev->miscdev.minor = MISC_DYNAMIC_MINOR;
snprintf(mdev->name, sizeof(mdev->name), "mic%d", mdev->id);
mdev->miscdev.name = mdev->name;
mdev->miscdev.fops = &mic_fops;
mdev->miscdev.parent = &mdev->pdev->dev;
rc = misc_register(&mdev->miscdev);
if (rc) {
dev_err(&pdev->dev, "misc_register err id %d rc %d\n",
mdev->id, rc);
goto cleanup_debug_dir;
}
mdev->cosm_dev = cosm_register_device(&mdev->pdev->dev, &cosm_hw_ops);
if (IS_ERR(mdev->cosm_dev)) {
rc = PTR_ERR(mdev->cosm_dev);
dev_err(&pdev->dev, "cosm_add_device failed rc %d\n", rc);
goto misc_dereg;
goto cleanup_debug_dir;
}
return 0;
misc_dereg:
misc_deregister(&mdev->miscdev);
cleanup_debug_dir:
mic_delete_debug_dir(mdev);
mic_dp_uninit(mdev);
......@@ -323,7 +294,6 @@ static void mic_remove(struct pci_dev *pdev)
return;
cosm_unregister_device(mdev->cosm_dev);
misc_deregister(&mdev->miscdev);
mic_delete_debug_dir(mdev);
mic_dp_uninit(mdev);
mic_smpt_uninit(mdev);
......@@ -347,26 +317,17 @@ static int __init mic_init(void)
{
int ret;
ret = alloc_chrdev_region(&g_mic_devno, 0,
MIC_MAX_NUM_DEVS, mic_driver_name);
if (ret) {
pr_err("alloc_chrdev_region failed ret %d\n", ret);
goto error;
}
mic_init_debugfs();
ida_init(&g_mic_ida);
ret = pci_register_driver(&mic_driver);
if (ret) {
pr_err("pci_register_driver failed ret %d\n", ret);
goto cleanup_chrdev;
goto cleanup_debugfs;
}
return ret;
cleanup_chrdev:
return 0;
cleanup_debugfs:
ida_destroy(&g_mic_ida);
mic_exit_debugfs();
unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS);
error:
return ret;
}
......@@ -375,7 +336,6 @@ static void __exit mic_exit(void)
pci_unregister_driver(&mic_driver);
ida_destroy(&g_mic_ida);
mic_exit_debugfs();
unregister_chrdev_region(g_mic_devno, MIC_MAX_NUM_DEVS);
}
module_init(mic_init);
......
/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2013 Intel Corporation.
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Intel MIC Host driver.
*
*/
#include <linux/pci.h>
#include <linux/sched.h>
#include <linux/uaccess.h>
#include <linux/dmaengine.h>
#include <linux/mic_common.h>
#include "../common/mic_dev.h"
#include "mic_device.h"
#include "mic_smpt.h"
#include "mic_virtio.h"
/*
* Size of the internal buffer used during DMA's as an intermediate buffer
* for copy to/from user.
*/
#define MIC_INT_DMA_BUF_SIZE PAGE_ALIGN(64 * 1024ULL)
static int mic_sync_dma(struct mic_device *mdev, dma_addr_t dst,
dma_addr_t src, size_t len)
{
int err = 0;
struct dma_async_tx_descriptor *tx;
struct dma_chan *mic_ch = mdev->dma_ch[0];
if (!mic_ch) {
err = -EBUSY;
goto error;
}
tx = mic_ch->device->device_prep_dma_memcpy(mic_ch, dst, src, len,
DMA_PREP_FENCE);
if (!tx) {
err = -ENOMEM;
goto error;
} else {
dma_cookie_t cookie = tx->tx_submit(tx);
err = dma_submit_error(cookie);
if (err)
goto error;
err = dma_sync_wait(mic_ch, cookie);
}
error:
if (err)
dev_err(&mdev->pdev->dev, "%s %d err %d\n",
__func__, __LINE__, err);
return err;
}
/*
* Initiates the copies across the PCIe bus from card memory to a user
* space buffer. When transfers are done using DMA, source/destination
* addresses and transfer length must follow the alignment requirements of
* the MIC DMA engine.
*/
static int mic_virtio_copy_to_user(struct mic_vdev *mvdev, void __user *ubuf,
size_t len, u64 daddr, size_t dlen,
int vr_idx)
{
struct mic_device *mdev = mvdev->mdev;
void __iomem *dbuf = mdev->aper.va + daddr;
struct mic_vringh *mvr = &mvdev->mvr[vr_idx];
size_t dma_alignment = 1 << mdev->dma_ch[0]->device->copy_align;
size_t dma_offset;
size_t partlen;
int err;
dma_offset = daddr - round_down(daddr, dma_alignment);
daddr -= dma_offset;
len += dma_offset;
while (len) {
partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE);
err = mic_sync_dma(mdev, mvr->buf_da, daddr,
ALIGN(partlen, dma_alignment));
if (err)
goto err;
if (copy_to_user(ubuf, mvr->buf + dma_offset,
partlen - dma_offset)) {
err = -EFAULT;
goto err;
}
daddr += partlen;
ubuf += partlen;
dbuf += partlen;
mvdev->in_bytes_dma += partlen;
mvdev->in_bytes += partlen;
len -= partlen;
dma_offset = 0;
}
return 0;
err:
dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err);
return err;
}
/*
* Initiates copies across the PCIe bus from a user space buffer to card
* memory. When transfers are done using DMA, source/destination addresses
* and transfer length must follow the alignment requirements of the MIC
* DMA engine.
*/
static int mic_virtio_copy_from_user(struct mic_vdev *mvdev, void __user *ubuf,
size_t len, u64 daddr, size_t dlen,
int vr_idx)
{
struct mic_device *mdev = mvdev->mdev;
void __iomem *dbuf = mdev->aper.va + daddr;
struct mic_vringh *mvr = &mvdev->mvr[vr_idx];
size_t dma_alignment = 1 << mdev->dma_ch[0]->device->copy_align;
size_t partlen;
int err;
if (daddr & (dma_alignment - 1)) {
mvdev->tx_dst_unaligned += len;
goto memcpy;
} else if (ALIGN(len, dma_alignment) > dlen) {
mvdev->tx_len_unaligned += len;
goto memcpy;
}
while (len) {
partlen = min_t(size_t, len, MIC_INT_DMA_BUF_SIZE);
if (copy_from_user(mvr->buf, ubuf, partlen)) {
err = -EFAULT;
goto err;
}
err = mic_sync_dma(mdev, daddr, mvr->buf_da,
ALIGN(partlen, dma_alignment));
if (err)
goto err;
daddr += partlen;
ubuf += partlen;
dbuf += partlen;
mvdev->out_bytes_dma += partlen;
mvdev->out_bytes += partlen;
len -= partlen;
}
memcpy:
/*
* We are copying to IO below and should ideally use something
* like copy_from_user_toio(..) if it existed.
*/
if (copy_from_user((void __force *)dbuf, ubuf, len)) {
err = -EFAULT;
goto err;
}
mvdev->out_bytes += len;
return 0;
err:
dev_err(mic_dev(mvdev), "%s %d err %d\n", __func__, __LINE__, err);
return err;
}
#define MIC_VRINGH_READ true
/* The function to call to notify the card about added buffers */
static void mic_notify(struct vringh *vrh)
{
struct mic_vringh *mvrh = container_of(vrh, struct mic_vringh, vrh);
struct mic_vdev *mvdev = mvrh->mvdev;
s8 db = mvdev->dc->h2c_vdev_db;
if (db != -1)
mvdev->mdev->ops->send_intr(mvdev->mdev, db);
}
/* Determine the total number of bytes consumed in a VRINGH KIOV */
static inline u32 mic_vringh_iov_consumed(struct vringh_kiov *iov)
{
int i;
u32 total = iov->consumed;
for (i = 0; i < iov->i; i++)
total += iov->iov[i].iov_len;
return total;
}
/*
* Traverse the VRINGH KIOV and issue the APIs to trigger the copies.
* This API is heavily based on the vringh_iov_xfer(..) implementation
* in vringh.c. The reason we cannot reuse vringh_iov_pull_kern(..)
* and vringh_iov_push_kern(..) directly is because there is no
* way to override the VRINGH xfer(..) routines as of v3.10.
*/
static int mic_vringh_copy(struct mic_vdev *mvdev, struct vringh_kiov *iov,
void __user *ubuf, size_t len, bool read, int vr_idx,
size_t *out_len)
{
int ret = 0;
size_t partlen, tot_len = 0;
while (len && iov->i < iov->used) {
partlen = min(iov->iov[iov->i].iov_len, len);
if (read)
ret = mic_virtio_copy_to_user(mvdev, ubuf, partlen,
(u64)iov->iov[iov->i].iov_base,
iov->iov[iov->i].iov_len,
vr_idx);
else
ret = mic_virtio_copy_from_user(mvdev, ubuf, partlen,
(u64)iov->iov[iov->i].iov_base,
iov->iov[iov->i].iov_len,
vr_idx);
if (ret) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
break;
}
len -= partlen;
ubuf += partlen;
tot_len += partlen;
iov->consumed += partlen;
iov->iov[iov->i].iov_len -= partlen;
iov->iov[iov->i].iov_base += partlen;
if (!iov->iov[iov->i].iov_len) {
/* Fix up old iov element then increment. */
iov->iov[iov->i].iov_len = iov->consumed;
iov->iov[iov->i].iov_base -= iov->consumed;
iov->consumed = 0;
iov->i++;
}
}
*out_len = tot_len;
return ret;
}
/*
* Use the standard VRINGH infrastructure in the kernel to fetch new
* descriptors, initiate the copies and update the used ring.
*/
static int _mic_virtio_copy(struct mic_vdev *mvdev,
struct mic_copy_desc *copy)
{
int ret = 0;
u32 iovcnt = copy->iovcnt;
struct iovec iov;
struct iovec __user *u_iov = copy->iov;
void __user *ubuf = NULL;
struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx];
struct vringh_kiov *riov = &mvr->riov;
struct vringh_kiov *wiov = &mvr->wiov;
struct vringh *vrh = &mvr->vrh;
u16 *head = &mvr->head;
struct mic_vring *vr = &mvr->vring;
size_t len = 0, out_len;
copy->out_len = 0;
/* Fetch a new IOVEC if all previous elements have been processed */
if (riov->i == riov->used && wiov->i == wiov->used) {
ret = vringh_getdesc_kern(vrh, riov, wiov,
head, GFP_KERNEL);
/* Check if there are available descriptors */
if (ret <= 0)
return ret;
}
while (iovcnt) {
if (!len) {
/* Copy over a new iovec from user space. */
ret = copy_from_user(&iov, u_iov, sizeof(*u_iov));
if (ret) {
ret = -EINVAL;
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
break;
}
len = iov.iov_len;
ubuf = iov.iov_base;
}
/* Issue all the read descriptors first */
ret = mic_vringh_copy(mvdev, riov, ubuf, len, MIC_VRINGH_READ,
copy->vr_idx, &out_len);
if (ret) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
break;
}
len -= out_len;
ubuf += out_len;
copy->out_len += out_len;
/* Issue the write descriptors next */
ret = mic_vringh_copy(mvdev, wiov, ubuf, len, !MIC_VRINGH_READ,
copy->vr_idx, &out_len);
if (ret) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
break;
}
len -= out_len;
ubuf += out_len;
copy->out_len += out_len;
if (!len) {
/* One user space iovec is now completed */
iovcnt--;
u_iov++;
}
/* Exit loop if all elements in KIOVs have been processed. */
if (riov->i == riov->used && wiov->i == wiov->used)
break;
}
/*
* Update the used ring if a descriptor was available and some data was
* copied in/out and the user asked for a used ring update.
*/
if (*head != USHRT_MAX && copy->out_len && copy->update_used) {
u32 total = 0;
/* Determine the total data consumed */
total += mic_vringh_iov_consumed(riov);
total += mic_vringh_iov_consumed(wiov);
vringh_complete_kern(vrh, *head, total);
*head = USHRT_MAX;
if (vringh_need_notify_kern(vrh) > 0)
vringh_notify(vrh);
vringh_kiov_cleanup(riov);
vringh_kiov_cleanup(wiov);
/* Update avail idx for user space */
vr->info->avail_idx = vrh->last_avail_idx;
}
return ret;
}
static inline int mic_verify_copy_args(struct mic_vdev *mvdev,
struct mic_copy_desc *copy)
{
if (copy->vr_idx >= mvdev->dd->num_vq) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, -EINVAL);
return -EINVAL;
}
return 0;
}
/* Copy a specified number of virtio descriptors in a chain */
int mic_virtio_copy_desc(struct mic_vdev *mvdev,
struct mic_copy_desc *copy)
{
int err;
struct mic_vringh *mvr = &mvdev->mvr[copy->vr_idx];
err = mic_verify_copy_args(mvdev, copy);
if (err)
return err;
mutex_lock(&mvr->vr_mutex);
if (!mic_vdevup(mvdev)) {
err = -ENODEV;
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, err);
goto err;
}
err = _mic_virtio_copy(mvdev, copy);
if (err) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, err);
}
err:
mutex_unlock(&mvr->vr_mutex);
return err;
}
static void mic_virtio_init_post(struct mic_vdev *mvdev)
{
struct mic_vqconfig *vqconfig = mic_vq_config(mvdev->dd);
int i;
for (i = 0; i < mvdev->dd->num_vq; i++) {
if (!le64_to_cpu(vqconfig[i].used_address)) {
dev_warn(mic_dev(mvdev), "used_address zero??\n");
continue;
}
mvdev->mvr[i].vrh.vring.used =
(void __force *)mvdev->mdev->aper.va +
le64_to_cpu(vqconfig[i].used_address);
}
mvdev->dc->used_address_updated = 0;
dev_dbg(mic_dev(mvdev), "%s: device type %d LINKUP\n",
__func__, mvdev->virtio_id);
}
static inline void mic_virtio_device_reset(struct mic_vdev *mvdev)
{
int i;
dev_dbg(mic_dev(mvdev), "%s: status %d device type %d RESET\n",
__func__, mvdev->dd->status, mvdev->virtio_id);
for (i = 0; i < mvdev->dd->num_vq; i++)
/*
* Avoid lockdep false positive. The + 1 is for the mic
* mutex which is held in the reset devices code path.
*/
mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1);
/* 0 status means "reset" */
mvdev->dd->status = 0;
mvdev->dc->vdev_reset = 0;
mvdev->dc->host_ack = 1;
for (i = 0; i < mvdev->dd->num_vq; i++) {
struct vringh *vrh = &mvdev->mvr[i].vrh;
mvdev->mvr[i].vring.info->avail_idx = 0;
vrh->completed = 0;
vrh->last_avail_idx = 0;
vrh->last_used_idx = 0;
}
for (i = 0; i < mvdev->dd->num_vq; i++)
mutex_unlock(&mvdev->mvr[i].vr_mutex);
}
void mic_virtio_reset_devices(struct mic_device *mdev)
{
struct list_head *pos, *tmp;
struct mic_vdev *mvdev;
dev_dbg(&mdev->pdev->dev, "%s\n", __func__);
list_for_each_safe(pos, tmp, &mdev->vdev_list) {
mvdev = list_entry(pos, struct mic_vdev, list);
mic_virtio_device_reset(mvdev);
mvdev->poll_wake = 1;
wake_up(&mvdev->waitq);
}
}
void mic_bh_handler(struct work_struct *work)
{
struct mic_vdev *mvdev = container_of(work, struct mic_vdev,
virtio_bh_work);
if (mvdev->dc->used_address_updated)
mic_virtio_init_post(mvdev);
if (mvdev->dc->vdev_reset)
mic_virtio_device_reset(mvdev);
mvdev->poll_wake = 1;
wake_up(&mvdev->waitq);
}
static irqreturn_t mic_virtio_intr_handler(int irq, void *data)
{
struct mic_vdev *mvdev = data;
struct mic_device *mdev = mvdev->mdev;
mdev->ops->intr_workarounds(mdev);
schedule_work(&mvdev->virtio_bh_work);
return IRQ_HANDLED;
}
int mic_virtio_config_change(struct mic_vdev *mvdev,
void __user *argp)
{
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
int ret = 0, retry, i;
struct mic_bootparam *bootparam = mvdev->mdev->dp;
s8 db = bootparam->h2c_config_db;
mutex_lock(&mvdev->mdev->mic_mutex);
for (i = 0; i < mvdev->dd->num_vq; i++)
mutex_lock_nested(&mvdev->mvr[i].vr_mutex, i + 1);
if (db == -1 || mvdev->dd->type == -1) {
ret = -EIO;
goto exit;
}
if (copy_from_user(mic_vq_configspace(mvdev->dd),
argp, mvdev->dd->config_len)) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, -EFAULT);
ret = -EFAULT;
goto exit;
}
mvdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED;
mvdev->mdev->ops->send_intr(mvdev->mdev, db);
for (retry = 100; retry--;) {
ret = wait_event_timeout(wake,
mvdev->dc->guest_ack, msecs_to_jiffies(100));
if (ret)
break;
}
dev_dbg(mic_dev(mvdev),
"%s %d retry: %d\n", __func__, __LINE__, retry);
mvdev->dc->config_change = 0;
mvdev->dc->guest_ack = 0;
exit:
for (i = 0; i < mvdev->dd->num_vq; i++)
mutex_unlock(&mvdev->mvr[i].vr_mutex);
mutex_unlock(&mvdev->mdev->mic_mutex);
return ret;
}
static int mic_copy_dp_entry(struct mic_vdev *mvdev,
void __user *argp,
__u8 *type,
struct mic_device_desc **devpage)
{
struct mic_device *mdev = mvdev->mdev;
struct mic_device_desc dd, *dd_config, *devp;
struct mic_vqconfig *vqconfig;
int ret = 0, i;
bool slot_found = false;
if (copy_from_user(&dd, argp, sizeof(dd))) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, -EFAULT);
return -EFAULT;
}
if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE ||
dd.num_vq > MIC_MAX_VRINGS) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, -EINVAL);
return -EINVAL;
}
dd_config = kmalloc(mic_desc_size(&dd), GFP_KERNEL);
if (dd_config == NULL) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, -ENOMEM);
return -ENOMEM;
}
if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) {
ret = -EFAULT;
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
goto exit;
}
vqconfig = mic_vq_config(dd_config);
for (i = 0; i < dd.num_vq; i++) {
if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) {
ret = -EINVAL;
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
goto exit;
}
}
/* Find the first free device page entry */
for (i = sizeof(struct mic_bootparam);
i < MIC_DP_SIZE - mic_total_desc_size(dd_config);
i += mic_total_desc_size(devp)) {
devp = mdev->dp + i;
if (devp->type == 0 || devp->type == -1) {
slot_found = true;
break;
}
}
if (!slot_found) {
ret = -EINVAL;
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
goto exit;
}
/*
* Save off the type before doing the memcpy. Type will be set in the
* end after completing all initialization for the new device.
*/
*type = dd_config->type;
dd_config->type = 0;
memcpy(devp, dd_config, mic_desc_size(dd_config));
*devpage = devp;
exit:
kfree(dd_config);
return ret;
}
static void mic_init_device_ctrl(struct mic_vdev *mvdev,
struct mic_device_desc *devpage)
{
struct mic_device_ctrl *dc;
dc = (void *)devpage + mic_aligned_desc_size(devpage);
dc->config_change = 0;
dc->guest_ack = 0;
dc->vdev_reset = 0;
dc->host_ack = 0;
dc->used_address_updated = 0;
dc->c2h_vdev_db = -1;
dc->h2c_vdev_db = -1;
mvdev->dc = dc;
}
int mic_virtio_add_device(struct mic_vdev *mvdev,
void __user *argp)
{
struct mic_device *mdev = mvdev->mdev;
struct mic_device_desc *dd = NULL;
struct mic_vqconfig *vqconfig;
int vr_size, i, j, ret;
u8 type = 0;
s8 db;
char irqname[10];
struct mic_bootparam *bootparam = mdev->dp;
u16 num;
dma_addr_t vr_addr;
mutex_lock(&mdev->mic_mutex);
ret = mic_copy_dp_entry(mvdev, argp, &type, &dd);
if (ret) {
mutex_unlock(&mdev->mic_mutex);
return ret;
}
mic_init_device_ctrl(mvdev, dd);
mvdev->dd = dd;
mvdev->virtio_id = type;
vqconfig = mic_vq_config(dd);
INIT_WORK(&mvdev->virtio_bh_work, mic_bh_handler);
for (i = 0; i < dd->num_vq; i++) {
struct mic_vringh *mvr = &mvdev->mvr[i];
struct mic_vring *vr = &mvdev->mvr[i].vring;
num = le16_to_cpu(vqconfig[i].num);
mutex_init(&mvr->vr_mutex);
vr_size = PAGE_ALIGN(vring_size(num, MIC_VIRTIO_RING_ALIGN) +
sizeof(struct _mic_vring_info));
vr->va = (void *)
__get_free_pages(GFP_KERNEL | __GFP_ZERO,
get_order(vr_size));
if (!vr->va) {
ret = -ENOMEM;
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
goto err;
}
vr->len = vr_size;
vr->info = vr->va + vring_size(num, MIC_VIRTIO_RING_ALIGN);
vr->info->magic = cpu_to_le32(MIC_MAGIC + mvdev->virtio_id + i);
vr_addr = mic_map_single(mdev, vr->va, vr_size);
if (mic_map_error(vr_addr)) {
free_pages((unsigned long)vr->va, get_order(vr_size));
ret = -ENOMEM;
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
goto err;
}
vqconfig[i].address = cpu_to_le64(vr_addr);
vring_init(&vr->vr, num, vr->va, MIC_VIRTIO_RING_ALIGN);
ret = vringh_init_kern(&mvr->vrh,
*(u32 *)mic_vq_features(mvdev->dd), num, false,
vr->vr.desc, vr->vr.avail, vr->vr.used);
if (ret) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, ret);
goto err;
}
vringh_kiov_init(&mvr->riov, NULL, 0);
vringh_kiov_init(&mvr->wiov, NULL, 0);
mvr->head = USHRT_MAX;
mvr->mvdev = mvdev;
mvr->vrh.notify = mic_notify;
dev_dbg(&mdev->pdev->dev,
"%s %d index %d va %p info %p vr_size 0x%x\n",
__func__, __LINE__, i, vr->va, vr->info, vr_size);
mvr->buf = (void *)__get_free_pages(GFP_KERNEL,
get_order(MIC_INT_DMA_BUF_SIZE));
mvr->buf_da = mic_map_single(mvdev->mdev, mvr->buf,
MIC_INT_DMA_BUF_SIZE);
}
snprintf(irqname, sizeof(irqname), "mic%dvirtio%d", mdev->id,
mvdev->virtio_id);
mvdev->virtio_db = mic_next_db(mdev);
mvdev->virtio_cookie = mic_request_threaded_irq(mdev,
mic_virtio_intr_handler,
NULL, irqname, mvdev,
mvdev->virtio_db, MIC_INTR_DB);
if (IS_ERR(mvdev->virtio_cookie)) {
ret = PTR_ERR(mvdev->virtio_cookie);
dev_dbg(&mdev->pdev->dev, "request irq failed\n");
goto err;
}
mvdev->dc->c2h_vdev_db = mvdev->virtio_db;
list_add_tail(&mvdev->list, &mdev->vdev_list);
/*
* Order the type update with previous stores. This write barrier
* is paired with the corresponding read barrier before the uncached
* system memory read of the type, on the card while scanning the
* device page.
*/
smp_wmb();
dd->type = type;
dev_dbg(&mdev->pdev->dev, "Added virtio device id %d\n", dd->type);
db = bootparam->h2c_config_db;
if (db != -1)
mdev->ops->send_intr(mdev, db);
mutex_unlock(&mdev->mic_mutex);
return 0;
err:
vqconfig = mic_vq_config(dd);
for (j = 0; j < i; j++) {
struct mic_vringh *mvr = &mvdev->mvr[j];
mic_unmap_single(mdev, le64_to_cpu(vqconfig[j].address),
mvr->vring.len);
free_pages((unsigned long)mvr->vring.va,
get_order(mvr->vring.len));
}
mutex_unlock(&mdev->mic_mutex);
return ret;
}
void mic_virtio_del_device(struct mic_vdev *mvdev)
{
struct list_head *pos, *tmp;
struct mic_vdev *tmp_mvdev;
struct mic_device *mdev = mvdev->mdev;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
int i, ret, retry;
struct mic_vqconfig *vqconfig;
struct mic_bootparam *bootparam = mdev->dp;
s8 db;
mutex_lock(&mdev->mic_mutex);
db = bootparam->h2c_config_db;
if (db == -1)
goto skip_hot_remove;
dev_dbg(&mdev->pdev->dev,
"Requesting hot remove id %d\n", mvdev->virtio_id);
mvdev->dc->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE;
mdev->ops->send_intr(mdev, db);
for (retry = 100; retry--;) {
ret = wait_event_timeout(wake,
mvdev->dc->guest_ack, msecs_to_jiffies(100));
if (ret)
break;
}
dev_dbg(&mdev->pdev->dev,
"Device id %d config_change %d guest_ack %d retry %d\n",
mvdev->virtio_id, mvdev->dc->config_change,
mvdev->dc->guest_ack, retry);
mvdev->dc->config_change = 0;
mvdev->dc->guest_ack = 0;
skip_hot_remove:
mic_free_irq(mdev, mvdev->virtio_cookie, mvdev);
flush_work(&mvdev->virtio_bh_work);
vqconfig = mic_vq_config(mvdev->dd);
for (i = 0; i < mvdev->dd->num_vq; i++) {
struct mic_vringh *mvr = &mvdev->mvr[i];
mic_unmap_single(mvdev->mdev, mvr->buf_da,
MIC_INT_DMA_BUF_SIZE);
free_pages((unsigned long)mvr->buf,
get_order(MIC_INT_DMA_BUF_SIZE));
vringh_kiov_cleanup(&mvr->riov);
vringh_kiov_cleanup(&mvr->wiov);
mic_unmap_single(mdev, le64_to_cpu(vqconfig[i].address),
mvr->vring.len);
free_pages((unsigned long)mvr->vring.va,
get_order(mvr->vring.len));
}
list_for_each_safe(pos, tmp, &mdev->vdev_list) {
tmp_mvdev = list_entry(pos, struct mic_vdev, list);
if (tmp_mvdev == mvdev) {
list_del(pos);
dev_dbg(&mdev->pdev->dev,
"Removing virtio device id %d\n",
mvdev->virtio_id);
break;
}
}
/*
* Order the type update with previous stores. This write barrier
* is paired with the corresponding read barrier before the uncached
* system memory read of the type, on the card while scanning the
* device page.
*/
smp_wmb();
mvdev->dd->type = -1;
mutex_unlock(&mdev->mic_mutex);
}
/*
* Intel MIC Platform Software Stack (MPSS)
*
* Copyright(c) 2013 Intel Corporation.
*
* 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.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* The full GNU General Public License is included in this distribution in
* the file called "COPYING".
*
* Intel MIC Host driver.
*
*/
#ifndef MIC_VIRTIO_H
#define MIC_VIRTIO_H
#include <linux/virtio_config.h>
#include <linux/mic_ioctl.h>
/*
* Note on endianness.
* 1. Host can be both BE or LE
* 2. Guest/card is LE. Host uses le_to_cpu to access desc/avail
* rings and ioreadXX/iowriteXX to access used ring.
* 3. Device page exposed by host to guest contains LE values. Guest
* accesses these using ioreadXX/iowriteXX etc. This way in general we
* obey the virtio spec according to which guest works with native
* endianness and host is aware of guest endianness and does all
* required endianness conversion.
* 4. Data provided from user space to guest (in ADD_DEVICE and
* CONFIG_CHANGE ioctl's) is not interpreted by the driver and should be
* in guest endianness.
*/
/**
* struct mic_vringh - Virtio ring host information.
*
* @vring: The MIC vring used for setting up user space mappings.
* @vrh: The host VRINGH used for accessing the card vrings.
* @riov: The VRINGH read kernel IOV.
* @wiov: The VRINGH write kernel IOV.
* @vr_mutex: Mutex for synchronizing access to the VRING.
* @buf: Temporary kernel buffer used to copy in/out data
* from/to the card via DMA.
* @buf_da: dma address of buf.
* @mvdev: Back pointer to MIC virtio device for vringh_notify(..).
* @head: The VRINGH head index address passed to vringh_getdesc_kern(..).
*/
struct mic_vringh {
struct mic_vring vring;
struct vringh vrh;
struct vringh_kiov riov;
struct vringh_kiov wiov;
struct mutex vr_mutex;
void *buf;
dma_addr_t buf_da;
struct mic_vdev *mvdev;
u16 head;
};
/**
* struct mic_vdev - Host information for a card Virtio device.
*
* @virtio_id - Virtio device id.
* @waitq - Waitqueue to allow ring3 apps to poll.
* @mdev - Back pointer to host MIC device.
* @poll_wake - Used for waking up threads blocked in poll.
* @out_bytes - Debug stats for number of bytes copied from host to card.
* @in_bytes - Debug stats for number of bytes copied from card to host.
* @out_bytes_dma - Debug stats for number of bytes copied from host to card
* using DMA.
* @in_bytes_dma - Debug stats for number of bytes copied from card to host
* using DMA.
* @tx_len_unaligned - Debug stats for number of bytes copied to the card where
* the transfer length did not have the required DMA alignment.
* @tx_dst_unaligned - Debug stats for number of bytes copied where the
* destination address on the card did not have the required DMA alignment.
* @mvr - Store per VRING data structures.
* @virtio_bh_work - Work struct used to schedule virtio bottom half handling.
* @dd - Virtio device descriptor.
* @dc - Virtio device control fields.
* @list - List of Virtio devices.
* @virtio_db - The doorbell used by the card to interrupt the host.
* @virtio_cookie - The cookie returned while requesting interrupts.
*/
struct mic_vdev {
int virtio_id;
wait_queue_head_t waitq;
struct mic_device *mdev;
int poll_wake;
unsigned long out_bytes;
unsigned long in_bytes;
unsigned long out_bytes_dma;
unsigned long in_bytes_dma;
unsigned long tx_len_unaligned;
unsigned long tx_dst_unaligned;
struct mic_vringh mvr[MIC_MAX_VRINGS];
struct work_struct virtio_bh_work;
struct mic_device_desc *dd;
struct mic_device_ctrl *dc;
struct list_head list;
int virtio_db;
struct mic_irq *virtio_cookie;
};
void mic_virtio_uninit(struct mic_device *mdev);
int mic_virtio_add_device(struct mic_vdev *mvdev,
void __user *argp);
void mic_virtio_del_device(struct mic_vdev *mvdev);
int mic_virtio_config_change(struct mic_vdev *mvdev,
void __user *argp);
int mic_virtio_copy_desc(struct mic_vdev *mvdev,
struct mic_copy_desc *request);
void mic_virtio_reset_devices(struct mic_device *mdev);
void mic_bh_handler(struct work_struct *work);
/* Helper API to obtain the MIC PCIe device */
static inline struct device *mic_dev(struct mic_vdev *mvdev)
{
return &mvdev->mdev->pdev->dev;
}
/* Helper API to check if a virtio device is initialized */
static inline int mic_vdev_inited(struct mic_vdev *mvdev)
{
/* Device has not been created yet */
if (!mvdev->dd || !mvdev->dd->type) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, -EINVAL);
return -EINVAL;
}
/* Device has been removed/deleted */
if (mvdev->dd->type == -1) {
dev_err(mic_dev(mvdev), "%s %d err %d\n",
__func__, __LINE__, -ENODEV);
return -ENODEV;
}
return 0;
}
/* Helper API to check if a virtio device is running */
static inline bool mic_vdevup(struct mic_vdev *mvdev)
{
return !!mvdev->dd->status;
}
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment