1
0

tty_outq.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /*
  2. * Copyright (c) 2006-2023, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2023-11-13 Shell init ver.
  9. */
  10. #include "../bsd_porting.h"
  11. #include "../terminal.h"
  12. /*-
  13. * SPDX-License-Identifier: BSD-2-Clause
  14. *
  15. * Copyright (c) 2008 Ed Schouten <ed@FreeBSD.org>
  16. * All rights reserved.
  17. *
  18. * Portions of this software were developed under sponsorship from Snow
  19. * B.V., the Netherlands.
  20. *
  21. * Redistribution and use in source and binary forms, with or without
  22. * modification, are permitted provided that the following conditions
  23. * are met:
  24. * 1. Redistributions of source code must retain the above copyright
  25. * notice, this list of conditions and the following disclaimer.
  26. * 2. Redistributions in binary form must reproduce the above copyright
  27. * notice, this list of conditions and the following disclaimer in the
  28. * documentation and/or other materials provided with the distribution.
  29. *
  30. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
  31. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  32. * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  33. * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
  34. * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  35. * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  36. * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  37. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  38. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  39. * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  40. * SUCH DAMAGE.
  41. */
  42. /*
  43. * TTY output queue buffering.
  44. *
  45. * The previous design of the TTY layer offered the so-called clists.
  46. * These clists were used for both the input queues and the output
  47. * queue. We don't use certain features on the output side, like quoting
  48. * bits for parity marking and such. This mechanism is similar to the
  49. * old clists, but only contains the features we need to buffer the
  50. * output.
  51. */
  52. struct ttyoutq_block
  53. {
  54. struct ttyoutq_block *tob_next;
  55. char tob_data[TTYOUTQ_DATASIZE];
  56. };
  57. static uma_zone_t ttyoutq_zone;
  58. #define TTYOUTQ_INSERT_TAIL(to, tob) \
  59. do \
  60. { \
  61. if (to->to_end == 0) \
  62. { \
  63. tob->tob_next = to->to_firstblock; \
  64. to->to_firstblock = tob; \
  65. } \
  66. else \
  67. { \
  68. tob->tob_next = to->to_lastblock->tob_next; \
  69. to->to_lastblock->tob_next = tob; \
  70. } \
  71. to->to_nblocks++; \
  72. } while (0)
  73. #define TTYOUTQ_REMOVE_HEAD(to) \
  74. do \
  75. { \
  76. to->to_firstblock = to->to_firstblock->tob_next; \
  77. to->to_nblocks--; \
  78. } while (0)
  79. #define TTYOUTQ_RECYCLE(to, tob) \
  80. do \
  81. { \
  82. if (to->to_quota <= to->to_nblocks) \
  83. uma_zfree(ttyoutq_zone, tob); \
  84. else \
  85. TTYOUTQ_INSERT_TAIL(to, tob); \
  86. } while (0)
  87. void ttyoutq_flush(struct ttyoutq *to)
  88. {
  89. to->to_begin = 0;
  90. to->to_end = 0;
  91. }
  92. int ttyoutq_setsize(struct ttyoutq *to, struct lwp_tty *tp, size_t size)
  93. {
  94. struct ttyoutq_block *tob;
  95. to->to_quota = howmany(size, TTYOUTQ_DATASIZE);
  96. while (to->to_quota > to->to_nblocks)
  97. {
  98. /*
  99. * List is getting bigger.
  100. * Add new blocks to the tail of the list.
  101. *
  102. * We must unlock the TTY temporarily, because we need
  103. * to allocate memory. This won't be a problem, because
  104. * in the worst case, another thread ends up here, which
  105. * may cause us to allocate too many blocks, but this
  106. * will be caught by the loop below.
  107. */
  108. tty_unlock(tp);
  109. tob = uma_zalloc(ttyoutq_zone, M_WAITOK);
  110. tty_lock(tp);
  111. if (tty_gone(tp))
  112. {
  113. uma_zfree(ttyoutq_zone, tob);
  114. return -ENXIO;
  115. }
  116. TTYOUTQ_INSERT_TAIL(to, tob);
  117. }
  118. return 0;
  119. }
  120. void ttyoutq_free(struct ttyoutq *to)
  121. {
  122. struct ttyoutq_block *tob;
  123. ttyoutq_flush(to);
  124. to->to_quota = 0;
  125. while ((tob = to->to_firstblock) != NULL)
  126. {
  127. TTYOUTQ_REMOVE_HEAD(to);
  128. uma_zfree(ttyoutq_zone, tob);
  129. }
  130. MPASS(to->to_nblocks == 0);
  131. }
  132. size_t ttyoutq_read(struct ttyoutq *to, void *buf, size_t len)
  133. {
  134. char *cbuf = buf;
  135. while (len > 0)
  136. {
  137. struct ttyoutq_block *tob;
  138. size_t cbegin, cend, clen;
  139. /* See if there still is data. */
  140. if (to->to_begin == to->to_end)
  141. break;
  142. tob = to->to_firstblock;
  143. if (tob == NULL)
  144. break;
  145. /*
  146. * The end address should be the lowest of these three:
  147. * - The write pointer
  148. * - The blocksize - we can't read beyond the block
  149. * - The end address if we could perform the full read
  150. */
  151. cbegin = to->to_begin;
  152. cend = MIN(MIN(to->to_end, to->to_begin + len), TTYOUTQ_DATASIZE);
  153. clen = cend - cbegin;
  154. /* Copy the data out of the buffers. */
  155. memcpy(cbuf, tob->tob_data + cbegin, clen);
  156. cbuf += clen;
  157. len -= clen;
  158. if (cend == to->to_end)
  159. {
  160. /* Read the complete queue. */
  161. to->to_begin = 0;
  162. to->to_end = 0;
  163. }
  164. else if (cend == TTYOUTQ_DATASIZE)
  165. {
  166. /* Read the block until the end. */
  167. TTYOUTQ_REMOVE_HEAD(to);
  168. to->to_begin = 0;
  169. to->to_end -= TTYOUTQ_DATASIZE;
  170. TTYOUTQ_RECYCLE(to, tob);
  171. }
  172. else
  173. {
  174. /* Read the block partially. */
  175. to->to_begin += clen;
  176. }
  177. }
  178. return cbuf - (char *)buf;
  179. }
  180. /*
  181. * An optimized version of ttyoutq_read() which can be used in pseudo
  182. * TTY drivers to directly copy data from the outq to userspace, instead
  183. * of buffering it.
  184. *
  185. * We can only copy data directly if we need to read the entire block
  186. * back to the user, because we temporarily remove the block from the
  187. * queue. Otherwise we need to copy it to a temporary buffer first, to
  188. * make sure data remains in the correct order.
  189. */
  190. int ttyoutq_read_uio(struct ttyoutq *to, struct lwp_tty *tp, struct uio *uio)
  191. {
  192. while (uio->uio_resid > 0)
  193. {
  194. int error;
  195. struct ttyoutq_block *tob;
  196. size_t cbegin, cend, clen;
  197. /* See if there still is data. */
  198. if (to->to_begin == to->to_end)
  199. return 0;
  200. tob = to->to_firstblock;
  201. if (tob == NULL)
  202. return 0;
  203. /*
  204. * The end address should be the lowest of these three:
  205. * - The write pointer
  206. * - The blocksize - we can't read beyond the block
  207. * - The end address if we could perform the full read
  208. */
  209. cbegin = to->to_begin;
  210. cend = MIN(MIN(to->to_end, to->to_begin + uio->uio_resid),
  211. TTYOUTQ_DATASIZE);
  212. clen = cend - cbegin;
  213. /*
  214. * We can prevent buffering in some cases:
  215. * - We need to read the block until the end.
  216. * - We don't need to read the block until the end, but
  217. * there is no data beyond it, which allows us to move
  218. * the write pointer to a new block.
  219. */
  220. if (cend == TTYOUTQ_DATASIZE || cend == to->to_end)
  221. {
  222. /*
  223. * Fast path: zero copy. Remove the first block,
  224. * so we can unlock the TTY temporarily.
  225. */
  226. TTYOUTQ_REMOVE_HEAD(to);
  227. to->to_begin = 0;
  228. if (to->to_end <= TTYOUTQ_DATASIZE)
  229. to->to_end = 0;
  230. else
  231. to->to_end -= TTYOUTQ_DATASIZE;
  232. /* Temporary unlock and copy the data to userspace. */
  233. tty_unlock(tp);
  234. error = uiomove(tob->tob_data + cbegin, clen, uio);
  235. tty_lock(tp);
  236. /* Block can now be readded to the list. */
  237. TTYOUTQ_RECYCLE(to, tob);
  238. }
  239. else
  240. {
  241. char ob[TTYOUTQ_DATASIZE - 1];
  242. /*
  243. * Slow path: store data in a temporary buffer.
  244. */
  245. memcpy(ob, tob->tob_data + cbegin, clen);
  246. to->to_begin += clen;
  247. MPASS(to->to_begin < TTYOUTQ_DATASIZE);
  248. /* Temporary unlock and copy the data to userspace. */
  249. tty_unlock(tp);
  250. error = uiomove(ob, clen, uio);
  251. tty_lock(tp);
  252. }
  253. if (error != 0)
  254. return error;
  255. }
  256. return 0;
  257. }
  258. size_t ttyoutq_write(struct ttyoutq *to, const void *buf, size_t nbytes)
  259. {
  260. const char *cbuf = buf;
  261. struct ttyoutq_block *tob;
  262. unsigned int boff;
  263. size_t l;
  264. while (nbytes > 0)
  265. {
  266. boff = to->to_end % TTYOUTQ_DATASIZE;
  267. if (to->to_end == 0)
  268. {
  269. /* First time we're being used or drained. */
  270. MPASS(to->to_begin == 0);
  271. tob = to->to_firstblock;
  272. if (tob == NULL)
  273. {
  274. /* Queue has no blocks. */
  275. break;
  276. }
  277. to->to_lastblock = tob;
  278. }
  279. else if (boff == 0)
  280. {
  281. /* We reached the end of this block on last write. */
  282. tob = to->to_lastblock->tob_next;
  283. if (tob == NULL)
  284. {
  285. /* We've reached the watermark. */
  286. break;
  287. }
  288. to->to_lastblock = tob;
  289. }
  290. else
  291. {
  292. tob = to->to_lastblock;
  293. }
  294. /* Don't copy more than was requested. */
  295. l = MIN(nbytes, TTYOUTQ_DATASIZE - boff);
  296. MPASS(l > 0);
  297. memcpy(tob->tob_data + boff, cbuf, l);
  298. cbuf += l;
  299. nbytes -= l;
  300. to->to_end += l;
  301. }
  302. return (cbuf - (const char *)buf);
  303. }
  304. int ttyoutq_write_nofrag(struct ttyoutq *to, const void *buf, size_t nbytes)
  305. {
  306. size_t ret __unused;
  307. if (ttyoutq_bytesleft(to) < nbytes)
  308. return -1;
  309. /* We should always be able to write it back. */
  310. ret = ttyoutq_write(to, buf, nbytes);
  311. MPASS(ret == nbytes);
  312. return 0;
  313. }
  314. static int ttyoutq_startup(void)
  315. {
  316. ttyoutq_zone = uma_zcreate("ttyoutq", sizeof(struct ttyoutq_block), NULL,
  317. NULL, NULL, NULL, UMA_ALIGN_PTR, 0);
  318. return 0;
  319. }
  320. INIT_PREV_EXPORT(ttyoutq_startup);
  321. #if 0
  322. SYSINIT(ttyoutq, SI_SUB_DRIVERS, SI_ORDER_FIRST, ttyoutq_startup, NULL);
  323. #endif