18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * The Virtio 9p transport driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * This is a block based transport driver based on the lguest block driver 68c2ecf20Sopenharmony_ci * code. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2007, 2008 Eric Van Hensbergen, IBM Corporation 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Based on virtio console driver 118c2ecf20Sopenharmony_ci * Copyright (C) 2006, 2007 Rusty Russell, IBM Corporation 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <linux/in.h> 178c2ecf20Sopenharmony_ci#include <linux/module.h> 188c2ecf20Sopenharmony_ci#include <linux/net.h> 198c2ecf20Sopenharmony_ci#include <linux/ipv6.h> 208c2ecf20Sopenharmony_ci#include <linux/errno.h> 218c2ecf20Sopenharmony_ci#include <linux/kernel.h> 228c2ecf20Sopenharmony_ci#include <linux/un.h> 238c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 248c2ecf20Sopenharmony_ci#include <linux/inet.h> 258c2ecf20Sopenharmony_ci#include <linux/idr.h> 268c2ecf20Sopenharmony_ci#include <linux/file.h> 278c2ecf20Sopenharmony_ci#include <linux/highmem.h> 288c2ecf20Sopenharmony_ci#include <linux/slab.h> 298c2ecf20Sopenharmony_ci#include <net/9p/9p.h> 308c2ecf20Sopenharmony_ci#include <linux/parser.h> 318c2ecf20Sopenharmony_ci#include <net/9p/client.h> 328c2ecf20Sopenharmony_ci#include <net/9p/transport.h> 338c2ecf20Sopenharmony_ci#include <linux/scatterlist.h> 348c2ecf20Sopenharmony_ci#include <linux/swap.h> 358c2ecf20Sopenharmony_ci#include <linux/virtio.h> 368c2ecf20Sopenharmony_ci#include <linux/virtio_9p.h> 378c2ecf20Sopenharmony_ci#include "trans_common.h" 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci#define VIRTQUEUE_NUM 128 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci/* a single mutex to manage channel initialization and attachment */ 428c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(virtio_9p_lock); 438c2ecf20Sopenharmony_cistatic DECLARE_WAIT_QUEUE_HEAD(vp_wq); 448c2ecf20Sopenharmony_cistatic atomic_t vp_pinned = ATOMIC_INIT(0); 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci/** 478c2ecf20Sopenharmony_ci * struct virtio_chan - per-instance transport information 488c2ecf20Sopenharmony_ci * @inuse: whether the channel is in use 498c2ecf20Sopenharmony_ci * @lock: protects multiple elements within this structure 508c2ecf20Sopenharmony_ci * @client: client instance 518c2ecf20Sopenharmony_ci * @vdev: virtio dev associated with this channel 528c2ecf20Sopenharmony_ci * @vq: virtio queue associated with this channel 538c2ecf20Sopenharmony_ci * @sg: scatter gather list which is used to pack a request (protected?) 548c2ecf20Sopenharmony_ci * 558c2ecf20Sopenharmony_ci * We keep all per-channel information in a structure. 568c2ecf20Sopenharmony_ci * This structure is allocated within the devices dev->mem space. 578c2ecf20Sopenharmony_ci * A pointer to the structure will get put in the transport private. 588c2ecf20Sopenharmony_ci * 598c2ecf20Sopenharmony_ci */ 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistruct virtio_chan { 628c2ecf20Sopenharmony_ci bool inuse; 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci spinlock_t lock; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci struct p9_client *client; 678c2ecf20Sopenharmony_ci struct virtio_device *vdev; 688c2ecf20Sopenharmony_ci struct virtqueue *vq; 698c2ecf20Sopenharmony_ci int ring_bufs_avail; 708c2ecf20Sopenharmony_ci wait_queue_head_t *vc_wq; 718c2ecf20Sopenharmony_ci /* This is global limit. Since we don't have a global structure, 728c2ecf20Sopenharmony_ci * will be placing it in each channel. 738c2ecf20Sopenharmony_ci */ 748c2ecf20Sopenharmony_ci unsigned long p9_max_pages; 758c2ecf20Sopenharmony_ci /* Scatterlist: can be too big for stack. */ 768c2ecf20Sopenharmony_ci struct scatterlist sg[VIRTQUEUE_NUM]; 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * tag name to identify a mount null terminated 798c2ecf20Sopenharmony_ci */ 808c2ecf20Sopenharmony_ci char *tag; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci struct list_head chan_list; 838c2ecf20Sopenharmony_ci}; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic struct list_head virtio_chan_list; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci/* How many bytes left in this page. */ 888c2ecf20Sopenharmony_cistatic unsigned int rest_of_page(void *data) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return PAGE_SIZE - offset_in_page(data); 918c2ecf20Sopenharmony_ci} 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci/** 948c2ecf20Sopenharmony_ci * p9_virtio_close - reclaim resources of a channel 958c2ecf20Sopenharmony_ci * @client: client instance 968c2ecf20Sopenharmony_ci * 978c2ecf20Sopenharmony_ci * This reclaims a channel by freeing its resources and 988c2ecf20Sopenharmony_ci * reseting its inuse flag. 998c2ecf20Sopenharmony_ci * 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_cistatic void p9_virtio_close(struct p9_client *client) 1038c2ecf20Sopenharmony_ci{ 1048c2ecf20Sopenharmony_ci struct virtio_chan *chan = client->trans; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci mutex_lock(&virtio_9p_lock); 1078c2ecf20Sopenharmony_ci if (chan) 1088c2ecf20Sopenharmony_ci chan->inuse = false; 1098c2ecf20Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci/** 1138c2ecf20Sopenharmony_ci * req_done - callback which signals activity from the server 1148c2ecf20Sopenharmony_ci * @vq: virtio queue activity was received on 1158c2ecf20Sopenharmony_ci * 1168c2ecf20Sopenharmony_ci * This notifies us that the server has triggered some activity 1178c2ecf20Sopenharmony_ci * on the virtio channel - most likely a response to request we 1188c2ecf20Sopenharmony_ci * sent. Figure out which requests now have responses and wake up 1198c2ecf20Sopenharmony_ci * those threads. 1208c2ecf20Sopenharmony_ci * 1218c2ecf20Sopenharmony_ci * Bugs: could do with some additional sanity checking, but appears to work. 1228c2ecf20Sopenharmony_ci * 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_cistatic void req_done(struct virtqueue *vq) 1268c2ecf20Sopenharmony_ci{ 1278c2ecf20Sopenharmony_ci struct virtio_chan *chan = vq->vdev->priv; 1288c2ecf20Sopenharmony_ci unsigned int len; 1298c2ecf20Sopenharmony_ci struct p9_req_t *req; 1308c2ecf20Sopenharmony_ci bool need_wakeup = false; 1318c2ecf20Sopenharmony_ci unsigned long flags; 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, ": request done\n"); 1348c2ecf20Sopenharmony_ci 1358c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 1368c2ecf20Sopenharmony_ci while ((req = virtqueue_get_buf(chan->vq, &len)) != NULL) { 1378c2ecf20Sopenharmony_ci if (!chan->ring_bufs_avail) { 1388c2ecf20Sopenharmony_ci chan->ring_bufs_avail = 1; 1398c2ecf20Sopenharmony_ci need_wakeup = true; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci if (len) { 1438c2ecf20Sopenharmony_ci req->rc.size = len; 1448c2ecf20Sopenharmony_ci p9_client_cb(chan->client, req, REQ_STATUS_RCVD); 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci } 1478c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 1488c2ecf20Sopenharmony_ci /* Wakeup if anyone waiting for VirtIO ring space. */ 1498c2ecf20Sopenharmony_ci if (need_wakeup) 1508c2ecf20Sopenharmony_ci wake_up(chan->vc_wq); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/** 1548c2ecf20Sopenharmony_ci * pack_sg_list - pack a scatter gather list from a linear buffer 1558c2ecf20Sopenharmony_ci * @sg: scatter/gather list to pack into 1568c2ecf20Sopenharmony_ci * @start: which segment of the sg_list to start at 1578c2ecf20Sopenharmony_ci * @limit: maximum segment to pack data to 1588c2ecf20Sopenharmony_ci * @data: data to pack into scatter/gather list 1598c2ecf20Sopenharmony_ci * @count: amount of data to pack into the scatter/gather list 1608c2ecf20Sopenharmony_ci * 1618c2ecf20Sopenharmony_ci * sg_lists have multiple segments of various sizes. This will pack 1628c2ecf20Sopenharmony_ci * arbitrary data into an existing scatter gather list, segmenting the 1638c2ecf20Sopenharmony_ci * data as necessary within constraints. 1648c2ecf20Sopenharmony_ci * 1658c2ecf20Sopenharmony_ci */ 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_cistatic int pack_sg_list(struct scatterlist *sg, int start, 1688c2ecf20Sopenharmony_ci int limit, char *data, int count) 1698c2ecf20Sopenharmony_ci{ 1708c2ecf20Sopenharmony_ci int s; 1718c2ecf20Sopenharmony_ci int index = start; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci while (count) { 1748c2ecf20Sopenharmony_ci s = rest_of_page(data); 1758c2ecf20Sopenharmony_ci if (s > count) 1768c2ecf20Sopenharmony_ci s = count; 1778c2ecf20Sopenharmony_ci BUG_ON(index >= limit); 1788c2ecf20Sopenharmony_ci /* Make sure we don't terminate early. */ 1798c2ecf20Sopenharmony_ci sg_unmark_end(&sg[index]); 1808c2ecf20Sopenharmony_ci sg_set_buf(&sg[index++], data, s); 1818c2ecf20Sopenharmony_ci count -= s; 1828c2ecf20Sopenharmony_ci data += s; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci if (index-start) 1858c2ecf20Sopenharmony_ci sg_mark_end(&sg[index - 1]); 1868c2ecf20Sopenharmony_ci return index-start; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_ci/* We don't currently allow canceling of virtio requests */ 1908c2ecf20Sopenharmony_cistatic int p9_virtio_cancel(struct p9_client *client, struct p9_req_t *req) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return 1; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci/* Reply won't come, so drop req ref */ 1968c2ecf20Sopenharmony_cistatic int p9_virtio_cancelled(struct p9_client *client, struct p9_req_t *req) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci p9_req_put(req); 1998c2ecf20Sopenharmony_ci return 0; 2008c2ecf20Sopenharmony_ci} 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci/** 2038c2ecf20Sopenharmony_ci * pack_sg_list_p - Just like pack_sg_list. Instead of taking a buffer, 2048c2ecf20Sopenharmony_ci * this takes a list of pages. 2058c2ecf20Sopenharmony_ci * @sg: scatter/gather list to pack into 2068c2ecf20Sopenharmony_ci * @start: which segment of the sg_list to start at 2078c2ecf20Sopenharmony_ci * @pdata: a list of pages to add into sg. 2088c2ecf20Sopenharmony_ci * @nr_pages: number of pages to pack into the scatter/gather list 2098c2ecf20Sopenharmony_ci * @offs: amount of data in the beginning of first page _not_ to pack 2108c2ecf20Sopenharmony_ci * @count: amount of data to pack into the scatter/gather list 2118c2ecf20Sopenharmony_ci */ 2128c2ecf20Sopenharmony_cistatic int 2138c2ecf20Sopenharmony_cipack_sg_list_p(struct scatterlist *sg, int start, int limit, 2148c2ecf20Sopenharmony_ci struct page **pdata, int nr_pages, size_t offs, int count) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci int i = 0, s; 2178c2ecf20Sopenharmony_ci int data_off = offs; 2188c2ecf20Sopenharmony_ci int index = start; 2198c2ecf20Sopenharmony_ci 2208c2ecf20Sopenharmony_ci BUG_ON(nr_pages > (limit - start)); 2218c2ecf20Sopenharmony_ci /* 2228c2ecf20Sopenharmony_ci * if the first page doesn't start at 2238c2ecf20Sopenharmony_ci * page boundary find the offset 2248c2ecf20Sopenharmony_ci */ 2258c2ecf20Sopenharmony_ci while (nr_pages) { 2268c2ecf20Sopenharmony_ci s = PAGE_SIZE - data_off; 2278c2ecf20Sopenharmony_ci if (s > count) 2288c2ecf20Sopenharmony_ci s = count; 2298c2ecf20Sopenharmony_ci BUG_ON(index >= limit); 2308c2ecf20Sopenharmony_ci /* Make sure we don't terminate early. */ 2318c2ecf20Sopenharmony_ci sg_unmark_end(&sg[index]); 2328c2ecf20Sopenharmony_ci sg_set_page(&sg[index++], pdata[i++], s, data_off); 2338c2ecf20Sopenharmony_ci data_off = 0; 2348c2ecf20Sopenharmony_ci count -= s; 2358c2ecf20Sopenharmony_ci nr_pages--; 2368c2ecf20Sopenharmony_ci } 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci if (index-start) 2398c2ecf20Sopenharmony_ci sg_mark_end(&sg[index - 1]); 2408c2ecf20Sopenharmony_ci return index - start; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci/** 2448c2ecf20Sopenharmony_ci * p9_virtio_request - issue a request 2458c2ecf20Sopenharmony_ci * @client: client instance issuing the request 2468c2ecf20Sopenharmony_ci * @req: request to be issued 2478c2ecf20Sopenharmony_ci * 2488c2ecf20Sopenharmony_ci */ 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic int 2518c2ecf20Sopenharmony_cip9_virtio_request(struct p9_client *client, struct p9_req_t *req) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci int err; 2548c2ecf20Sopenharmony_ci int in, out, out_sgs, in_sgs; 2558c2ecf20Sopenharmony_ci unsigned long flags; 2568c2ecf20Sopenharmony_ci struct virtio_chan *chan = client->trans; 2578c2ecf20Sopenharmony_ci struct scatterlist *sgs[2]; 2588c2ecf20Sopenharmony_ci 2598c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "9p debug: virtio request\n"); 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci req->status = REQ_STATUS_SENT; 2628c2ecf20Sopenharmony_cireq_retry: 2638c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci out_sgs = in_sgs = 0; 2668c2ecf20Sopenharmony_ci /* Handle out VirtIO ring buffers */ 2678c2ecf20Sopenharmony_ci out = pack_sg_list(chan->sg, 0, 2688c2ecf20Sopenharmony_ci VIRTQUEUE_NUM, req->tc.sdata, req->tc.size); 2698c2ecf20Sopenharmony_ci if (out) 2708c2ecf20Sopenharmony_ci sgs[out_sgs++] = chan->sg; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci in = pack_sg_list(chan->sg, out, 2738c2ecf20Sopenharmony_ci VIRTQUEUE_NUM, req->rc.sdata, req->rc.capacity); 2748c2ecf20Sopenharmony_ci if (in) 2758c2ecf20Sopenharmony_ci sgs[out_sgs + in_sgs++] = chan->sg + out; 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req, 2788c2ecf20Sopenharmony_ci GFP_ATOMIC); 2798c2ecf20Sopenharmony_ci if (err < 0) { 2808c2ecf20Sopenharmony_ci if (err == -ENOSPC) { 2818c2ecf20Sopenharmony_ci chan->ring_bufs_avail = 0; 2828c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 2838c2ecf20Sopenharmony_ci err = wait_event_killable(*chan->vc_wq, 2848c2ecf20Sopenharmony_ci chan->ring_bufs_avail); 2858c2ecf20Sopenharmony_ci if (err == -ERESTARTSYS) 2868c2ecf20Sopenharmony_ci return err; 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n"); 2898c2ecf20Sopenharmony_ci goto req_retry; 2908c2ecf20Sopenharmony_ci } else { 2918c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 2928c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, 2938c2ecf20Sopenharmony_ci "virtio rpc add_sgs returned failure\n"); 2948c2ecf20Sopenharmony_ci return -EIO; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci virtqueue_kick(chan->vq); 2988c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n"); 3018c2ecf20Sopenharmony_ci return 0; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_cistatic int p9_get_mapped_pages(struct virtio_chan *chan, 3058c2ecf20Sopenharmony_ci struct page ***pages, 3068c2ecf20Sopenharmony_ci struct iov_iter *data, 3078c2ecf20Sopenharmony_ci int count, 3088c2ecf20Sopenharmony_ci size_t *offs, 3098c2ecf20Sopenharmony_ci int *need_drop) 3108c2ecf20Sopenharmony_ci{ 3118c2ecf20Sopenharmony_ci int nr_pages; 3128c2ecf20Sopenharmony_ci int err; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci if (!iov_iter_count(data)) 3158c2ecf20Sopenharmony_ci return 0; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci if (!iov_iter_is_kvec(data)) { 3188c2ecf20Sopenharmony_ci int n; 3198c2ecf20Sopenharmony_ci /* 3208c2ecf20Sopenharmony_ci * We allow only p9_max_pages pinned. We wait for the 3218c2ecf20Sopenharmony_ci * Other zc request to finish here 3228c2ecf20Sopenharmony_ci */ 3238c2ecf20Sopenharmony_ci if (atomic_read(&vp_pinned) >= chan->p9_max_pages) { 3248c2ecf20Sopenharmony_ci err = wait_event_killable(vp_wq, 3258c2ecf20Sopenharmony_ci (atomic_read(&vp_pinned) < chan->p9_max_pages)); 3268c2ecf20Sopenharmony_ci if (err == -ERESTARTSYS) 3278c2ecf20Sopenharmony_ci return err; 3288c2ecf20Sopenharmony_ci } 3298c2ecf20Sopenharmony_ci n = iov_iter_get_pages_alloc(data, pages, count, offs); 3308c2ecf20Sopenharmony_ci if (n < 0) 3318c2ecf20Sopenharmony_ci return n; 3328c2ecf20Sopenharmony_ci *need_drop = 1; 3338c2ecf20Sopenharmony_ci nr_pages = DIV_ROUND_UP(n + *offs, PAGE_SIZE); 3348c2ecf20Sopenharmony_ci atomic_add(nr_pages, &vp_pinned); 3358c2ecf20Sopenharmony_ci return n; 3368c2ecf20Sopenharmony_ci } else { 3378c2ecf20Sopenharmony_ci /* kernel buffer, no need to pin pages */ 3388c2ecf20Sopenharmony_ci int index; 3398c2ecf20Sopenharmony_ci size_t len; 3408c2ecf20Sopenharmony_ci void *p; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci /* we'd already checked that it's non-empty */ 3438c2ecf20Sopenharmony_ci while (1) { 3448c2ecf20Sopenharmony_ci len = iov_iter_single_seg_count(data); 3458c2ecf20Sopenharmony_ci if (likely(len)) { 3468c2ecf20Sopenharmony_ci p = data->kvec->iov_base + data->iov_offset; 3478c2ecf20Sopenharmony_ci break; 3488c2ecf20Sopenharmony_ci } 3498c2ecf20Sopenharmony_ci iov_iter_advance(data, 0); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci if (len > count) 3528c2ecf20Sopenharmony_ci len = count; 3538c2ecf20Sopenharmony_ci 3548c2ecf20Sopenharmony_ci nr_pages = DIV_ROUND_UP((unsigned long)p + len, PAGE_SIZE) - 3558c2ecf20Sopenharmony_ci (unsigned long)p / PAGE_SIZE; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci *pages = kmalloc_array(nr_pages, sizeof(struct page *), 3588c2ecf20Sopenharmony_ci GFP_NOFS); 3598c2ecf20Sopenharmony_ci if (!*pages) 3608c2ecf20Sopenharmony_ci return -ENOMEM; 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci *need_drop = 0; 3638c2ecf20Sopenharmony_ci p -= (*offs = offset_in_page(p)); 3648c2ecf20Sopenharmony_ci for (index = 0; index < nr_pages; index++) { 3658c2ecf20Sopenharmony_ci if (is_vmalloc_addr(p)) 3668c2ecf20Sopenharmony_ci (*pages)[index] = vmalloc_to_page(p); 3678c2ecf20Sopenharmony_ci else 3688c2ecf20Sopenharmony_ci (*pages)[index] = kmap_to_page(p); 3698c2ecf20Sopenharmony_ci p += PAGE_SIZE; 3708c2ecf20Sopenharmony_ci } 3718c2ecf20Sopenharmony_ci return len; 3728c2ecf20Sopenharmony_ci } 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci/** 3768c2ecf20Sopenharmony_ci * p9_virtio_zc_request - issue a zero copy request 3778c2ecf20Sopenharmony_ci * @client: client instance issuing the request 3788c2ecf20Sopenharmony_ci * @req: request to be issued 3798c2ecf20Sopenharmony_ci * @uidata: user buffer that should be used for zero copy read 3808c2ecf20Sopenharmony_ci * @uodata: user buffer that should be used for zero copy write 3818c2ecf20Sopenharmony_ci * @inlen: read buffer size 3828c2ecf20Sopenharmony_ci * @outlen: write buffer size 3838c2ecf20Sopenharmony_ci * @in_hdr_len: reader header size, This is the size of response protocol data 3848c2ecf20Sopenharmony_ci * 3858c2ecf20Sopenharmony_ci */ 3868c2ecf20Sopenharmony_cistatic int 3878c2ecf20Sopenharmony_cip9_virtio_zc_request(struct p9_client *client, struct p9_req_t *req, 3888c2ecf20Sopenharmony_ci struct iov_iter *uidata, struct iov_iter *uodata, 3898c2ecf20Sopenharmony_ci int inlen, int outlen, int in_hdr_len) 3908c2ecf20Sopenharmony_ci{ 3918c2ecf20Sopenharmony_ci int in, out, err, out_sgs, in_sgs; 3928c2ecf20Sopenharmony_ci unsigned long flags; 3938c2ecf20Sopenharmony_ci int in_nr_pages = 0, out_nr_pages = 0; 3948c2ecf20Sopenharmony_ci struct page **in_pages = NULL, **out_pages = NULL; 3958c2ecf20Sopenharmony_ci struct virtio_chan *chan = client->trans; 3968c2ecf20Sopenharmony_ci struct scatterlist *sgs[4]; 3978c2ecf20Sopenharmony_ci size_t offs = 0; 3988c2ecf20Sopenharmony_ci int need_drop = 0; 3998c2ecf20Sopenharmony_ci int kicked = 0; 4008c2ecf20Sopenharmony_ci 4018c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "virtio request\n"); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci if (uodata) { 4048c2ecf20Sopenharmony_ci __le32 sz; 4058c2ecf20Sopenharmony_ci int n = p9_get_mapped_pages(chan, &out_pages, uodata, 4068c2ecf20Sopenharmony_ci outlen, &offs, &need_drop); 4078c2ecf20Sopenharmony_ci if (n < 0) { 4088c2ecf20Sopenharmony_ci err = n; 4098c2ecf20Sopenharmony_ci goto err_out; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci out_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE); 4128c2ecf20Sopenharmony_ci if (n != outlen) { 4138c2ecf20Sopenharmony_ci __le32 v = cpu_to_le32(n); 4148c2ecf20Sopenharmony_ci memcpy(&req->tc.sdata[req->tc.size - 4], &v, 4); 4158c2ecf20Sopenharmony_ci outlen = n; 4168c2ecf20Sopenharmony_ci } 4178c2ecf20Sopenharmony_ci /* The size field of the message must include the length of the 4188c2ecf20Sopenharmony_ci * header and the length of the data. We didn't actually know 4198c2ecf20Sopenharmony_ci * the length of the data until this point so add it in now. 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_ci sz = cpu_to_le32(req->tc.size + outlen); 4228c2ecf20Sopenharmony_ci memcpy(&req->tc.sdata[0], &sz, sizeof(sz)); 4238c2ecf20Sopenharmony_ci } else if (uidata) { 4248c2ecf20Sopenharmony_ci int n = p9_get_mapped_pages(chan, &in_pages, uidata, 4258c2ecf20Sopenharmony_ci inlen, &offs, &need_drop); 4268c2ecf20Sopenharmony_ci if (n < 0) { 4278c2ecf20Sopenharmony_ci err = n; 4288c2ecf20Sopenharmony_ci goto err_out; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci in_nr_pages = DIV_ROUND_UP(n + offs, PAGE_SIZE); 4318c2ecf20Sopenharmony_ci if (n != inlen) { 4328c2ecf20Sopenharmony_ci __le32 v = cpu_to_le32(n); 4338c2ecf20Sopenharmony_ci memcpy(&req->tc.sdata[req->tc.size - 4], &v, 4); 4348c2ecf20Sopenharmony_ci inlen = n; 4358c2ecf20Sopenharmony_ci } 4368c2ecf20Sopenharmony_ci } 4378c2ecf20Sopenharmony_ci req->status = REQ_STATUS_SENT; 4388c2ecf20Sopenharmony_cireq_retry_pinned: 4398c2ecf20Sopenharmony_ci spin_lock_irqsave(&chan->lock, flags); 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci out_sgs = in_sgs = 0; 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci /* out data */ 4448c2ecf20Sopenharmony_ci out = pack_sg_list(chan->sg, 0, 4458c2ecf20Sopenharmony_ci VIRTQUEUE_NUM, req->tc.sdata, req->tc.size); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci if (out) 4488c2ecf20Sopenharmony_ci sgs[out_sgs++] = chan->sg; 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci if (out_pages) { 4518c2ecf20Sopenharmony_ci sgs[out_sgs++] = chan->sg + out; 4528c2ecf20Sopenharmony_ci out += pack_sg_list_p(chan->sg, out, VIRTQUEUE_NUM, 4538c2ecf20Sopenharmony_ci out_pages, out_nr_pages, offs, outlen); 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci /* 4578c2ecf20Sopenharmony_ci * Take care of in data 4588c2ecf20Sopenharmony_ci * For example TREAD have 11. 4598c2ecf20Sopenharmony_ci * 11 is the read/write header = PDU Header(7) + IO Size (4). 4608c2ecf20Sopenharmony_ci * Arrange in such a way that server places header in the 4618c2ecf20Sopenharmony_ci * alloced memory and payload onto the user buffer. 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_ci in = pack_sg_list(chan->sg, out, 4648c2ecf20Sopenharmony_ci VIRTQUEUE_NUM, req->rc.sdata, in_hdr_len); 4658c2ecf20Sopenharmony_ci if (in) 4668c2ecf20Sopenharmony_ci sgs[out_sgs + in_sgs++] = chan->sg + out; 4678c2ecf20Sopenharmony_ci 4688c2ecf20Sopenharmony_ci if (in_pages) { 4698c2ecf20Sopenharmony_ci sgs[out_sgs + in_sgs++] = chan->sg + out + in; 4708c2ecf20Sopenharmony_ci in += pack_sg_list_p(chan->sg, out + in, VIRTQUEUE_NUM, 4718c2ecf20Sopenharmony_ci in_pages, in_nr_pages, offs, inlen); 4728c2ecf20Sopenharmony_ci } 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci BUG_ON(out_sgs + in_sgs > ARRAY_SIZE(sgs)); 4758c2ecf20Sopenharmony_ci err = virtqueue_add_sgs(chan->vq, sgs, out_sgs, in_sgs, req, 4768c2ecf20Sopenharmony_ci GFP_ATOMIC); 4778c2ecf20Sopenharmony_ci if (err < 0) { 4788c2ecf20Sopenharmony_ci if (err == -ENOSPC) { 4798c2ecf20Sopenharmony_ci chan->ring_bufs_avail = 0; 4808c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 4818c2ecf20Sopenharmony_ci err = wait_event_killable(*chan->vc_wq, 4828c2ecf20Sopenharmony_ci chan->ring_bufs_avail); 4838c2ecf20Sopenharmony_ci if (err == -ERESTARTSYS) 4848c2ecf20Sopenharmony_ci goto err_out; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "Retry virtio request\n"); 4878c2ecf20Sopenharmony_ci goto req_retry_pinned; 4888c2ecf20Sopenharmony_ci } else { 4898c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 4908c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, 4918c2ecf20Sopenharmony_ci "virtio rpc add_sgs returned failure\n"); 4928c2ecf20Sopenharmony_ci err = -EIO; 4938c2ecf20Sopenharmony_ci goto err_out; 4948c2ecf20Sopenharmony_ci } 4958c2ecf20Sopenharmony_ci } 4968c2ecf20Sopenharmony_ci virtqueue_kick(chan->vq); 4978c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&chan->lock, flags); 4988c2ecf20Sopenharmony_ci kicked = 1; 4998c2ecf20Sopenharmony_ci p9_debug(P9_DEBUG_TRANS, "virtio request kicked\n"); 5008c2ecf20Sopenharmony_ci err = wait_event_killable(req->wq, req->status >= REQ_STATUS_RCVD); 5018c2ecf20Sopenharmony_ci /* 5028c2ecf20Sopenharmony_ci * Non kernel buffers are pinned, unpin them 5038c2ecf20Sopenharmony_ci */ 5048c2ecf20Sopenharmony_cierr_out: 5058c2ecf20Sopenharmony_ci if (need_drop) { 5068c2ecf20Sopenharmony_ci if (in_pages) { 5078c2ecf20Sopenharmony_ci p9_release_pages(in_pages, in_nr_pages); 5088c2ecf20Sopenharmony_ci atomic_sub(in_nr_pages, &vp_pinned); 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci if (out_pages) { 5118c2ecf20Sopenharmony_ci p9_release_pages(out_pages, out_nr_pages); 5128c2ecf20Sopenharmony_ci atomic_sub(out_nr_pages, &vp_pinned); 5138c2ecf20Sopenharmony_ci } 5148c2ecf20Sopenharmony_ci /* wakeup anybody waiting for slots to pin pages */ 5158c2ecf20Sopenharmony_ci wake_up(&vp_wq); 5168c2ecf20Sopenharmony_ci } 5178c2ecf20Sopenharmony_ci kvfree(in_pages); 5188c2ecf20Sopenharmony_ci kvfree(out_pages); 5198c2ecf20Sopenharmony_ci if (!kicked) { 5208c2ecf20Sopenharmony_ci /* reply won't come */ 5218c2ecf20Sopenharmony_ci p9_req_put(req); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci return err; 5248c2ecf20Sopenharmony_ci} 5258c2ecf20Sopenharmony_ci 5268c2ecf20Sopenharmony_cistatic ssize_t p9_mount_tag_show(struct device *dev, 5278c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci struct virtio_chan *chan; 5308c2ecf20Sopenharmony_ci struct virtio_device *vdev; 5318c2ecf20Sopenharmony_ci int tag_len; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci vdev = dev_to_virtio(dev); 5348c2ecf20Sopenharmony_ci chan = vdev->priv; 5358c2ecf20Sopenharmony_ci tag_len = strlen(chan->tag); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci memcpy(buf, chan->tag, tag_len + 1); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return tag_len + 1; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_cistatic DEVICE_ATTR(mount_tag, 0444, p9_mount_tag_show, NULL); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci/** 5458c2ecf20Sopenharmony_ci * p9_virtio_probe - probe for existence of 9P virtio channels 5468c2ecf20Sopenharmony_ci * @vdev: virtio device to probe 5478c2ecf20Sopenharmony_ci * 5488c2ecf20Sopenharmony_ci * This probes for existing virtio channels. 5498c2ecf20Sopenharmony_ci * 5508c2ecf20Sopenharmony_ci */ 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cistatic int p9_virtio_probe(struct virtio_device *vdev) 5538c2ecf20Sopenharmony_ci{ 5548c2ecf20Sopenharmony_ci __u16 tag_len; 5558c2ecf20Sopenharmony_ci char *tag; 5568c2ecf20Sopenharmony_ci int err; 5578c2ecf20Sopenharmony_ci struct virtio_chan *chan; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (!vdev->config->get) { 5608c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "%s failure: config access disabled\n", 5618c2ecf20Sopenharmony_ci __func__); 5628c2ecf20Sopenharmony_ci return -EINVAL; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci chan = kmalloc(sizeof(struct virtio_chan), GFP_KERNEL); 5668c2ecf20Sopenharmony_ci if (!chan) { 5678c2ecf20Sopenharmony_ci pr_err("Failed to allocate virtio 9P channel\n"); 5688c2ecf20Sopenharmony_ci err = -ENOMEM; 5698c2ecf20Sopenharmony_ci goto fail; 5708c2ecf20Sopenharmony_ci } 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci chan->vdev = vdev; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* We expect one virtqueue, for requests. */ 5758c2ecf20Sopenharmony_ci chan->vq = virtio_find_single_vq(vdev, req_done, "requests"); 5768c2ecf20Sopenharmony_ci if (IS_ERR(chan->vq)) { 5778c2ecf20Sopenharmony_ci err = PTR_ERR(chan->vq); 5788c2ecf20Sopenharmony_ci goto out_free_chan; 5798c2ecf20Sopenharmony_ci } 5808c2ecf20Sopenharmony_ci chan->vq->vdev->priv = chan; 5818c2ecf20Sopenharmony_ci spin_lock_init(&chan->lock); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci sg_init_table(chan->sg, VIRTQUEUE_NUM); 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci chan->inuse = false; 5868c2ecf20Sopenharmony_ci if (virtio_has_feature(vdev, VIRTIO_9P_MOUNT_TAG)) { 5878c2ecf20Sopenharmony_ci virtio_cread(vdev, struct virtio_9p_config, tag_len, &tag_len); 5888c2ecf20Sopenharmony_ci } else { 5898c2ecf20Sopenharmony_ci err = -EINVAL; 5908c2ecf20Sopenharmony_ci goto out_free_vq; 5918c2ecf20Sopenharmony_ci } 5928c2ecf20Sopenharmony_ci tag = kzalloc(tag_len + 1, GFP_KERNEL); 5938c2ecf20Sopenharmony_ci if (!tag) { 5948c2ecf20Sopenharmony_ci err = -ENOMEM; 5958c2ecf20Sopenharmony_ci goto out_free_vq; 5968c2ecf20Sopenharmony_ci } 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci virtio_cread_bytes(vdev, offsetof(struct virtio_9p_config, tag), 5998c2ecf20Sopenharmony_ci tag, tag_len); 6008c2ecf20Sopenharmony_ci chan->tag = tag; 6018c2ecf20Sopenharmony_ci err = sysfs_create_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr); 6028c2ecf20Sopenharmony_ci if (err) { 6038c2ecf20Sopenharmony_ci goto out_free_tag; 6048c2ecf20Sopenharmony_ci } 6058c2ecf20Sopenharmony_ci chan->vc_wq = kmalloc(sizeof(wait_queue_head_t), GFP_KERNEL); 6068c2ecf20Sopenharmony_ci if (!chan->vc_wq) { 6078c2ecf20Sopenharmony_ci err = -ENOMEM; 6088c2ecf20Sopenharmony_ci goto out_remove_file; 6098c2ecf20Sopenharmony_ci } 6108c2ecf20Sopenharmony_ci init_waitqueue_head(chan->vc_wq); 6118c2ecf20Sopenharmony_ci chan->ring_bufs_avail = 1; 6128c2ecf20Sopenharmony_ci /* Ceiling limit to avoid denial of service attacks */ 6138c2ecf20Sopenharmony_ci chan->p9_max_pages = nr_free_buffer_pages()/4; 6148c2ecf20Sopenharmony_ci 6158c2ecf20Sopenharmony_ci virtio_device_ready(vdev); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci mutex_lock(&virtio_9p_lock); 6188c2ecf20Sopenharmony_ci list_add_tail(&chan->chan_list, &virtio_chan_list); 6198c2ecf20Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci /* Let udev rules use the new mount_tag attribute. */ 6228c2ecf20Sopenharmony_ci kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE); 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci return 0; 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ciout_remove_file: 6278c2ecf20Sopenharmony_ci sysfs_remove_file(&vdev->dev.kobj, &dev_attr_mount_tag.attr); 6288c2ecf20Sopenharmony_ciout_free_tag: 6298c2ecf20Sopenharmony_ci kfree(tag); 6308c2ecf20Sopenharmony_ciout_free_vq: 6318c2ecf20Sopenharmony_ci vdev->config->del_vqs(vdev); 6328c2ecf20Sopenharmony_ciout_free_chan: 6338c2ecf20Sopenharmony_ci kfree(chan); 6348c2ecf20Sopenharmony_cifail: 6358c2ecf20Sopenharmony_ci return err; 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci/** 6408c2ecf20Sopenharmony_ci * p9_virtio_create - allocate a new virtio channel 6418c2ecf20Sopenharmony_ci * @client: client instance invoking this transport 6428c2ecf20Sopenharmony_ci * @devname: string identifying the channel to connect to (unused) 6438c2ecf20Sopenharmony_ci * @args: args passed from sys_mount() for per-transport options (unused) 6448c2ecf20Sopenharmony_ci * 6458c2ecf20Sopenharmony_ci * This sets up a transport channel for 9p communication. Right now 6468c2ecf20Sopenharmony_ci * we only match the first available channel, but eventually we couldlook up 6478c2ecf20Sopenharmony_ci * alternate channels by matching devname versus a virtio_config entry. 6488c2ecf20Sopenharmony_ci * We use a simple reference count mechanism to ensure that only a single 6498c2ecf20Sopenharmony_ci * mount has a channel open at a time. 6508c2ecf20Sopenharmony_ci * 6518c2ecf20Sopenharmony_ci */ 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic int 6548c2ecf20Sopenharmony_cip9_virtio_create(struct p9_client *client, const char *devname, char *args) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct virtio_chan *chan; 6578c2ecf20Sopenharmony_ci int ret = -ENOENT; 6588c2ecf20Sopenharmony_ci int found = 0; 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (devname == NULL) 6618c2ecf20Sopenharmony_ci return -EINVAL; 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci mutex_lock(&virtio_9p_lock); 6648c2ecf20Sopenharmony_ci list_for_each_entry(chan, &virtio_chan_list, chan_list) { 6658c2ecf20Sopenharmony_ci if (!strcmp(devname, chan->tag)) { 6668c2ecf20Sopenharmony_ci if (!chan->inuse) { 6678c2ecf20Sopenharmony_ci chan->inuse = true; 6688c2ecf20Sopenharmony_ci found = 1; 6698c2ecf20Sopenharmony_ci break; 6708c2ecf20Sopenharmony_ci } 6718c2ecf20Sopenharmony_ci ret = -EBUSY; 6728c2ecf20Sopenharmony_ci } 6738c2ecf20Sopenharmony_ci } 6748c2ecf20Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci if (!found) { 6778c2ecf20Sopenharmony_ci pr_err("no channels available for device %s\n", devname); 6788c2ecf20Sopenharmony_ci return ret; 6798c2ecf20Sopenharmony_ci } 6808c2ecf20Sopenharmony_ci 6818c2ecf20Sopenharmony_ci client->trans = (void *)chan; 6828c2ecf20Sopenharmony_ci client->status = Connected; 6838c2ecf20Sopenharmony_ci chan->client = client; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci return 0; 6868c2ecf20Sopenharmony_ci} 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci/** 6898c2ecf20Sopenharmony_ci * p9_virtio_remove - clean up resources associated with a virtio device 6908c2ecf20Sopenharmony_ci * @vdev: virtio device to remove 6918c2ecf20Sopenharmony_ci * 6928c2ecf20Sopenharmony_ci */ 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic void p9_virtio_remove(struct virtio_device *vdev) 6958c2ecf20Sopenharmony_ci{ 6968c2ecf20Sopenharmony_ci struct virtio_chan *chan = vdev->priv; 6978c2ecf20Sopenharmony_ci unsigned long warning_time; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci mutex_lock(&virtio_9p_lock); 7008c2ecf20Sopenharmony_ci 7018c2ecf20Sopenharmony_ci /* Remove self from list so we don't get new users. */ 7028c2ecf20Sopenharmony_ci list_del(&chan->chan_list); 7038c2ecf20Sopenharmony_ci warning_time = jiffies; 7048c2ecf20Sopenharmony_ci 7058c2ecf20Sopenharmony_ci /* Wait for existing users to close. */ 7068c2ecf20Sopenharmony_ci while (chan->inuse) { 7078c2ecf20Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 7088c2ecf20Sopenharmony_ci msleep(250); 7098c2ecf20Sopenharmony_ci if (time_after(jiffies, warning_time + 10 * HZ)) { 7108c2ecf20Sopenharmony_ci dev_emerg(&vdev->dev, 7118c2ecf20Sopenharmony_ci "p9_virtio_remove: waiting for device in use.\n"); 7128c2ecf20Sopenharmony_ci warning_time = jiffies; 7138c2ecf20Sopenharmony_ci } 7148c2ecf20Sopenharmony_ci mutex_lock(&virtio_9p_lock); 7158c2ecf20Sopenharmony_ci } 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci mutex_unlock(&virtio_9p_lock); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci vdev->config->reset(vdev); 7208c2ecf20Sopenharmony_ci vdev->config->del_vqs(vdev); 7218c2ecf20Sopenharmony_ci 7228c2ecf20Sopenharmony_ci sysfs_remove_file(&(vdev->dev.kobj), &dev_attr_mount_tag.attr); 7238c2ecf20Sopenharmony_ci kobject_uevent(&(vdev->dev.kobj), KOBJ_CHANGE); 7248c2ecf20Sopenharmony_ci kfree(chan->tag); 7258c2ecf20Sopenharmony_ci kfree(chan->vc_wq); 7268c2ecf20Sopenharmony_ci kfree(chan); 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_ci} 7298c2ecf20Sopenharmony_ci 7308c2ecf20Sopenharmony_cistatic struct virtio_device_id id_table[] = { 7318c2ecf20Sopenharmony_ci { VIRTIO_ID_9P, VIRTIO_DEV_ANY_ID }, 7328c2ecf20Sopenharmony_ci { 0 }, 7338c2ecf20Sopenharmony_ci}; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_cistatic unsigned int features[] = { 7368c2ecf20Sopenharmony_ci VIRTIO_9P_MOUNT_TAG, 7378c2ecf20Sopenharmony_ci}; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci/* The standard "struct lguest_driver": */ 7408c2ecf20Sopenharmony_cistatic struct virtio_driver p9_virtio_drv = { 7418c2ecf20Sopenharmony_ci .feature_table = features, 7428c2ecf20Sopenharmony_ci .feature_table_size = ARRAY_SIZE(features), 7438c2ecf20Sopenharmony_ci .driver.name = KBUILD_MODNAME, 7448c2ecf20Sopenharmony_ci .driver.owner = THIS_MODULE, 7458c2ecf20Sopenharmony_ci .id_table = id_table, 7468c2ecf20Sopenharmony_ci .probe = p9_virtio_probe, 7478c2ecf20Sopenharmony_ci .remove = p9_virtio_remove, 7488c2ecf20Sopenharmony_ci}; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic struct p9_trans_module p9_virtio_trans = { 7518c2ecf20Sopenharmony_ci .name = "virtio", 7528c2ecf20Sopenharmony_ci .create = p9_virtio_create, 7538c2ecf20Sopenharmony_ci .close = p9_virtio_close, 7548c2ecf20Sopenharmony_ci .request = p9_virtio_request, 7558c2ecf20Sopenharmony_ci .zc_request = p9_virtio_zc_request, 7568c2ecf20Sopenharmony_ci .cancel = p9_virtio_cancel, 7578c2ecf20Sopenharmony_ci .cancelled = p9_virtio_cancelled, 7588c2ecf20Sopenharmony_ci /* 7598c2ecf20Sopenharmony_ci * We leave one entry for input and one entry for response 7608c2ecf20Sopenharmony_ci * headers. We also skip one more entry to accomodate, address 7618c2ecf20Sopenharmony_ci * that are not at page boundary, that can result in an extra 7628c2ecf20Sopenharmony_ci * page in zero copy. 7638c2ecf20Sopenharmony_ci */ 7648c2ecf20Sopenharmony_ci .maxsize = PAGE_SIZE * (VIRTQUEUE_NUM - 3), 7658c2ecf20Sopenharmony_ci .def = 1, 7668c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 7678c2ecf20Sopenharmony_ci}; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ci/* The standard init function */ 7708c2ecf20Sopenharmony_cistatic int __init p9_virtio_init(void) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci int rc; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&virtio_chan_list); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci v9fs_register_trans(&p9_virtio_trans); 7778c2ecf20Sopenharmony_ci rc = register_virtio_driver(&p9_virtio_drv); 7788c2ecf20Sopenharmony_ci if (rc) 7798c2ecf20Sopenharmony_ci v9fs_unregister_trans(&p9_virtio_trans); 7808c2ecf20Sopenharmony_ci 7818c2ecf20Sopenharmony_ci return rc; 7828c2ecf20Sopenharmony_ci} 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_cistatic void __exit p9_virtio_cleanup(void) 7858c2ecf20Sopenharmony_ci{ 7868c2ecf20Sopenharmony_ci unregister_virtio_driver(&p9_virtio_drv); 7878c2ecf20Sopenharmony_ci v9fs_unregister_trans(&p9_virtio_trans); 7888c2ecf20Sopenharmony_ci} 7898c2ecf20Sopenharmony_ci 7908c2ecf20Sopenharmony_cimodule_init(p9_virtio_init); 7918c2ecf20Sopenharmony_cimodule_exit(p9_virtio_cleanup); 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(virtio, id_table); 7948c2ecf20Sopenharmony_ciMODULE_AUTHOR("Eric Van Hensbergen <ericvh@gmail.com>"); 7958c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Virtio 9p Transport"); 7968c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 797