iob_alloc.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. /****************************************************************************
  2. * mm/iob/iob_alloc.c
  3. *
  4. * Copyright (C) 2014, 2016-2017 Gregory Nutt. All rights reserved.
  5. * Author: Gregory Nutt <gnutt@nuttx.org>
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. * 2. Redistributions in binary form must reproduce the above copyright
  14. * notice, this list of conditions and the following disclaimer in
  15. * the documentation and/or other materials provided with the
  16. * distribution.
  17. * 3. Neither the name NuttX nor the names of its contributors may be
  18. * used to endorse or promote products derived from this software
  19. * without specific prior written permission.
  20. *
  21. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  22. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  23. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  24. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  26. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  27. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  28. * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
  29. * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  31. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  32. * POSSIBILITY OF SUCH DAMAGE.
  33. *
  34. ****************************************************************************/
  35. /****************************************************************************
  36. * Included Files
  37. ****************************************************************************/
  38. #include <nuttx/config.h>
  39. #include <semaphore.h>
  40. #include <assert.h>
  41. #include <errno.h>
  42. #include <nuttx/irq.h>
  43. #include <nuttx/arch.h>
  44. #include <nuttx/sched.h>
  45. #include <nuttx/mm/iob.h>
  46. #include "iob.h"
  47. /****************************************************************************
  48. * Private Functions
  49. ****************************************************************************/
  50. /****************************************************************************
  51. * Name: iob_alloc_committed
  52. *
  53. * Description:
  54. * Allocate an I/O buffer by taking the buffer at the head of the committed
  55. * list.
  56. *
  57. ****************************************************************************/
  58. static FAR struct iob_s *iob_alloc_committed(enum iob_user_e consumerid)
  59. {
  60. FAR struct iob_s *iob = NULL;
  61. irqstate_t flags;
  62. /* We don't know what context we are called from so we use extreme measures
  63. * to protect the committed list: We disable interrupts very briefly.
  64. */
  65. flags = enter_critical_section();
  66. /* Take the I/O buffer from the head of the committed list */
  67. iob = g_iob_committed;
  68. if (iob != NULL)
  69. {
  70. /* Remove the I/O buffer from the committed list */
  71. g_iob_committed = iob->io_flink;
  72. /* Put the I/O buffer in a known state */
  73. iob->io_flink = NULL; /* Not in a chain */
  74. iob->io_len = 0; /* Length of the data in the entry */
  75. iob->io_offset = 0; /* Offset to the beginning of data */
  76. iob->io_pktlen = 0; /* Total length of the packet */
  77. #if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) && \
  78. defined(CONFIG_MM_IOB) && !defined(CONFIG_FS_PROCFS_EXCLUDE_IOBINFO)
  79. iob_stats_onalloc(consumerid);
  80. #endif
  81. }
  82. leave_critical_section(flags);
  83. return iob;
  84. }
  85. /****************************************************************************
  86. * Name: iob_allocwait
  87. *
  88. * Description:
  89. * Allocate an I/O buffer, waiting if necessary. This function cannot be
  90. * called from any interrupt level logic.
  91. *
  92. ****************************************************************************/
  93. static FAR struct iob_s *iob_allocwait(bool throttled,
  94. enum iob_user_e consumerid)
  95. {
  96. FAR struct iob_s *iob;
  97. irqstate_t flags;
  98. FAR sem_t *sem;
  99. int ret = OK;
  100. #if CONFIG_IOB_THROTTLE > 0
  101. /* Select the semaphore count to check. */
  102. sem = (throttled ? &g_throttle_sem : &g_iob_sem);
  103. #else
  104. sem = &g_iob_sem;
  105. #endif
  106. /* The following must be atomic; interrupt must be disabled so that there
  107. * is no conflict with interrupt level I/O buffer allocations. This is
  108. * not as bad as it sounds because interrupts will be re-enabled while
  109. * we are waiting for I/O buffers to become free.
  110. */
  111. flags = enter_critical_section();
  112. /* Try to get an I/O buffer. If successful, the semaphore count will be
  113. * decremented atomically.
  114. */
  115. iob = iob_tryalloc(throttled, consumerid);
  116. while (ret == OK && iob == NULL)
  117. {
  118. /* If not successful, then the semaphore count was less than or equal
  119. * to zero (meaning that there are no free buffers). We need to wait
  120. * for an I/O buffer to be released and placed in the committed
  121. * list.
  122. */
  123. ret = nxsem_wait(sem);
  124. if (ret < 0)
  125. {
  126. /* EINTR is not an error! EINTR simply means that we were
  127. * awakened by a signal and we should try again.
  128. *
  129. * REVISIT: Many end-user interfaces are required to return with
  130. * an error if EINTR is set. Most uses of this function are in
  131. * internal, non-user logic. But are there cases where the error
  132. * should be returned.
  133. */
  134. if (ret == -EINTR)
  135. {
  136. /* Force a success indication so that we will continue looping. */
  137. ret = OK;
  138. }
  139. }
  140. else
  141. {
  142. /* When we wake up from wait successfully, an I/O buffer was
  143. * freed and we hold a count for one IOB.
  144. */
  145. iob = iob_alloc_committed(consumerid);
  146. if (iob == NULL)
  147. {
  148. /* We need release our count so that it is available to
  149. * iob_tryalloc(), perhaps allowing another thread to take our
  150. * count. In that event, iob_tryalloc() will fail above and
  151. * we will have to wait again.
  152. */
  153. nxsem_post(sem);
  154. iob = iob_tryalloc(throttled, consumerid);
  155. }
  156. /* REVISIT: I think this logic should be moved inside of
  157. * iob_alloc_committed, so that it can exist inside of the critical
  158. * section along with all other sem count changes.
  159. */
  160. #if CONFIG_IOB_THROTTLE > 0
  161. else
  162. {
  163. if (throttled)
  164. {
  165. g_iob_sem.semcount--;
  166. }
  167. else
  168. {
  169. g_throttle_sem.semcount--;
  170. }
  171. }
  172. #endif
  173. }
  174. }
  175. leave_critical_section(flags);
  176. return iob;
  177. }
  178. /****************************************************************************
  179. * Public Functions
  180. ****************************************************************************/
  181. /****************************************************************************
  182. * Name: iob_alloc
  183. *
  184. * Description:
  185. * Allocate an I/O buffer by taking the buffer at the head of the free list.
  186. *
  187. ****************************************************************************/
  188. FAR struct iob_s *iob_alloc(bool throttled, enum iob_user_e consumerid)
  189. {
  190. /* Were we called from the interrupt level? */
  191. if (up_interrupt_context() || sched_idletask())
  192. {
  193. /* Yes, then try to allocate an I/O buffer without waiting */
  194. return iob_tryalloc(throttled, consumerid);
  195. }
  196. else
  197. {
  198. /* Then allocate an I/O buffer, waiting as necessary */
  199. return iob_allocwait(throttled, consumerid);
  200. }
  201. }
  202. /****************************************************************************
  203. * Name: iob_tryalloc
  204. *
  205. * Description:
  206. * Try to allocate an I/O buffer by taking the buffer at the head of the
  207. * free list without waiting for a buffer to become free.
  208. *
  209. ****************************************************************************/
  210. FAR struct iob_s *iob_tryalloc(bool throttled, enum iob_user_e consumerid)
  211. {
  212. FAR struct iob_s *iob;
  213. irqstate_t flags;
  214. #if CONFIG_IOB_THROTTLE > 0
  215. FAR sem_t *sem;
  216. #endif
  217. #if CONFIG_IOB_THROTTLE > 0
  218. /* Select the semaphore count to check. */
  219. sem = (throttled ? &g_throttle_sem : &g_iob_sem);
  220. #endif
  221. /* We don't know what context we are called from so we use extreme measures
  222. * to protect the free list: We disable interrupts very briefly.
  223. */
  224. flags = enter_critical_section();
  225. #if CONFIG_IOB_THROTTLE > 0
  226. /* If there are free I/O buffers for this allocation */
  227. if (sem->semcount > 0)
  228. #endif
  229. {
  230. /* Take the I/O buffer from the head of the free list */
  231. iob = g_iob_freelist;
  232. if (iob != NULL)
  233. {
  234. /* Remove the I/O buffer from the free list and decrement the
  235. * counting semaphore(s) that tracks the number of available
  236. * IOBs.
  237. */
  238. g_iob_freelist = iob->io_flink;
  239. /* Take a semaphore count. Note that we cannot do this in
  240. * in the orthodox way by calling nxsem_wait() or nxsem_trywait()
  241. * because this function may be called from an interrupt
  242. * handler. Fortunately we know at at least one free buffer
  243. * so a simple decrement is all that is needed.
  244. */
  245. g_iob_sem.semcount--;
  246. DEBUGASSERT(g_iob_sem.semcount >= 0);
  247. #if CONFIG_IOB_THROTTLE > 0
  248. /* The throttle semaphore is a little more complicated because
  249. * it can be negative! Decrementing is still safe, however.
  250. */
  251. g_throttle_sem.semcount--;
  252. DEBUGASSERT(g_throttle_sem.semcount >= -CONFIG_IOB_THROTTLE);
  253. #endif
  254. #if !defined(CONFIG_DISABLE_MOUNTPOINT) && defined(CONFIG_FS_PROCFS) && \
  255. defined(CONFIG_MM_IOB) && !defined(CONFIG_FS_PROCFS_EXCLUDE_IOBINFO)
  256. iob_stats_onalloc(consumerid);
  257. #endif
  258. leave_critical_section(flags);
  259. /* Put the I/O buffer in a known state */
  260. iob->io_flink = NULL; /* Not in a chain */
  261. iob->io_len = 0; /* Length of the data in the entry */
  262. iob->io_offset = 0; /* Offset to the beginning of data */
  263. iob->io_pktlen = 0; /* Total length of the packet */
  264. return iob;
  265. }
  266. }
  267. leave_critical_section(flags);
  268. return NULL;
  269. }