123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319 |
- #define _XOPEN_SOURCE 700
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <strings.h>
- #include <assert.h>
- #include <stdint.h>
- #include <sys/file.h>
- #include "locking.h"
- #include "error.h"
- #include "pci.h"
- #include "kmem.h"
- /*
- * this function allocates the kernel memory for the locks for software registers
- */
- int pcilib_init_locking(pcilib_t* ctx) {
- int i;
- int err;
- pcilib_kmem_reuse_state_t reused;
- assert(PCILIB_LOCK_PAGES * PCILIB_KMEM_PAGE_SIZE >= PCILIB_MAX_LOCKS * PCILIB_LOCK_SIZE);
-
- /*protection against multiple creations of kernel space*/
- err = pcilib_lock_global(ctx);
- if (err) return err;
- /* 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*/
- 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);
- if (!ctx->locks.kmem) {
- pcilib_unlock_global(ctx);
- pcilib_error("Allocation of kernel memory for locking subsystem has failed");
- return PCILIB_ERROR_FAILED;
- }
- reused = pcilib_kmem_is_reused(ctx, ctx->locks.kmem);
- if (reused & PCILIB_KMEM_REUSE_PARTIAL) {
- pcilib_unlock_global(ctx);
- pcilib_error("Inconsistent kernel memory for locking subsystem is found (only part of the required buffers is available)");
- return PCILIB_ERROR_INVALID_STATE;
- }
- if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) {
- for (i = 0; i < PCILIB_LOCK_PAGES; i++) {
- void *addr = (void*)pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, i);
- memset(addr, 0, PCILIB_KMEM_PAGE_SIZE);
- }
- }
- /* the lock that has been used for the creation of kernel space is declared unlocked, has we shouldnot use it anymore*/
- ctx->locks.locking = pcilib_get_lock(ctx, PCILIB_LOCK_FLAG_UNLOCKED, "locking");
- pcilib_unlock_global(ctx);
- if ((!ctx->locks.locking)) {
- pcilib_error("Locking subsystem has failed to initialized mandatory global locks");
- return PCILIB_ERROR_FAILED;
- }
- return 0;
- }
- /*
- * this function free the kernel memory allocated for them and destroys locks by setting memory to 0
- */
- void pcilib_free_locking(pcilib_t *ctx) {
- if (ctx->locks.locking)
- pcilib_return_lock(ctx, PCILIB_LOCK_FLAGS_DEFAULT, ctx->locks.locking);
- if (ctx->locks.kmem) {
- pcilib_free_kernel_memory(ctx, ctx->locks.kmem, PCILIB_KMEM_FLAG_REUSE);
- }
- memset(&ctx->locks, 0, sizeof(pcilib_locking_t));
- }
- int pcilib_lock_global(pcilib_t *ctx) {
- int err;
-
- /* 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) */
- if ((err = flock(ctx->handle, LOCK_EX))==-1) {
- pcilib_error("Can't get flock on device file");
- return PCILIB_ERROR_FAILED;
- }
- return 0;
- }
- void pcilib_unlock_global(pcilib_t *ctx) {
- if (flock(ctx->handle, LOCK_UN) == -1)
- pcilib_warning("Could not correctly remove lock from the device file");
- }
- pcilib_lock_t *pcilib_get_lock_by_id(pcilib_t *ctx, pcilib_lock_id_t id) {
- int page = id / PCILIB_LOCKS_PER_PAGE;
- int offset = id - page * PCILIB_LOCKS_PER_PAGE;
- volatile void *addr = pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, page);
- pcilib_lock_t *lock = (pcilib_lock_t*)(addr + offset * PCILIB_LOCK_SIZE);
- return lock;
- }
- pcilib_lock_t *pcilib_get_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, const char *lock_id, ...) {
- pcilib_lock_id_t i;
- int err, ret;
- pcilib_lock_t *lock;
- char buffer[PCILIB_LOCK_SIZE];
- /* we construct the complete lock_id given the parameters of the function*/
- va_list pa;
- va_start(pa, lock_id);
- ret = vsnprintf(buffer, PCILIB_LOCK_SIZE, lock_id, pa);
- va_end(pa);
- if (ret < 0) {
- pcilib_error("Failed to construct the lock id, probably arguments does not match the format string (%s)...", lock_id);
- return NULL;
- }
-
-
- /* we iterate through locks to see if there is one already with the same name*/
- // Would be nice to have hash here
- for (i = 0; i < PCILIB_MAX_LOCKS; i++) {
- lock = pcilib_get_lock_by_id(ctx, i);
- const char *name = pcilib_lock_get_name(lock);
- if (!name) break;
-
- if (!strcmp(buffer, name)) {
- if ((pcilib_lock_get_flags(lock)&PCILIB_LOCK_FLAG_PERSISTENT) != (flags&PCILIB_LOCK_FLAG_PERSISTENT)) {
- if (flags&PCILIB_LOCK_FLAG_PERSISTENT)
- pcilib_error("Requesting persistent lock (%s), but requested lock is already existing and is robust", name);
- else
- pcilib_error("Requesting robust lock (%s), but requested lock is already existing and is persistent", name);
- return NULL;
- }
- #ifndef HAVE_STDATOMIC_H
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) {
- err = pcilib_lock(ctx->locks.locking);
- if (err) {
- pcilib_error("Error (%i) obtaining global lock", err);
- return NULL;
- }
- }
- #endif /* ! HAVE_STDATOMIC_H */
- /* if yes, we increment its ref variable*/
- pcilib_lock_ref(lock);
- #ifndef HAVE_STDATOMIC_H
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
- pcilib_unlock(ctx->locks.locking);
- #endif /* ! HAVE_STDATOMIC_H */
- return lock;
- }
- }
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) {
- err = pcilib_lock(ctx->locks.locking);
- if (err) {
- pcilib_error("Error (%i) obtaining global lock", err);
- return NULL;
- }
- }
- // Make sure it was not allocated meanwhile
- for (; i < PCILIB_MAX_LOCKS; i++) {
- lock = pcilib_get_lock_by_id(ctx, i);
- const char *name = pcilib_lock_get_name(lock);
- if (!name) break;
- if (!strcmp(buffer, name)) {
- if ((pcilib_lock_get_flags(lock)&PCILIB_LOCK_FLAG_PERSISTENT) != (flags&PCILIB_LOCK_FLAG_PERSISTENT)) {
- if (flags&PCILIB_LOCK_FLAG_PERSISTENT)
- pcilib_error("Requesting persistent lock (%s), but requested lock is already existing and is robust", name);
- else
- pcilib_error("Requesting robust lock (%s), but requested lock is already existing and is persistent", name);
-
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
- pcilib_unlock(ctx->locks.locking);
- return NULL;
- }
- pcilib_lock_ref(lock);
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
- pcilib_unlock(ctx->locks.locking);
- return lock;
- }
- }
- if (i == PCILIB_MAX_LOCKS) {
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
- pcilib_unlock(ctx->locks.locking);
- pcilib_error("Failed to create lock (%s), only %u locks is supported", buffer, PCILIB_MAX_LOCKS);
- return NULL;
- }
- /* if the lock did not exist before, then we create it*/
- err = pcilib_init_lock(lock, flags, buffer);
-
- if (err) {
- pcilib_error("Lock initialization failed with error %i", err);
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
- pcilib_unlock(ctx->locks.locking);
-
- return NULL;
- }
-
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
- pcilib_unlock(ctx->locks.locking);
- return lock;
- }
- void pcilib_return_lock(pcilib_t *ctx, pcilib_lock_flags_t flags, pcilib_lock_t *lock) {
- #ifndef HAVE_STDATOMIC_H
- int err;
-
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0) {
- err = pcilib_lock(ctx->locks.locking);
- if (err) {
- pcilib_error("Error (%i) obtaining global lock", err);
- return;
- }
- }
- #endif /* ! HAVE_STDATOMIC_H */
- pcilib_lock_unref(lock);
- #ifndef HAVE_STDATOMIC_H
- if ((flags&PCILIB_LOCK_FLAG_UNLOCKED)==0)
- pcilib_unlock(ctx->locks.locking);
- #endif /* ! HAVE_STDATOMIC_H */
- }
- /*
- * Destroy all existing locks. This is unsafe call as this and other running applications
- * will still have all initialized lock pointers. It is user responsibility to issue this
- * command when no other application is running.
- */
- int pcilib_destroy_all_locks(pcilib_t *ctx, int force) {
- int err;
- pcilib_lock_id_t i;
- pcilib_kmem_reuse_state_t reused;
- if (strcasecmp(ctx->model, "maintenance")) {
- pcilib_error("Can't destroy locks while locking subsystem is initialized, use maintenance model");
- return PCILIB_ERROR_INVALID_STATE;
- }
- err = pcilib_lock_global(ctx);
- if (err) return err;
- // ToDo: We should check here that no other instances of pcitool are running, the driver can provide this information
- 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);
- if (!ctx->locks.kmem) {
- pcilib_unlock_global(ctx);
- pcilib_error("Failed to allocate kernel memory of locking subsystem");
- return PCILIB_ERROR_FAILED;
- }
- reused = pcilib_kmem_is_reused(ctx, ctx->locks.kmem);
- if (reused & PCILIB_KMEM_REUSE_PARTIAL) {
- pcilib_unlock_global(ctx);
- pcilib_error("Inconsistent kernel memory for locking subsystem is found (only part of the required buffers is available)");
- return PCILIB_ERROR_INVALID_STATE;
- }
- if ((reused & PCILIB_KMEM_REUSE_REUSED) == 0) {
- pcilib_free_kernel_memory(ctx, ctx->locks.kmem, PCILIB_KMEM_FLAG_REUSE|PCILIB_KMEM_FLAG_PERSISTENT);
- pcilib_unlock_global(ctx);
- return 0;
- }
- /* 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*/
- if (!force) {
- for (i = 0; i < PCILIB_MAX_LOCKS; i++) {
- pcilib_lock_t *lock = pcilib_get_lock_by_id(ctx, i);
- const char *name = pcilib_lock_get_name(lock);
- if (!name) break;
-
- size_t refs = pcilib_lock_get_refs(lock);
- if (refs > 0) {
- char *stmp = strdup(name);
- pcilib_free_locking(ctx);
- pcilib_unlock_global(ctx);
- pcilib_error("Lock (%s) has %zu references, destroying references may result in crashes and data corruption", stmp, refs);
- free(stmp);
- return PCILIB_ERROR_BUSY;
- }
- }
- }
- // Do we really need this? I guess zeroing should be enough
- for (i = 0; i < PCILIB_MAX_LOCKS; i++) {
- pcilib_lock_t *lock = pcilib_get_lock_by_id(ctx, i);
- const char *name = pcilib_lock_get_name(lock);
- if (!name) break;
- pcilib_free_lock(lock);
- }
- for (i = 0; i < PCILIB_LOCK_PAGES; i++) {
- void *addr = (void*)pcilib_kmem_get_block_ua(ctx, ctx->locks.kmem, i);
- memset(addr, 0, PCILIB_KMEM_PAGE_SIZE);
- }
- pcilib_free_locking(ctx);
- pcilib_unlock_global(ctx);
- return 0;
- }
|