rcuref.txt 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. Reference-count design for elements of lists/arrays protected by RCU.
  2. Reference counting on elements of lists which are protected by traditional
  3. reader/writer spinlocks or semaphores are straightforward:
  4. 1. 2.
  5. add() search_and_reference()
  6. { {
  7. alloc_object read_lock(&list_lock);
  8. ... search_for_element
  9. atomic_set(&el->rc, 1); atomic_inc(&el->rc);
  10. write_lock(&list_lock); ...
  11. add_element read_unlock(&list_lock);
  12. ... ...
  13. write_unlock(&list_lock); }
  14. }
  15. 3. 4.
  16. release_referenced() delete()
  17. { {
  18. ... write_lock(&list_lock);
  19. atomic_dec(&el->rc, relfunc) ...
  20. ... remove_element
  21. } write_unlock(&list_lock);
  22. ...
  23. if (atomic_dec_and_test(&el->rc))
  24. kfree(el);
  25. ...
  26. }
  27. If this list/array is made lock free using RCU as in changing the
  28. write_lock() in add() and delete() to spin_lock() and changing read_lock()
  29. in search_and_reference() to rcu_read_lock(), the atomic_inc() in
  30. search_and_reference() could potentially hold reference to an element which
  31. has already been deleted from the list/array. Use atomic_inc_not_zero()
  32. in this scenario as follows:
  33. 1. 2.
  34. add() search_and_reference()
  35. { {
  36. alloc_object rcu_read_lock();
  37. ... search_for_element
  38. atomic_set(&el->rc, 1); if (!atomic_inc_not_zero(&el->rc)) {
  39. spin_lock(&list_lock); rcu_read_unlock();
  40. return FAIL;
  41. add_element }
  42. ... ...
  43. spin_unlock(&list_lock); rcu_read_unlock();
  44. } }
  45. 3. 4.
  46. release_referenced() delete()
  47. { {
  48. ... spin_lock(&list_lock);
  49. if (atomic_dec_and_test(&el->rc)) ...
  50. call_rcu(&el->head, el_free); remove_element
  51. ... spin_unlock(&list_lock);
  52. } ...
  53. if (atomic_dec_and_test(&el->rc))
  54. call_rcu(&el->head, el_free);
  55. ...
  56. }
  57. Sometimes, a reference to the element needs to be obtained in the
  58. update (write) stream. In such cases, atomic_inc_not_zero() might be
  59. overkill, since we hold the update-side spinlock. One might instead
  60. use atomic_inc() in such cases.
  61. It is not always convenient to deal with "FAIL" in the
  62. search_and_reference() code path. In such cases, the
  63. atomic_dec_and_test() may be moved from delete() to el_free()
  64. as follows:
  65. 1. 2.
  66. add() search_and_reference()
  67. { {
  68. alloc_object rcu_read_lock();
  69. ... search_for_element
  70. atomic_set(&el->rc, 1); atomic_inc(&el->rc);
  71. spin_lock(&list_lock); ...
  72. add_element rcu_read_unlock();
  73. ... }
  74. spin_unlock(&list_lock); 4.
  75. } delete()
  76. 3. {
  77. release_referenced() spin_lock(&list_lock);
  78. { ...
  79. ... remove_element
  80. if (atomic_dec_and_test(&el->rc)) spin_unlock(&list_lock);
  81. kfree(el); ...
  82. ... call_rcu(&el->head, el_free);
  83. } ...
  84. 5. }
  85. void el_free(struct rcu_head *rhp)
  86. {
  87. release_referenced();
  88. }
  89. The key point is that the initial reference added by add() is not removed
  90. until after a grace period has elapsed following removal. This means that
  91. search_and_reference() cannot find this element, which means that the value
  92. of el->rc cannot increase. Thus, once it reaches zero, there are no
  93. readers that can or ever will be able to reference the element. The
  94. element can therefore safely be freed. This in turn guarantees that if
  95. any reader finds the element, that reader may safely acquire a reference
  96. without checking the value of the reference counter.
  97. In cases where delete() can sleep, synchronize_rcu() can be called from
  98. delete(), so that el_free() can be subsumed into delete as follows:
  99. 4.
  100. delete()
  101. {
  102. spin_lock(&list_lock);
  103. ...
  104. remove_element
  105. spin_unlock(&list_lock);
  106. ...
  107. synchronize_rcu();
  108. if (atomic_dec_and_test(&el->rc))
  109. kfree(el);
  110. ...
  111. }