locking.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. #define _XOPEN_SOURCE 700
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. #include <strings.h>
  6. #include <assert.h>
  7. #include <stdint.h>
  8. #include <sys/file.h>
  9. #include "locking.h"
  10. #include "error.h"
  11. #include "pci.h"
  12. #include "kmem.h"
  13. /*
  14. * this function allocates the kernel memory for the locks for software registers
  15. */
  16. int pcilib_init_locking(pcilib_t* ctx) {
  17. int i;
  18. int err;
  19. pcilib_kmem_reuse_state_t reused;
  20. assert(PCILIB_LOCK_PAGES * PCILIB_KMEM_PAGE_SIZE >= PCILIB_MAX_LOCKS * PCILIB_LOCK_SIZE);
  21. /*protection against multiple creations of kernel space*/
  22. err = pcilib_lock_global(ctx);
  23. if (err) return err;
  24. /* by default, this kernel space is persistent and will be reused, in order to avoid the big initialization times for robust mutexes each time we run pcitool*/
  25. ctx->locks.kmem = pcilib_alloc_kernel_memory(ctx, PCILIB_KMEM_TYPE_PAGE, PCILIB_LOCK_PAGES, PCILIB_KMEM_PAGE_SIZE, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_LOCKS,0), PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT);
  26. if (!ctx->locks.kmem) {
  27. pcilib_unlock_global(ctx);
  28. pcilib_error("Allocation of kernel memory for locking subsystem has failed");
  29. return PCILIB_ERROR_FAILED;
  30. }
  31. reused = pcilib_kmem_is_reused(ctx, ctx->locks.kmem);
  32. if (reused & PCILIB_KMEM_REUSE_PARTIAL) {
  33. pcilib_unlock_global(ctx);
  34. pcilib_error("Inconsistent kernel memory for locking subsystem is found (only part of the required buffers is available)");
  35. return PCILIB_ERROR_INVALID_STATE;
  36. }
  37. if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) {
  38. for (i = 0; i < PCILIB_LOCK_PAGES; i++) {
  39. void *addr = (void*)pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, i);
  40. memset(addr, 0, PCILIB_KMEM_PAGE_SIZE);
  41. }
  42. }
  43. /* the lock that has been used for the creation of kernel space is declared unlocked, has we shouldnot use it anymore*/
  44. ctx->locks.locking = pcilib_get_lock(ctx, PCILIB_LOCK_FLAG_UNLOCKED, "locking");
  45. pcilib_unlock_global(ctx);
  46. if ((!ctx->locks.locking)) {
  47. pcilib_error("Locking subsystem has failed to initialized mandatory global locks");
  48. return PCILIB_ERROR_FAILED;
  49. }
  50. return 0;
  51. }
  52. /*
  53. * this function free the kernel memory allocated for them and destroys locks by setting memory to 0
  54. */
  55. void pcilib_free_locking(pcilib_t *ctx) {
  56. if (ctx->locks.locking)
  57. pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, ctx->locks.locking);
  58. if (ctx->locks.kmem) {
  59. pcilib_free_kernel_memory(ctx, ctx->locks.kmem, PCILIB_KMEM_FLAG_REUSE);
  60. }
  61. memset(&ctx->locks, 0, sizeof(pcilib_locking_t));
  62. }
  63. int pcilib_lock_global(pcilib_t *ctx) {
  64. int err;
  65. /* we flock() on the board's device file to make sure to not have two initialization in the same time (possible long time to init) */
  66. if ((err = flock(ctx->handle, LOCK_EX))==-1) {
  67. pcilib_error("Can't get flock on device file");
  68. return PCILIB_ERROR_FAILED;
  69. }
  70. return 0;
  71. }
  72. void pcilib_unlock_global(pcilib_t *ctx) {
  73. if (flock(ctx->handle, LOCK_UN) == -1)
  74. pcilib_warning("Could not correctly remove lock from the device file");
  75. }
  76. pcilib_lock_t *pcilib_get_lock_by_id(pcilib_t *ctx, pcilib_lock_id_t id) {
  77. int page = id / PCILIB_LOCKS_PER_PAGE;
  78. int offset = id - page * PCILIB_LOCKS_PER_PAGE;
  79. volatile void *addr = pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, page);
  80. pcilib_lock_t *lock = (pcilib_lock_t*)(addr + offset * PCILIB_LOCK_SIZE);
  81. return lock;
  82. }
  83. pcilib_lock_t *pcilib_get_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, const char *lock_id, ...) {
  84. pcilib_lock_id_t i;
  85. int err, ret;
  86. pcilib_lock_t *lock;
  87. char buffer[PCILIB_LOCK_SIZE];
  88. /* we construct the complete lock_id given the parameters of the function*/
  89. va_list pa;
  90. va_start(pa, lock_id);
  91. ret = vsnprintf(buffer, PCILIB_LOCK_SIZE, lock_id, pa);
  92. va_end(pa);
  93. if (ret < 0) {
  94. pcilib_error("Failed to construct the lock id, probably arguments does not match the format string (%s)...", lock_id);
  95. return NULL;
  96. }
  97. /* we iterate through locks to see if there is one already with the same name*/
  98. // Would be nice to have hash here
  99. for (i = 0; i < PCILIB_MAX_LOCKS; i++) {
  100. lock = pcilib_get_lock_by_id(ctx, i);
  101. const char *name = pcilib_lock_get_name(lock);
  102. if (!name) break;
  103. if (!strcmp(buffer, name)) {
  104. if ((pcilib_lock_get_flags(lock)&PCILIB_LOCK_FLAG_PERSISTENT) != (flags&PCILIB_LOCK_FLAG_PERSISTENT)) {
  105. if (flags&PCILIB_LOCK_FLAG_PERSISTENT)
  106. pcilib_error("Requesting persistent lock (%s), but requested lock is already existing and is robust", name);
  107. else
  108. pcilib_error("Requesting robust lock (%s), but requested lock is already existing and is persistent", name);
  109. return NULL;
  110. }
  111. #ifndef HAVE_STDATOMIC_H
  112. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) {
  113. err = pcilib_lock(ctx->locks.locking);
  114. if (err) {
  115. pcilib_error("Error (%i) obtaining global lock", err);
  116. return NULL;
  117. }
  118. }
  119. #endif /* ! HAVE_STDATOMIC_H */
  120. /* if yes, we increment its ref variable*/
  121. pcilib_lock_ref(lock);
  122. #ifndef HAVE_STDATOMIC_H
  123. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
  124. pcilib_unlock(ctx->locks.locking);
  125. #endif /* ! HAVE_STDATOMIC_H */
  126. return lock;
  127. }
  128. }
  129. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) {
  130. err = pcilib_lock(ctx->locks.locking);
  131. if (err) {
  132. pcilib_error("Error (%i) obtaining global lock", err);
  133. return NULL;
  134. }
  135. }
  136. // Make sure it was not allocated meanwhile
  137. for (; i < PCILIB_MAX_LOCKS; i++) {
  138. lock = pcilib_get_lock_by_id(ctx, i);
  139. const char *name = pcilib_lock_get_name(lock);
  140. if (!name) break;
  141. if (!strcmp(buffer, name)) {
  142. if ((pcilib_lock_get_flags(lock)&PCILIB_LOCK_FLAG_PERSISTENT) != (flags&PCILIB_LOCK_FLAG_PERSISTENT)) {
  143. if (flags&PCILIB_LOCK_FLAG_PERSISTENT)
  144. pcilib_error("Requesting persistent lock (%s), but requested lock is already existing and is robust", name);
  145. else
  146. pcilib_error("Requesting robust lock (%s), but requested lock is already existing and is persistent", name);
  147. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
  148. pcilib_unlock(ctx->locks.locking);
  149. return NULL;
  150. }
  151. pcilib_lock_ref(lock);
  152. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
  153. pcilib_unlock(ctx->locks.locking);
  154. return lock;
  155. }
  156. }
  157. if (i == PCILIB_MAX_LOCKS) {
  158. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
  159. pcilib_unlock(ctx->locks.locking);
  160. pcilib_error("Failed to create lock (%s), only %u locks is supported", buffer, PCILIB_MAX_LOCKS);
  161. return NULL;
  162. }
  163. /* if the lock did not exist before, then we create it*/
  164. err = pcilib_init_lock(lock, flags, buffer);
  165. if (err) {
  166. pcilib_error("Lock initialization failed with error %i", err);
  167. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
  168. pcilib_unlock(ctx->locks.locking);
  169. return NULL;
  170. }
  171. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
  172. pcilib_unlock(ctx->locks.locking);
  173. return lock;
  174. }
  175. void pcilib_return_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, pcilib_lock_t *lock) {
  176. #ifndef HAVE_STDATOMIC_H
  177. int err;
  178. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) {
  179. err = pcilib_lock(ctx->locks.locking);
  180. if (err) {
  181. pcilib_error("Error (%i) obtaining global lock", err);
  182. return;
  183. }
  184. }
  185. #endif /* ! HAVE_STDATOMIC_H */
  186. pcilib_lock_unref(lock);
  187. #ifndef HAVE_STDATOMIC_H
  188. if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
  189. pcilib_unlock(ctx->locks.locking);
  190. #endif /* ! HAVE_STDATOMIC_H */
  191. }
  192. /*
  193. * Destroy all existing locks. This is unsafe call as this and other running applications
  194. * will still have all initialized lock pointers. It is user responsibility to issue this
  195. * command when no other application is running.
  196. */
  197. int pcilib_destroy_all_locks(pcilib_t *ctx, int force) {
  198. int err;
  199. pcilib_lock_id_t i;
  200. pcilib_kmem_reuse_state_t reused;
  201. if (strcasecmp(ctx->model, "maintenance")) {
  202. pcilib_error("Can't destroy locks while locking subsystem is initialized, use maintenance model");
  203. return PCILIB_ERROR_INVALID_STATE;
  204. }
  205. err = pcilib_lock_global(ctx);
  206. if (err) return err;
  207. // ToDo: We should check here that no other instances of pcitool are running, the driver can provide this information
  208. ctx->locks.kmem = pcilib_alloc_kernel_memory(ctx, PCILIB_KMEM_TYPE_PAGE, PCILIB_LOCK_PAGES, PCILIB_KMEM_PAGE_SIZE, 0, PCILIB_KMEM_USE(PCILIB_KMEM_USE_LOCKS,0), PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT);
  209. if (!ctx->locks.kmem) {
  210. pcilib_unlock_global(ctx);
  211. pcilib_error("Failed to allocate kernel memory of locking subsystem");
  212. return PCILIB_ERROR_FAILED;
  213. }
  214. reused = pcilib_kmem_is_reused(ctx, ctx->locks.kmem);
  215. if (reused & PCILIB_KMEM_REUSE_PARTIAL) {
  216. pcilib_unlock_global(ctx);
  217. pcilib_error("Inconsistent kernel memory for locking subsystem is found (only part of the required buffers is available)");
  218. return PCILIB_ERROR_INVALID_STATE;
  219. }
  220. if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) {
  221. pcilib_free_kernel_memory(ctx, ctx->locks.kmem, PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT);
  222. pcilib_unlock_global(ctx);
  223. return 0;
  224. }
  225. /* if we run in non-forced case, then if it may be still processes that can have access to the locks, they are not destroyed*/
  226. if (!force) {
  227. for (i = 0; i < PCILIB_MAX_LOCKS; i++) {
  228. pcilib_lock_t *lock = pcilib_get_lock_by_id(ctx, i);
  229. const char *name = pcilib_lock_get_name(lock);
  230. if (!name) break;
  231. size_t refs = pcilib_lock_get_refs(lock);
  232. if (refs > 0) {
  233. char *stmp = strdup(name);
  234. pcilib_free_locking(ctx);
  235. pcilib_unlock_global(ctx);
  236. pcilib_error("Lock (%s) has %zu references, destroying references may result in crashes and data corruption", stmp, refs);
  237. free(stmp);
  238. return PCILIB_ERROR_BUSY;
  239. }
  240. }
  241. }
  242. // Do we really need this? I guess zeroing should be enough
  243. for (i = 0; i < PCILIB_MAX_LOCKS; i++) {
  244. pcilib_lock_t *lock = pcilib_get_lock_by_id(ctx, i);
  245. const char *name = pcilib_lock_get_name(lock);
  246. if (!name) break;
  247. pcilib_free_lock(lock);
  248. }
  249. for (i = 0; i < PCILIB_LOCK_PAGES; i++) {
  250. void *addr = (void*)pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, i);
  251. memset(addr, 0, PCILIB_KMEM_PAGE_SIZE);
  252. }
  253. pcilib_free_locking(ctx);
  254. pcilib_unlock_global(ctx);
  255. return 0;
  256. }