net_lock.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406
  1. /****************************************************************************
  2. * net/utils/net_lock.c
  3. *
  4. * Copyright (C) 2011-2012, 2014-2018 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 <unistd.h>
  40. #include <semaphore.h>
  41. #include <assert.h>
  42. #include <errno.h>
  43. #include <debug.h>
  44. #include <nuttx/irq.h>
  45. #include <nuttx/arch.h>
  46. #include <nuttx/semaphore.h>
  47. #include <nuttx/mm/iob.h>
  48. #include <nuttx/net/net.h>
  49. #include "utils/utils.h"
  50. /****************************************************************************
  51. * Pre-processor Definitions
  52. ****************************************************************************/
  53. #define NO_HOLDER (pid_t)-1
  54. /****************************************************************************
  55. * Private Data
  56. ****************************************************************************/
  57. static sem_t g_netlock;
  58. static pid_t g_holder = NO_HOLDER;
  59. static unsigned int g_count = 0;
  60. /****************************************************************************
  61. * Private Functions
  62. ****************************************************************************/
  63. /****************************************************************************
  64. * Name: _net_takesem
  65. *
  66. * Description:
  67. * Take the semaphore, waiting indefinitely.
  68. * REVISIT: Should this return if -EINTR?
  69. *
  70. ****************************************************************************/
  71. static void _net_takesem(void)
  72. {
  73. int ret;
  74. do
  75. {
  76. /* Take the semaphore (perhaps waiting) */
  77. ret = nxsem_wait(&g_netlock);
  78. /* The only case that an error should occur here is if the wait was
  79. * awakened by a signal.
  80. */
  81. DEBUGASSERT(ret == OK || ret == -EINTR);
  82. }
  83. while (ret == -EINTR);
  84. }
  85. /****************************************************************************
  86. * Public Functions
  87. ****************************************************************************/
  88. /****************************************************************************
  89. * Name: net_lockinitialize
  90. *
  91. * Description:
  92. * Initialize the locking facility
  93. *
  94. ****************************************************************************/
  95. void net_lockinitialize(void)
  96. {
  97. nxsem_init(&g_netlock, 0, 1);
  98. }
  99. /****************************************************************************
  100. * Name: net_lock
  101. *
  102. * Description:
  103. * Take the network lock
  104. *
  105. * Input Parameters:
  106. * None
  107. *
  108. * Returned Value:
  109. * None
  110. *
  111. ****************************************************************************/
  112. void net_lock(void)
  113. {
  114. #ifdef CONFIG_SMP
  115. irqstate_t flags = enter_critical_section();
  116. #endif
  117. pid_t me = getpid();
  118. /* Does this thread already hold the semaphore? */
  119. if (g_holder == me)
  120. {
  121. /* Yes.. just increment the reference count */
  122. g_count++;
  123. }
  124. else
  125. {
  126. /* No.. take the semaphore (perhaps waiting) */
  127. _net_takesem();
  128. /* Now this thread holds the semaphore */
  129. g_holder = me;
  130. g_count = 1;
  131. }
  132. #ifdef CONFIG_SMP
  133. leave_critical_section(flags);
  134. #endif
  135. }
  136. /****************************************************************************
  137. * Name: net_unlock
  138. *
  139. * Description:
  140. * Release the network lock.
  141. *
  142. * Input Parameters:
  143. * None
  144. *
  145. * Returned Value:
  146. * None
  147. *
  148. ****************************************************************************/
  149. void net_unlock(void)
  150. {
  151. #ifdef CONFIG_SMP
  152. irqstate_t flags = enter_critical_section();
  153. #endif
  154. DEBUGASSERT(g_holder == getpid() && g_count > 0);
  155. /* If the count would go to zero, then release the semaphore */
  156. if (g_count == 1)
  157. {
  158. /* We no longer hold the semaphore */
  159. g_holder = NO_HOLDER;
  160. g_count = 0;
  161. nxsem_post(&g_netlock);
  162. }
  163. else
  164. {
  165. /* We still hold the semaphore. Just decrement the count */
  166. g_count--;
  167. }
  168. #ifdef CONFIG_SMP
  169. leave_critical_section(flags);
  170. #endif
  171. }
  172. /****************************************************************************
  173. * Name: net_breaklock
  174. *
  175. * Description:
  176. * Break the lock, return information needed to restore re-entrant lock
  177. * state.
  178. *
  179. ****************************************************************************/
  180. int net_breaklock(FAR unsigned int *count)
  181. {
  182. irqstate_t flags;
  183. pid_t me = getpid();
  184. int ret = -EPERM;
  185. DEBUGASSERT(count != NULL);
  186. flags = enter_critical_section(); /* No interrupts */
  187. if (g_holder == me)
  188. {
  189. /* Return the lock setting */
  190. *count = g_count;
  191. /* Release the network lock */
  192. g_holder = NO_HOLDER;
  193. g_count = 0;
  194. (void)nxsem_post(&g_netlock);
  195. ret = OK;
  196. }
  197. leave_critical_section(flags);
  198. return ret;
  199. }
  200. /****************************************************************************
  201. * Name: net_breaklock
  202. *
  203. * Description:
  204. * Restore the locked state
  205. *
  206. ****************************************************************************/
  207. void net_restorelock(unsigned int count)
  208. {
  209. pid_t me = getpid();
  210. DEBUGASSERT(g_holder != me);
  211. /* Recover the network lock at the proper count */
  212. _net_takesem();
  213. g_holder = me;
  214. g_count = count;
  215. }
  216. /****************************************************************************
  217. * Name: net_timedwait
  218. *
  219. * Description:
  220. * Atomically wait for sem (or a timeout( while temporarily releasing
  221. * the lock on the network.
  222. *
  223. * Caution should be utilized. Because the network lock is relinquished
  224. * during the wait, there could changes in the network state that occur
  225. * before the lock is recovered. Your design should account for this
  226. * possibility.
  227. *
  228. * Input Parameters:
  229. * sem - A reference to the semaphore to be taken.
  230. * abstime - The absolute time to wait until a timeout is declared.
  231. *
  232. * Returned Value:
  233. * Zero (OK) is returned on success; a negated errno value is returned on
  234. * any failure.
  235. *
  236. ****************************************************************************/
  237. int net_timedwait(sem_t *sem, FAR const struct timespec *abstime)
  238. {
  239. unsigned int count;
  240. irqstate_t flags;
  241. int blresult;
  242. int ret;
  243. flags = enter_critical_section(); /* No interrupts */
  244. sched_lock(); /* No context switches */
  245. /* Release the network lock, remembering my count. net_breaklock will
  246. * return a negated value if the caller does not hold the network lock.
  247. */
  248. blresult = net_breaklock(&count);
  249. /* Now take the semaphore, waiting if so requested. */
  250. if (abstime != NULL)
  251. {
  252. /* Wait until we get the lock or until the timeout expires */
  253. ret = nxsem_timedwait(sem, abstime);
  254. }
  255. else
  256. {
  257. /* Wait as long as necessary to get the lock */
  258. ret = nxsem_wait(sem);
  259. }
  260. /* Recover the network lock at the proper count (if we held it before) */
  261. if (blresult >= 0)
  262. {
  263. net_restorelock(count);
  264. }
  265. sched_unlock();
  266. leave_critical_section(flags);
  267. return ret;
  268. }
  269. /****************************************************************************
  270. * Name: net_lockedwait
  271. *
  272. * Description:
  273. * Atomically wait for sem while temporarily releasing the network lock.
  274. *
  275. * Caution should be utilized. Because the network lock is relinquished
  276. * during the wait, there could changes in the network state that occur
  277. * before the lock is recovered. Your design should account for this
  278. * possibility.
  279. *
  280. * Input Parameters:
  281. * sem - A reference to the semaphore to be taken.
  282. *
  283. * Returned Value:
  284. * Zero (OK) is returned on success; a negated errno value is returned on
  285. * any failure.
  286. *
  287. ****************************************************************************/
  288. int net_lockedwait(sem_t *sem)
  289. {
  290. return net_timedwait(sem, NULL);
  291. }
  292. /****************************************************************************
  293. * Name: net_ioballoc
  294. *
  295. * Description:
  296. * Allocate an IOB. If no IOBs are available, then atomically wait for
  297. * for the IOB while temporarily releasing the lock on the network.
  298. *
  299. * Caution should be utilized. Because the network lock is relinquished
  300. * during the wait, there could changes in the network state that occur
  301. * before the lock is recovered. Your design should account for this
  302. * possibility.
  303. *
  304. * Input Parameters:
  305. * throttled - An indication of the IOB allocation is "throttled"
  306. *
  307. * Returned Value:
  308. * A pointer to the newly allocated IOB is returned on success. NULL is
  309. * returned on any allocation failure.
  310. *
  311. ****************************************************************************/
  312. #ifdef CONFIG_MM_IOB
  313. FAR struct iob_s *net_ioballoc(bool throttled)
  314. {
  315. FAR struct iob_s *iob;
  316. iob = iob_tryalloc(throttled);
  317. if (iob == NULL)
  318. {
  319. irqstate_t flags;
  320. unsigned int count;
  321. int blresult;
  322. /* There are no buffers available now. We will have to wait for one to
  323. * become available. But let's not do that with the network locked.
  324. */
  325. flags = enter_critical_section();
  326. blresult = net_breaklock(&count);
  327. iob = iob_alloc(throttled);
  328. if (blresult >= 0)
  329. {
  330. net_restorelock(count);
  331. }
  332. leave_critical_section(flags);
  333. }
  334. return iob;
  335. }
  336. #endif