// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2022-2023, Qualcomm Innovation Center, Inc. All rights reserved. */ #include #include #include "synx_debugfs.h" #include "synx_global.h" static struct synx_shared_mem synx_gmem; static struct hwspinlock *synx_hwlock; static u32 synx_gmem_lock_owner(u32 idx) { /* * subscribers field of global table index 0 is used to * maintain synx gmem lock owner data. * core updates the field after acquiring the lock and * before releasing the lock appropriately. */ return synx_gmem.table[0].subscribers; } static void synx_gmem_lock_owner_set(u32 idx) { synx_gmem.table[0].subscribers = SYNX_CORE_APSS; } static void synx_gmem_lock_owner_clear(u32 idx) { if (synx_gmem.table[0].subscribers != SYNX_CORE_APSS) dprintk(SYNX_WARN, "reset lock owned by core %u\n", synx_gmem.table[0].subscribers); synx_gmem.table[0].subscribers = SYNX_CORE_MAX; } static int synx_gmem_lock(u32 idx, unsigned long *flags) { int rc; if (!synx_hwlock) return -SYNX_INVALID; rc = hwspin_lock_timeout_irqsave( synx_hwlock, SYNX_HWSPIN_TIMEOUT, flags); if (!rc) synx_gmem_lock_owner_set(idx); return rc; } static void synx_gmem_unlock(u32 idx, unsigned long *flags) { synx_gmem_lock_owner_clear(idx); hwspin_unlock_irqrestore(synx_hwlock, flags); } static void synx_global_print_data( struct synx_global_coredata *synx_g_obj, const char *func) { int i = 0; dprintk(SYNX_VERB, "%s: status %u, handle %u, refcount %u", func, synx_g_obj->status, synx_g_obj->handle, synx_g_obj->refcount); dprintk(SYNX_VERB, "%s: subscribers %u, waiters %u, pending %u", func, synx_g_obj->subscribers, synx_g_obj->waiters, synx_g_obj->num_child); for (i = 0; i < SYNX_GLOBAL_MAX_PARENTS; i++) if (synx_g_obj->parents[i]) dprintk(SYNX_VERB, "%s: parents %u:%u", func, i, synx_g_obj->parents[i]); } bool synx_fetch_global_shared_memory_handle_details(u32 synx_handle, struct synx_global_coredata *synx_global_entry) { int rc = SYNX_SUCCESS; u32 idx; unsigned long flags; struct synx_global_coredata *entry; if (!synx_gmem.table) { dprintk(SYNX_VERB, "synx_gmem is NULL\n"); return false; } idx = synx_handle & SYNX_HANDLE_INDEX_MASK; if (!synx_is_valid_idx(idx)) return false; rc = synx_gmem_lock(idx, &flags); if (rc) { dprintk(SYNX_VERB, "Failed to lock entry %d\n", idx); return false; } entry = &synx_gmem.table[idx]; memcpy(synx_global_entry, entry, sizeof(struct synx_global_coredata)); synx_gmem_unlock(idx, &flags); return true; } int synx_global_dump_shared_memory(void) { int rc = SYNX_SUCCESS, idx; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return -SYNX_INVALID; /* Print bitmap memory*/ for (idx = 0; idx < SHRD_MEM_DUMP_NUM_BMAP_WORDS; idx++) { rc = synx_gmem_lock(idx, &flags); if (rc) return rc; dprintk(SYNX_VERB, "%s: idx %d, bitmap value %d", __func__, idx, synx_gmem.bitmap[idx]); synx_gmem_unlock(idx, &flags); } /* Print table memory*/ for (idx = 0; idx < SHRD_MEM_DUMP_NUM_BMAP_WORDS * sizeof(u32) * NUM_CHAR_BIT; idx++) { rc = synx_gmem_lock(idx, &flags); if (rc) return rc; dprintk(SYNX_VERB, "%s: idx %d\n", __func__, idx); synx_g_obj = &synx_gmem.table[idx]; synx_global_print_data(synx_g_obj, __func__); synx_gmem_unlock(idx, &flags); } return rc; } static int synx_gmem_init(void) { if (!synx_gmem.table) return -SYNX_NOMEM; synx_hwlock = hwspin_lock_request_specific(SYNX_HWSPIN_ID); if (!synx_hwlock) { dprintk(SYNX_ERR, "hwspinlock request failed\n"); return -SYNX_NOMEM; } /* zero idx not allocated for clients */ ipclite_global_test_and_set_bit(0, (ipclite_atomic_uint32_t *)synx_gmem.bitmap); memset(&synx_gmem.table[0], 0, sizeof(struct synx_global_coredata)); return SYNX_SUCCESS; } u32 synx_global_map_core_id(enum synx_core_id id) { u32 host_id; switch (id) { case SYNX_CORE_APSS: host_id = IPCMEM_APPS; break; case SYNX_CORE_NSP: host_id = IPCMEM_CDSP; break; case SYNX_CORE_IRIS: host_id = IPCMEM_VPU; break; case SYNX_CORE_EVA: host_id = IPCMEM_CVP; break; case SYNX_CORE_ICP: host_id = IPCMEM_CAM; break; default: host_id = IPCMEM_NUM_HOSTS; dprintk(SYNX_ERR, "invalid core id\n"); } return host_id; } int synx_global_alloc_index(u32 *idx) { int rc = SYNX_SUCCESS; u32 prev, index; const u32 size = SYNX_GLOBAL_MAX_OBJS; if (!synx_gmem.table) return -SYNX_NOMEM; if (IS_ERR_OR_NULL(idx)) return -SYNX_INVALID; do { index = find_first_zero_bit((unsigned long *)synx_gmem.bitmap, size); if (index >= size) { rc = -SYNX_NOMEM; break; } prev = ipclite_global_test_and_set_bit(index % 32, (ipclite_atomic_uint32_t *)(synx_gmem.bitmap + index/32)); if ((prev & (1UL << (index % 32))) == 0) { *idx = index; dprintk(SYNX_MEM, "allocated global idx %u\n", *idx); break; } } while (true); return rc; } int synx_global_init_coredata(u32 h_synx) { int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; u32 idx = h_synx & SYNX_HANDLE_INDEX_MASK; if (!synx_gmem.table) return -SYNX_NOMEM; if (!synx_is_valid_idx(idx)) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; if (synx_g_obj->status != 0 || synx_g_obj->refcount != 0 || synx_g_obj->subscribers != 0 || synx_g_obj->handle != 0 || synx_g_obj->parents[0] != 0) { dprintk(SYNX_ERR, "entry not cleared for idx %u,\n" "synx_g_obj->status %d,\n" "synx_g_obj->refcount %d,\n" "synx_g_obj->subscribers %d,\n" "synx_g_obj->handle %u,\n" "synx_g_obj->parents[0] %d\n", idx, synx_g_obj->status, synx_g_obj->refcount, synx_g_obj->subscribers, synx_g_obj->handle, synx_g_obj->parents[0]); synx_gmem_unlock(idx, &flags); return -SYNX_INVALID; } memset(synx_g_obj, 0, sizeof(*synx_g_obj)); /* set status to active */ synx_g_obj->status = SYNX_STATE_ACTIVE; synx_g_obj->refcount = 1; synx_g_obj->subscribers = (1UL << SYNX_CORE_APSS); synx_g_obj->handle = h_synx; synx_gmem_unlock(idx, &flags); return SYNX_SUCCESS; } static int synx_global_get_waiting_cores_locked( struct synx_global_coredata *synx_g_obj, bool *cores) { int i; synx_global_print_data(synx_g_obj, __func__); for (i = 0; i < SYNX_CORE_MAX; i++) { if (synx_g_obj->waiters & (1UL << i)) { cores[i] = true; dprintk(SYNX_VERB, "waiting for handle %u/n", synx_g_obj->handle); } } /* clear waiter list so signals are not repeated */ synx_g_obj->waiters = 0; return SYNX_SUCCESS; } int synx_global_get_waiting_cores(u32 idx, bool *cores) { int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return -SYNX_NOMEM; if (IS_ERR_OR_NULL(cores) || !synx_is_valid_idx(idx)) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; synx_global_get_waiting_cores_locked(synx_g_obj, cores); synx_gmem_unlock(idx, &flags); return SYNX_SUCCESS; } int synx_global_set_waiting_core(u32 idx, enum synx_core_id id) { int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return -SYNX_NOMEM; if (id >= SYNX_CORE_MAX || !synx_is_valid_idx(idx)) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; synx_g_obj->waiters |= (1UL << id); synx_gmem_unlock(idx, &flags); return SYNX_SUCCESS; } int synx_global_get_subscribed_cores(u32 idx, bool *cores) { int i; int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return -SYNX_NOMEM; if (IS_ERR_OR_NULL(cores) || !synx_is_valid_idx(idx)) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; for (i = 0; i < SYNX_CORE_MAX; i++) if (synx_g_obj->subscribers & (1UL << i)) cores[i] = true; synx_gmem_unlock(idx, &flags); return SYNX_SUCCESS; } int synx_global_fetch_handle_details(u32 idx, u32 *h_synx) { int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return -SYNX_NOMEM; if (IS_ERR_OR_NULL(h_synx) || !synx_is_valid_idx(idx)) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; *h_synx = synx_g_obj->handle; synx_gmem_unlock(idx, &flags); return SYNX_SUCCESS; } int synx_global_set_subscribed_core(u32 idx, enum synx_core_id id) { int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return -SYNX_NOMEM; if (id >= SYNX_CORE_MAX || !synx_is_valid_idx(idx)) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; synx_g_obj->subscribers |= (1UL << id); synx_gmem_unlock(idx, &flags); return SYNX_SUCCESS; } int synx_global_clear_subscribed_core(u32 idx, enum synx_core_id id) { int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return -SYNX_NOMEM; if (id >= SYNX_CORE_MAX || !synx_is_valid_idx(idx)) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; synx_g_obj->subscribers &= ~(1UL << id); synx_gmem_unlock(idx, &flags); return SYNX_SUCCESS; } u32 synx_global_get_parents_num(u32 idx) { int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; u32 i, count = 0; if (!synx_gmem.table) return 0; if (!synx_is_valid_idx(idx)) return 0; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; for (i = 0; i < SYNX_GLOBAL_MAX_PARENTS; i++) { if (synx_g_obj->parents[i] != 0) count++; } synx_gmem_unlock(idx, &flags); return count; } static int synx_global_get_parents_locked( struct synx_global_coredata *synx_g_obj, u32 *parents) { u32 i; if (!synx_g_obj || !parents) return -SYNX_NOMEM; for (i = 0; i < SYNX_GLOBAL_MAX_PARENTS; i++) parents[i] = synx_g_obj->parents[i]; return SYNX_SUCCESS; } int synx_global_get_parents(u32 idx, u32 *parents) { int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table || !parents) return -SYNX_NOMEM; if (!synx_is_valid_idx(idx)) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; rc = synx_global_get_parents_locked(synx_g_obj, parents); synx_gmem_unlock(idx, &flags); return rc; } u32 synx_global_get_status(u32 idx) { int rc; unsigned long flags; u32 status = SYNX_STATE_ACTIVE; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return 0; if (!synx_is_valid_idx(idx)) return 0; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; if (synx_g_obj->status != SYNX_STATE_ACTIVE && synx_g_obj->num_child == 0) status = synx_g_obj->status; synx_gmem_unlock(idx, &flags); return status; } u32 synx_global_test_status_set_wait(u32 idx, enum synx_core_id id) { int rc; unsigned long flags; u32 status; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return 0; if (id >= SYNX_CORE_MAX || !synx_is_valid_idx(idx)) return 0; rc = synx_gmem_lock(idx, &flags); if (rc) return 0; synx_g_obj = &synx_gmem.table[idx]; synx_global_print_data(synx_g_obj, __func__); status = synx_g_obj->status; /* if handle is still ACTIVE */ if (status == SYNX_STATE_ACTIVE || synx_g_obj->num_child != 0) { synx_g_obj->waiters |= (1UL << id); status = SYNX_STATE_ACTIVE; } else dprintk(SYNX_DBG, "handle %u already signaled %u", synx_g_obj->handle, synx_g_obj->status); synx_gmem_unlock(idx, &flags); return status; } static int synx_global_update_status_core(u32 idx, u32 status) { u32 i, p_idx; int rc; bool clear = false; unsigned long flags; uint64_t data; struct synx_global_coredata *synx_g_obj; u32 h_parents[SYNX_GLOBAL_MAX_PARENTS] = {0}; bool wait_cores[SYNX_CORE_MAX] = {false}; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; synx_global_print_data(synx_g_obj, __func__); /* prepare for cross core signaling */ data = synx_g_obj->handle; data <<= 32; if (synx_g_obj->num_child != 0) { /* composite handle */ synx_g_obj->num_child--; if (synx_g_obj->status == SYNX_STATE_ACTIVE || (status > SYNX_STATE_SIGNALED_SUCCESS && status <= SYNX_STATE_SIGNALED_MAX)) synx_g_obj->status = status; if (synx_g_obj->num_child == 0) { data |= synx_g_obj->status; synx_global_get_waiting_cores_locked(synx_g_obj, wait_cores); synx_global_get_parents_locked(synx_g_obj, h_parents); /* release ref held by constituting handles */ synx_g_obj->refcount--; if (synx_g_obj->refcount == 0) { memset(synx_g_obj, 0, sizeof(*synx_g_obj)); clear = true; } } else { /* pending notification from handles */ data = 0; dprintk(SYNX_DBG, "Child notified parent handle %u, pending %u\n", synx_g_obj->handle, synx_g_obj->num_child); } } else { synx_g_obj->status = status; data |= synx_g_obj->status; synx_global_get_waiting_cores_locked(synx_g_obj, wait_cores); synx_global_get_parents_locked(synx_g_obj, h_parents); } synx_gmem_unlock(idx, &flags); if (clear) { ipclite_global_test_and_clear_bit(idx%32, (ipclite_atomic_uint32_t *)(synx_gmem.bitmap + idx/32)); dprintk(SYNX_MEM, "cleared global idx %u\n", idx); } /* notify waiting clients on signal */ if (data) { /* notify wait client */ /* In case of SSR, someone might be waiting on same core * However, in other cases, synx_signal API will take care * of signaling handles on same core and thus we don't need * to send interrupt */ if (status == SYNX_STATE_SIGNALED_SSR) i = 0; else i = 1; for (; i < SYNX_CORE_MAX ; i++) { if (!wait_cores[i]) continue; dprintk(SYNX_DBG, "invoking ipc signal handle %u, status %u\n", synx_g_obj->handle, synx_g_obj->status); if (ipclite_msg_send( synx_global_map_core_id(i), data)) dprintk(SYNX_ERR, "ipc signaling %llu to core %u failed\n", data, i); } } /* handle parent notifications */ for (i = 0; i < SYNX_GLOBAL_MAX_PARENTS; i++) { p_idx = h_parents[i]; if (p_idx == 0) continue; synx_global_update_status_core(p_idx, status); } return SYNX_SUCCESS; } int synx_global_update_status(u32 idx, u32 status) { int rc = -SYNX_INVALID; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return -SYNX_NOMEM; if (!synx_is_valid_idx(idx) || status <= SYNX_STATE_ACTIVE) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; if (synx_g_obj->num_child != 0) { /* composite handle cannot be signaled */ goto fail; } else if (synx_g_obj->status != SYNX_STATE_ACTIVE) { rc = -SYNX_ALREADY; goto fail; } synx_gmem_unlock(idx, &flags); return synx_global_update_status_core(idx, status); fail: synx_gmem_unlock(idx, &flags); return rc; } int synx_global_get_ref(u32 idx) { int rc; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return -SYNX_NOMEM; if (!synx_is_valid_idx(idx)) return -SYNX_INVALID; rc = synx_gmem_lock(idx, &flags); if (rc) return rc; synx_g_obj = &synx_gmem.table[idx]; synx_global_print_data(synx_g_obj, __func__); if (synx_g_obj->handle && synx_g_obj->refcount) synx_g_obj->refcount++; else rc = -SYNX_NOENT; synx_gmem_unlock(idx, &flags); return rc; } void synx_global_put_ref(u32 idx) { int rc; bool clear = false; unsigned long flags; struct synx_global_coredata *synx_g_obj; if (!synx_gmem.table) return; if (!synx_is_valid_idx(idx)) return; rc = synx_gmem_lock(idx, &flags); if (rc) return; synx_g_obj = &synx_gmem.table[idx]; synx_g_obj->refcount--; if (synx_g_obj->refcount == 0) { memset(synx_g_obj, 0, sizeof(*synx_g_obj)); clear = true; } synx_gmem_unlock(idx, &flags); if (clear) { ipclite_global_test_and_clear_bit(idx%32, (ipclite_atomic_uint32_t *)(synx_gmem.bitmap + idx/32)); dprintk(SYNX_MEM, "cleared global idx %u\n", idx); } } int synx_global_merge(u32 *idx_list, u32 num_list, u32 p_idx) { int rc = -SYNX_INVALID; unsigned long flags; struct synx_global_coredata *synx_g_obj; u32 i, j = 0; u32 idx; u32 num_child = 0; u32 parent_status = SYNX_STATE_ACTIVE; if (!synx_gmem.table) return -SYNX_NOMEM; if (!synx_is_valid_idx(p_idx)) return -SYNX_INVALID; if (num_list == 0) return SYNX_SUCCESS; while (j < num_list) { idx = idx_list[j]; if (!synx_is_valid_idx(idx)) goto fail; rc = synx_gmem_lock(idx, &flags); if (rc) goto fail; synx_g_obj = &synx_gmem.table[idx]; for (i = 0; i < SYNX_GLOBAL_MAX_PARENTS; i++) { if (synx_g_obj->parents[i] == 0) { synx_g_obj->parents[i] = p_idx; break; } } if (synx_g_obj->status == SYNX_STATE_ACTIVE) num_child++; else if (synx_g_obj->status > SYNX_STATE_SIGNALED_SUCCESS && synx_g_obj->status <= SYNX_STATE_SIGNALED_MAX) parent_status = synx_g_obj->status; else if (parent_status == SYNX_STATE_ACTIVE) parent_status = synx_g_obj->status; if (synx_g_obj->status != SYNX_STATE_ACTIVE && synx_g_obj->num_child != 0) num_child++; dprintk(SYNX_MEM, "synx_obj->status %d parent status %d\n", synx_g_obj->status, parent_status); synx_gmem_unlock(idx, &flags); if (i >= SYNX_GLOBAL_MAX_PARENTS) { rc = -SYNX_NOMEM; goto fail; } j++; } rc = synx_gmem_lock(p_idx, &flags); if (rc) goto fail; synx_g_obj = &synx_gmem.table[p_idx]; synx_g_obj->num_child += num_child; if (synx_g_obj->num_child != 0) synx_g_obj->refcount++; synx_g_obj->status = parent_status; synx_global_print_data(synx_g_obj, __func__); synx_gmem_unlock(p_idx, &flags); return SYNX_SUCCESS; fail: while (num_child--) { idx = idx_list[num_child]; if (synx_gmem_lock(idx, &flags)) continue; synx_g_obj = &synx_gmem.table[idx]; for (i = 0; i < SYNX_GLOBAL_MAX_PARENTS; i++) { if (synx_g_obj->parents[i] == p_idx) { synx_g_obj->parents[i] = 0; break; } } synx_gmem_unlock(idx, &flags); } return rc; } int synx_global_recover(enum synx_core_id core_id) { int rc = SYNX_SUCCESS; u32 idx = 0; const u32 size = SYNX_GLOBAL_MAX_OBJS; unsigned long flags; struct synx_global_coredata *synx_g_obj; bool update; int *clear_idx = NULL; if (!synx_gmem.table) return -SYNX_NOMEM; clear_idx = kzalloc(sizeof(int)*SYNX_GLOBAL_MAX_OBJS, GFP_KERNEL); if (!clear_idx) return -SYNX_NOMEM; ipclite_recover(synx_global_map_core_id(core_id)); /* recover synx gmem lock if it was owned by core in ssr */ if (synx_gmem_lock_owner(0) == core_id) { synx_gmem_lock_owner_clear(0); hwspin_unlock_raw(synx_hwlock); } idx = find_next_bit((unsigned long *)synx_gmem.bitmap, size, idx + 1); while (idx < size) { update = false; rc = synx_gmem_lock(idx, &flags); if (rc) goto free; synx_g_obj = &synx_gmem.table[idx]; if (synx_g_obj->refcount && synx_g_obj->subscribers & (1UL << core_id)) { synx_g_obj->subscribers &= ~(1UL << core_id); synx_g_obj->refcount--; if (synx_g_obj->refcount == 0) { memset(synx_g_obj, 0, sizeof(*synx_g_obj)); clear_idx[idx] = 1; } else if (synx_g_obj->status == SYNX_STATE_ACTIVE) { update = true; } } synx_gmem_unlock(idx, &flags); if (update) synx_global_update_status(idx, SYNX_STATE_SIGNALED_SSR); idx = find_next_bit((unsigned long *)synx_gmem.bitmap, size, idx + 1); } for (idx = 1; idx < size; idx++) { if (clear_idx[idx]) { ipclite_global_test_and_clear_bit(idx % 32, (ipclite_atomic_uint32_t *)(synx_gmem.bitmap + idx/32)); dprintk(SYNX_MEM, "released global idx %u\n", idx); } } free: kfree(clear_idx); return rc; } int synx_global_mem_init(void) { int rc; int bitmap_size = SYNX_GLOBAL_MAX_OBJS/32; struct global_region_info mem_info; rc = get_global_partition_info(&mem_info); if (rc) { dprintk(SYNX_ERR, "error setting up global shared memory\n"); return rc; } memset(mem_info.virt_base, 0, mem_info.size); dprintk(SYNX_DBG, "global shared memory %pK size %u\n", mem_info.virt_base, mem_info.size); synx_gmem.bitmap = (u32 *)mem_info.virt_base; synx_gmem.locks = synx_gmem.bitmap + bitmap_size; synx_gmem.table = (struct synx_global_coredata *)(synx_gmem.locks + 2); dprintk(SYNX_DBG, "global memory bitmap %pK, table %pK\n", synx_gmem.bitmap, synx_gmem.table); return synx_gmem_init(); }