162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * The Virtio 9p transport driver 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This is a block based transport driver based on the lguest block driver 662306a36Sopenharmony_ci * code. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Based on virtio console driver 1162306a36Sopenharmony_ci * Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <linux/in.h> 1762306a36Sopenharmony_ci#include <linux/module.h> 1862306a36Sopenharmony_ci#include <linux/net.h> 1962306a36Sopenharmony_ci#include <linux/ipv6.h> 2062306a36Sopenharmony_ci#include <linux/errno.h> 2162306a36Sopenharmony_ci#include <linux/kernel.h> 2262306a36Sopenharmony_ci#include <linux/un.h> 2362306a36Sopenharmony_ci#include <linux/uaccess.h> 2462306a36Sopenharmony_ci#include <linux/inet.h> 2562306a36Sopenharmony_ci#include <linux/file.h> 2662306a36Sopenharmony_ci#include <linux/highmem.h> 2762306a36Sopenharmony_ci#include <linux/slab.h> 2862306a36Sopenharmony_ci#include <net/9p/9p.h> 2962306a36Sopenharmony_ci#include <linux/parser.h> 3062306a36Sopenharmony_ci#include <net/9p/client.h> 3162306a36Sopenharmony_ci#include <net/9p/transport.h> 3262306a36Sopenharmony_ci#include <linux/scatterlist.h> 3362306a36Sopenharmony_ci#include <linux/swap.h> 3462306a36Sopenharmony_ci#include <linux/virtio.h> 3562306a36Sopenharmony_ci#include <linux/virtio_9p.h> 3662306a36Sopenharmony_ci#include "trans_common.h" 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#define VIRTQUEUE_NUM 128 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci/* a single mutex to manage channel initialization and attachment */ 4162306a36Sopenharmony_cistatic DEFINE_MUTEX(virtio_9p_lock); 4262306a36Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(vp_wq); 4362306a36Sopenharmony_cistatic atomic_t vp_pinned = ATOMIC_INIT(0); 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_ci/** 4662306a36Sopenharmony_ci * struct virtio_chan - per-instance transport information 4762306a36Sopenharmony_ci * @inuse: whether the channel is in use 4862306a36Sopenharmony_ci * @lock: protects multiple elements within this structure 4962306a36Sopenharmony_ci * @client: client instance 5062306a36Sopenharmony_ci * @vdev: virtio dev associated with this channel 5162306a36Sopenharmony_ci * @vq: virtio queue associated with this channel 5262306a36Sopenharmony_ci * @ring_bufs_avail: flag to indicate there is some available in the ring buf 5362306a36Sopenharmony_ci * @vc_wq: wait queue for waiting for thing to be added to ring buf 5462306a36Sopenharmony_ci * @p9_max_pages: maximum number of pinned pages 5562306a36Sopenharmony_ci * @sg: scatter gather list which is used to pack a request (protected?) 5662306a36Sopenharmony_ci * @chan_list: linked list of channels 5762306a36Sopenharmony_ci * 5862306a36Sopenharmony_ci * We keep all per-channel information in a structure. 5962306a36Sopenharmony_ci * This structure is allocated within the devices dev->mem space. 6062306a36Sopenharmony_ci * A pointer to the structure will get put in the transport private. 6162306a36Sopenharmony_ci * 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_cistruct virtio_chan { 6562306a36Sopenharmony_ci bool inuse; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci spinlock_t lock; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci struct p9_client *client; 7062306a36Sopenharmony_ci struct virtio_device *vdev; 7162306a36Sopenharmony_ci struct virtqueue *vq; 7262306a36Sopenharmony_ci int ring_bufs_avail; 7362306a36Sopenharmony_ci wait_queue_head_t *vc_wq; 7462306a36Sopenharmony_ci /* This is global limit. Since we don't have a global structure, 7562306a36Sopenharmony_ci * will be placing it in each channel. 7662306a36Sopenharmony_ci */ 7762306a36Sopenharmony_ci unsigned long p9_max_pages; 7862306a36Sopenharmony_ci /* Scatterlist: can be too big for stack. */ 7962306a36Sopenharmony_ci struct scatterlist sg[VIRTQUEUE_NUM]; 8062306a36Sopenharmony_ci /** 8162306a36Sopenharmony_ci * @tag: name to identify a mount null terminated 8262306a36Sopenharmony_ci */ 8362306a36Sopenharmony_ci char *tag; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci struct list_head chan_list; 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic struct list_head virtio_chan_list; 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci/* How many bytes left in this page. */ 9162306a36Sopenharmony_cistatic unsigned int rest_of_page(void *data) 9262306a36Sopenharmony_ci{ 9362306a36Sopenharmony_ci return PAGE_SIZE - offset_in_page(data); 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci/** 9762306a36Sopenharmony_ci * p9_virtio_close - reclaim resources of a channel 9862306a36Sopenharmony_ci * @client: client instance 9962306a36Sopenharmony_ci * 10062306a36Sopenharmony_ci * This reclaims a channel by freeing its resources and 10162306a36Sopenharmony_ci * resetting its inuse flag. 10262306a36Sopenharmony_ci * 10362306a36Sopenharmony_ci */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cistatic void p9_virtio_close(struct p9_client *client) 10662306a36Sopenharmony_ci{ 10762306a36Sopenharmony_ci struct virtio_chan *chan = client->trans; 10862306a36Sopenharmony_ci 10962306a36Sopenharmony_ci mutex_lock(&virtio_9p_lock); 11062306a36Sopenharmony_ci if (chan) 11162306a36Sopenharmony_ci chan->inuse = false; 11262306a36Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 11362306a36Sopenharmony_ci} 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/** 11662306a36Sopenharmony_ci * req_done - callback which signals activity from the server 11762306a36Sopenharmony_ci * @vq: virtio queue activity was received on 11862306a36Sopenharmony_ci * 11962306a36Sopenharmony_ci * This notifies us that the server has triggered some activity 12062306a36Sopenharmony_ci * on the virtio channel - most likely a response to request we 12162306a36Sopenharmony_ci * sent. Figure out which requests now have responses and wake up 12262306a36Sopenharmony_ci * those threads. 12362306a36Sopenharmony_ci * 12462306a36Sopenharmony_ci * Bugs: could do with some additional sanity checking, but appears to work. 12562306a36Sopenharmony_ci * 12662306a36Sopenharmony_ci */ 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_cistatic void req_done(struct virtqueue *vq) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct virtio_chan *chan = vq->vdev->priv; 13162306a36Sopenharmony_ci unsigned int len; 13262306a36Sopenharmony_ci struct p9_req_t *req; 13362306a36Sopenharmony_ci bool need_wakeup = false; 13462306a36Sopenharmony_ci unsigned long flags; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, ": request done\n"); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 13962306a36Sopenharmony_ci while ((req = virtqueue_get_buf(chan->vq, &len)) != NULL) { 14062306a36Sopenharmony_ci if (!chan->ring_bufs_avail) { 14162306a36Sopenharmony_ci chan->ring_bufs_avail = 1; 14262306a36Sopenharmony_ci need_wakeup = true; 14362306a36Sopenharmony_ci } 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (len) { 14662306a36Sopenharmony_ci req->rc.size = len; 14762306a36Sopenharmony_ci p9_client_cb(chan->client, req, REQ_STATUS_RCVD); 14862306a36Sopenharmony_ci } 14962306a36Sopenharmony_ci } 15062306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 15162306a36Sopenharmony_ci /* Wakeup if anyone waiting for VirtIO ring space. */ 15262306a36Sopenharmony_ci if (need_wakeup) 15362306a36Sopenharmony_ci wake_up(chan->vc_wq); 15462306a36Sopenharmony_ci} 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci/** 15762306a36Sopenharmony_ci * pack_sg_list - pack a scatter gather list from a linear buffer 15862306a36Sopenharmony_ci * @sg: scatter/gather list to pack into 15962306a36Sopenharmony_ci * @start: which segment of the sg_list to start at 16062306a36Sopenharmony_ci * @limit: maximum segment to pack data to 16162306a36Sopenharmony_ci * @data: data to pack into scatter/gather list 16262306a36Sopenharmony_ci * @count: amount of data to pack into the scatter/gather list 16362306a36Sopenharmony_ci * 16462306a36Sopenharmony_ci * sg_lists have multiple segments of various sizes. This will pack 16562306a36Sopenharmony_ci * arbitrary data into an existing scatter gather list, segmenting the 16662306a36Sopenharmony_ci * data as necessary within constraints. 16762306a36Sopenharmony_ci * 16862306a36Sopenharmony_ci */ 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_cistatic int pack_sg_list(struct scatterlist *sg, int start, 17162306a36Sopenharmony_ci int limit, char *data, int count) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci int s; 17462306a36Sopenharmony_ci int index = start; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_ci while (count) { 17762306a36Sopenharmony_ci s = rest_of_page(data); 17862306a36Sopenharmony_ci if (s > count) 17962306a36Sopenharmony_ci s = count; 18062306a36Sopenharmony_ci BUG_ON(index >= limit); 18162306a36Sopenharmony_ci /* Make sure we don't terminate early. */ 18262306a36Sopenharmony_ci sg_unmark_end(&sg[index]); 18362306a36Sopenharmony_ci sg_set_buf(&sg[index++], data, s); 18462306a36Sopenharmony_ci count -= s; 18562306a36Sopenharmony_ci data += s; 18662306a36Sopenharmony_ci } 18762306a36Sopenharmony_ci if (index-start) 18862306a36Sopenharmony_ci sg_mark_end(&sg[index - 1]); 18962306a36Sopenharmony_ci return index-start; 19062306a36Sopenharmony_ci} 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci/* We don't currently allow canceling of virtio requests */ 19362306a36Sopenharmony_cistatic int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) 19462306a36Sopenharmony_ci{ 19562306a36Sopenharmony_ci return 1; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* Reply won't come, so drop req ref */ 19962306a36Sopenharmony_cistatic int p9_virtio_cancelled(struct p9_client *client, struct p9_req_t *req) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci p9_req_put(client, req); 20262306a36Sopenharmony_ci return 0; 20362306a36Sopenharmony_ci} 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci/** 20662306a36Sopenharmony_ci * pack_sg_list_p - Just like pack_sg_list. Instead of taking a buffer, 20762306a36Sopenharmony_ci * this takes a list of pages. 20862306a36Sopenharmony_ci * @sg: scatter/gather list to pack into 20962306a36Sopenharmony_ci * @start: which segment of the sg_list to start at 21062306a36Sopenharmony_ci * @limit: maximum number of pages in sg list. 21162306a36Sopenharmony_ci * @pdata: a list of pages to add into sg. 21262306a36Sopenharmony_ci * @nr_pages: number of pages to pack into the scatter/gather list 21362306a36Sopenharmony_ci * @offs: amount of data in the beginning of first page _not_ to pack 21462306a36Sopenharmony_ci * @count: amount of data to pack into the scatter/gather list 21562306a36Sopenharmony_ci */ 21662306a36Sopenharmony_cistatic int 21762306a36Sopenharmony_cipack_sg_list_p(struct scatterlist *sg, int start, int limit, 21862306a36Sopenharmony_ci struct page **pdata, int nr_pages, size_t offs, int count) 21962306a36Sopenharmony_ci{ 22062306a36Sopenharmony_ci int i = 0, s; 22162306a36Sopenharmony_ci int data_off = offs; 22262306a36Sopenharmony_ci int index = start; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci BUG_ON(nr_pages > (limit - start)); 22562306a36Sopenharmony_ci /* 22662306a36Sopenharmony_ci * if the first page doesn't start at 22762306a36Sopenharmony_ci * page boundary find the offset 22862306a36Sopenharmony_ci */ 22962306a36Sopenharmony_ci while (nr_pages) { 23062306a36Sopenharmony_ci s = PAGE_SIZE - data_off; 23162306a36Sopenharmony_ci if (s > count) 23262306a36Sopenharmony_ci s = count; 23362306a36Sopenharmony_ci BUG_ON(index >= limit); 23462306a36Sopenharmony_ci /* Make sure we don't terminate early. */ 23562306a36Sopenharmony_ci sg_unmark_end(&sg[index]); 23662306a36Sopenharmony_ci sg_set_page(&sg[index++], pdata[i++], s, data_off); 23762306a36Sopenharmony_ci data_off = 0; 23862306a36Sopenharmony_ci count -= s; 23962306a36Sopenharmony_ci nr_pages--; 24062306a36Sopenharmony_ci } 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (index-start) 24362306a36Sopenharmony_ci sg_mark_end(&sg[index - 1]); 24462306a36Sopenharmony_ci return index - start; 24562306a36Sopenharmony_ci} 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci/** 24862306a36Sopenharmony_ci * p9_virtio_request - issue a request 24962306a36Sopenharmony_ci * @client: client instance issuing the request 25062306a36Sopenharmony_ci * @req: request to be issued 25162306a36Sopenharmony_ci * 25262306a36Sopenharmony_ci */ 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int 25562306a36Sopenharmony_cip9_virtio_request(struct p9_client *client, struct p9_req_t *req) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci int err; 25862306a36Sopenharmony_ci int in, out, out_sgs, in_sgs; 25962306a36Sopenharmony_ci unsigned long flags; 26062306a36Sopenharmony_ci struct virtio_chan *chan = client->trans; 26162306a36Sopenharmony_ci struct scatterlist *sgs[2]; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "9p debug: virtio request\n"); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci WRITE_ONCE(req->status, REQ_STATUS_SENT); 26662306a36Sopenharmony_cireq_retry: 26762306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 26862306a36Sopenharmony_ci 26962306a36Sopenharmony_ci out_sgs = in_sgs = 0; 27062306a36Sopenharmony_ci /* Handle out VirtIO ring buffers */ 27162306a36Sopenharmony_ci out = pack_sg_list(chan->sg, 0, 27262306a36Sopenharmony_ci VIRTQUEUE_NUM, req->tc.sdata, req->tc.size); 27362306a36Sopenharmony_ci if (out) 27462306a36Sopenharmony_ci sgs[out_sgs++] = chan->sg; 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_ci in = pack_sg_list(chan->sg, out, 27762306a36Sopenharmony_ci VIRTQUEUE_NUM, req->rc.sdata, req->rc.capacity); 27862306a36Sopenharmony_ci if (in) 27962306a36Sopenharmony_ci sgs[out_sgs + in_sgs++] = chan->sg + out; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req, 28262306a36Sopenharmony_ci GFP_ATOMIC); 28362306a36Sopenharmony_ci if (err < 0) { 28462306a36Sopenharmony_ci if (err == -ENOSPC) { 28562306a36Sopenharmony_ci chan->ring_bufs_avail = 0; 28662306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 28762306a36Sopenharmony_ci err = wait_event_killable(*chan->vc_wq, 28862306a36Sopenharmony_ci chan->ring_bufs_avail); 28962306a36Sopenharmony_ci if (err == -ERESTARTSYS) 29062306a36Sopenharmony_ci return err; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n"); 29362306a36Sopenharmony_ci goto req_retry; 29462306a36Sopenharmony_ci } else { 29562306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 29662306a36Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, 29762306a36Sopenharmony_ci "virtio rpc add_sgs returned failure\n"); 29862306a36Sopenharmony_ci return -EIO; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci } 30162306a36Sopenharmony_ci virtqueue_kick(chan->vq); 30262306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n"); 30562306a36Sopenharmony_ci return 0; 30662306a36Sopenharmony_ci} 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_cistatic int p9_get_mapped_pages(struct virtio_chan *chan, 30962306a36Sopenharmony_ci struct page ***pages, 31062306a36Sopenharmony_ci struct iov_iter *data, 31162306a36Sopenharmony_ci int count, 31262306a36Sopenharmony_ci size_t *offs, 31362306a36Sopenharmony_ci int *need_drop) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci int nr_pages; 31662306a36Sopenharmony_ci int err; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (!iov_iter_count(data)) 31962306a36Sopenharmony_ci return 0; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci if (!iov_iter_is_kvec(data)) { 32262306a36Sopenharmony_ci int n; 32362306a36Sopenharmony_ci /* 32462306a36Sopenharmony_ci * We allow only p9_max_pages pinned. We wait for the 32562306a36Sopenharmony_ci * Other zc request to finish here 32662306a36Sopenharmony_ci */ 32762306a36Sopenharmony_ci if (atomic_read(&vp_pinned) >= chan->p9_max_pages) { 32862306a36Sopenharmony_ci err = wait_event_killable(vp_wq, 32962306a36Sopenharmony_ci (atomic_read(&vp_pinned) < chan->p9_max_pages)); 33062306a36Sopenharmony_ci if (err == -ERESTARTSYS) 33162306a36Sopenharmony_ci return err; 33262306a36Sopenharmony_ci } 33362306a36Sopenharmony_ci n = iov_iter_get_pages_alloc2(data, pages, count, offs); 33462306a36Sopenharmony_ci if (n < 0) 33562306a36Sopenharmony_ci return n; 33662306a36Sopenharmony_ci *need_drop = 1; 33762306a36Sopenharmony_ci nr_pages = DIV_ROUND_UP(n + *offs, PAGE_SIZE); 33862306a36Sopenharmony_ci atomic_add(nr_pages, &vp_pinned); 33962306a36Sopenharmony_ci return n; 34062306a36Sopenharmony_ci } else { 34162306a36Sopenharmony_ci /* kernel buffer, no need to pin pages */ 34262306a36Sopenharmony_ci int index; 34362306a36Sopenharmony_ci size_t len; 34462306a36Sopenharmony_ci void *p; 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci /* we'd already checked that it's non-empty */ 34762306a36Sopenharmony_ci while (1) { 34862306a36Sopenharmony_ci len = iov_iter_single_seg_count(data); 34962306a36Sopenharmony_ci if (likely(len)) { 35062306a36Sopenharmony_ci p = data->kvec->iov_base + data->iov_offset; 35162306a36Sopenharmony_ci break; 35262306a36Sopenharmony_ci } 35362306a36Sopenharmony_ci iov_iter_advance(data, 0); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci if (len > count) 35662306a36Sopenharmony_ci len = count; 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci nr_pages = DIV_ROUND_UP((unsigned long)p + len, PAGE_SIZE) - 35962306a36Sopenharmony_ci (unsigned long)p / PAGE_SIZE; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci *pages = kmalloc_array(nr_pages, sizeof(struct page *), 36262306a36Sopenharmony_ci GFP_NOFS); 36362306a36Sopenharmony_ci if (!*pages) 36462306a36Sopenharmony_ci return -ENOMEM; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci *need_drop = 0; 36762306a36Sopenharmony_ci p -= (*offs = offset_in_page(p)); 36862306a36Sopenharmony_ci for (index = 0; index < nr_pages; index++) { 36962306a36Sopenharmony_ci if (is_vmalloc_addr(p)) 37062306a36Sopenharmony_ci (*pages)[index] = vmalloc_to_page(p); 37162306a36Sopenharmony_ci else 37262306a36Sopenharmony_ci (*pages)[index] = kmap_to_page(p); 37362306a36Sopenharmony_ci p += PAGE_SIZE; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci iov_iter_advance(data, len); 37662306a36Sopenharmony_ci return len; 37762306a36Sopenharmony_ci } 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_cistatic void handle_rerror(struct p9_req_t *req, int in_hdr_len, 38162306a36Sopenharmony_ci size_t offs, struct page **pages) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci unsigned size, n; 38462306a36Sopenharmony_ci void *to = req->rc.sdata + in_hdr_len; 38562306a36Sopenharmony_ci 38662306a36Sopenharmony_ci // Fits entirely into the static data? Nothing to do. 38762306a36Sopenharmony_ci if (req->rc.size < in_hdr_len || !pages) 38862306a36Sopenharmony_ci return; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci // Really long error message? Tough, truncate the reply. Might get 39162306a36Sopenharmony_ci // rejected (we can't be arsed to adjust the size encoded in header, 39262306a36Sopenharmony_ci // or string size for that matter), but it wouldn't be anything valid 39362306a36Sopenharmony_ci // anyway. 39462306a36Sopenharmony_ci if (unlikely(req->rc.size > P9_ZC_HDR_SZ)) 39562306a36Sopenharmony_ci req->rc.size = P9_ZC_HDR_SZ; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci // data won't span more than two pages 39862306a36Sopenharmony_ci size = req->rc.size - in_hdr_len; 39962306a36Sopenharmony_ci n = PAGE_SIZE - offs; 40062306a36Sopenharmony_ci if (size > n) { 40162306a36Sopenharmony_ci memcpy_from_page(to, *pages++, offs, n); 40262306a36Sopenharmony_ci offs = 0; 40362306a36Sopenharmony_ci to += n; 40462306a36Sopenharmony_ci size -= n; 40562306a36Sopenharmony_ci } 40662306a36Sopenharmony_ci memcpy_from_page(to, *pages, offs, size); 40762306a36Sopenharmony_ci} 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci/** 41062306a36Sopenharmony_ci * p9_virtio_zc_request - issue a zero copy request 41162306a36Sopenharmony_ci * @client: client instance issuing the request 41262306a36Sopenharmony_ci * @req: request to be issued 41362306a36Sopenharmony_ci * @uidata: user buffer that should be used for zero copy read 41462306a36Sopenharmony_ci * @uodata: user buffer that should be used for zero copy write 41562306a36Sopenharmony_ci * @inlen: read buffer size 41662306a36Sopenharmony_ci * @outlen: write buffer size 41762306a36Sopenharmony_ci * @in_hdr_len: reader header size, This is the size of response protocol data 41862306a36Sopenharmony_ci * 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_cistatic int 42162306a36Sopenharmony_cip9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, 42262306a36Sopenharmony_ci struct iov_iter *uidata, struct iov_iter *uodata, 42362306a36Sopenharmony_ci int inlen, int outlen, int in_hdr_len) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci int in, out, err, out_sgs, in_sgs; 42662306a36Sopenharmony_ci unsigned long flags; 42762306a36Sopenharmony_ci int in_nr_pages = 0, out_nr_pages = 0; 42862306a36Sopenharmony_ci struct page **in_pages = NULL, **out_pages = NULL; 42962306a36Sopenharmony_ci struct virtio_chan *chan = client->trans; 43062306a36Sopenharmony_ci struct scatterlist *sgs[4]; 43162306a36Sopenharmony_ci size_t offs = 0; 43262306a36Sopenharmony_ci int need_drop = 0; 43362306a36Sopenharmony_ci int kicked = 0; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "virtio request\n"); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (uodata) { 43862306a36Sopenharmony_ci __le32 sz; 43962306a36Sopenharmony_ci int n = p9_get_mapped_pages(chan, &out_pages, uodata, 44062306a36Sopenharmony_ci outlen, &offs, &need_drop); 44162306a36Sopenharmony_ci if (n < 0) { 44262306a36Sopenharmony_ci err = n; 44362306a36Sopenharmony_ci goto err_out; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci out_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE); 44662306a36Sopenharmony_ci if (n != outlen) { 44762306a36Sopenharmony_ci __le32 v = cpu_to_le32(n); 44862306a36Sopenharmony_ci memcpy(&req->tc.sdata[req->tc.size - 4], &v, 4); 44962306a36Sopenharmony_ci outlen = n; 45062306a36Sopenharmony_ci } 45162306a36Sopenharmony_ci /* The size field of the message must include the length of the 45262306a36Sopenharmony_ci * header and the length of the data. We didn't actually know 45362306a36Sopenharmony_ci * the length of the data until this point so add it in now. 45462306a36Sopenharmony_ci */ 45562306a36Sopenharmony_ci sz = cpu_to_le32(req->tc.size + outlen); 45662306a36Sopenharmony_ci memcpy(&req->tc.sdata[0], &sz, sizeof(sz)); 45762306a36Sopenharmony_ci } else if (uidata) { 45862306a36Sopenharmony_ci int n = p9_get_mapped_pages(chan, &in_pages, uidata, 45962306a36Sopenharmony_ci inlen, &offs, &need_drop); 46062306a36Sopenharmony_ci if (n < 0) { 46162306a36Sopenharmony_ci err = n; 46262306a36Sopenharmony_ci goto err_out; 46362306a36Sopenharmony_ci } 46462306a36Sopenharmony_ci in_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE); 46562306a36Sopenharmony_ci if (n != inlen) { 46662306a36Sopenharmony_ci __le32 v = cpu_to_le32(n); 46762306a36Sopenharmony_ci memcpy(&req->tc.sdata[req->tc.size - 4], &v, 4); 46862306a36Sopenharmony_ci inlen = n; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci WRITE_ONCE(req->status, REQ_STATUS_SENT); 47262306a36Sopenharmony_cireq_retry_pinned: 47362306a36Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci out_sgs = in_sgs = 0; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci /* out data */ 47862306a36Sopenharmony_ci out = pack_sg_list(chan->sg, 0, 47962306a36Sopenharmony_ci VIRTQUEUE_NUM, req->tc.sdata, req->tc.size); 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_ci if (out) 48262306a36Sopenharmony_ci sgs[out_sgs++] = chan->sg; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci if (out_pages) { 48562306a36Sopenharmony_ci sgs[out_sgs++] = chan->sg + out; 48662306a36Sopenharmony_ci out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM, 48762306a36Sopenharmony_ci out_pages, out_nr_pages, offs, outlen); 48862306a36Sopenharmony_ci } 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci /* 49162306a36Sopenharmony_ci * Take care of in data 49262306a36Sopenharmony_ci * For example TREAD have 11. 49362306a36Sopenharmony_ci * 11 is the read/write header = PDU Header(7) + IO Size (4). 49462306a36Sopenharmony_ci * Arrange in such a way that server places header in the 49562306a36Sopenharmony_ci * allocated memory and payload onto the user buffer. 49662306a36Sopenharmony_ci */ 49762306a36Sopenharmony_ci in = pack_sg_list(chan->sg, out, 49862306a36Sopenharmony_ci VIRTQUEUE_NUM, req->rc.sdata, in_hdr_len); 49962306a36Sopenharmony_ci if (in) 50062306a36Sopenharmony_ci sgs[out_sgs + in_sgs++] = chan->sg + out; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci if (in_pages) { 50362306a36Sopenharmony_ci sgs[out_sgs + in_sgs++] = chan->sg + out + in; 50462306a36Sopenharmony_ci pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM, 50562306a36Sopenharmony_ci in_pages, in_nr_pages, offs, inlen); 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs)); 50962306a36Sopenharmony_ci err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req, 51062306a36Sopenharmony_ci GFP_ATOMIC); 51162306a36Sopenharmony_ci if (err < 0) { 51262306a36Sopenharmony_ci if (err == -ENOSPC) { 51362306a36Sopenharmony_ci chan->ring_bufs_avail = 0; 51462306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 51562306a36Sopenharmony_ci err = wait_event_killable(*chan->vc_wq, 51662306a36Sopenharmony_ci chan->ring_bufs_avail); 51762306a36Sopenharmony_ci if (err == -ERESTARTSYS) 51862306a36Sopenharmony_ci goto err_out; 51962306a36Sopenharmony_ci 52062306a36Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n"); 52162306a36Sopenharmony_ci goto req_retry_pinned; 52262306a36Sopenharmony_ci } else { 52362306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 52462306a36Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, 52562306a36Sopenharmony_ci "virtio rpc add_sgs returned failure\n"); 52662306a36Sopenharmony_ci err = -EIO; 52762306a36Sopenharmony_ci goto err_out; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci virtqueue_kick(chan->vq); 53162306a36Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 53262306a36Sopenharmony_ci kicked = 1; 53362306a36Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n"); 53462306a36Sopenharmony_ci err = wait_event_killable(req->wq, 53562306a36Sopenharmony_ci READ_ONCE(req->status) >= REQ_STATUS_RCVD); 53662306a36Sopenharmony_ci // RERROR needs reply (== error string) in static data 53762306a36Sopenharmony_ci if (READ_ONCE(req->status) == REQ_STATUS_RCVD && 53862306a36Sopenharmony_ci unlikely(req->rc.sdata[4] == P9_RERROR)) 53962306a36Sopenharmony_ci handle_rerror(req, in_hdr_len, offs, in_pages); 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci /* 54262306a36Sopenharmony_ci * Non kernel buffers are pinned, unpin them 54362306a36Sopenharmony_ci */ 54462306a36Sopenharmony_cierr_out: 54562306a36Sopenharmony_ci if (need_drop) { 54662306a36Sopenharmony_ci if (in_pages) { 54762306a36Sopenharmony_ci p9_release_pages(in_pages, in_nr_pages); 54862306a36Sopenharmony_ci atomic_sub(in_nr_pages, &vp_pinned); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci if (out_pages) { 55162306a36Sopenharmony_ci p9_release_pages(out_pages, out_nr_pages); 55262306a36Sopenharmony_ci atomic_sub(out_nr_pages, &vp_pinned); 55362306a36Sopenharmony_ci } 55462306a36Sopenharmony_ci /* wakeup anybody waiting for slots to pin pages */ 55562306a36Sopenharmony_ci wake_up(&vp_wq); 55662306a36Sopenharmony_ci } 55762306a36Sopenharmony_ci kvfree(in_pages); 55862306a36Sopenharmony_ci kvfree(out_pages); 55962306a36Sopenharmony_ci if (!kicked) { 56062306a36Sopenharmony_ci /* reply won't come */ 56162306a36Sopenharmony_ci p9_req_put(client, req); 56262306a36Sopenharmony_ci } 56362306a36Sopenharmony_ci return err; 56462306a36Sopenharmony_ci} 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_cistatic ssize_t p9_mount_tag_show(struct device *dev, 56762306a36Sopenharmony_ci struct device_attribute *attr, char *buf) 56862306a36Sopenharmony_ci{ 56962306a36Sopenharmony_ci struct virtio_chan *chan; 57062306a36Sopenharmony_ci struct virtio_device *vdev; 57162306a36Sopenharmony_ci int tag_len; 57262306a36Sopenharmony_ci 57362306a36Sopenharmony_ci vdev = dev_to_virtio(dev); 57462306a36Sopenharmony_ci chan = vdev->priv; 57562306a36Sopenharmony_ci tag_len = strlen(chan->tag); 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci memcpy(buf, chan->tag, tag_len + 1); 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci return tag_len + 1; 58062306a36Sopenharmony_ci} 58162306a36Sopenharmony_ci 58262306a36Sopenharmony_cistatic DEVICE_ATTR(mount_tag, 0444, p9_mount_tag_show, NULL); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci/** 58562306a36Sopenharmony_ci * p9_virtio_probe - probe for existence of 9P virtio channels 58662306a36Sopenharmony_ci * @vdev: virtio device to probe 58762306a36Sopenharmony_ci * 58862306a36Sopenharmony_ci * This probes for existing virtio channels. 58962306a36Sopenharmony_ci * 59062306a36Sopenharmony_ci */ 59162306a36Sopenharmony_ci 59262306a36Sopenharmony_cistatic int p9_virtio_probe(struct virtio_device *vdev) 59362306a36Sopenharmony_ci{ 59462306a36Sopenharmony_ci __u16 tag_len; 59562306a36Sopenharmony_ci char *tag; 59662306a36Sopenharmony_ci int err; 59762306a36Sopenharmony_ci struct virtio_chan *chan; 59862306a36Sopenharmony_ci 59962306a36Sopenharmony_ci if (!vdev->config->get) { 60062306a36Sopenharmony_ci dev_err(&vdev->dev, "%s failure: config access disabled\n", 60162306a36Sopenharmony_ci __func__); 60262306a36Sopenharmony_ci return -EINVAL; 60362306a36Sopenharmony_ci } 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci chan = kmalloc(sizeof(struct virtio_chan), GFP_KERNEL); 60662306a36Sopenharmony_ci if (!chan) { 60762306a36Sopenharmony_ci pr_err("Failed to allocate virtio 9P channel\n"); 60862306a36Sopenharmony_ci err = -ENOMEM; 60962306a36Sopenharmony_ci goto fail; 61062306a36Sopenharmony_ci } 61162306a36Sopenharmony_ci 61262306a36Sopenharmony_ci chan->vdev = vdev; 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci /* We expect one virtqueue, for requests. */ 61562306a36Sopenharmony_ci chan->vq = virtio_find_single_vq(vdev, req_done, "requests"); 61662306a36Sopenharmony_ci if (IS_ERR(chan->vq)) { 61762306a36Sopenharmony_ci err = PTR_ERR(chan->vq); 61862306a36Sopenharmony_ci goto out_free_chan; 61962306a36Sopenharmony_ci } 62062306a36Sopenharmony_ci chan->vq->vdev->priv = chan; 62162306a36Sopenharmony_ci spin_lock_init(&chan->lock); 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci sg_init_table(chan->sg, VIRTQUEUE_NUM); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci chan->inuse = false; 62662306a36Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG)) { 62762306a36Sopenharmony_ci virtio_cread(vdev, struct virtio_9p_config, tag_len, &tag_len); 62862306a36Sopenharmony_ci } else { 62962306a36Sopenharmony_ci err = -EINVAL; 63062306a36Sopenharmony_ci goto out_free_vq; 63162306a36Sopenharmony_ci } 63262306a36Sopenharmony_ci tag = kzalloc(tag_len + 1, GFP_KERNEL); 63362306a36Sopenharmony_ci if (!tag) { 63462306a36Sopenharmony_ci err = -ENOMEM; 63562306a36Sopenharmony_ci goto out_free_vq; 63662306a36Sopenharmony_ci } 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci virtio_cread_bytes(vdev, offsetof(struct virtio_9p_config, tag), 63962306a36Sopenharmony_ci tag, tag_len); 64062306a36Sopenharmony_ci chan->tag = tag; 64162306a36Sopenharmony_ci err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr); 64262306a36Sopenharmony_ci if (err) { 64362306a36Sopenharmony_ci goto out_free_tag; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci chan->vc_wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); 64662306a36Sopenharmony_ci if (!chan->vc_wq) { 64762306a36Sopenharmony_ci err = -ENOMEM; 64862306a36Sopenharmony_ci goto out_remove_file; 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci init_waitqueue_head(chan->vc_wq); 65162306a36Sopenharmony_ci chan->ring_bufs_avail = 1; 65262306a36Sopenharmony_ci /* Ceiling limit to avoid denial of service attacks */ 65362306a36Sopenharmony_ci chan->p9_max_pages = nr_free_buffer_pages()/4; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci virtio_device_ready(vdev); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci mutex_lock(&virtio_9p_lock); 65862306a36Sopenharmony_ci list_add_tail(&chan->chan_list, &virtio_chan_list); 65962306a36Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci /* Let udev rules use the new mount_tag attribute. */ 66262306a36Sopenharmony_ci kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE); 66362306a36Sopenharmony_ci 66462306a36Sopenharmony_ci return 0; 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ciout_remove_file: 66762306a36Sopenharmony_ci sysfs_remove_file(&vdev->dev.kobj, &dev_attr_mount_tag.attr); 66862306a36Sopenharmony_ciout_free_tag: 66962306a36Sopenharmony_ci kfree(tag); 67062306a36Sopenharmony_ciout_free_vq: 67162306a36Sopenharmony_ci vdev->config->del_vqs(vdev); 67262306a36Sopenharmony_ciout_free_chan: 67362306a36Sopenharmony_ci kfree(chan); 67462306a36Sopenharmony_cifail: 67562306a36Sopenharmony_ci return err; 67662306a36Sopenharmony_ci} 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci/** 68062306a36Sopenharmony_ci * p9_virtio_create - allocate a new virtio channel 68162306a36Sopenharmony_ci * @client: client instance invoking this transport 68262306a36Sopenharmony_ci * @devname: string identifying the channel to connect to (unused) 68362306a36Sopenharmony_ci * @args: args passed from sys_mount() for per-transport options (unused) 68462306a36Sopenharmony_ci * 68562306a36Sopenharmony_ci * This sets up a transport channel for 9p communication. Right now 68662306a36Sopenharmony_ci * we only match the first available channel, but eventually we could look up 68762306a36Sopenharmony_ci * alternate channels by matching devname versus a virtio_config entry. 68862306a36Sopenharmony_ci * We use a simple reference count mechanism to ensure that only a single 68962306a36Sopenharmony_ci * mount has a channel open at a time. 69062306a36Sopenharmony_ci * 69162306a36Sopenharmony_ci */ 69262306a36Sopenharmony_ci 69362306a36Sopenharmony_cistatic int 69462306a36Sopenharmony_cip9_virtio_create(struct p9_client *client, const char *devname, char *args) 69562306a36Sopenharmony_ci{ 69662306a36Sopenharmony_ci struct virtio_chan *chan; 69762306a36Sopenharmony_ci int ret = -ENOENT; 69862306a36Sopenharmony_ci int found = 0; 69962306a36Sopenharmony_ci 70062306a36Sopenharmony_ci if (devname == NULL) 70162306a36Sopenharmony_ci return -EINVAL; 70262306a36Sopenharmony_ci 70362306a36Sopenharmony_ci mutex_lock(&virtio_9p_lock); 70462306a36Sopenharmony_ci list_for_each_entry(chan, &virtio_chan_list, chan_list) { 70562306a36Sopenharmony_ci if (!strcmp(devname, chan->tag)) { 70662306a36Sopenharmony_ci if (!chan->inuse) { 70762306a36Sopenharmony_ci chan->inuse = true; 70862306a36Sopenharmony_ci found = 1; 70962306a36Sopenharmony_ci break; 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci ret = -EBUSY; 71262306a36Sopenharmony_ci } 71362306a36Sopenharmony_ci } 71462306a36Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci if (!found) { 71762306a36Sopenharmony_ci pr_err("no channels available for device %s\n", devname); 71862306a36Sopenharmony_ci return ret; 71962306a36Sopenharmony_ci } 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci client->trans = (void *)chan; 72262306a36Sopenharmony_ci client->status = Connected; 72362306a36Sopenharmony_ci chan->client = client; 72462306a36Sopenharmony_ci 72562306a36Sopenharmony_ci return 0; 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci/** 72962306a36Sopenharmony_ci * p9_virtio_remove - clean up resources associated with a virtio device 73062306a36Sopenharmony_ci * @vdev: virtio device to remove 73162306a36Sopenharmony_ci * 73262306a36Sopenharmony_ci */ 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_cistatic void p9_virtio_remove(struct virtio_device *vdev) 73562306a36Sopenharmony_ci{ 73662306a36Sopenharmony_ci struct virtio_chan *chan = vdev->priv; 73762306a36Sopenharmony_ci unsigned long warning_time; 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci mutex_lock(&virtio_9p_lock); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci /* Remove self from list so we don't get new users. */ 74262306a36Sopenharmony_ci list_del(&chan->chan_list); 74362306a36Sopenharmony_ci warning_time = jiffies; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci /* Wait for existing users to close. */ 74662306a36Sopenharmony_ci while (chan->inuse) { 74762306a36Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 74862306a36Sopenharmony_ci msleep(250); 74962306a36Sopenharmony_ci if (time_after(jiffies, warning_time + 10 * HZ)) { 75062306a36Sopenharmony_ci dev_emerg(&vdev->dev, 75162306a36Sopenharmony_ci "p9_virtio_remove: waiting for device in use.\n"); 75262306a36Sopenharmony_ci warning_time = jiffies; 75362306a36Sopenharmony_ci } 75462306a36Sopenharmony_ci mutex_lock(&virtio_9p_lock); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci virtio_reset_device(vdev); 76062306a36Sopenharmony_ci vdev->config->del_vqs(vdev); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci sysfs_remove_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr); 76362306a36Sopenharmony_ci kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE); 76462306a36Sopenharmony_ci kfree(chan->tag); 76562306a36Sopenharmony_ci kfree(chan->vc_wq); 76662306a36Sopenharmony_ci kfree(chan); 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ci} 76962306a36Sopenharmony_ci 77062306a36Sopenharmony_cistatic struct virtio_device_id id_table[] = { 77162306a36Sopenharmony_ci { VIRTIO_ID_9P, VIRTIO_DEV_ANY_ID }, 77262306a36Sopenharmony_ci { 0 }, 77362306a36Sopenharmony_ci}; 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic unsigned int features[] = { 77662306a36Sopenharmony_ci VIRTIO_9P_MOUNT_TAG, 77762306a36Sopenharmony_ci}; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci/* The standard "struct lguest_driver": */ 78062306a36Sopenharmony_cistatic struct virtio_driver p9_virtio_drv = { 78162306a36Sopenharmony_ci .feature_table = features, 78262306a36Sopenharmony_ci .feature_table_size = ARRAY_SIZE(features), 78362306a36Sopenharmony_ci .driver.name = KBUILD_MODNAME, 78462306a36Sopenharmony_ci .driver.owner = THIS_MODULE, 78562306a36Sopenharmony_ci .id_table = id_table, 78662306a36Sopenharmony_ci .probe = p9_virtio_probe, 78762306a36Sopenharmony_ci .remove = p9_virtio_remove, 78862306a36Sopenharmony_ci}; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic struct p9_trans_module p9_virtio_trans = { 79162306a36Sopenharmony_ci .name = "virtio", 79262306a36Sopenharmony_ci .create = p9_virtio_create, 79362306a36Sopenharmony_ci .close = p9_virtio_close, 79462306a36Sopenharmony_ci .request = p9_virtio_request, 79562306a36Sopenharmony_ci .zc_request = p9_virtio_zc_request, 79662306a36Sopenharmony_ci .cancel = p9_virtio_cancel, 79762306a36Sopenharmony_ci .cancelled = p9_virtio_cancelled, 79862306a36Sopenharmony_ci /* 79962306a36Sopenharmony_ci * We leave one entry for input and one entry for response 80062306a36Sopenharmony_ci * headers. We also skip one more entry to accommodate, address 80162306a36Sopenharmony_ci * that are not at page boundary, that can result in an extra 80262306a36Sopenharmony_ci * page in zero copy. 80362306a36Sopenharmony_ci */ 80462306a36Sopenharmony_ci .maxsize = PAGE_SIZE * (VIRTQUEUE_NUM - 3), 80562306a36Sopenharmony_ci .pooled_rbuffers = false, 80662306a36Sopenharmony_ci .def = 1, 80762306a36Sopenharmony_ci .owner = THIS_MODULE, 80862306a36Sopenharmony_ci}; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci/* The standard init function */ 81162306a36Sopenharmony_cistatic int __init p9_virtio_init(void) 81262306a36Sopenharmony_ci{ 81362306a36Sopenharmony_ci int rc; 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci INIT_LIST_HEAD(&virtio_chan_list); 81662306a36Sopenharmony_ci 81762306a36Sopenharmony_ci v9fs_register_trans(&p9_virtio_trans); 81862306a36Sopenharmony_ci rc = register_virtio_driver(&p9_virtio_drv); 81962306a36Sopenharmony_ci if (rc) 82062306a36Sopenharmony_ci v9fs_unregister_trans(&p9_virtio_trans); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return rc; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic void __exit p9_virtio_cleanup(void) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci unregister_virtio_driver(&p9_virtio_drv); 82862306a36Sopenharmony_ci v9fs_unregister_trans(&p9_virtio_trans); 82962306a36Sopenharmony_ci} 83062306a36Sopenharmony_ci 83162306a36Sopenharmony_cimodule_init(p9_virtio_init); 83262306a36Sopenharmony_cimodule_exit(p9_virtio_cleanup); 83362306a36Sopenharmony_ciMODULE_ALIAS_9P("virtio"); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table); 83662306a36Sopenharmony_ciMODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); 83762306a36Sopenharmony_ciMODULE_DESCRIPTION("Virtio 9p Transport"); 83862306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 839