123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170 |
- /*
- * Intel MIC Platform Software Stack (MPSS)
- *
- * Copyright(c) 2016 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 Virtio Over PCIe (VOP) driver.
- *
- */
- #include <linux/sched.h>
- #include <linux/poll.h>
- #include <linux/dma-mapping.h>
- #include <linux/mic_common.h>
- #include "../common/mic_dev.h"
- #include <linux/mic_ioctl.h>
- #include "vop_main.h"
- /* Helper API to obtain the VOP PCIe device */
- static inline struct device *vop_dev(struct vop_vdev *vdev)
- {
- return vdev->vpdev->dev.parent;
- }
- /* Helper API to check if a virtio device is initialized */
- static inline int vop_vdev_inited(struct vop_vdev *vdev)
- {
- if (!vdev)
- return -EINVAL;
- /* Device has not been created yet */
- if (!vdev->dd || !vdev->dd->type) {
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, -EINVAL);
- return -EINVAL;
- }
- /* Device has been removed/deleted */
- if (vdev->dd->type == -1) {
- dev_dbg(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, -ENODEV);
- return -ENODEV;
- }
- return 0;
- }
- static void _vop_notify(struct vringh *vrh)
- {
- struct vop_vringh *vvrh = container_of(vrh, struct vop_vringh, vrh);
- struct vop_vdev *vdev = vvrh->vdev;
- struct vop_device *vpdev = vdev->vpdev;
- s8 db = vdev->dc->h2c_vdev_db;
- if (db != -1)
- vpdev->hw_ops->send_intr(vpdev, db);
- }
- static void vop_virtio_init_post(struct vop_vdev *vdev)
- {
- struct mic_vqconfig *vqconfig = mic_vq_config(vdev->dd);
- struct vop_device *vpdev = vdev->vpdev;
- int i, used_size;
- for (i = 0; i < vdev->dd->num_vq; i++) {
- used_size = PAGE_ALIGN(sizeof(u16) * 3 +
- sizeof(struct vring_used_elem) *
- le16_to_cpu(vqconfig->num));
- if (!le64_to_cpu(vqconfig[i].used_address)) {
- dev_warn(vop_dev(vdev), "used_address zero??\n");
- continue;
- }
- vdev->vvr[i].vrh.vring.used =
- (void __force *)vpdev->hw_ops->ioremap(
- vpdev,
- le64_to_cpu(vqconfig[i].used_address),
- used_size);
- }
- vdev->dc->used_address_updated = 0;
- dev_info(vop_dev(vdev), "%s: device type %d LINKUP\n",
- __func__, vdev->virtio_id);
- }
- static inline void vop_virtio_device_reset(struct vop_vdev *vdev)
- {
- int i;
- dev_dbg(vop_dev(vdev), "%s: status %d device type %d RESET\n",
- __func__, vdev->dd->status, vdev->virtio_id);
- for (i = 0; i < vdev->dd->num_vq; i++)
- /*
- * Avoid lockdep false positive. The + 1 is for the vop
- * mutex which is held in the reset devices code path.
- */
- mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1);
- /* 0 status means "reset" */
- vdev->dd->status = 0;
- vdev->dc->vdev_reset = 0;
- vdev->dc->host_ack = 1;
- for (i = 0; i < vdev->dd->num_vq; i++) {
- struct vringh *vrh = &vdev->vvr[i].vrh;
- vdev->vvr[i].vring.info->avail_idx = 0;
- vrh->completed = 0;
- vrh->last_avail_idx = 0;
- vrh->last_used_idx = 0;
- }
- for (i = 0; i < vdev->dd->num_vq; i++)
- mutex_unlock(&vdev->vvr[i].vr_mutex);
- }
- static void vop_virtio_reset_devices(struct vop_info *vi)
- {
- struct list_head *pos, *tmp;
- struct vop_vdev *vdev;
- list_for_each_safe(pos, tmp, &vi->vdev_list) {
- vdev = list_entry(pos, struct vop_vdev, list);
- vop_virtio_device_reset(vdev);
- vdev->poll_wake = 1;
- wake_up(&vdev->waitq);
- }
- }
- static void vop_bh_handler(struct work_struct *work)
- {
- struct vop_vdev *vdev = container_of(work, struct vop_vdev,
- virtio_bh_work);
- if (vdev->dc->used_address_updated)
- vop_virtio_init_post(vdev);
- if (vdev->dc->vdev_reset)
- vop_virtio_device_reset(vdev);
- vdev->poll_wake = 1;
- wake_up(&vdev->waitq);
- }
- static irqreturn_t _vop_virtio_intr_handler(int irq, void *data)
- {
- struct vop_vdev *vdev = data;
- struct vop_device *vpdev = vdev->vpdev;
- vpdev->hw_ops->ack_interrupt(vpdev, vdev->virtio_db);
- schedule_work(&vdev->virtio_bh_work);
- return IRQ_HANDLED;
- }
- static int vop_virtio_config_change(struct vop_vdev *vdev, void *argp)
- {
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
- int ret = 0, retry, i;
- struct vop_device *vpdev = vdev->vpdev;
- struct vop_info *vi = dev_get_drvdata(&vpdev->dev);
- struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev);
- s8 db = bootparam->h2c_config_db;
- mutex_lock(&vi->vop_mutex);
- for (i = 0; i < vdev->dd->num_vq; i++)
- mutex_lock_nested(&vdev->vvr[i].vr_mutex, i + 1);
- if (db == -1 || vdev->dd->type == -1) {
- ret = -EIO;
- goto exit;
- }
- memcpy(mic_vq_configspace(vdev->dd), argp, vdev->dd->config_len);
- vdev->dc->config_change = MIC_VIRTIO_PARAM_CONFIG_CHANGED;
- vpdev->hw_ops->send_intr(vpdev, db);
- for (retry = 100; retry--;) {
- ret = wait_event_timeout(wake, vdev->dc->guest_ack,
- msecs_to_jiffies(100));
- if (ret)
- break;
- }
- dev_dbg(vop_dev(vdev),
- "%s %d retry: %d\n", __func__, __LINE__, retry);
- vdev->dc->config_change = 0;
- vdev->dc->guest_ack = 0;
- exit:
- for (i = 0; i < vdev->dd->num_vq; i++)
- mutex_unlock(&vdev->vvr[i].vr_mutex);
- mutex_unlock(&vi->vop_mutex);
- return ret;
- }
- static int vop_copy_dp_entry(struct vop_vdev *vdev,
- struct mic_device_desc *argp, __u8 *type,
- struct mic_device_desc **devpage)
- {
- struct vop_device *vpdev = vdev->vpdev;
- struct mic_device_desc *devp;
- struct mic_vqconfig *vqconfig;
- int ret = 0, i;
- bool slot_found = false;
- vqconfig = mic_vq_config(argp);
- for (i = 0; i < argp->num_vq; i++) {
- if (le16_to_cpu(vqconfig[i].num) > MIC_MAX_VRING_ENTRIES) {
- ret = -EINVAL;
- dev_err(vop_dev(vdev), "%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(argp);
- i += mic_total_desc_size(devp)) {
- devp = vpdev->hw_ops->get_dp(vpdev) + i;
- if (devp->type == 0 || devp->type == -1) {
- slot_found = true;
- break;
- }
- }
- if (!slot_found) {
- ret = -EINVAL;
- dev_err(vop_dev(vdev), "%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 = argp->type;
- argp->type = 0;
- memcpy(devp, argp, mic_desc_size(argp));
- *devpage = devp;
- exit:
- return ret;
- }
- static void vop_init_device_ctrl(struct vop_vdev *vdev,
- 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;
- vdev->dc = dc;
- }
- static int vop_virtio_add_device(struct vop_vdev *vdev,
- struct mic_device_desc *argp)
- {
- struct vop_info *vi = vdev->vi;
- struct vop_device *vpdev = vi->vpdev;
- struct mic_device_desc *dd = NULL;
- struct mic_vqconfig *vqconfig;
- int vr_size, i, j, ret;
- u8 type = 0;
- s8 db = -1;
- char irqname[16];
- struct mic_bootparam *bootparam;
- u16 num;
- dma_addr_t vr_addr;
- bootparam = vpdev->hw_ops->get_dp(vpdev);
- init_waitqueue_head(&vdev->waitq);
- INIT_LIST_HEAD(&vdev->list);
- vdev->vpdev = vpdev;
- ret = vop_copy_dp_entry(vdev, argp, &type, &dd);
- if (ret) {
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, ret);
- return ret;
- }
- vop_init_device_ctrl(vdev, dd);
- vdev->dd = dd;
- vdev->virtio_id = type;
- vqconfig = mic_vq_config(dd);
- INIT_WORK(&vdev->virtio_bh_work, vop_bh_handler);
- for (i = 0; i < dd->num_vq; i++) {
- struct vop_vringh *vvr = &vdev->vvr[i];
- struct mic_vring *vr = &vdev->vvr[i].vring;
- num = le16_to_cpu(vqconfig[i].num);
- mutex_init(&vvr->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(vop_dev(vdev), "%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 + vdev->virtio_id + i);
- vr_addr = dma_map_single(&vpdev->dev, vr->va, vr_size,
- DMA_BIDIRECTIONAL);
- if (dma_mapping_error(&vpdev->dev, vr_addr)) {
- free_pages((unsigned long)vr->va, get_order(vr_size));
- ret = -ENOMEM;
- dev_err(vop_dev(vdev), "%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(&vvr->vrh,
- *(u32 *)mic_vq_features(vdev->dd),
- num, false, vr->vr.desc, vr->vr.avail,
- vr->vr.used);
- if (ret) {
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, ret);
- goto err;
- }
- vringh_kiov_init(&vvr->riov, NULL, 0);
- vringh_kiov_init(&vvr->wiov, NULL, 0);
- vvr->head = USHRT_MAX;
- vvr->vdev = vdev;
- vvr->vrh.notify = _vop_notify;
- dev_dbg(&vpdev->dev,
- "%s %d index %d va %p info %p vr_size 0x%x\n",
- __func__, __LINE__, i, vr->va, vr->info, vr_size);
- vvr->buf = (void *)__get_free_pages(GFP_KERNEL,
- get_order(VOP_INT_DMA_BUF_SIZE));
- vvr->buf_da = dma_map_single(&vpdev->dev,
- vvr->buf, VOP_INT_DMA_BUF_SIZE,
- DMA_BIDIRECTIONAL);
- }
- snprintf(irqname, sizeof(irqname), "vop%dvirtio%d", vpdev->index,
- vdev->virtio_id);
- vdev->virtio_db = vpdev->hw_ops->next_db(vpdev);
- vdev->virtio_cookie = vpdev->hw_ops->request_irq(vpdev,
- _vop_virtio_intr_handler, irqname, vdev,
- vdev->virtio_db);
- if (IS_ERR(vdev->virtio_cookie)) {
- ret = PTR_ERR(vdev->virtio_cookie);
- dev_dbg(&vpdev->dev, "request irq failed\n");
- goto err;
- }
- vdev->dc->c2h_vdev_db = vdev->virtio_db;
- /*
- * 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;
- argp->type = type;
- if (bootparam) {
- db = bootparam->h2c_config_db;
- if (db != -1)
- vpdev->hw_ops->send_intr(vpdev, db);
- }
- dev_dbg(&vpdev->dev, "Added virtio id %d db %d\n", dd->type, db);
- return 0;
- err:
- vqconfig = mic_vq_config(dd);
- for (j = 0; j < i; j++) {
- struct vop_vringh *vvr = &vdev->vvr[j];
- dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[j].address),
- vvr->vring.len, DMA_BIDIRECTIONAL);
- free_pages((unsigned long)vvr->vring.va,
- get_order(vvr->vring.len));
- }
- return ret;
- }
- static void vop_dev_remove(struct vop_info *pvi, struct mic_device_ctrl *devp,
- struct vop_device *vpdev)
- {
- struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev);
- s8 db;
- int ret, retry;
- DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
- devp->config_change = MIC_VIRTIO_PARAM_DEV_REMOVE;
- db = bootparam->h2c_config_db;
- if (db != -1)
- vpdev->hw_ops->send_intr(vpdev, db);
- else
- goto done;
- for (retry = 15; retry--;) {
- ret = wait_event_timeout(wake, devp->guest_ack,
- msecs_to_jiffies(1000));
- if (ret)
- break;
- }
- done:
- devp->config_change = 0;
- devp->guest_ack = 0;
- }
- static void vop_virtio_del_device(struct vop_vdev *vdev)
- {
- struct vop_info *vi = vdev->vi;
- struct vop_device *vpdev = vdev->vpdev;
- int i;
- struct mic_vqconfig *vqconfig;
- struct mic_bootparam *bootparam = vpdev->hw_ops->get_dp(vpdev);
- if (!bootparam)
- goto skip_hot_remove;
- vop_dev_remove(vi, vdev->dc, vpdev);
- skip_hot_remove:
- vpdev->hw_ops->free_irq(vpdev, vdev->virtio_cookie, vdev);
- flush_work(&vdev->virtio_bh_work);
- vqconfig = mic_vq_config(vdev->dd);
- for (i = 0; i < vdev->dd->num_vq; i++) {
- struct vop_vringh *vvr = &vdev->vvr[i];
- dma_unmap_single(&vpdev->dev,
- vvr->buf_da, VOP_INT_DMA_BUF_SIZE,
- DMA_BIDIRECTIONAL);
- free_pages((unsigned long)vvr->buf,
- get_order(VOP_INT_DMA_BUF_SIZE));
- vringh_kiov_cleanup(&vvr->riov);
- vringh_kiov_cleanup(&vvr->wiov);
- dma_unmap_single(&vpdev->dev, le64_to_cpu(vqconfig[i].address),
- vvr->vring.len, DMA_BIDIRECTIONAL);
- free_pages((unsigned long)vvr->vring.va,
- get_order(vvr->vring.len));
- }
- /*
- * 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();
- vdev->dd->type = -1;
- }
- /*
- * vop_sync_dma - Wrapper for synchronous DMAs.
- *
- * @dev - The address of the pointer to the device instance used
- * for DMA registration.
- * @dst - destination DMA address.
- * @src - source DMA address.
- * @len - size of the transfer.
- *
- * Return DMA_SUCCESS on success
- */
- static int vop_sync_dma(struct vop_vdev *vdev, dma_addr_t dst, dma_addr_t src,
- size_t len)
- {
- int err = 0;
- struct dma_device *ddev;
- struct dma_async_tx_descriptor *tx;
- struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev);
- struct dma_chan *vop_ch = vi->dma_ch;
- if (!vop_ch) {
- err = -EBUSY;
- goto error;
- }
- ddev = vop_ch->device;
- tx = ddev->device_prep_dma_memcpy(vop_ch, dst, src, len,
- DMA_PREP_FENCE);
- if (!tx) {
- err = -ENOMEM;
- goto error;
- } else {
- dma_cookie_t cookie;
- cookie = tx->tx_submit(tx);
- if (dma_submit_error(cookie)) {
- err = -ENOMEM;
- goto error;
- }
- dma_async_issue_pending(vop_ch);
- err = dma_sync_wait(vop_ch, cookie);
- }
- error:
- if (err)
- dev_err(&vi->vpdev->dev, "%s %d err %d\n",
- __func__, __LINE__, err);
- return err;
- }
- #define VOP_USE_DMA true
- /*
- * 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 vop_virtio_copy_to_user(struct vop_vdev *vdev, void __user *ubuf,
- size_t len, u64 daddr, size_t dlen,
- int vr_idx)
- {
- struct vop_device *vpdev = vdev->vpdev;
- void __iomem *dbuf = vpdev->hw_ops->ioremap(vpdev, daddr, len);
- struct vop_vringh *vvr = &vdev->vvr[vr_idx];
- struct vop_info *vi = dev_get_drvdata(&vpdev->dev);
- size_t dma_alignment = 1 << vi->dma_ch->device->copy_align;
- bool x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1);
- size_t dma_offset, partlen;
- int err;
- if (!VOP_USE_DMA) {
- if (copy_to_user(ubuf, (void __force *)dbuf, len)) {
- err = -EFAULT;
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, err);
- goto err;
- }
- vdev->in_bytes += len;
- err = 0;
- goto err;
- }
- dma_offset = daddr - round_down(daddr, dma_alignment);
- daddr -= dma_offset;
- len += dma_offset;
- /*
- * X100 uses DMA addresses as seen by the card so adding
- * the aperture base is not required for DMA. However x200
- * requires DMA addresses to be an offset into the bar so
- * add the aperture base for x200.
- */
- if (x200)
- daddr += vpdev->aper->pa;
- while (len) {
- partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE);
- err = vop_sync_dma(vdev, vvr->buf_da, daddr,
- ALIGN(partlen, dma_alignment));
- if (err) {
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, err);
- goto err;
- }
- if (copy_to_user(ubuf, vvr->buf + dma_offset,
- partlen - dma_offset)) {
- err = -EFAULT;
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, err);
- goto err;
- }
- daddr += partlen;
- ubuf += partlen;
- dbuf += partlen;
- vdev->in_bytes_dma += partlen;
- vdev->in_bytes += partlen;
- len -= partlen;
- dma_offset = 0;
- }
- err = 0;
- err:
- vpdev->hw_ops->iounmap(vpdev, dbuf);
- dev_dbg(vop_dev(vdev),
- "%s: ubuf %p dbuf %p len 0x%lx vr_idx 0x%x\n",
- __func__, ubuf, dbuf, len, vr_idx);
- 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 vop_virtio_copy_from_user(struct vop_vdev *vdev, void __user *ubuf,
- size_t len, u64 daddr, size_t dlen,
- int vr_idx)
- {
- struct vop_device *vpdev = vdev->vpdev;
- void __iomem *dbuf = vpdev->hw_ops->ioremap(vpdev, daddr, len);
- struct vop_vringh *vvr = &vdev->vvr[vr_idx];
- struct vop_info *vi = dev_get_drvdata(&vdev->vpdev->dev);
- size_t dma_alignment = 1 << vi->dma_ch->device->copy_align;
- bool x200 = is_dma_copy_aligned(vi->dma_ch->device, 1, 1, 1);
- size_t partlen;
- bool dma = VOP_USE_DMA;
- int err = 0;
- if (daddr & (dma_alignment - 1)) {
- vdev->tx_dst_unaligned += len;
- dma = false;
- } else if (ALIGN(len, dma_alignment) > dlen) {
- vdev->tx_len_unaligned += len;
- dma = false;
- }
- if (!dma)
- goto memcpy;
- /*
- * X100 uses DMA addresses as seen by the card so adding
- * the aperture base is not required for DMA. However x200
- * requires DMA addresses to be an offset into the bar so
- * add the aperture base for x200.
- */
- if (x200)
- daddr += vpdev->aper->pa;
- while (len) {
- partlen = min_t(size_t, len, VOP_INT_DMA_BUF_SIZE);
- if (copy_from_user(vvr->buf, ubuf, partlen)) {
- err = -EFAULT;
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, err);
- goto err;
- }
- err = vop_sync_dma(vdev, daddr, vvr->buf_da,
- ALIGN(partlen, dma_alignment));
- if (err) {
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, err);
- goto err;
- }
- daddr += partlen;
- ubuf += partlen;
- dbuf += partlen;
- vdev->out_bytes_dma += partlen;
- vdev->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;
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, err);
- goto err;
- }
- vdev->out_bytes += len;
- err = 0;
- err:
- vpdev->hw_ops->iounmap(vpdev, dbuf);
- dev_dbg(vop_dev(vdev),
- "%s: ubuf %p dbuf %p len 0x%lx vr_idx 0x%x\n",
- __func__, ubuf, dbuf, len, vr_idx);
- return err;
- }
- #define MIC_VRINGH_READ true
- /* Determine the total number of bytes consumed in a VRINGH KIOV */
- static inline u32 vop_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 vop_vringh_copy(struct vop_vdev *vdev, 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) {
- struct kvec *kiov = &iov->iov[iov->i];
- partlen = min(kiov->iov_len, len);
- if (read)
- ret = vop_virtio_copy_to_user(vdev, ubuf, partlen,
- (u64)kiov->iov_base,
- kiov->iov_len,
- vr_idx);
- else
- ret = vop_virtio_copy_from_user(vdev, ubuf, partlen,
- (u64)kiov->iov_base,
- kiov->iov_len,
- vr_idx);
- if (ret) {
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, ret);
- break;
- }
- len -= partlen;
- ubuf += partlen;
- tot_len += partlen;
- iov->consumed += partlen;
- kiov->iov_len -= partlen;
- kiov->iov_base += partlen;
- if (!kiov->iov_len) {
- /* Fix up old iov element then increment. */
- kiov->iov_len = iov->consumed;
- kiov->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 _vop_virtio_copy(struct vop_vdev *vdev, 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 vop_vringh *vvr = &vdev->vvr[copy->vr_idx];
- struct vringh_kiov *riov = &vvr->riov;
- struct vringh_kiov *wiov = &vvr->wiov;
- struct vringh *vrh = &vvr->vrh;
- u16 *head = &vvr->head;
- struct mic_vring *vr = &vvr->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(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, ret);
- break;
- }
- len = iov.iov_len;
- ubuf = iov.iov_base;
- }
- /* Issue all the read descriptors first */
- ret = vop_vringh_copy(vdev, riov, ubuf, len,
- MIC_VRINGH_READ, copy->vr_idx, &out_len);
- if (ret) {
- dev_err(vop_dev(vdev), "%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 = vop_vringh_copy(vdev, wiov, ubuf, len,
- !MIC_VRINGH_READ, copy->vr_idx, &out_len);
- if (ret) {
- dev_err(vop_dev(vdev), "%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 += vop_vringh_iov_consumed(riov);
- total += vop_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 vop_verify_copy_args(struct vop_vdev *vdev,
- struct mic_copy_desc *copy)
- {
- if (!vdev || copy->vr_idx >= vdev->dd->num_vq)
- return -EINVAL;
- return 0;
- }
- /* Copy a specified number of virtio descriptors in a chain */
- static int vop_virtio_copy_desc(struct vop_vdev *vdev,
- struct mic_copy_desc *copy)
- {
- int err;
- struct vop_vringh *vvr;
- err = vop_verify_copy_args(vdev, copy);
- if (err)
- return err;
- vvr = &vdev->vvr[copy->vr_idx];
- mutex_lock(&vvr->vr_mutex);
- if (!vop_vdevup(vdev)) {
- err = -ENODEV;
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, err);
- goto err;
- }
- err = _vop_virtio_copy(vdev, copy);
- if (err) {
- dev_err(vop_dev(vdev), "%s %d err %d\n",
- __func__, __LINE__, err);
- }
- err:
- mutex_unlock(&vvr->vr_mutex);
- return err;
- }
- static int vop_open(struct inode *inode, struct file *f)
- {
- struct vop_vdev *vdev;
- struct vop_info *vi = container_of(f->private_data,
- struct vop_info, miscdev);
- vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
- if (!vdev)
- return -ENOMEM;
- vdev->vi = vi;
- mutex_init(&vdev->vdev_mutex);
- f->private_data = vdev;
- init_completion(&vdev->destroy);
- complete(&vdev->destroy);
- return 0;
- }
- static int vop_release(struct inode *inode, struct file *f)
- {
- struct vop_vdev *vdev = f->private_data, *vdev_tmp;
- struct vop_info *vi = vdev->vi;
- struct list_head *pos, *tmp;
- bool found = false;
- mutex_lock(&vdev->vdev_mutex);
- if (vdev->deleted)
- goto unlock;
- mutex_lock(&vi->vop_mutex);
- list_for_each_safe(pos, tmp, &vi->vdev_list) {
- vdev_tmp = list_entry(pos, struct vop_vdev, list);
- if (vdev == vdev_tmp) {
- vop_virtio_del_device(vdev);
- list_del(pos);
- found = true;
- break;
- }
- }
- mutex_unlock(&vi->vop_mutex);
- unlock:
- mutex_unlock(&vdev->vdev_mutex);
- if (!found)
- wait_for_completion(&vdev->destroy);
- f->private_data = NULL;
- kfree(vdev);
- return 0;
- }
- static long vop_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
- {
- struct vop_vdev *vdev = f->private_data;
- struct vop_info *vi = vdev->vi;
- void __user *argp = (void __user *)arg;
- int ret;
- switch (cmd) {
- case MIC_VIRTIO_ADD_DEVICE:
- {
- struct mic_device_desc dd, *dd_config;
- if (copy_from_user(&dd, argp, sizeof(dd)))
- return -EFAULT;
- if (mic_aligned_desc_size(&dd) > MIC_MAX_DESC_BLK_SIZE ||
- dd.num_vq > MIC_MAX_VRINGS)
- return -EINVAL;
- dd_config = kzalloc(mic_desc_size(&dd), GFP_KERNEL);
- if (!dd_config)
- return -ENOMEM;
- if (copy_from_user(dd_config, argp, mic_desc_size(&dd))) {
- ret = -EFAULT;
- goto free_ret;
- }
- /* Ensure desc has not changed between the two reads */
- if (memcmp(&dd, dd_config, sizeof(dd))) {
- ret = -EINVAL;
- goto free_ret;
- }
- mutex_lock(&vdev->vdev_mutex);
- mutex_lock(&vi->vop_mutex);
- ret = vop_virtio_add_device(vdev, dd_config);
- if (ret)
- goto unlock_ret;
- list_add_tail(&vdev->list, &vi->vdev_list);
- unlock_ret:
- mutex_unlock(&vi->vop_mutex);
- mutex_unlock(&vdev->vdev_mutex);
- free_ret:
- kfree(dd_config);
- return ret;
- }
- case MIC_VIRTIO_COPY_DESC:
- {
- struct mic_copy_desc copy;
- mutex_lock(&vdev->vdev_mutex);
- ret = vop_vdev_inited(vdev);
- if (ret)
- goto _unlock_ret;
- if (copy_from_user(©, argp, sizeof(copy))) {
- ret = -EFAULT;
- goto _unlock_ret;
- }
- ret = vop_virtio_copy_desc(vdev, ©);
- if (ret < 0)
- goto _unlock_ret;
- if (copy_to_user(
- &((struct mic_copy_desc __user *)argp)->out_len,
- ©.out_len, sizeof(copy.out_len)))
- ret = -EFAULT;
- _unlock_ret:
- mutex_unlock(&vdev->vdev_mutex);
- return ret;
- }
- case MIC_VIRTIO_CONFIG_CHANGE:
- {
- void *buf;
- mutex_lock(&vdev->vdev_mutex);
- ret = vop_vdev_inited(vdev);
- if (ret)
- goto __unlock_ret;
- buf = kzalloc(vdev->dd->config_len, GFP_KERNEL);
- if (!buf) {
- ret = -ENOMEM;
- goto __unlock_ret;
- }
- if (copy_from_user(buf, argp, vdev->dd->config_len)) {
- ret = -EFAULT;
- goto done;
- }
- ret = vop_virtio_config_change(vdev, buf);
- done:
- kfree(buf);
- __unlock_ret:
- mutex_unlock(&vdev->vdev_mutex);
- return ret;
- }
- 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.
- */
- static unsigned int vop_poll(struct file *f, poll_table *wait)
- {
- struct vop_vdev *vdev = f->private_data;
- int mask = 0;
- mutex_lock(&vdev->vdev_mutex);
- if (vop_vdev_inited(vdev)) {
- mask = POLLERR;
- goto done;
- }
- poll_wait(f, &vdev->waitq, wait);
- if (vop_vdev_inited(vdev)) {
- mask = POLLERR;
- } else if (vdev->poll_wake) {
- vdev->poll_wake = 0;
- mask = POLLIN | POLLOUT;
- }
- done:
- mutex_unlock(&vdev->vdev_mutex);
- return mask;
- }
- static inline int
- vop_query_offset(struct vop_vdev *vdev, unsigned long offset,
- unsigned long *size, unsigned long *pa)
- {
- struct vop_device *vpdev = vdev->vpdev;
- 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(vpdev->hw_ops->get_dp(vpdev));
- *size = MIC_DP_SIZE;
- return 0;
- }
- for (i = 0; i < vdev->dd->num_vq; i++) {
- struct vop_vringh *vvr = &vdev->vvr[i];
- if (offset == start) {
- *pa = virt_to_phys(vvr->vring.va);
- *size = vvr->vring.len;
- return 0;
- }
- start += vvr->vring.len;
- }
- return -1;
- }
- /*
- * Maps the device page and virtio rings to user space for readonly access.
- */
- static int vop_mmap(struct file *f, struct vm_area_struct *vma)
- {
- struct vop_vdev *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 = vop_vdev_inited(vdev);
- if (err)
- goto ret;
- if (vma->vm_flags & VM_WRITE) {
- err = -EACCES;
- goto ret;
- }
- while (size_rem) {
- i = vop_query_offset(vdev, offset, &size, &pa);
- if (i < 0) {
- err = -EINVAL;
- goto ret;
- }
- err = remap_pfn_range(vma, vma->vm_start + offset,
- pa >> PAGE_SHIFT, size,
- vma->vm_page_prot);
- if (err)
- goto ret;
- size_rem -= size;
- offset += size;
- }
- ret:
- return err;
- }
- static const struct file_operations vop_fops = {
- .open = vop_open,
- .release = vop_release,
- .unlocked_ioctl = vop_ioctl,
- .poll = vop_poll,
- .mmap = vop_mmap,
- .owner = THIS_MODULE,
- };
- int vop_host_init(struct vop_info *vi)
- {
- int rc;
- struct miscdevice *mdev;
- struct vop_device *vpdev = vi->vpdev;
- INIT_LIST_HEAD(&vi->vdev_list);
- vi->dma_ch = vpdev->dma_ch;
- mdev = &vi->miscdev;
- mdev->minor = MISC_DYNAMIC_MINOR;
- snprintf(vi->name, sizeof(vi->name), "vop_virtio%d", vpdev->index);
- mdev->name = vi->name;
- mdev->fops = &vop_fops;
- mdev->parent = &vpdev->dev;
- rc = misc_register(mdev);
- if (rc)
- dev_err(&vpdev->dev, "%s failed rc %d\n", __func__, rc);
- return rc;
- }
- void vop_host_uninit(struct vop_info *vi)
- {
- struct list_head *pos, *tmp;
- struct vop_vdev *vdev;
- mutex_lock(&vi->vop_mutex);
- vop_virtio_reset_devices(vi);
- list_for_each_safe(pos, tmp, &vi->vdev_list) {
- vdev = list_entry(pos, struct vop_vdev, list);
- list_del(pos);
- reinit_completion(&vdev->destroy);
- mutex_unlock(&vi->vop_mutex);
- mutex_lock(&vdev->vdev_mutex);
- vop_virtio_del_device(vdev);
- vdev->deleted = true;
- mutex_unlock(&vdev->vdev_mutex);
- complete(&vdev->destroy);
- mutex_lock(&vi->vop_mutex);
- }
- mutex_unlock(&vi->vop_mutex);
- misc_deregister(&vi->miscdev);
- }
|