| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * Fd transport layer.  Includes deprecated socket layer. | 
|---|
| 4 | * | 
|---|
| 5 | *  Copyright (C) 2006 by Russ Cox <rsc@swtch.com> | 
|---|
| 6 | *  Copyright (C) 2004-2005 by Latchesar Ionkov <lucho@ionkov.net> | 
|---|
| 7 | *  Copyright (C) 2004-2008 by Eric Van Hensbergen <ericvh@gmail.com> | 
|---|
| 8 | *  Copyright (C) 1997-2002 by Ron Minnich <rminnich@sarnoff.com> | 
|---|
| 9 | */ | 
|---|
| 10 |  | 
|---|
| 11 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|---|
| 12 |  | 
|---|
| 13 | #include <linux/in.h> | 
|---|
| 14 | #include <linux/in6.h> | 
|---|
| 15 | #include <linux/module.h> | 
|---|
| 16 | #include <linux/net.h> | 
|---|
| 17 | #include <linux/ipv6.h> | 
|---|
| 18 | #include <linux/kthread.h> | 
|---|
| 19 | #include <linux/errno.h> | 
|---|
| 20 | #include <linux/kernel.h> | 
|---|
| 21 | #include <linux/un.h> | 
|---|
| 22 | #include <linux/uaccess.h> | 
|---|
| 23 | #include <linux/inet.h> | 
|---|
| 24 | #include <linux/file.h> | 
|---|
| 25 | #include <linux/parser.h> | 
|---|
| 26 | #include <linux/slab.h> | 
|---|
| 27 | #include <linux/seq_file.h> | 
|---|
| 28 | #include <net/9p/9p.h> | 
|---|
| 29 | #include <net/9p/client.h> | 
|---|
| 30 | #include <net/9p/transport.h> | 
|---|
| 31 |  | 
|---|
| 32 | #include <linux/syscalls.h> /* killme */ | 
|---|
| 33 |  | 
|---|
| 34 | #define P9_PORT 564 | 
|---|
| 35 | #define MAX_SOCK_BUF (1024*1024) | 
|---|
| 36 | #define MAXPOLLWADDR	2 | 
|---|
| 37 |  | 
|---|
| 38 | static struct p9_trans_module p9_tcp_trans; | 
|---|
| 39 | static struct p9_trans_module p9_fd_trans; | 
|---|
| 40 |  | 
|---|
| 41 | /** | 
|---|
| 42 | * struct p9_fd_opts - per-transport options | 
|---|
| 43 | * @rfd: file descriptor for reading (trans=fd) | 
|---|
| 44 | * @wfd: file descriptor for writing (trans=fd) | 
|---|
| 45 | * @port: port to connect to (trans=tcp) | 
|---|
| 46 | * @privport: port is privileged | 
|---|
| 47 | */ | 
|---|
| 48 |  | 
|---|
| 49 | struct p9_fd_opts { | 
|---|
| 50 | int rfd; | 
|---|
| 51 | int wfd; | 
|---|
| 52 | u16 port; | 
|---|
| 53 | bool privport; | 
|---|
| 54 | }; | 
|---|
| 55 |  | 
|---|
| 56 | /* | 
|---|
| 57 | * Option Parsing (code inspired by NFS code) | 
|---|
| 58 | *  - a little lazy - parse all fd-transport options | 
|---|
| 59 | */ | 
|---|
| 60 |  | 
|---|
| 61 | enum { | 
|---|
| 62 | /* Options that take integer arguments */ | 
|---|
| 63 | Opt_port, Opt_rfdno, Opt_wfdno, Opt_err, | 
|---|
| 64 | /* Options that take no arguments */ | 
|---|
| 65 | Opt_privport, | 
|---|
| 66 | }; | 
|---|
| 67 |  | 
|---|
| 68 | static const match_table_t tokens = { | 
|---|
| 69 | {Opt_port, "port=%u"}, | 
|---|
| 70 | {.token: Opt_rfdno, .pattern: "rfdno=%u"}, | 
|---|
| 71 | {.token: Opt_wfdno, .pattern: "wfdno=%u"}, | 
|---|
| 72 | {.token: Opt_privport, .pattern: "privport"}, | 
|---|
| 73 | {.token: Opt_err, NULL}, | 
|---|
| 74 | }; | 
|---|
| 75 |  | 
|---|
| 76 | enum { | 
|---|
| 77 | Rworksched = 1,		/* read work scheduled or running */ | 
|---|
| 78 | Rpending = 2,		/* can read */ | 
|---|
| 79 | Wworksched = 4,		/* write work scheduled or running */ | 
|---|
| 80 | Wpending = 8,		/* can write */ | 
|---|
| 81 | }; | 
|---|
| 82 |  | 
|---|
| 83 | struct p9_poll_wait { | 
|---|
| 84 | struct p9_conn *conn; | 
|---|
| 85 | wait_queue_entry_t wait; | 
|---|
| 86 | wait_queue_head_t *wait_addr; | 
|---|
| 87 | }; | 
|---|
| 88 |  | 
|---|
| 89 | /** | 
|---|
| 90 | * struct p9_conn - fd mux connection state information | 
|---|
| 91 | * @mux_list: list link for mux to manage multiple connections (?) | 
|---|
| 92 | * @client: reference to client instance for this connection | 
|---|
| 93 | * @err: error state | 
|---|
| 94 | * @req_lock: lock protecting req_list and requests statuses | 
|---|
| 95 | * @req_list: accounting for requests which have been sent | 
|---|
| 96 | * @unsent_req_list: accounting for requests that haven't been sent | 
|---|
| 97 | * @rreq: read request | 
|---|
| 98 | * @wreq: write request | 
|---|
| 99 | * @tmp_buf: temporary buffer to read in header | 
|---|
| 100 | * @rc: temporary fcall for reading current frame | 
|---|
| 101 | * @wpos: write position for current frame | 
|---|
| 102 | * @wsize: amount of data to write for current frame | 
|---|
| 103 | * @wbuf: current write buffer | 
|---|
| 104 | * @poll_pending_link: pending links to be polled per conn | 
|---|
| 105 | * @poll_wait: array of wait_q's for various worker threads | 
|---|
| 106 | * @pt: poll state | 
|---|
| 107 | * @rq: current read work | 
|---|
| 108 | * @wq: current write work | 
|---|
| 109 | * @wsched: ???? | 
|---|
| 110 | * | 
|---|
| 111 | */ | 
|---|
| 112 |  | 
|---|
| 113 | struct p9_conn { | 
|---|
| 114 | struct list_head mux_list; | 
|---|
| 115 | struct p9_client *client; | 
|---|
| 116 | int err; | 
|---|
| 117 | spinlock_t req_lock; | 
|---|
| 118 | struct list_head req_list; | 
|---|
| 119 | struct list_head unsent_req_list; | 
|---|
| 120 | struct p9_req_t *rreq; | 
|---|
| 121 | struct p9_req_t *wreq; | 
|---|
| 122 | char tmp_buf[P9_HDRSZ]; | 
|---|
| 123 | struct p9_fcall rc; | 
|---|
| 124 | int wpos; | 
|---|
| 125 | int wsize; | 
|---|
| 126 | char *wbuf; | 
|---|
| 127 | struct list_head poll_pending_link; | 
|---|
| 128 | struct p9_poll_wait poll_wait[MAXPOLLWADDR]; | 
|---|
| 129 | poll_table pt; | 
|---|
| 130 | struct work_struct rq; | 
|---|
| 131 | struct work_struct wq; | 
|---|
| 132 | unsigned long wsched; | 
|---|
| 133 | }; | 
|---|
| 134 |  | 
|---|
| 135 | /** | 
|---|
| 136 | * struct p9_trans_fd - transport state | 
|---|
| 137 | * @rd: reference to file to read from | 
|---|
| 138 | * @wr: reference of file to write to | 
|---|
| 139 | * @conn: connection state reference | 
|---|
| 140 | * | 
|---|
| 141 | */ | 
|---|
| 142 |  | 
|---|
| 143 | struct p9_trans_fd { | 
|---|
| 144 | struct file *rd; | 
|---|
| 145 | struct file *wr; | 
|---|
| 146 | struct p9_conn conn; | 
|---|
| 147 | }; | 
|---|
| 148 |  | 
|---|
| 149 | static void p9_poll_workfn(struct work_struct *work); | 
|---|
| 150 |  | 
|---|
| 151 | static DEFINE_SPINLOCK(p9_poll_lock); | 
|---|
| 152 | static LIST_HEAD(p9_poll_pending_list); | 
|---|
| 153 | static DECLARE_WORK(p9_poll_work, p9_poll_workfn); | 
|---|
| 154 |  | 
|---|
| 155 | static unsigned int p9_ipport_resv_min = P9_DEF_MIN_RESVPORT; | 
|---|
| 156 | static unsigned int p9_ipport_resv_max = P9_DEF_MAX_RESVPORT; | 
|---|
| 157 |  | 
|---|
| 158 | static void p9_mux_poll_stop(struct p9_conn *m) | 
|---|
| 159 | { | 
|---|
| 160 | unsigned long flags; | 
|---|
| 161 | int i; | 
|---|
| 162 |  | 
|---|
| 163 | for (i = 0; i < ARRAY_SIZE(m->poll_wait); i++) { | 
|---|
| 164 | struct p9_poll_wait *pwait = &m->poll_wait[i]; | 
|---|
| 165 |  | 
|---|
| 166 | if (pwait->wait_addr) { | 
|---|
| 167 | remove_wait_queue(wq_head: pwait->wait_addr, wq_entry: &pwait->wait); | 
|---|
| 168 | pwait->wait_addr = NULL; | 
|---|
| 169 | } | 
|---|
| 170 | } | 
|---|
| 171 |  | 
|---|
| 172 | spin_lock_irqsave(&p9_poll_lock, flags); | 
|---|
| 173 | list_del_init(entry: &m->poll_pending_link); | 
|---|
| 174 | spin_unlock_irqrestore(lock: &p9_poll_lock, flags); | 
|---|
| 175 |  | 
|---|
| 176 | flush_work(work: &p9_poll_work); | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | /** | 
|---|
| 180 | * p9_conn_cancel - cancel all pending requests with error | 
|---|
| 181 | * @m: mux data | 
|---|
| 182 | * @err: error code | 
|---|
| 183 | * | 
|---|
| 184 | */ | 
|---|
| 185 |  | 
|---|
| 186 | static void p9_conn_cancel(struct p9_conn *m, int err) | 
|---|
| 187 | { | 
|---|
| 188 | struct p9_req_t *req, *rtmp; | 
|---|
| 189 | LIST_HEAD(cancel_list); | 
|---|
| 190 |  | 
|---|
| 191 | p9_debug(P9_DEBUG_ERROR, "mux %p err %d\n", m, err); | 
|---|
| 192 |  | 
|---|
| 193 | spin_lock(lock: &m->req_lock); | 
|---|
| 194 |  | 
|---|
| 195 | if (READ_ONCE(m->err)) { | 
|---|
| 196 | spin_unlock(lock: &m->req_lock); | 
|---|
| 197 | return; | 
|---|
| 198 | } | 
|---|
| 199 |  | 
|---|
| 200 | WRITE_ONCE(m->err, err); | 
|---|
| 201 | ASSERT_EXCLUSIVE_WRITER(m->err); | 
|---|
| 202 |  | 
|---|
| 203 | list_for_each_entry_safe(req, rtmp, &m->req_list, req_list) { | 
|---|
| 204 | list_move(list: &req->req_list, head: &cancel_list); | 
|---|
| 205 | WRITE_ONCE(req->status, REQ_STATUS_ERROR); | 
|---|
| 206 | } | 
|---|
| 207 | list_for_each_entry_safe(req, rtmp, &m->unsent_req_list, req_list) { | 
|---|
| 208 | list_move(list: &req->req_list, head: &cancel_list); | 
|---|
| 209 | WRITE_ONCE(req->status, REQ_STATUS_ERROR); | 
|---|
| 210 | } | 
|---|
| 211 |  | 
|---|
| 212 | spin_unlock(lock: &m->req_lock); | 
|---|
| 213 |  | 
|---|
| 214 | list_for_each_entry_safe(req, rtmp, &cancel_list, req_list) { | 
|---|
| 215 | p9_debug(P9_DEBUG_ERROR, "call back req %p\n", req); | 
|---|
| 216 | list_del(entry: &req->req_list); | 
|---|
| 217 | if (!req->t_err) | 
|---|
| 218 | req->t_err = err; | 
|---|
| 219 | p9_client_cb(c: m->client, req, status: REQ_STATUS_ERROR); | 
|---|
| 220 | } | 
|---|
| 221 | } | 
|---|
| 222 |  | 
|---|
| 223 | static __poll_t | 
|---|
| 224 | p9_fd_poll(struct p9_client *client, struct poll_table_struct *pt, int *err) | 
|---|
| 225 | { | 
|---|
| 226 | __poll_t ret; | 
|---|
| 227 | struct p9_trans_fd *ts = NULL; | 
|---|
| 228 |  | 
|---|
| 229 | if (client && client->status == Connected) | 
|---|
| 230 | ts = client->trans; | 
|---|
| 231 |  | 
|---|
| 232 | if (!ts) { | 
|---|
| 233 | if (err) | 
|---|
| 234 | *err = -EREMOTEIO; | 
|---|
| 235 | return EPOLLERR; | 
|---|
| 236 | } | 
|---|
| 237 |  | 
|---|
| 238 | ret = vfs_poll(file: ts->rd, pt); | 
|---|
| 239 | if (ts->rd != ts->wr) | 
|---|
| 240 | ret = (ret & ~EPOLLOUT) | (vfs_poll(file: ts->wr, pt) & ~EPOLLIN); | 
|---|
| 241 | return ret; | 
|---|
| 242 | } | 
|---|
| 243 |  | 
|---|
| 244 | /** | 
|---|
| 245 | * p9_fd_read- read from a fd | 
|---|
| 246 | * @client: client instance | 
|---|
| 247 | * @v: buffer to receive data into | 
|---|
| 248 | * @len: size of receive buffer | 
|---|
| 249 | * | 
|---|
| 250 | */ | 
|---|
| 251 |  | 
|---|
| 252 | static int p9_fd_read(struct p9_client *client, void *v, int len) | 
|---|
| 253 | { | 
|---|
| 254 | int ret; | 
|---|
| 255 | struct p9_trans_fd *ts = NULL; | 
|---|
| 256 | loff_t pos; | 
|---|
| 257 |  | 
|---|
| 258 | if (client && client->status != Disconnected) | 
|---|
| 259 | ts = client->trans; | 
|---|
| 260 |  | 
|---|
| 261 | if (!ts) | 
|---|
| 262 | return -EREMOTEIO; | 
|---|
| 263 |  | 
|---|
| 264 | if (!(ts->rd->f_flags & O_NONBLOCK)) | 
|---|
| 265 | p9_debug(P9_DEBUG_ERROR, "blocking read ...\n"); | 
|---|
| 266 |  | 
|---|
| 267 | pos = ts->rd->f_pos; | 
|---|
| 268 | ret = kernel_read(ts->rd, v, len, &pos); | 
|---|
| 269 | if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN) | 
|---|
| 270 | client->status = Disconnected; | 
|---|
| 271 | return ret; | 
|---|
| 272 | } | 
|---|
| 273 |  | 
|---|
| 274 | /** | 
|---|
| 275 | * p9_read_work - called when there is some data to be read from a transport | 
|---|
| 276 | * @work: container of work to be done | 
|---|
| 277 | * | 
|---|
| 278 | */ | 
|---|
| 279 |  | 
|---|
| 280 | static void p9_read_work(struct work_struct *work) | 
|---|
| 281 | { | 
|---|
| 282 | __poll_t n; | 
|---|
| 283 | int err; | 
|---|
| 284 | struct p9_conn *m; | 
|---|
| 285 |  | 
|---|
| 286 | m = container_of(work, struct p9_conn, rq); | 
|---|
| 287 |  | 
|---|
| 288 | if (READ_ONCE(m->err) < 0) | 
|---|
| 289 | return; | 
|---|
| 290 |  | 
|---|
| 291 | p9_debug(P9_DEBUG_TRANS, "start mux %p pos %zd\n", m, m->rc.offset); | 
|---|
| 292 |  | 
|---|
| 293 | if (!m->rc.sdata) { | 
|---|
| 294 | m->rc.sdata = m->tmp_buf; | 
|---|
| 295 | m->rc.offset = 0; | 
|---|
| 296 | m->rc.capacity = P9_HDRSZ; /* start by reading header */ | 
|---|
| 297 | } | 
|---|
| 298 |  | 
|---|
| 299 | clear_bit(nr: Rpending, addr: &m->wsched); | 
|---|
| 300 | p9_debug(P9_DEBUG_TRANS, "read mux %p pos %zd size: %zd = %zd\n", | 
|---|
| 301 | m, m->rc.offset, m->rc.capacity, | 
|---|
| 302 | m->rc.capacity - m->rc.offset); | 
|---|
| 303 | err = p9_fd_read(client: m->client, v: m->rc.sdata + m->rc.offset, | 
|---|
| 304 | len: m->rc.capacity - m->rc.offset); | 
|---|
| 305 | p9_debug(P9_DEBUG_TRANS, "mux %p got %d bytes\n", m, err); | 
|---|
| 306 | if (err == -EAGAIN) | 
|---|
| 307 | goto end_clear; | 
|---|
| 308 |  | 
|---|
| 309 | if (err <= 0) | 
|---|
| 310 | goto error; | 
|---|
| 311 |  | 
|---|
| 312 | m->rc.offset += err; | 
|---|
| 313 |  | 
|---|
| 314 | /* header read in */ | 
|---|
| 315 | if ((!m->rreq) && (m->rc.offset == m->rc.capacity)) { | 
|---|
| 316 | p9_debug(P9_DEBUG_TRANS, "got new header\n"); | 
|---|
| 317 |  | 
|---|
| 318 | /* Header size */ | 
|---|
| 319 | m->rc.size = P9_HDRSZ; | 
|---|
| 320 | err = p9_parse_header(pdu: &m->rc, size: &m->rc.size, NULL, NULL, rewind: 0); | 
|---|
| 321 | if (err) { | 
|---|
| 322 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 323 | "error parsing header: %d\n", err); | 
|---|
| 324 | goto error; | 
|---|
| 325 | } | 
|---|
| 326 |  | 
|---|
| 327 | p9_debug(P9_DEBUG_TRANS, | 
|---|
| 328 | "mux %p pkt: size: %d bytes tag: %d\n", | 
|---|
| 329 | m, m->rc.size, m->rc.tag); | 
|---|
| 330 |  | 
|---|
| 331 | m->rreq = p9_tag_lookup(c: m->client, tag: m->rc.tag); | 
|---|
| 332 | if (!m->rreq || (m->rreq->status != REQ_STATUS_SENT)) { | 
|---|
| 333 | p9_debug(P9_DEBUG_ERROR, "Unexpected packet tag %d\n", | 
|---|
| 334 | m->rc.tag); | 
|---|
| 335 | err = -EIO; | 
|---|
| 336 | goto error; | 
|---|
| 337 | } | 
|---|
| 338 |  | 
|---|
| 339 | if (m->rc.size > m->rreq->rc.capacity) { | 
|---|
| 340 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 341 | "requested packet size too big: %d for tag %d with capacity %zd\n", | 
|---|
| 342 | m->rc.size, m->rc.tag, m->rreq->rc.capacity); | 
|---|
| 343 | err = -EIO; | 
|---|
| 344 | goto error; | 
|---|
| 345 | } | 
|---|
| 346 |  | 
|---|
| 347 | if (!m->rreq->rc.sdata) { | 
|---|
| 348 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 349 | "No recv fcall for tag %d (req %p), disconnecting!\n", | 
|---|
| 350 | m->rc.tag, m->rreq); | 
|---|
| 351 | p9_req_put(c: m->client, r: m->rreq); | 
|---|
| 352 | m->rreq = NULL; | 
|---|
| 353 | err = -EIO; | 
|---|
| 354 | goto error; | 
|---|
| 355 | } | 
|---|
| 356 | m->rc.sdata = m->rreq->rc.sdata; | 
|---|
| 357 | memcpy(to: m->rc.sdata, from: m->tmp_buf, len: m->rc.capacity); | 
|---|
| 358 | m->rc.capacity = m->rc.size; | 
|---|
| 359 | } | 
|---|
| 360 |  | 
|---|
| 361 | /* packet is read in | 
|---|
| 362 | * not an else because some packets (like clunk) have no payload | 
|---|
| 363 | */ | 
|---|
| 364 | if ((m->rreq) && (m->rc.offset == m->rc.capacity)) { | 
|---|
| 365 | p9_debug(P9_DEBUG_TRANS, "got new packet\n"); | 
|---|
| 366 | m->rreq->rc.size = m->rc.offset; | 
|---|
| 367 | spin_lock(lock: &m->req_lock); | 
|---|
| 368 | if (m->rreq->status == REQ_STATUS_SENT) { | 
|---|
| 369 | list_del(entry: &m->rreq->req_list); | 
|---|
| 370 | p9_client_cb(c: m->client, req: m->rreq, status: REQ_STATUS_RCVD); | 
|---|
| 371 | } else if (m->rreq->status == REQ_STATUS_FLSHD) { | 
|---|
| 372 | /* Ignore replies associated with a cancelled request. */ | 
|---|
| 373 | p9_debug(P9_DEBUG_TRANS, | 
|---|
| 374 | "Ignore replies associated with a cancelled request\n"); | 
|---|
| 375 | } else { | 
|---|
| 376 | spin_unlock(lock: &m->req_lock); | 
|---|
| 377 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 378 | "Request tag %d errored out while we were reading the reply\n", | 
|---|
| 379 | m->rc.tag); | 
|---|
| 380 | err = -EIO; | 
|---|
| 381 | goto error; | 
|---|
| 382 | } | 
|---|
| 383 | spin_unlock(lock: &m->req_lock); | 
|---|
| 384 | m->rc.sdata = NULL; | 
|---|
| 385 | m->rc.offset = 0; | 
|---|
| 386 | m->rc.capacity = 0; | 
|---|
| 387 | p9_req_put(c: m->client, r: m->rreq); | 
|---|
| 388 | m->rreq = NULL; | 
|---|
| 389 | } | 
|---|
| 390 |  | 
|---|
| 391 | end_clear: | 
|---|
| 392 | clear_bit(nr: Rworksched, addr: &m->wsched); | 
|---|
| 393 |  | 
|---|
| 394 | if (!list_empty(head: &m->req_list)) { | 
|---|
| 395 | if (test_and_clear_bit(nr: Rpending, addr: &m->wsched)) | 
|---|
| 396 | n = EPOLLIN; | 
|---|
| 397 | else | 
|---|
| 398 | n = p9_fd_poll(client: m->client, NULL, NULL); | 
|---|
| 399 |  | 
|---|
| 400 | if ((n & EPOLLIN) && !test_and_set_bit(nr: Rworksched, addr: &m->wsched)) { | 
|---|
| 401 | p9_debug(P9_DEBUG_TRANS, "sched read work %p\n", m); | 
|---|
| 402 | schedule_work(work: &m->rq); | 
|---|
| 403 | } | 
|---|
| 404 | } | 
|---|
| 405 |  | 
|---|
| 406 | return; | 
|---|
| 407 | error: | 
|---|
| 408 | p9_conn_cancel(m, err); | 
|---|
| 409 | clear_bit(nr: Rworksched, addr: &m->wsched); | 
|---|
| 410 | } | 
|---|
| 411 |  | 
|---|
| 412 | /** | 
|---|
| 413 | * p9_fd_write - write to a socket | 
|---|
| 414 | * @client: client instance | 
|---|
| 415 | * @v: buffer to send data from | 
|---|
| 416 | * @len: size of send buffer | 
|---|
| 417 | * | 
|---|
| 418 | */ | 
|---|
| 419 |  | 
|---|
| 420 | static int p9_fd_write(struct p9_client *client, void *v, int len) | 
|---|
| 421 | { | 
|---|
| 422 | ssize_t ret; | 
|---|
| 423 | struct p9_trans_fd *ts = NULL; | 
|---|
| 424 |  | 
|---|
| 425 | if (client && client->status != Disconnected) | 
|---|
| 426 | ts = client->trans; | 
|---|
| 427 |  | 
|---|
| 428 | if (!ts) | 
|---|
| 429 | return -EREMOTEIO; | 
|---|
| 430 |  | 
|---|
| 431 | if (!(ts->wr->f_flags & O_NONBLOCK)) | 
|---|
| 432 | p9_debug(P9_DEBUG_ERROR, "blocking write ...\n"); | 
|---|
| 433 |  | 
|---|
| 434 | ret = kernel_write(ts->wr, v, len, &ts->wr->f_pos); | 
|---|
| 435 | if (ret <= 0 && ret != -ERESTARTSYS && ret != -EAGAIN) | 
|---|
| 436 | client->status = Disconnected; | 
|---|
| 437 | return ret; | 
|---|
| 438 | } | 
|---|
| 439 |  | 
|---|
| 440 | /** | 
|---|
| 441 | * p9_write_work - called when a transport can send some data | 
|---|
| 442 | * @work: container for work to be done | 
|---|
| 443 | * | 
|---|
| 444 | */ | 
|---|
| 445 |  | 
|---|
| 446 | static void p9_write_work(struct work_struct *work) | 
|---|
| 447 | { | 
|---|
| 448 | __poll_t n; | 
|---|
| 449 | int err; | 
|---|
| 450 | struct p9_conn *m; | 
|---|
| 451 | struct p9_req_t *req; | 
|---|
| 452 |  | 
|---|
| 453 | m = container_of(work, struct p9_conn, wq); | 
|---|
| 454 |  | 
|---|
| 455 | if (READ_ONCE(m->err) < 0) { | 
|---|
| 456 | clear_bit(nr: Wworksched, addr: &m->wsched); | 
|---|
| 457 | return; | 
|---|
| 458 | } | 
|---|
| 459 |  | 
|---|
| 460 | if (!m->wsize) { | 
|---|
| 461 | spin_lock(lock: &m->req_lock); | 
|---|
| 462 | if (list_empty(head: &m->unsent_req_list)) { | 
|---|
| 463 | clear_bit(nr: Wworksched, addr: &m->wsched); | 
|---|
| 464 | spin_unlock(lock: &m->req_lock); | 
|---|
| 465 | return; | 
|---|
| 466 | } | 
|---|
| 467 |  | 
|---|
| 468 | req = list_entry(m->unsent_req_list.next, struct p9_req_t, | 
|---|
| 469 | req_list); | 
|---|
| 470 | WRITE_ONCE(req->status, REQ_STATUS_SENT); | 
|---|
| 471 | p9_debug(P9_DEBUG_TRANS, "move req %p\n", req); | 
|---|
| 472 | list_move_tail(list: &req->req_list, head: &m->req_list); | 
|---|
| 473 |  | 
|---|
| 474 | m->wbuf = req->tc.sdata; | 
|---|
| 475 | m->wsize = req->tc.size; | 
|---|
| 476 | m->wpos = 0; | 
|---|
| 477 | p9_req_get(r: req); | 
|---|
| 478 | m->wreq = req; | 
|---|
| 479 | spin_unlock(lock: &m->req_lock); | 
|---|
| 480 | } | 
|---|
| 481 |  | 
|---|
| 482 | p9_debug(P9_DEBUG_TRANS, "mux %p pos %d size %d\n", | 
|---|
| 483 | m, m->wpos, m->wsize); | 
|---|
| 484 | clear_bit(nr: Wpending, addr: &m->wsched); | 
|---|
| 485 | err = p9_fd_write(client: m->client, v: m->wbuf + m->wpos, len: m->wsize - m->wpos); | 
|---|
| 486 | p9_debug(P9_DEBUG_TRANS, "mux %p sent %d bytes\n", m, err); | 
|---|
| 487 | if (err == -EAGAIN) | 
|---|
| 488 | goto end_clear; | 
|---|
| 489 |  | 
|---|
| 490 |  | 
|---|
| 491 | if (err < 0) | 
|---|
| 492 | goto error; | 
|---|
| 493 | else if (err == 0) { | 
|---|
| 494 | err = -EREMOTEIO; | 
|---|
| 495 | goto error; | 
|---|
| 496 | } | 
|---|
| 497 |  | 
|---|
| 498 | m->wpos += err; | 
|---|
| 499 | if (m->wpos == m->wsize) { | 
|---|
| 500 | m->wpos = m->wsize = 0; | 
|---|
| 501 | p9_req_put(c: m->client, r: m->wreq); | 
|---|
| 502 | m->wreq = NULL; | 
|---|
| 503 | } | 
|---|
| 504 |  | 
|---|
| 505 | end_clear: | 
|---|
| 506 | clear_bit(nr: Wworksched, addr: &m->wsched); | 
|---|
| 507 |  | 
|---|
| 508 | if (m->wsize || !list_empty(head: &m->unsent_req_list)) { | 
|---|
| 509 | if (test_and_clear_bit(nr: Wpending, addr: &m->wsched)) | 
|---|
| 510 | n = EPOLLOUT; | 
|---|
| 511 | else | 
|---|
| 512 | n = p9_fd_poll(client: m->client, NULL, NULL); | 
|---|
| 513 |  | 
|---|
| 514 | if ((n & EPOLLOUT) && | 
|---|
| 515 | !test_and_set_bit(nr: Wworksched, addr: &m->wsched)) { | 
|---|
| 516 | p9_debug(P9_DEBUG_TRANS, "sched write work %p\n", m); | 
|---|
| 517 | schedule_work(work: &m->wq); | 
|---|
| 518 | } | 
|---|
| 519 | } | 
|---|
| 520 |  | 
|---|
| 521 | return; | 
|---|
| 522 |  | 
|---|
| 523 | error: | 
|---|
| 524 | p9_conn_cancel(m, err); | 
|---|
| 525 | clear_bit(nr: Wworksched, addr: &m->wsched); | 
|---|
| 526 | } | 
|---|
| 527 |  | 
|---|
| 528 | static int p9_pollwake(wait_queue_entry_t *wait, unsigned int mode, int sync, void *key) | 
|---|
| 529 | { | 
|---|
| 530 | struct p9_poll_wait *pwait = | 
|---|
| 531 | container_of(wait, struct p9_poll_wait, wait); | 
|---|
| 532 | struct p9_conn *m = pwait->conn; | 
|---|
| 533 | unsigned long flags; | 
|---|
| 534 |  | 
|---|
| 535 | spin_lock_irqsave(&p9_poll_lock, flags); | 
|---|
| 536 | if (list_empty(head: &m->poll_pending_link)) | 
|---|
| 537 | list_add_tail(new: &m->poll_pending_link, head: &p9_poll_pending_list); | 
|---|
| 538 | spin_unlock_irqrestore(lock: &p9_poll_lock, flags); | 
|---|
| 539 |  | 
|---|
| 540 | schedule_work(work: &p9_poll_work); | 
|---|
| 541 | return 1; | 
|---|
| 542 | } | 
|---|
| 543 |  | 
|---|
| 544 | /** | 
|---|
| 545 | * p9_pollwait - add poll task to the wait queue | 
|---|
| 546 | * @filp: file pointer being polled | 
|---|
| 547 | * @wait_address: wait_q to block on | 
|---|
| 548 | * @p: poll state | 
|---|
| 549 | * | 
|---|
| 550 | * called by files poll operation to add v9fs-poll task to files wait queue | 
|---|
| 551 | */ | 
|---|
| 552 |  | 
|---|
| 553 | static void | 
|---|
| 554 | p9_pollwait(struct file *filp, wait_queue_head_t *wait_address, poll_table *p) | 
|---|
| 555 | { | 
|---|
| 556 | struct p9_conn *m = container_of(p, struct p9_conn, pt); | 
|---|
| 557 | struct p9_poll_wait *pwait = NULL; | 
|---|
| 558 | int i; | 
|---|
| 559 |  | 
|---|
| 560 | for (i = 0; i < ARRAY_SIZE(m->poll_wait); i++) { | 
|---|
| 561 | if (m->poll_wait[i].wait_addr == NULL) { | 
|---|
| 562 | pwait = &m->poll_wait[i]; | 
|---|
| 563 | break; | 
|---|
| 564 | } | 
|---|
| 565 | } | 
|---|
| 566 |  | 
|---|
| 567 | if (!pwait) { | 
|---|
| 568 | p9_debug(P9_DEBUG_ERROR, "not enough wait_address slots\n"); | 
|---|
| 569 | return; | 
|---|
| 570 | } | 
|---|
| 571 |  | 
|---|
| 572 | pwait->conn = m; | 
|---|
| 573 | pwait->wait_addr = wait_address; | 
|---|
| 574 | init_waitqueue_func_entry(wq_entry: &pwait->wait, func: p9_pollwake); | 
|---|
| 575 | add_wait_queue(wq_head: wait_address, wq_entry: &pwait->wait); | 
|---|
| 576 | } | 
|---|
| 577 |  | 
|---|
| 578 | /** | 
|---|
| 579 | * p9_conn_create - initialize the per-session mux data | 
|---|
| 580 | * @client: client instance | 
|---|
| 581 | * | 
|---|
| 582 | * Note: Creates the polling task if this is the first session. | 
|---|
| 583 | */ | 
|---|
| 584 |  | 
|---|
| 585 | static void p9_conn_create(struct p9_client *client) | 
|---|
| 586 | { | 
|---|
| 587 | __poll_t n; | 
|---|
| 588 | struct p9_trans_fd *ts = client->trans; | 
|---|
| 589 | struct p9_conn *m = &ts->conn; | 
|---|
| 590 |  | 
|---|
| 591 | p9_debug(P9_DEBUG_TRANS, "client %p msize %d\n", client, client->msize); | 
|---|
| 592 |  | 
|---|
| 593 | INIT_LIST_HEAD(list: &m->mux_list); | 
|---|
| 594 | m->client = client; | 
|---|
| 595 |  | 
|---|
| 596 | spin_lock_init(&m->req_lock); | 
|---|
| 597 | INIT_LIST_HEAD(list: &m->req_list); | 
|---|
| 598 | INIT_LIST_HEAD(list: &m->unsent_req_list); | 
|---|
| 599 | INIT_WORK(&m->rq, p9_read_work); | 
|---|
| 600 | INIT_WORK(&m->wq, p9_write_work); | 
|---|
| 601 | INIT_LIST_HEAD(list: &m->poll_pending_link); | 
|---|
| 602 | init_poll_funcptr(pt: &m->pt, qproc: p9_pollwait); | 
|---|
| 603 |  | 
|---|
| 604 | n = p9_fd_poll(client, pt: &m->pt, NULL); | 
|---|
| 605 | if (n & EPOLLIN) { | 
|---|
| 606 | p9_debug(P9_DEBUG_TRANS, "mux %p can read\n", m); | 
|---|
| 607 | set_bit(nr: Rpending, addr: &m->wsched); | 
|---|
| 608 | } | 
|---|
| 609 |  | 
|---|
| 610 | if (n & EPOLLOUT) { | 
|---|
| 611 | p9_debug(P9_DEBUG_TRANS, "mux %p can write\n", m); | 
|---|
| 612 | set_bit(nr: Wpending, addr: &m->wsched); | 
|---|
| 613 | } | 
|---|
| 614 | } | 
|---|
| 615 |  | 
|---|
| 616 | /** | 
|---|
| 617 | * p9_poll_mux - polls a mux and schedules read or write works if necessary | 
|---|
| 618 | * @m: connection to poll | 
|---|
| 619 | * | 
|---|
| 620 | */ | 
|---|
| 621 |  | 
|---|
| 622 | static void p9_poll_mux(struct p9_conn *m) | 
|---|
| 623 | { | 
|---|
| 624 | __poll_t n; | 
|---|
| 625 | int err = -ECONNRESET; | 
|---|
| 626 |  | 
|---|
| 627 | if (READ_ONCE(m->err) < 0) | 
|---|
| 628 | return; | 
|---|
| 629 |  | 
|---|
| 630 | n = p9_fd_poll(client: m->client, NULL, err: &err); | 
|---|
| 631 | if (n & (EPOLLERR | EPOLLHUP | EPOLLNVAL)) { | 
|---|
| 632 | p9_debug(P9_DEBUG_TRANS, "error mux %p err %d\n", m, n); | 
|---|
| 633 | p9_conn_cancel(m, err); | 
|---|
| 634 | } | 
|---|
| 635 |  | 
|---|
| 636 | if (n & EPOLLIN) { | 
|---|
| 637 | set_bit(nr: Rpending, addr: &m->wsched); | 
|---|
| 638 | p9_debug(P9_DEBUG_TRANS, "mux %p can read\n", m); | 
|---|
| 639 | if (!test_and_set_bit(nr: Rworksched, addr: &m->wsched)) { | 
|---|
| 640 | p9_debug(P9_DEBUG_TRANS, "sched read work %p\n", m); | 
|---|
| 641 | schedule_work(work: &m->rq); | 
|---|
| 642 | } | 
|---|
| 643 | } | 
|---|
| 644 |  | 
|---|
| 645 | if (n & EPOLLOUT) { | 
|---|
| 646 | set_bit(nr: Wpending, addr: &m->wsched); | 
|---|
| 647 | p9_debug(P9_DEBUG_TRANS, "mux %p can write\n", m); | 
|---|
| 648 | if ((m->wsize || !list_empty(head: &m->unsent_req_list)) && | 
|---|
| 649 | !test_and_set_bit(nr: Wworksched, addr: &m->wsched)) { | 
|---|
| 650 | p9_debug(P9_DEBUG_TRANS, "sched write work %p\n", m); | 
|---|
| 651 | schedule_work(work: &m->wq); | 
|---|
| 652 | } | 
|---|
| 653 | } | 
|---|
| 654 | } | 
|---|
| 655 |  | 
|---|
| 656 | /** | 
|---|
| 657 | * p9_fd_request - send 9P request | 
|---|
| 658 | * The function can sleep until the request is scheduled for sending. | 
|---|
| 659 | * The function can be interrupted. Return from the function is not | 
|---|
| 660 | * a guarantee that the request is sent successfully. | 
|---|
| 661 | * | 
|---|
| 662 | * @client: client instance | 
|---|
| 663 | * @req: request to be sent | 
|---|
| 664 | * | 
|---|
| 665 | */ | 
|---|
| 666 |  | 
|---|
| 667 | static int p9_fd_request(struct p9_client *client, struct p9_req_t *req) | 
|---|
| 668 | { | 
|---|
| 669 | int err; | 
|---|
| 670 | struct p9_trans_fd *ts = client->trans; | 
|---|
| 671 | struct p9_conn *m = &ts->conn; | 
|---|
| 672 |  | 
|---|
| 673 | p9_debug(P9_DEBUG_TRANS, "mux %p task %p tcall %p id %d\n", | 
|---|
| 674 | m, current, &req->tc, req->tc.id); | 
|---|
| 675 |  | 
|---|
| 676 | spin_lock(lock: &m->req_lock); | 
|---|
| 677 |  | 
|---|
| 678 | err = READ_ONCE(m->err); | 
|---|
| 679 | if (err < 0) { | 
|---|
| 680 | spin_unlock(lock: &m->req_lock); | 
|---|
| 681 | return err; | 
|---|
| 682 | } | 
|---|
| 683 |  | 
|---|
| 684 | WRITE_ONCE(req->status, REQ_STATUS_UNSENT); | 
|---|
| 685 | list_add_tail(new: &req->req_list, head: &m->unsent_req_list); | 
|---|
| 686 | spin_unlock(lock: &m->req_lock); | 
|---|
| 687 |  | 
|---|
| 688 | p9_poll_mux(m); | 
|---|
| 689 |  | 
|---|
| 690 | return 0; | 
|---|
| 691 | } | 
|---|
| 692 |  | 
|---|
| 693 | static int p9_fd_cancel(struct p9_client *client, struct p9_req_t *req) | 
|---|
| 694 | { | 
|---|
| 695 | struct p9_trans_fd *ts = client->trans; | 
|---|
| 696 | struct p9_conn *m = &ts->conn; | 
|---|
| 697 | int ret = 1; | 
|---|
| 698 |  | 
|---|
| 699 | p9_debug(P9_DEBUG_TRANS, "client %p req %p\n", client, req); | 
|---|
| 700 |  | 
|---|
| 701 | spin_lock(lock: &m->req_lock); | 
|---|
| 702 |  | 
|---|
| 703 | if (req->status == REQ_STATUS_UNSENT) { | 
|---|
| 704 | list_del(entry: &req->req_list); | 
|---|
| 705 | WRITE_ONCE(req->status, REQ_STATUS_FLSHD); | 
|---|
| 706 | p9_req_put(c: client, r: req); | 
|---|
| 707 | ret = 0; | 
|---|
| 708 | } | 
|---|
| 709 | spin_unlock(lock: &m->req_lock); | 
|---|
| 710 |  | 
|---|
| 711 | return ret; | 
|---|
| 712 | } | 
|---|
| 713 |  | 
|---|
| 714 | static int p9_fd_cancelled(struct p9_client *client, struct p9_req_t *req) | 
|---|
| 715 | { | 
|---|
| 716 | struct p9_trans_fd *ts = client->trans; | 
|---|
| 717 | struct p9_conn *m = &ts->conn; | 
|---|
| 718 |  | 
|---|
| 719 | p9_debug(P9_DEBUG_TRANS, "client %p req %p\n", client, req); | 
|---|
| 720 |  | 
|---|
| 721 | spin_lock(lock: &m->req_lock); | 
|---|
| 722 | /* Ignore cancelled request if status changed since the request was | 
|---|
| 723 | * processed in p9_client_flush() | 
|---|
| 724 | */ | 
|---|
| 725 | if (req->status != REQ_STATUS_SENT) { | 
|---|
| 726 | spin_unlock(lock: &m->req_lock); | 
|---|
| 727 | return 0; | 
|---|
| 728 | } | 
|---|
| 729 |  | 
|---|
| 730 | /* we haven't received a response for oldreq, | 
|---|
| 731 | * remove it from the list. | 
|---|
| 732 | */ | 
|---|
| 733 | list_del(entry: &req->req_list); | 
|---|
| 734 | WRITE_ONCE(req->status, REQ_STATUS_FLSHD); | 
|---|
| 735 | spin_unlock(lock: &m->req_lock); | 
|---|
| 736 |  | 
|---|
| 737 | p9_req_put(c: client, r: req); | 
|---|
| 738 |  | 
|---|
| 739 | return 0; | 
|---|
| 740 | } | 
|---|
| 741 |  | 
|---|
| 742 | static int p9_fd_show_options(struct seq_file *m, struct p9_client *clnt) | 
|---|
| 743 | { | 
|---|
| 744 | if (clnt->trans_mod == &p9_tcp_trans) { | 
|---|
| 745 | if (clnt->trans_opts.tcp.port != P9_PORT) | 
|---|
| 746 | seq_printf(m, fmt: ",port=%u", clnt->trans_opts.tcp.port); | 
|---|
| 747 | } else if (clnt->trans_mod == &p9_fd_trans) { | 
|---|
| 748 | if (clnt->trans_opts.fd.rfd != ~0) | 
|---|
| 749 | seq_printf(m, fmt: ",rfd=%u", clnt->trans_opts.fd.rfd); | 
|---|
| 750 | if (clnt->trans_opts.fd.wfd != ~0) | 
|---|
| 751 | seq_printf(m, fmt: ",wfd=%u", clnt->trans_opts.fd.wfd); | 
|---|
| 752 | } | 
|---|
| 753 | return 0; | 
|---|
| 754 | } | 
|---|
| 755 |  | 
|---|
| 756 | /** | 
|---|
| 757 | * parse_opts - parse mount options into p9_fd_opts structure | 
|---|
| 758 | * @params: options string passed from mount | 
|---|
| 759 | * @opts: fd transport-specific structure to parse options into | 
|---|
| 760 | * | 
|---|
| 761 | * Returns 0 upon success, -ERRNO upon failure | 
|---|
| 762 | */ | 
|---|
| 763 |  | 
|---|
| 764 | static int parse_opts(char *params, struct p9_fd_opts *opts) | 
|---|
| 765 | { | 
|---|
| 766 | char *p; | 
|---|
| 767 | substring_t args[MAX_OPT_ARGS]; | 
|---|
| 768 | int option; | 
|---|
| 769 | char *options, *tmp_options; | 
|---|
| 770 |  | 
|---|
| 771 | opts->port = P9_PORT; | 
|---|
| 772 | opts->rfd = ~0; | 
|---|
| 773 | opts->wfd = ~0; | 
|---|
| 774 | opts->privport = false; | 
|---|
| 775 |  | 
|---|
| 776 | if (!params) | 
|---|
| 777 | return 0; | 
|---|
| 778 |  | 
|---|
| 779 | tmp_options = kstrdup(s: params, GFP_KERNEL); | 
|---|
| 780 | if (!tmp_options) { | 
|---|
| 781 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 782 | "failed to allocate copy of option string\n"); | 
|---|
| 783 | return -ENOMEM; | 
|---|
| 784 | } | 
|---|
| 785 | options = tmp_options; | 
|---|
| 786 |  | 
|---|
| 787 | while ((p = strsep(&options, ",")) != NULL) { | 
|---|
| 788 | int token; | 
|---|
| 789 | int r; | 
|---|
| 790 | if (!*p) | 
|---|
| 791 | continue; | 
|---|
| 792 | token = match_token(p, table: tokens, args); | 
|---|
| 793 | if ((token != Opt_err) && (token != Opt_privport)) { | 
|---|
| 794 | r = match_int(&args[0], result: &option); | 
|---|
| 795 | if (r < 0) { | 
|---|
| 796 | p9_debug(P9_DEBUG_ERROR, | 
|---|
| 797 | "integer field, but no integer?\n"); | 
|---|
| 798 | continue; | 
|---|
| 799 | } | 
|---|
| 800 | } | 
|---|
| 801 | switch (token) { | 
|---|
| 802 | case Opt_port: | 
|---|
| 803 | opts->port = option; | 
|---|
| 804 | break; | 
|---|
| 805 | case Opt_rfdno: | 
|---|
| 806 | opts->rfd = option; | 
|---|
| 807 | break; | 
|---|
| 808 | case Opt_wfdno: | 
|---|
| 809 | opts->wfd = option; | 
|---|
| 810 | break; | 
|---|
| 811 | case Opt_privport: | 
|---|
| 812 | opts->privport = true; | 
|---|
| 813 | break; | 
|---|
| 814 | default: | 
|---|
| 815 | continue; | 
|---|
| 816 | } | 
|---|
| 817 | } | 
|---|
| 818 |  | 
|---|
| 819 | kfree(objp: tmp_options); | 
|---|
| 820 | return 0; | 
|---|
| 821 | } | 
|---|
| 822 |  | 
|---|
| 823 | static int p9_fd_open(struct p9_client *client, int rfd, int wfd) | 
|---|
| 824 | { | 
|---|
| 825 | struct p9_trans_fd *ts = kzalloc(sizeof(struct p9_trans_fd), | 
|---|
| 826 | GFP_KERNEL); | 
|---|
| 827 | if (!ts) | 
|---|
| 828 | return -ENOMEM; | 
|---|
| 829 |  | 
|---|
| 830 | ts->rd = fget(fd: rfd); | 
|---|
| 831 | if (!ts->rd) | 
|---|
| 832 | goto out_free_ts; | 
|---|
| 833 | if (!(ts->rd->f_mode & FMODE_READ)) | 
|---|
| 834 | goto out_put_rd; | 
|---|
| 835 | /* Prevent workers from hanging on IO when fd is a pipe. | 
|---|
| 836 | * It's technically possible for userspace or concurrent mounts to | 
|---|
| 837 | * modify this flag concurrently, which will likely result in a | 
|---|
| 838 | * broken filesystem. However, just having bad flags here should | 
|---|
| 839 | * not crash the kernel or cause any other sort of bug, so mark this | 
|---|
| 840 | * particular data race as intentional so that tooling (like KCSAN) | 
|---|
| 841 | * can allow it and detect further problems. | 
|---|
| 842 | */ | 
|---|
| 843 | data_race(ts->rd->f_flags |= O_NONBLOCK); | 
|---|
| 844 | ts->wr = fget(fd: wfd); | 
|---|
| 845 | if (!ts->wr) | 
|---|
| 846 | goto out_put_rd; | 
|---|
| 847 | if (!(ts->wr->f_mode & FMODE_WRITE)) | 
|---|
| 848 | goto out_put_wr; | 
|---|
| 849 | data_race(ts->wr->f_flags |= O_NONBLOCK); | 
|---|
| 850 |  | 
|---|
| 851 | client->trans = ts; | 
|---|
| 852 | client->status = Connected; | 
|---|
| 853 |  | 
|---|
| 854 | return 0; | 
|---|
| 855 |  | 
|---|
| 856 | out_put_wr: | 
|---|
| 857 | fput(ts->wr); | 
|---|
| 858 | out_put_rd: | 
|---|
| 859 | fput(ts->rd); | 
|---|
| 860 | out_free_ts: | 
|---|
| 861 | kfree(objp: ts); | 
|---|
| 862 | return -EIO; | 
|---|
| 863 | } | 
|---|
| 864 |  | 
|---|
| 865 | static int p9_socket_open(struct p9_client *client, struct socket *csocket) | 
|---|
| 866 | { | 
|---|
| 867 | struct p9_trans_fd *p; | 
|---|
| 868 | struct file *file; | 
|---|
| 869 |  | 
|---|
| 870 | p = kzalloc(sizeof(struct p9_trans_fd), GFP_KERNEL); | 
|---|
| 871 | if (!p) { | 
|---|
| 872 | sock_release(sock: csocket); | 
|---|
| 873 | return -ENOMEM; | 
|---|
| 874 | } | 
|---|
| 875 |  | 
|---|
| 876 | csocket->sk->sk_allocation = GFP_NOIO; | 
|---|
| 877 | csocket->sk->sk_use_task_frag = false; | 
|---|
| 878 | file = sock_alloc_file(sock: csocket, flags: 0, NULL); | 
|---|
| 879 | if (IS_ERR(ptr: file)) { | 
|---|
| 880 | pr_err( "%s (%d): failed to map fd\n", | 
|---|
| 881 | __func__, task_pid_nr(current)); | 
|---|
| 882 | kfree(objp: p); | 
|---|
| 883 | return PTR_ERR(ptr: file); | 
|---|
| 884 | } | 
|---|
| 885 |  | 
|---|
| 886 | get_file(f: file); | 
|---|
| 887 | p->wr = p->rd = file; | 
|---|
| 888 | client->trans = p; | 
|---|
| 889 | client->status = Connected; | 
|---|
| 890 |  | 
|---|
| 891 | p->rd->f_flags |= O_NONBLOCK; | 
|---|
| 892 |  | 
|---|
| 893 | p9_conn_create(client); | 
|---|
| 894 | return 0; | 
|---|
| 895 | } | 
|---|
| 896 |  | 
|---|
| 897 | /** | 
|---|
| 898 | * p9_conn_destroy - cancels all pending requests of mux | 
|---|
| 899 | * @m: mux to destroy | 
|---|
| 900 | * | 
|---|
| 901 | */ | 
|---|
| 902 |  | 
|---|
| 903 | static void p9_conn_destroy(struct p9_conn *m) | 
|---|
| 904 | { | 
|---|
| 905 | p9_debug(P9_DEBUG_TRANS, "mux %p prev %p next %p\n", | 
|---|
| 906 | m, m->mux_list.prev, m->mux_list.next); | 
|---|
| 907 |  | 
|---|
| 908 | p9_mux_poll_stop(m); | 
|---|
| 909 | cancel_work_sync(work: &m->rq); | 
|---|
| 910 | if (m->rreq) { | 
|---|
| 911 | p9_req_put(c: m->client, r: m->rreq); | 
|---|
| 912 | m->rreq = NULL; | 
|---|
| 913 | } | 
|---|
| 914 | cancel_work_sync(work: &m->wq); | 
|---|
| 915 | if (m->wreq) { | 
|---|
| 916 | p9_req_put(c: m->client, r: m->wreq); | 
|---|
| 917 | m->wreq = NULL; | 
|---|
| 918 | } | 
|---|
| 919 |  | 
|---|
| 920 | p9_conn_cancel(m, err: -ECONNRESET); | 
|---|
| 921 |  | 
|---|
| 922 | m->client = NULL; | 
|---|
| 923 | } | 
|---|
| 924 |  | 
|---|
| 925 | /** | 
|---|
| 926 | * p9_fd_close - shutdown file descriptor transport | 
|---|
| 927 | * @client: client instance | 
|---|
| 928 | * | 
|---|
| 929 | */ | 
|---|
| 930 |  | 
|---|
| 931 | static void p9_fd_close(struct p9_client *client) | 
|---|
| 932 | { | 
|---|
| 933 | struct p9_trans_fd *ts; | 
|---|
| 934 |  | 
|---|
| 935 | if (!client) | 
|---|
| 936 | return; | 
|---|
| 937 |  | 
|---|
| 938 | ts = client->trans; | 
|---|
| 939 | if (!ts) | 
|---|
| 940 | return; | 
|---|
| 941 |  | 
|---|
| 942 | client->status = Disconnected; | 
|---|
| 943 |  | 
|---|
| 944 | p9_conn_destroy(m: &ts->conn); | 
|---|
| 945 |  | 
|---|
| 946 | if (ts->rd) | 
|---|
| 947 | fput(ts->rd); | 
|---|
| 948 | if (ts->wr) | 
|---|
| 949 | fput(ts->wr); | 
|---|
| 950 |  | 
|---|
| 951 | kfree(objp: ts); | 
|---|
| 952 | } | 
|---|
| 953 |  | 
|---|
| 954 | static int p9_bind_privport(struct socket *sock) | 
|---|
| 955 | { | 
|---|
| 956 | struct sockaddr_storage stor = { 0 }; | 
|---|
| 957 | int port, err = -EINVAL; | 
|---|
| 958 |  | 
|---|
| 959 | stor.ss_family = sock->ops->family; | 
|---|
| 960 | if (stor.ss_family == AF_INET) | 
|---|
| 961 | ((struct sockaddr_in *)&stor)->sin_addr.s_addr = htonl(INADDR_ANY); | 
|---|
| 962 | else | 
|---|
| 963 | ((struct sockaddr_in6 *)&stor)->sin6_addr = in6addr_any; | 
|---|
| 964 | for (port = p9_ipport_resv_max; port >= p9_ipport_resv_min; port--) { | 
|---|
| 965 | if (stor.ss_family == AF_INET) | 
|---|
| 966 | ((struct sockaddr_in *)&stor)->sin_port = htons((ushort)port); | 
|---|
| 967 | else | 
|---|
| 968 | ((struct sockaddr_in6 *)&stor)->sin6_port = htons((ushort)port); | 
|---|
| 969 | err = kernel_bind(sock, addr: (struct sockaddr *)&stor, addrlen: sizeof(stor)); | 
|---|
| 970 | if (err != -EADDRINUSE) | 
|---|
| 971 | break; | 
|---|
| 972 | } | 
|---|
| 973 | return err; | 
|---|
| 974 | } | 
|---|
| 975 |  | 
|---|
| 976 | static int | 
|---|
| 977 | p9_fd_create_tcp(struct p9_client *client, const char *addr, char *args) | 
|---|
| 978 | { | 
|---|
| 979 | int err; | 
|---|
| 980 | char port_str[6]; | 
|---|
| 981 | struct socket *csocket; | 
|---|
| 982 | struct sockaddr_storage stor = { 0 }; | 
|---|
| 983 | struct p9_fd_opts opts; | 
|---|
| 984 |  | 
|---|
| 985 | err = parse_opts(params: args, opts: &opts); | 
|---|
| 986 | if (err < 0) | 
|---|
| 987 | return err; | 
|---|
| 988 |  | 
|---|
| 989 | if (!addr) | 
|---|
| 990 | return -EINVAL; | 
|---|
| 991 |  | 
|---|
| 992 | sprintf(buf: port_str, fmt: "%u", opts.port); | 
|---|
| 993 | err = inet_pton_with_scope(current->nsproxy->net_ns, AF_UNSPEC, src: addr, | 
|---|
| 994 | port: port_str, addr: &stor); | 
|---|
| 995 | if (err < 0) | 
|---|
| 996 | return err; | 
|---|
| 997 |  | 
|---|
| 998 | csocket = NULL; | 
|---|
| 999 |  | 
|---|
| 1000 | client->trans_opts.tcp.port = opts.port; | 
|---|
| 1001 | client->trans_opts.tcp.privport = opts.privport; | 
|---|
| 1002 | err = __sock_create(current->nsproxy->net_ns, family: stor.ss_family, | 
|---|
| 1003 | type: SOCK_STREAM, IPPROTO_TCP, res: &csocket, kern: 1); | 
|---|
| 1004 | if (err) { | 
|---|
| 1005 | pr_err( "%s (%d): problem creating socket\n", | 
|---|
| 1006 | __func__, task_pid_nr(current)); | 
|---|
| 1007 | return err; | 
|---|
| 1008 | } | 
|---|
| 1009 |  | 
|---|
| 1010 | if (opts.privport) { | 
|---|
| 1011 | err = p9_bind_privport(sock: csocket); | 
|---|
| 1012 | if (err < 0) { | 
|---|
| 1013 | pr_err( "%s (%d): problem binding to privport\n", | 
|---|
| 1014 | __func__, task_pid_nr(current)); | 
|---|
| 1015 | sock_release(sock: csocket); | 
|---|
| 1016 | return err; | 
|---|
| 1017 | } | 
|---|
| 1018 | } | 
|---|
| 1019 |  | 
|---|
| 1020 | err = READ_ONCE(csocket->ops)->connect(csocket, | 
|---|
| 1021 | (struct sockaddr *)&stor, | 
|---|
| 1022 | sizeof(stor), 0); | 
|---|
| 1023 | if (err < 0) { | 
|---|
| 1024 | pr_err( "%s (%d): problem connecting socket to %s\n", | 
|---|
| 1025 | __func__, task_pid_nr(current), addr); | 
|---|
| 1026 | sock_release(sock: csocket); | 
|---|
| 1027 | return err; | 
|---|
| 1028 | } | 
|---|
| 1029 |  | 
|---|
| 1030 | return p9_socket_open(client, csocket); | 
|---|
| 1031 | } | 
|---|
| 1032 |  | 
|---|
| 1033 | static int | 
|---|
| 1034 | p9_fd_create_unix(struct p9_client *client, const char *addr, char *args) | 
|---|
| 1035 | { | 
|---|
| 1036 | int err; | 
|---|
| 1037 | struct socket *csocket; | 
|---|
| 1038 | struct sockaddr_un sun_server; | 
|---|
| 1039 |  | 
|---|
| 1040 | csocket = NULL; | 
|---|
| 1041 |  | 
|---|
| 1042 | if (!addr || !strlen(addr)) | 
|---|
| 1043 | return -EINVAL; | 
|---|
| 1044 |  | 
|---|
| 1045 | if (strlen(addr) >= UNIX_PATH_MAX) { | 
|---|
| 1046 | pr_err( "%s (%d): address too long: %s\n", | 
|---|
| 1047 | __func__, task_pid_nr(current), addr); | 
|---|
| 1048 | return -ENAMETOOLONG; | 
|---|
| 1049 | } | 
|---|
| 1050 |  | 
|---|
| 1051 | sun_server.sun_family = PF_UNIX; | 
|---|
| 1052 | strcpy(sun_server.sun_path, addr); | 
|---|
| 1053 | err = __sock_create(current->nsproxy->net_ns, PF_UNIX, | 
|---|
| 1054 | type: SOCK_STREAM, proto: 0, res: &csocket, kern: 1); | 
|---|
| 1055 | if (err < 0) { | 
|---|
| 1056 | pr_err( "%s (%d): problem creating socket\n", | 
|---|
| 1057 | __func__, task_pid_nr(current)); | 
|---|
| 1058 |  | 
|---|
| 1059 | return err; | 
|---|
| 1060 | } | 
|---|
| 1061 | err = READ_ONCE(csocket->ops)->connect(csocket, (struct sockaddr *)&sun_server, | 
|---|
| 1062 | sizeof(struct sockaddr_un) - 1, 0); | 
|---|
| 1063 | if (err < 0) { | 
|---|
| 1064 | pr_err( "%s (%d): problem connecting socket: %s: %d\n", | 
|---|
| 1065 | __func__, task_pid_nr(current), addr, err); | 
|---|
| 1066 | sock_release(sock: csocket); | 
|---|
| 1067 | return err; | 
|---|
| 1068 | } | 
|---|
| 1069 |  | 
|---|
| 1070 | return p9_socket_open(client, csocket); | 
|---|
| 1071 | } | 
|---|
| 1072 |  | 
|---|
| 1073 | static int | 
|---|
| 1074 | p9_fd_create(struct p9_client *client, const char *addr, char *args) | 
|---|
| 1075 | { | 
|---|
| 1076 | int err; | 
|---|
| 1077 | struct p9_fd_opts opts; | 
|---|
| 1078 |  | 
|---|
| 1079 | err = parse_opts(params: args, opts: &opts); | 
|---|
| 1080 | if (err < 0) | 
|---|
| 1081 | return err; | 
|---|
| 1082 | client->trans_opts.fd.rfd = opts.rfd; | 
|---|
| 1083 | client->trans_opts.fd.wfd = opts.wfd; | 
|---|
| 1084 |  | 
|---|
| 1085 | if (opts.rfd == ~0 || opts.wfd == ~0) { | 
|---|
| 1086 | pr_err( "Insufficient options for proto=fd\n"); | 
|---|
| 1087 | return -ENOPROTOOPT; | 
|---|
| 1088 | } | 
|---|
| 1089 |  | 
|---|
| 1090 | err = p9_fd_open(client, rfd: opts.rfd, wfd: opts.wfd); | 
|---|
| 1091 | if (err < 0) | 
|---|
| 1092 | return err; | 
|---|
| 1093 |  | 
|---|
| 1094 | p9_conn_create(client); | 
|---|
| 1095 |  | 
|---|
| 1096 | return 0; | 
|---|
| 1097 | } | 
|---|
| 1098 |  | 
|---|
| 1099 | static struct p9_trans_module p9_tcp_trans = { | 
|---|
| 1100 | .name = "tcp", | 
|---|
| 1101 | .maxsize = MAX_SOCK_BUF, | 
|---|
| 1102 | .pooled_rbuffers = false, | 
|---|
| 1103 | .def = 0, | 
|---|
| 1104 | .create = p9_fd_create_tcp, | 
|---|
| 1105 | .close = p9_fd_close, | 
|---|
| 1106 | .request = p9_fd_request, | 
|---|
| 1107 | .cancel = p9_fd_cancel, | 
|---|
| 1108 | .cancelled = p9_fd_cancelled, | 
|---|
| 1109 | .show_options = p9_fd_show_options, | 
|---|
| 1110 | .owner = THIS_MODULE, | 
|---|
| 1111 | }; | 
|---|
| 1112 | MODULE_ALIAS_9P( "tcp"); | 
|---|
| 1113 |  | 
|---|
| 1114 | static struct p9_trans_module p9_unix_trans = { | 
|---|
| 1115 | .name = "unix", | 
|---|
| 1116 | .maxsize = MAX_SOCK_BUF, | 
|---|
| 1117 | .def = 0, | 
|---|
| 1118 | .create = p9_fd_create_unix, | 
|---|
| 1119 | .close = p9_fd_close, | 
|---|
| 1120 | .request = p9_fd_request, | 
|---|
| 1121 | .cancel = p9_fd_cancel, | 
|---|
| 1122 | .cancelled = p9_fd_cancelled, | 
|---|
| 1123 | .show_options = p9_fd_show_options, | 
|---|
| 1124 | .owner = THIS_MODULE, | 
|---|
| 1125 | }; | 
|---|
| 1126 | MODULE_ALIAS_9P( "unix"); | 
|---|
| 1127 |  | 
|---|
| 1128 | static struct p9_trans_module p9_fd_trans = { | 
|---|
| 1129 | .name = "fd", | 
|---|
| 1130 | .maxsize = MAX_SOCK_BUF, | 
|---|
| 1131 | .def = 0, | 
|---|
| 1132 | .create = p9_fd_create, | 
|---|
| 1133 | .close = p9_fd_close, | 
|---|
| 1134 | .request = p9_fd_request, | 
|---|
| 1135 | .cancel = p9_fd_cancel, | 
|---|
| 1136 | .cancelled = p9_fd_cancelled, | 
|---|
| 1137 | .show_options = p9_fd_show_options, | 
|---|
| 1138 | .owner = THIS_MODULE, | 
|---|
| 1139 | }; | 
|---|
| 1140 | MODULE_ALIAS_9P( "fd"); | 
|---|
| 1141 |  | 
|---|
| 1142 | /** | 
|---|
| 1143 | * p9_poll_workfn - poll worker thread | 
|---|
| 1144 | * @work: work queue | 
|---|
| 1145 | * | 
|---|
| 1146 | * polls all v9fs transports for new events and queues the appropriate | 
|---|
| 1147 | * work to the work queue | 
|---|
| 1148 | * | 
|---|
| 1149 | */ | 
|---|
| 1150 |  | 
|---|
| 1151 | static void p9_poll_workfn(struct work_struct *work) | 
|---|
| 1152 | { | 
|---|
| 1153 | unsigned long flags; | 
|---|
| 1154 |  | 
|---|
| 1155 | p9_debug(P9_DEBUG_TRANS, "start %p\n", current); | 
|---|
| 1156 |  | 
|---|
| 1157 | spin_lock_irqsave(&p9_poll_lock, flags); | 
|---|
| 1158 | while (!list_empty(head: &p9_poll_pending_list)) { | 
|---|
| 1159 | struct p9_conn *conn = list_first_entry(&p9_poll_pending_list, | 
|---|
| 1160 | struct p9_conn, | 
|---|
| 1161 | poll_pending_link); | 
|---|
| 1162 | list_del_init(entry: &conn->poll_pending_link); | 
|---|
| 1163 | spin_unlock_irqrestore(lock: &p9_poll_lock, flags); | 
|---|
| 1164 |  | 
|---|
| 1165 | p9_poll_mux(m: conn); | 
|---|
| 1166 |  | 
|---|
| 1167 | spin_lock_irqsave(&p9_poll_lock, flags); | 
|---|
| 1168 | } | 
|---|
| 1169 | spin_unlock_irqrestore(lock: &p9_poll_lock, flags); | 
|---|
| 1170 |  | 
|---|
| 1171 | p9_debug(P9_DEBUG_TRANS, "finish\n"); | 
|---|
| 1172 | } | 
|---|
| 1173 |  | 
|---|
| 1174 | static int __init p9_trans_fd_init(void) | 
|---|
| 1175 | { | 
|---|
| 1176 | v9fs_register_trans(m: &p9_tcp_trans); | 
|---|
| 1177 | v9fs_register_trans(m: &p9_unix_trans); | 
|---|
| 1178 | v9fs_register_trans(m: &p9_fd_trans); | 
|---|
| 1179 |  | 
|---|
| 1180 | return 0; | 
|---|
| 1181 | } | 
|---|
| 1182 |  | 
|---|
| 1183 | static void __exit p9_trans_fd_exit(void) | 
|---|
| 1184 | { | 
|---|
| 1185 | flush_work(work: &p9_poll_work); | 
|---|
| 1186 | v9fs_unregister_trans(m: &p9_tcp_trans); | 
|---|
| 1187 | v9fs_unregister_trans(m: &p9_unix_trans); | 
|---|
| 1188 | v9fs_unregister_trans(m: &p9_fd_trans); | 
|---|
| 1189 | } | 
|---|
| 1190 |  | 
|---|
| 1191 | module_init(p9_trans_fd_init); | 
|---|
| 1192 | module_exit(p9_trans_fd_exit); | 
|---|
| 1193 |  | 
|---|
| 1194 | MODULE_AUTHOR( "Eric Van Hensbergen <ericvh@gmail.com>"); | 
|---|
| 1195 | MODULE_DESCRIPTION( "Filedescriptor Transport for 9P"); | 
|---|
| 1196 | MODULE_LICENSE( "GPL"); | 
|---|
| 1197 |  | 
|---|