sleepq.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. /*
  2. * Copyright (c) 2017-2018 Richard Braun.
  3. *
  4. * This program is free software: you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation, either version 3 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. *
  17. *
  18. * TODO Analyse hash parameters.
  19. */
  20. #include <assert.h>
  21. #include <stdalign.h>
  22. #include <stdbool.h>
  23. #include <stddef.h>
  24. #include <stdint.h>
  25. #include <kern/hlist.h>
  26. #include <kern/init.h>
  27. #include <kern/kmem.h>
  28. #include <kern/list.h>
  29. #include <kern/macros.h>
  30. #include <kern/sleepq.h>
  31. #include <kern/spinlock.h>
  32. #include <kern/thread.h>
  33. #include <machine/cpu.h>
  34. struct sleepq_bucket {
  35. alignas(CPU_L1_SIZE) struct spinlock lock;
  36. struct hlist sleepqs;
  37. };
  38. struct sleepq_waiter {
  39. struct list node;
  40. struct thread *thread;
  41. bool pending_wakeup;
  42. };
  43. /*
  44. * Waiters are queued in FIFO order and inserted at the head of the
  45. * list of waiters. The pointer to the "oldest" waiter is used as
  46. * a marker between threads waiting for a signal/broadcast (from the
  47. * beginning up to and including the oldest waiter) and threads pending
  48. * for wake-up (all the following threads up to the end of the list).
  49. */
  50. struct sleepq {
  51. alignas(CPU_L1_SIZE) struct sleepq_bucket *bucket;
  52. struct hlist_node node;
  53. const void *sync_obj;
  54. struct list waiters;
  55. struct sleepq_waiter *oldest_waiter;
  56. struct sleepq *next_free;
  57. };
  58. #define SLEEPQ_HTABLE_SIZE 128
  59. #define SLEEPQ_COND_HTABLE_SIZE 64
  60. #if !ISP2(SLEEPQ_HTABLE_SIZE) || !ISP2(SLEEPQ_COND_HTABLE_SIZE)
  61. #error "hash table size must be a power of two"
  62. #endif /* !ISP2(SLEEPQ_HTABLE_SIZE) */
  63. #define SLEEPQ_HTABLE_MASK (SLEEPQ_HTABLE_SIZE - 1)
  64. #define SLEEPQ_COND_HTABLE_MASK (SLEEPQ_COND_HTABLE_SIZE - 1)
  65. static struct sleepq_bucket sleepq_htable[SLEEPQ_HTABLE_SIZE];
  66. static struct sleepq_bucket sleepq_cond_htable[SLEEPQ_COND_HTABLE_SIZE];
  67. static struct kmem_cache sleepq_cache;
  68. static uintptr_t
  69. sleepq_hash(const void *addr)
  70. {
  71. return ((uintptr_t)addr >> 8) ^ (uintptr_t)addr;
  72. }
  73. static void
  74. sleepq_waiter_init(struct sleepq_waiter *waiter, struct thread *thread)
  75. {
  76. waiter->thread = thread;
  77. waiter->pending_wakeup = false;
  78. }
  79. static bool
  80. sleepq_waiter_pending_wakeup(const struct sleepq_waiter *waiter)
  81. {
  82. return waiter->pending_wakeup;
  83. }
  84. static void
  85. sleepq_waiter_set_pending_wakeup(struct sleepq_waiter *waiter)
  86. {
  87. waiter->pending_wakeup = true;
  88. }
  89. static void
  90. sleepq_waiter_wakeup(struct sleepq_waiter *waiter)
  91. {
  92. if (!sleepq_waiter_pending_wakeup(waiter)) {
  93. return;
  94. }
  95. thread_wakeup(waiter->thread);
  96. }
  97. static bool
  98. sleepq_init_state_valid(const struct sleepq *sleepq)
  99. {
  100. return (sleepq->bucket == NULL)
  101. && (sleepq->sync_obj == NULL)
  102. && (list_empty(&sleepq->waiters))
  103. && (sleepq->oldest_waiter == NULL)
  104. && (sleepq->next_free == NULL);
  105. }
  106. static void
  107. sleepq_use(struct sleepq *sleepq, const void *sync_obj)
  108. {
  109. assert(sleepq->sync_obj == NULL);
  110. sleepq->sync_obj = sync_obj;
  111. }
  112. static void
  113. sleepq_unuse(struct sleepq *sleepq)
  114. {
  115. assert(sleepq->sync_obj != NULL);
  116. sleepq->sync_obj = NULL;
  117. }
  118. static bool
  119. sleepq_in_use(const struct sleepq *sleepq)
  120. {
  121. return sleepq->sync_obj != NULL;
  122. }
  123. static bool
  124. sleepq_in_use_by(const struct sleepq *sleepq, const void *sync_obj)
  125. {
  126. return sleepq->sync_obj == sync_obj;
  127. }
  128. static void
  129. sleepq_bucket_init(struct sleepq_bucket *bucket)
  130. {
  131. spinlock_init(&bucket->lock);
  132. hlist_init(&bucket->sleepqs);
  133. }
  134. static struct sleepq_bucket *
  135. sleepq_bucket_get_cond(const void *sync_obj)
  136. {
  137. uintptr_t index;
  138. index = sleepq_hash(sync_obj) & SLEEPQ_COND_HTABLE_MASK;
  139. assert(index < ARRAY_SIZE(sleepq_cond_htable));
  140. return &sleepq_cond_htable[index];
  141. }
  142. static struct sleepq_bucket *
  143. sleepq_bucket_get(const void *sync_obj, bool condition)
  144. {
  145. uintptr_t index;
  146. if (condition) {
  147. return sleepq_bucket_get_cond(sync_obj);
  148. }
  149. index = sleepq_hash(sync_obj) & SLEEPQ_HTABLE_MASK;
  150. assert(index < ARRAY_SIZE(sleepq_htable));
  151. return &sleepq_htable[index];
  152. }
  153. static void
  154. sleepq_bucket_add(struct sleepq_bucket *bucket, struct sleepq *sleepq)
  155. {
  156. assert(sleepq->bucket == NULL);
  157. sleepq->bucket = bucket;
  158. hlist_insert_head(&bucket->sleepqs, &sleepq->node);
  159. }
  160. static void
  161. sleepq_bucket_remove(struct sleepq_bucket *bucket, struct sleepq *sleepq)
  162. {
  163. assert(sleepq->bucket == bucket);
  164. sleepq->bucket = NULL;
  165. hlist_remove(&sleepq->node);
  166. }
  167. static struct sleepq *
  168. sleepq_bucket_lookup(const struct sleepq_bucket *bucket, const void *sync_obj)
  169. {
  170. struct sleepq *sleepq;
  171. hlist_for_each_entry(&bucket->sleepqs, sleepq, node) {
  172. if (sleepq_in_use_by(sleepq, sync_obj)) {
  173. assert(sleepq->bucket == bucket);
  174. return sleepq;
  175. }
  176. }
  177. return NULL;
  178. }
  179. static void
  180. sleepq_ctor(void *ptr)
  181. {
  182. struct sleepq *sleepq;
  183. sleepq = ptr;
  184. sleepq->bucket = NULL;
  185. sleepq->sync_obj = NULL;
  186. list_init(&sleepq->waiters);
  187. sleepq->oldest_waiter = NULL;
  188. sleepq->next_free = NULL;
  189. }
  190. static int __init
  191. sleepq_setup(void)
  192. {
  193. unsigned int i;
  194. for (i = 0; i < ARRAY_SIZE(sleepq_htable); i++) {
  195. sleepq_bucket_init(&sleepq_htable[i]);
  196. }
  197. for (i = 0; i < ARRAY_SIZE(sleepq_cond_htable); i++) {
  198. sleepq_bucket_init(&sleepq_cond_htable[i]);
  199. }
  200. kmem_cache_init(&sleepq_cache, "sleepq", sizeof(struct sleepq),
  201. CPU_L1_SIZE, sleepq_ctor, 0);
  202. return 0;
  203. }
  204. INIT_OP_DEFINE(sleepq_setup,
  205. INIT_OP_DEP(kmem_setup, true));
  206. struct sleepq *
  207. sleepq_create(void)
  208. {
  209. struct sleepq *sleepq;
  210. sleepq = kmem_cache_alloc(&sleepq_cache);
  211. if (sleepq == NULL) {
  212. return NULL;
  213. }
  214. assert(sleepq_init_state_valid(sleepq));
  215. return sleepq;
  216. }
  217. void
  218. sleepq_destroy(struct sleepq *sleepq)
  219. {
  220. assert(sleepq_init_state_valid(sleepq));
  221. kmem_cache_free(&sleepq_cache, sleepq);
  222. }
  223. static struct sleepq *
  224. sleepq_acquire_common(const void *sync_obj, bool condition, unsigned long *flags)
  225. {
  226. struct sleepq_bucket *bucket;
  227. struct sleepq *sleepq;
  228. assert(sync_obj != NULL);
  229. bucket = sleepq_bucket_get(sync_obj, condition);
  230. if (flags) {
  231. spinlock_lock_intr_save(&bucket->lock, flags);
  232. } else {
  233. spinlock_lock(&bucket->lock);
  234. }
  235. sleepq = sleepq_bucket_lookup(bucket, sync_obj);
  236. if (sleepq == NULL) {
  237. if (flags) {
  238. spinlock_unlock_intr_restore(&bucket->lock, *flags);
  239. } else {
  240. spinlock_unlock(&bucket->lock);
  241. }
  242. return NULL;
  243. }
  244. return sleepq;
  245. }
  246. static struct sleepq *
  247. sleepq_tryacquire_common(const void *sync_obj, bool condition,
  248. unsigned long *flags)
  249. {
  250. struct sleepq_bucket *bucket;
  251. struct sleepq *sleepq;
  252. int error;
  253. assert(sync_obj != NULL);
  254. bucket = sleepq_bucket_get(sync_obj, condition);
  255. if (flags) {
  256. error = spinlock_trylock_intr_save(&bucket->lock, flags);
  257. } else {
  258. error = spinlock_trylock(&bucket->lock);
  259. }
  260. if (error) {
  261. return NULL;
  262. }
  263. sleepq = sleepq_bucket_lookup(bucket, sync_obj);
  264. if (sleepq == NULL) {
  265. if (flags) {
  266. spinlock_unlock_intr_restore(&bucket->lock, *flags);
  267. } else {
  268. spinlock_unlock(&bucket->lock);
  269. }
  270. return NULL;
  271. }
  272. return sleepq;
  273. }
  274. struct sleepq *
  275. sleepq_acquire(const void *sync_obj, bool condition)
  276. {
  277. return sleepq_acquire_common(sync_obj, condition, NULL);
  278. }
  279. struct sleepq *
  280. sleepq_tryacquire(const void *sync_obj, bool condition)
  281. {
  282. return sleepq_tryacquire_common(sync_obj, condition, NULL);
  283. }
  284. void
  285. sleepq_release(struct sleepq *sleepq)
  286. {
  287. spinlock_unlock(&sleepq->bucket->lock);
  288. }
  289. struct sleepq *
  290. sleepq_acquire_intr_save(const void *sync_obj, bool condition,
  291. unsigned long *flags)
  292. {
  293. return sleepq_acquire_common(sync_obj, condition, flags);
  294. }
  295. struct sleepq *
  296. sleepq_tryacquire_intr_save(const void *sync_obj, bool condition,
  297. unsigned long *flags)
  298. {
  299. return sleepq_tryacquire_common(sync_obj, condition, flags);
  300. }
  301. void
  302. sleepq_release_intr_restore(struct sleepq *sleepq, unsigned long flags)
  303. {
  304. spinlock_unlock_intr_restore(&sleepq->bucket->lock, flags);
  305. }
  306. static void
  307. sleepq_push_free(struct sleepq *sleepq, struct sleepq *free_sleepq)
  308. {
  309. assert(free_sleepq->next_free == NULL);
  310. free_sleepq->next_free = sleepq->next_free;
  311. sleepq->next_free = free_sleepq;
  312. }
  313. static struct sleepq *
  314. sleepq_pop_free(struct sleepq *sleepq)
  315. {
  316. struct sleepq *free_sleepq;
  317. free_sleepq = sleepq->next_free;
  318. if (free_sleepq == NULL) {
  319. return NULL;
  320. }
  321. sleepq->next_free = free_sleepq->next_free;
  322. free_sleepq->next_free = NULL;
  323. return free_sleepq;
  324. }
  325. static struct sleepq *
  326. sleepq_lend_common(const void *sync_obj, bool condition, unsigned long *flags)
  327. {
  328. struct sleepq_bucket *bucket;
  329. struct sleepq *sleepq, *prev;
  330. assert(sync_obj != NULL);
  331. sleepq = thread_sleepq_lend();
  332. assert(sleepq_init_state_valid(sleepq));
  333. bucket = sleepq_bucket_get(sync_obj, condition);
  334. if (flags) {
  335. spinlock_lock_intr_save(&bucket->lock, flags);
  336. } else {
  337. spinlock_lock(&bucket->lock);
  338. }
  339. prev = sleepq_bucket_lookup(bucket, sync_obj);
  340. if (prev == NULL) {
  341. sleepq_use(sleepq, sync_obj);
  342. sleepq_bucket_add(bucket, sleepq);
  343. } else {
  344. sleepq_push_free(prev, sleepq);
  345. sleepq = prev;
  346. }
  347. return sleepq;
  348. }
  349. static void
  350. sleepq_return_common(struct sleepq *sleepq, unsigned long *flags)
  351. {
  352. struct sleepq_bucket *bucket;
  353. struct sleepq *free_sleepq;
  354. assert(sleepq_in_use(sleepq));
  355. bucket = sleepq->bucket;
  356. free_sleepq = sleepq_pop_free(sleepq);
  357. if (free_sleepq == NULL) {
  358. sleepq_bucket_remove(bucket, sleepq);
  359. sleepq_unuse(sleepq);
  360. free_sleepq = sleepq;
  361. }
  362. if (flags) {
  363. spinlock_unlock_intr_restore(&bucket->lock, *flags);
  364. } else {
  365. spinlock_unlock(&bucket->lock);
  366. }
  367. assert(sleepq_init_state_valid(free_sleepq));
  368. thread_sleepq_return(free_sleepq);
  369. }
  370. struct sleepq *
  371. sleepq_lend(const void *sync_obj, bool condition)
  372. {
  373. return sleepq_lend_common(sync_obj, condition, NULL);
  374. }
  375. void
  376. sleepq_return(struct sleepq *sleepq)
  377. {
  378. sleepq_return_common(sleepq, NULL);
  379. }
  380. struct sleepq *
  381. sleepq_lend_intr_save(const void *sync_obj, bool condition,
  382. unsigned long *flags)
  383. {
  384. return sleepq_lend_common(sync_obj, condition, flags);
  385. }
  386. void
  387. sleepq_return_intr_restore(struct sleepq *sleepq, unsigned long flags)
  388. {
  389. sleepq_return_common(sleepq, &flags);
  390. }
  391. static void
  392. sleepq_shift_oldest_waiter(struct sleepq *sleepq)
  393. {
  394. struct list *node;
  395. assert(sleepq->oldest_waiter != NULL);
  396. node = list_prev(&sleepq->oldest_waiter->node);
  397. if (list_end(&sleepq->waiters, node)) {
  398. sleepq->oldest_waiter = NULL;
  399. } else {
  400. sleepq->oldest_waiter = list_entry(node, struct sleepq_waiter, node);
  401. }
  402. }
  403. static void
  404. sleepq_add_waiter(struct sleepq *sleepq, struct sleepq_waiter *waiter)
  405. {
  406. list_insert_head(&sleepq->waiters, &waiter->node);
  407. if (sleepq->oldest_waiter == NULL) {
  408. sleepq->oldest_waiter = waiter;
  409. }
  410. }
  411. static void
  412. sleepq_remove_waiter(struct sleepq *sleepq, struct sleepq_waiter *waiter)
  413. {
  414. if (sleepq->oldest_waiter == waiter) {
  415. sleepq_shift_oldest_waiter(sleepq);
  416. }
  417. list_remove(&waiter->node);
  418. }
  419. static struct sleepq_waiter *
  420. sleepq_get_last_waiter(struct sleepq *sleepq)
  421. {
  422. if (list_empty(&sleepq->waiters)) {
  423. return NULL;
  424. }
  425. return list_last_entry(&sleepq->waiters, struct sleepq_waiter, node);
  426. }
  427. bool
  428. sleepq_empty(const struct sleepq *sleepq)
  429. {
  430. return list_empty(&sleepq->waiters);
  431. }
  432. static int
  433. sleepq_wait_common(struct sleepq *sleepq, const char *wchan,
  434. bool timed, uint64_t ticks)
  435. {
  436. struct sleepq_waiter waiter, *next;
  437. struct thread *thread;
  438. int error;
  439. thread = thread_self();
  440. sleepq_waiter_init(&waiter, thread);
  441. sleepq_add_waiter(sleepq, &waiter);
  442. do {
  443. if (!timed) {
  444. thread_sleep(&sleepq->bucket->lock, sleepq->sync_obj, wchan);
  445. error = 0;
  446. } else {
  447. error = thread_timedsleep(&sleepq->bucket->lock, sleepq->sync_obj,
  448. wchan, ticks);
  449. if (error) {
  450. if (sleepq_waiter_pending_wakeup(&waiter)) {
  451. error = 0;
  452. } else {
  453. break;
  454. }
  455. }
  456. }
  457. } while (!sleepq_waiter_pending_wakeup(&waiter));
  458. sleepq_remove_waiter(sleepq, &waiter);
  459. /*
  460. * Chain wake-ups here to prevent broadacasting from walking a list
  461. * with preemption disabled. Note that this doesn't guard against
  462. * the thundering herd effect for condition variables.
  463. */
  464. next = sleepq_get_last_waiter(sleepq);
  465. /*
  466. * Checking against the oldest waiter is enough as waiters are awoken
  467. * in strict FIFO order.
  468. */
  469. if (next && (next != sleepq->oldest_waiter)) {
  470. sleepq_waiter_set_pending_wakeup(next);
  471. sleepq_waiter_wakeup(next);
  472. }
  473. return error;
  474. }
  475. void
  476. sleepq_wait(struct sleepq *sleepq, const char *wchan)
  477. {
  478. int error;
  479. error = sleepq_wait_common(sleepq, wchan, false, 0);
  480. assert(!error);
  481. }
  482. int
  483. sleepq_timedwait(struct sleepq *sleepq, const char *wchan, uint64_t ticks)
  484. {
  485. return sleepq_wait_common(sleepq, wchan, true, ticks);
  486. }
  487. void
  488. sleepq_signal(struct sleepq *sleepq)
  489. {
  490. struct sleepq_waiter *waiter;
  491. waiter = sleepq->oldest_waiter;
  492. if (!waiter) {
  493. return;
  494. }
  495. sleepq_shift_oldest_waiter(sleepq);
  496. sleepq_waiter_set_pending_wakeup(waiter);
  497. sleepq_waiter_wakeup(waiter);
  498. }
  499. void
  500. sleepq_broadcast(struct sleepq *sleepq)
  501. {
  502. struct sleepq_waiter *waiter;
  503. waiter = sleepq->oldest_waiter;
  504. if (!waiter) {
  505. return;
  506. }
  507. sleepq->oldest_waiter = NULL;
  508. sleepq_waiter_set_pending_wakeup(waiter);
  509. sleepq_waiter_wakeup(waiter);
  510. }