forked from Minki/linux
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: Ashutosh Dixit <ashutosh.dixit@intel.com> Signed-off-by: Sudeep Dutt <sudeep.dutt@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
4ddbdbb90d
commit
ef39830c35
@ -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;
|
||||
|
@ -1,222 +0,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.
|
||||
*
|
||||
*/
|
||||
#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(©, 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, ©);
|
||||
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,
|
||||
©.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;
|
||||
}
|
@ -1,32 +0,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);
|
||||
|
@ -1,811 +0,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.
|
||||
*
|
||||
*/
|
||||
#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);
|
||||
}
|
@ -1,155 +0,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_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
|
Loading…
Reference in New Issue
Block a user