mux.c 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  1. /*
  2. * linux/fs/9p/mux.c
  3. *
  4. * Protocol Multiplexer
  5. *
  6. * Copyright (C) 2004 by Eric Van Hensbergen <ericvh@gmail.com>
  7. * Copyright (C) 2004 by Latchesar Ionkov <lucho@ionkov.net>
  8. *
  9. * This program is free software; you can redistribute it and/or modify
  10. * it under the terms of the GNU General Public License as published by
  11. * the Free Software Foundation; either version 2 of the License, or
  12. * (at your option) any later version.
  13. *
  14. * This program is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. * GNU General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU General Public License
  20. * along with this program; if not, write to:
  21. * Free Software Foundation
  22. * 51 Franklin Street, Fifth Floor
  23. * Boston, MA 02111-1301 USA
  24. *
  25. */
  26. #include <linux/config.h>
  27. #include <linux/module.h>
  28. #include <linux/errno.h>
  29. #include <linux/fs.h>
  30. #include <linux/kthread.h>
  31. #include <linux/idr.h>
  32. #include "debug.h"
  33. #include "v9fs.h"
  34. #include "9p.h"
  35. #include "transport.h"
  36. #include "conv.h"
  37. #include "mux.h"
  38. /**
  39. * dprintcond - print condition of session info
  40. * @v9ses: session info structure
  41. * @req: RPC request structure
  42. *
  43. */
  44. static inline int
  45. dprintcond(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
  46. {
  47. dprintk(DEBUG_MUX, "condition: %d, %p\n", v9ses->transport->status,
  48. req->rcall);
  49. return 0;
  50. }
  51. /**
  52. * xread - force read of a certain number of bytes
  53. * @v9ses: session info structure
  54. * @ptr: pointer to buffer
  55. * @sz: number of bytes to read
  56. *
  57. * Chuck Cranor CS-533 project1
  58. */
  59. static int xread(struct v9fs_session_info *v9ses, void *ptr, unsigned long sz)
  60. {
  61. int rd = 0;
  62. int ret = 0;
  63. while (rd < sz) {
  64. ret = v9ses->transport->read(v9ses->transport, ptr, sz - rd);
  65. if (ret <= 0) {
  66. dprintk(DEBUG_ERROR, "xread errno %d\n", ret);
  67. return ret;
  68. }
  69. rd += ret;
  70. ptr += ret;
  71. }
  72. return (rd);
  73. }
  74. /**
  75. * read_message - read a full 9P2000 fcall packet
  76. * @v9ses: session info structure
  77. * @rcall: fcall structure to read into
  78. * @rcalllen: size of fcall buffer
  79. *
  80. */
  81. static int
  82. read_message(struct v9fs_session_info *v9ses,
  83. struct v9fs_fcall *rcall, int rcalllen)
  84. {
  85. unsigned char buf[4];
  86. void *data;
  87. int size = 0;
  88. int res = 0;
  89. res = xread(v9ses, buf, sizeof(buf));
  90. if (res < 0) {
  91. dprintk(DEBUG_ERROR,
  92. "Reading of count field failed returned: %d\n", res);
  93. return res;
  94. }
  95. if (res < 4) {
  96. dprintk(DEBUG_ERROR,
  97. "Reading of count field failed returned: %d\n", res);
  98. return -EIO;
  99. }
  100. size = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
  101. dprintk(DEBUG_MUX, "got a packet count: %d\n", size);
  102. /* adjust for the four bytes of size */
  103. size -= 4;
  104. if (size > v9ses->maxdata) {
  105. dprintk(DEBUG_ERROR, "packet too big: %d\n", size);
  106. return -E2BIG;
  107. }
  108. data = kmalloc(size, GFP_KERNEL);
  109. if (!data) {
  110. eprintk(KERN_WARNING, "out of memory\n");
  111. return -ENOMEM;
  112. }
  113. res = xread(v9ses, data, size);
  114. if (res < size) {
  115. dprintk(DEBUG_ERROR, "Reading of fcall failed returned: %d\n",
  116. res);
  117. kfree(data);
  118. return res;
  119. }
  120. /* we now have an in-memory string that is the reply.
  121. * deserialize it. There is very little to go wrong at this point
  122. * save for v9fs_alloc errors.
  123. */
  124. res = v9fs_deserialize_fcall(v9ses, size, data, v9ses->maxdata,
  125. rcall, rcalllen);
  126. kfree(data);
  127. if (res < 0)
  128. return res;
  129. return 0;
  130. }
  131. /**
  132. * v9fs_recv - receive an RPC response for a particular tag
  133. * @v9ses: session info structure
  134. * @req: RPC request structure
  135. *
  136. */
  137. static int v9fs_recv(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
  138. {
  139. int ret = 0;
  140. dprintk(DEBUG_MUX, "waiting for response: %d\n", req->tcall->tag);
  141. ret = wait_event_interruptible(v9ses->read_wait,
  142. ((v9ses->transport->status != Connected) ||
  143. (req->rcall != 0) || dprintcond(v9ses, req)));
  144. dprintk(DEBUG_MUX, "got it: rcall %p\n", req->rcall);
  145. if (v9ses->transport->status == Disconnected)
  146. return -ECONNRESET;
  147. if (ret == 0) {
  148. spin_lock(&v9ses->muxlock);
  149. list_del(&req->next);
  150. spin_unlock(&v9ses->muxlock);
  151. }
  152. return ret;
  153. }
  154. /**
  155. * v9fs_send - send a 9P request
  156. * @v9ses: session info structure
  157. * @req: RPC request to send
  158. *
  159. */
  160. static int v9fs_send(struct v9fs_session_info *v9ses, struct v9fs_rpcreq *req)
  161. {
  162. int ret = -1;
  163. void *data = NULL;
  164. struct v9fs_fcall *tcall = req->tcall;
  165. data = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL);
  166. if (!data)
  167. return -ENOMEM;
  168. tcall->size = 0; /* enforce size recalculation */
  169. ret =
  170. v9fs_serialize_fcall(v9ses, tcall, data,
  171. v9ses->maxdata + V9FS_IOHDRSZ);
  172. if (ret < 0)
  173. goto free_data;
  174. spin_lock(&v9ses->muxlock);
  175. list_add(&req->next, &v9ses->mux_fcalls);
  176. spin_unlock(&v9ses->muxlock);
  177. dprintk(DEBUG_MUX, "sending message: tag %d size %d\n", tcall->tag,
  178. tcall->size);
  179. ret = v9ses->transport->write(v9ses->transport, data, tcall->size);
  180. if (ret != tcall->size) {
  181. spin_lock(&v9ses->muxlock);
  182. list_del(&req->next);
  183. kfree(req->rcall);
  184. spin_unlock(&v9ses->muxlock);
  185. if (ret >= 0)
  186. ret = -EREMOTEIO;
  187. } else
  188. ret = 0;
  189. free_data:
  190. kfree(data);
  191. return ret;
  192. }
  193. /**
  194. * v9fs_mux_rpc - send a request, receive a response
  195. * @v9ses: session info structure
  196. * @tcall: fcall to send
  197. * @rcall: buffer to place response into
  198. *
  199. */
  200. long
  201. v9fs_mux_rpc(struct v9fs_session_info *v9ses, struct v9fs_fcall *tcall,
  202. struct v9fs_fcall **rcall)
  203. {
  204. int tid = -1;
  205. struct v9fs_fcall *fcall = NULL;
  206. struct v9fs_rpcreq req;
  207. int ret = -1;
  208. if (!v9ses)
  209. return -EINVAL;
  210. if (rcall)
  211. *rcall = NULL;
  212. if (tcall->id != TVERSION) {
  213. tid = v9fs_get_idpool(&v9ses->tidpool);
  214. if (tid < 0)
  215. return -ENOMEM;
  216. }
  217. tcall->tag = tid;
  218. req.tcall = tcall;
  219. req.rcall = NULL;
  220. ret = v9fs_send(v9ses, &req);
  221. if (ret < 0) {
  222. if (tcall->id != TVERSION)
  223. v9fs_put_idpool(tid, &v9ses->tidpool);
  224. dprintk(DEBUG_MUX, "error %d\n", ret);
  225. return ret;
  226. }
  227. ret = v9fs_recv(v9ses, &req);
  228. fcall = req.rcall;
  229. dprintk(DEBUG_MUX, "received: tag=%x, ret=%d\n", tcall->tag, ret);
  230. if (ret == -ERESTARTSYS) {
  231. if (v9ses->transport->status != Disconnected
  232. && tcall->id != TFLUSH) {
  233. unsigned long flags;
  234. dprintk(DEBUG_MUX, "flushing the tag: %d\n",
  235. tcall->tag);
  236. clear_thread_flag(TIF_SIGPENDING);
  237. v9fs_t_flush(v9ses, tcall->tag);
  238. spin_lock_irqsave(&current->sighand->siglock, flags);
  239. recalc_sigpending();
  240. spin_unlock_irqrestore(&current->sighand->siglock,
  241. flags);
  242. dprintk(DEBUG_MUX, "flushing done\n");
  243. }
  244. goto release_req;
  245. } else if (ret < 0)
  246. goto release_req;
  247. if (!fcall)
  248. ret = -EIO;
  249. else {
  250. if (fcall->id == RERROR) {
  251. ret = v9fs_errstr2errno(fcall->params.rerror.error);
  252. if (ret == 0) { /* string match failed */
  253. if (fcall->params.rerror.errno)
  254. ret = -(fcall->params.rerror.errno);
  255. else
  256. ret = -ESERVERFAULT;
  257. }
  258. } else if (fcall->id != tcall->id + 1) {
  259. dprintk(DEBUG_ERROR,
  260. "fcall mismatch: expected %d, got %d\n",
  261. tcall->id + 1, fcall->id);
  262. ret = -EIO;
  263. }
  264. }
  265. release_req:
  266. if (tcall->id != TVERSION)
  267. v9fs_put_idpool(tid, &v9ses->tidpool);
  268. if (rcall)
  269. *rcall = fcall;
  270. else
  271. kfree(fcall);
  272. return ret;
  273. }
  274. /**
  275. * v9fs_recvproc - kproc to handle demultiplexing responses
  276. * @data: session info structure
  277. *
  278. */
  279. static int v9fs_recvproc(void *data)
  280. {
  281. struct v9fs_session_info *v9ses = (struct v9fs_session_info *)data;
  282. struct v9fs_fcall *rcall = NULL;
  283. struct v9fs_rpcreq *rptr;
  284. struct v9fs_rpcreq *req;
  285. struct v9fs_rpcreq *rreq;
  286. int err = 0;
  287. allow_signal(SIGKILL);
  288. set_current_state(TASK_INTERRUPTIBLE);
  289. complete(&v9ses->proccmpl);
  290. while (!kthread_should_stop() && err >= 0) {
  291. req = rptr = rreq = NULL;
  292. rcall = kmalloc(v9ses->maxdata + V9FS_IOHDRSZ, GFP_KERNEL);
  293. if (!rcall) {
  294. eprintk(KERN_ERR, "no memory for buffers\n");
  295. break;
  296. }
  297. err = read_message(v9ses, rcall, v9ses->maxdata + V9FS_IOHDRSZ);
  298. if (err < 0) {
  299. kfree(rcall);
  300. break;
  301. }
  302. spin_lock(&v9ses->muxlock);
  303. list_for_each_entry_safe(rreq, rptr, &v9ses->mux_fcalls, next) {
  304. if (rreq->tcall->tag == rcall->tag) {
  305. req = rreq;
  306. req->rcall = rcall;
  307. break;
  308. }
  309. }
  310. if (req && (req->tcall->id == TFLUSH)) {
  311. struct v9fs_rpcreq *treq = NULL;
  312. list_for_each_entry_safe(treq, rptr, &v9ses->mux_fcalls, next) {
  313. if (treq->tcall->tag ==
  314. req->tcall->params.tflush.oldtag) {
  315. list_del(&rptr->next);
  316. kfree(treq->rcall);
  317. break;
  318. }
  319. }
  320. }
  321. spin_unlock(&v9ses->muxlock);
  322. if (!req) {
  323. dprintk(DEBUG_ERROR,
  324. "unexpected response: id %d tag %d\n",
  325. rcall->id, rcall->tag);
  326. kfree(rcall);
  327. }
  328. wake_up_all(&v9ses->read_wait);
  329. set_current_state(TASK_INTERRUPTIBLE);
  330. }
  331. /* Inform all pending processes about the failure */
  332. wake_up_all(&v9ses->read_wait);
  333. if (signal_pending(current))
  334. complete(&v9ses->proccmpl);
  335. dprintk(DEBUG_MUX, "recvproc: end\n");
  336. v9ses->recvproc = NULL;
  337. return err >= 0;
  338. }
  339. /**
  340. * v9fs_mux_init - initialize multiplexer (spawn kproc)
  341. * @v9ses: session info structure
  342. * @dev_name: mount device information (to create unique kproc)
  343. *
  344. */
  345. int v9fs_mux_init(struct v9fs_session_info *v9ses, const char *dev_name)
  346. {
  347. char procname[60];
  348. strncpy(procname, dev_name, sizeof(procname));
  349. procname[sizeof(procname) - 1] = 0;
  350. init_waitqueue_head(&v9ses->read_wait);
  351. init_completion(&v9ses->fcread);
  352. init_completion(&v9ses->proccmpl);
  353. spin_lock_init(&v9ses->muxlock);
  354. INIT_LIST_HEAD(&v9ses->mux_fcalls);
  355. v9ses->recvproc = NULL;
  356. v9ses->curfcall = NULL;
  357. v9ses->recvproc = kthread_create(v9fs_recvproc, v9ses,
  358. "v9fs_recvproc %s", procname);
  359. if (IS_ERR(v9ses->recvproc)) {
  360. eprintk(KERN_ERR, "cannot create receiving thread\n");
  361. v9fs_session_close(v9ses);
  362. return -ECONNABORTED;
  363. }
  364. wake_up_process(v9ses->recvproc);
  365. wait_for_completion(&v9ses->proccmpl);
  366. return 0;
  367. }