-/*****************************************************************************\
+/*
* Copyright (C) 2010 Lawrence Livermore National Security, LLC.
* Produced at Lawrence Livermore National Laboratory (cf, DISCLAIMER).
* Written by Brian Behlendorf <behlendorf1@llnl.gov>.
* UCRL-CODE-235197
*
* This file is part of the SPL, Solaris Porting Layer.
- * For details, see <http://github.com/behlendorf/spl/>.
+ * For details, see <http://zfsonlinux.org/>.
*
* The SPL is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
*
* You should have received a copy of the GNU General Public License along
* with the SPL. If not, see <http://www.gnu.org/licenses/>.
- *****************************************************************************
+ *
+ *
* Solaris Porting Layer (SPL) Thread Specific Data Implementation.
*
* Thread specific data has implemented using a hash table, this avoids
* so if your using the Solaris thread API you should not need to call
* tsd_exit() directly.
*
-\*****************************************************************************/
+ */
#include <sys/kmem.h>
#include <sys/thread.h>
#include <sys/tsd.h>
-#include <spl-debug.h>
-
-#ifdef DEBUG_SUBSYSTEM
-#undef DEBUG_SUBSYSTEM
-#endif
-
-#define DEBUG_SUBSYSTEM SS_TSD
-#define DEBUG_SUBSYSTEM SS_TSD
+#include <linux/hash.h>
typedef struct tsd_hash_bin {
spinlock_t hb_lock;
tsd_hash_entry_t *entry;
tsd_hash_bin_t *bin;
ulong_t hash;
- SENTRY;
hash = hash_long((ulong_t)key * (ulong_t)pid, table->ht_bits);
bin = &table->ht_bins[hash];
spin_lock(&bin->hb_lock);
- hlist_for_each_entry(entry, node, &bin->hb_head, he_list) {
+ hlist_for_each(node, &bin->hb_head) {
+ entry = list_entry(node, tsd_hash_entry_t, he_list);
if ((entry->he_key == key) && (entry->he_pid == pid)) {
spin_unlock(&bin->hb_lock);
- SRETURN(entry);
+ return (entry);
}
}
spin_unlock(&bin->hb_lock);
- SRETURN(NULL);
+ return (NULL);
}
/*
tsd_hash_dtor(struct hlist_head *work)
{
tsd_hash_entry_t *entry;
- SENTRY;
while (!hlist_empty(work)) {
entry = hlist_entry(work->first, tsd_hash_entry_t, he_list);
if (entry->he_dtor && entry->he_pid != DTOR_PID)
entry->he_dtor(entry->he_value);
- kmem_free(entry, sizeof(tsd_hash_entry_t));
+ kmem_free(entry, sizeof (tsd_hash_entry_t));
}
-
- SEXIT;
}
/*
tsd_hash_bin_t *bin;
ulong_t hash;
int rc = 0;
- SENTRY;
ASSERT3P(tsd_hash_search(table, key, pid), ==, NULL);
/* New entry allocate structure, set value, and add to hash */
- entry = kmem_alloc(sizeof(tsd_hash_entry_t), KM_PUSHPAGE);
+ entry = kmem_alloc(sizeof (tsd_hash_entry_t), KM_PUSHPAGE);
if (entry == NULL)
- SRETURN(ENOMEM);
+ return (ENOMEM);
entry->he_key = key;
entry->he_pid = pid;
spin_unlock(&bin->hb_lock);
spin_unlock(&table->ht_lock);
- SRETURN(rc);
+ return (rc);
}
/*
tsd_hash_bin_t *bin;
ulong_t hash;
int keys_checked = 0;
- SENTRY;
ASSERT3P(table, !=, NULL);
/* Allocate entry to be used as a destructor for this key */
- entry = kmem_alloc(sizeof(tsd_hash_entry_t), KM_PUSHPAGE);
+ entry = kmem_alloc(sizeof (tsd_hash_entry_t), KM_PUSHPAGE);
if (entry == NULL)
- SRETURN(ENOMEM);
+ return (ENOMEM);
/* Determine next available key value */
spin_lock(&table->ht_lock);
/* Ensure failure when all TSD_KEYS_MAX keys are in use */
if (keys_checked++ >= TSD_KEYS_MAX) {
spin_unlock(&table->ht_lock);
- SRETURN(ENOENT);
+ return (ENOENT);
}
tmp_entry = tsd_hash_search(table, table->ht_key, DTOR_PID);
spin_unlock(&bin->hb_lock);
spin_unlock(&table->ht_lock);
- SRETURN(0);
+ return (0);
}
/*
tsd_hash_entry_t *entry;
tsd_hash_bin_t *bin;
ulong_t hash;
- SENTRY;
/* Allocate entry to be used as the process reference */
- entry = kmem_alloc(sizeof(tsd_hash_entry_t), KM_PUSHPAGE);
+ entry = kmem_alloc(sizeof (tsd_hash_entry_t), KM_PUSHPAGE);
if (entry == NULL)
- SRETURN(ENOMEM);
+ return (ENOMEM);
spin_lock(&table->ht_lock);
entry->he_key = PID_KEY;
spin_unlock(&bin->hb_lock);
spin_unlock(&table->ht_lock);
- SRETURN(0);
+ return (0);
}
/*
static void
tsd_hash_del(tsd_hash_table_t *table, tsd_hash_entry_t *entry)
{
- SENTRY;
-
ASSERT(spin_is_locked(&table->ht_lock));
hlist_del(&entry->he_list);
list_del_init(&entry->he_key_list);
list_del_init(&entry->he_pid_list);
-
- SEXIT;
}
/*
{
tsd_hash_table_t *table;
int hash, size = (1 << bits);
- SENTRY;
- table = kmem_zalloc(sizeof(tsd_hash_table_t), KM_SLEEP);
+ table = kmem_zalloc(sizeof (tsd_hash_table_t), KM_SLEEP);
if (table == NULL)
- SRETURN(NULL);
+ return (NULL);
- table->ht_bins = kmem_zalloc(sizeof(tsd_hash_bin_t) * size,
- KM_SLEEP | KM_NODEBUG);
+ table->ht_bins = kmem_zalloc(sizeof (tsd_hash_bin_t) * size, KM_SLEEP);
if (table->ht_bins == NULL) {
- kmem_free(table, sizeof(tsd_hash_table_t));
- SRETURN(NULL);
+ kmem_free(table, sizeof (tsd_hash_table_t));
+ return (NULL);
}
for (hash = 0; hash < size; hash++) {
table->ht_bits = bits;
table->ht_key = 1;
- SRETURN(table);
+ return (table);
}
/*
tsd_hash_bin_t *bin;
tsd_hash_entry_t *entry;
int size, i;
- SENTRY;
ASSERT3P(table, !=, NULL);
spin_lock(&table->ht_lock);
for (i = 0, size = (1 << table->ht_bits); i < size; i++) {
bin = &table->ht_bins[i];
spin_lock(&bin->hb_lock);
- while (!hlist_empty(&bin->hb_head)) {
+ while (!hlist_empty(&bin->hb_head)) {
entry = hlist_entry(bin->hb_head.first,
- tsd_hash_entry_t, he_list);
+ tsd_hash_entry_t, he_list);
tsd_hash_del(table, entry);
hlist_add_head(&entry->he_list, &work);
}
spin_unlock(&table->ht_lock);
tsd_hash_dtor(&work);
- kmem_free(table->ht_bins, sizeof(tsd_hash_bin_t)*(1<<table->ht_bits));
- kmem_free(table, sizeof(tsd_hash_table_t));
+ kmem_free(table->ht_bins, sizeof (tsd_hash_bin_t)*(1<<table->ht_bits));
+ kmem_free(table, sizeof (tsd_hash_table_t));
+}
+
+/*
+ * tsd_remove_entry - remove a tsd entry for this thread
+ * @entry: entry to remove
+ *
+ * Remove the thread specific data @entry for this thread.
+ * If this is the last entry for this thread, also remove the PID entry.
+ */
+static void
+tsd_remove_entry(tsd_hash_entry_t *entry)
+{
+ HLIST_HEAD(work);
+ tsd_hash_table_t *table;
+ tsd_hash_entry_t *pid_entry;
+ tsd_hash_bin_t *pid_entry_bin, *entry_bin;
+ ulong_t hash;
+
+ table = tsd_hash_table;
+ ASSERT3P(table, !=, NULL);
+ ASSERT3P(entry, !=, NULL);
+
+ spin_lock(&table->ht_lock);
+
+ hash = hash_long((ulong_t)entry->he_key *
+ (ulong_t)entry->he_pid, table->ht_bits);
+ entry_bin = &table->ht_bins[hash];
+
+ /* save the possible pid_entry */
+ pid_entry = list_entry(entry->he_pid_list.next, tsd_hash_entry_t,
+ he_pid_list);
+
+ /* remove entry */
+ spin_lock(&entry_bin->hb_lock);
+ tsd_hash_del(table, entry);
+ hlist_add_head(&entry->he_list, &work);
+ spin_unlock(&entry_bin->hb_lock);
+
+ /* if pid_entry is indeed pid_entry, then remove it if it's empty */
+ if (pid_entry->he_key == PID_KEY &&
+ list_empty(&pid_entry->he_pid_list)) {
+ hash = hash_long((ulong_t)pid_entry->he_key *
+ (ulong_t)pid_entry->he_pid, table->ht_bits);
+ pid_entry_bin = &table->ht_bins[hash];
+
+ spin_lock(&pid_entry_bin->hb_lock);
+ tsd_hash_del(table, pid_entry);
+ hlist_add_head(&pid_entry->he_list, &work);
+ spin_unlock(&pid_entry_bin->hb_lock);
+ }
+
+ spin_unlock(&table->ht_lock);
- SEXIT;
+ tsd_hash_dtor(&work);
}
/*
tsd_hash_entry_t *entry;
pid_t pid;
int rc;
- SENTRY;
+ /* mark remove if value is NULL */
+ boolean_t remove = (value == NULL);
table = tsd_hash_table;
pid = curthread->pid;
ASSERT3P(table, !=, NULL);
if ((key == 0) || (key > TSD_KEYS_MAX))
- SRETURN(EINVAL);
+ return (EINVAL);
/* Entry already exists in hash table update value */
entry = tsd_hash_search(table, key, pid);
if (entry) {
entry->he_value = value;
- SRETURN(0);
+ /* remove the entry */
+ if (remove)
+ tsd_remove_entry(entry);
+ return (0);
}
+ /* don't create entry if value is NULL */
+ if (remove)
+ return (0);
+
/* Add a process entry to the hash if not yet exists */
entry = tsd_hash_search(table, PID_KEY, pid);
if (entry == NULL) {
rc = tsd_hash_add_pid(table, pid);
if (rc)
- SRETURN(rc);
+ return (rc);
}
rc = tsd_hash_add(table, key, pid, value);
- SRETURN(rc);
+ return (rc);
}
EXPORT_SYMBOL(tsd_set);
tsd_get(uint_t key)
{
tsd_hash_entry_t *entry;
- SENTRY;
ASSERT3P(tsd_hash_table, !=, NULL);
if ((key == 0) || (key > TSD_KEYS_MAX))
- SRETURN(NULL);
+ return (NULL);
entry = tsd_hash_search(tsd_hash_table, key, curthread->pid);
if (entry == NULL)
- SRETURN(NULL);
+ return (NULL);
- SRETURN(entry->he_value);
+ return (entry->he_value);
}
EXPORT_SYMBOL(tsd_get);
+/*
+ * tsd_get_by_thread - get thread specific data for specified thread
+ * @key: lookup key
+ * @thread: thread to lookup
+ *
+ * Caller must prevent racing tsd_create() or tsd_destroy(). This
+ * implementation is designed to be fast and scalable, it does not
+ * lock the entire table only a single hash bin.
+ */
+void *
+tsd_get_by_thread(uint_t key, kthread_t *thread)
+{
+ tsd_hash_entry_t *entry;
+
+ ASSERT3P(tsd_hash_table, !=, NULL);
+
+ if ((key == 0) || (key > TSD_KEYS_MAX))
+ return (NULL);
+
+ entry = tsd_hash_search(tsd_hash_table, key, thread->pid);
+ if (entry == NULL)
+ return (NULL);
+
+ return (entry->he_value);
+}
+EXPORT_SYMBOL(tsd_get_by_thread);
+
/*
* tsd_create - create thread specific data key
* @keyp: lookup key address
void
tsd_create(uint_t *keyp, dtor_func_t dtor)
{
- SENTRY;
-
ASSERT3P(keyp, !=, NULL);
- if (*keyp) {
- SEXIT;
+ if (*keyp)
return;
- }
- (void)tsd_hash_add_key(tsd_hash_table, keyp, dtor);
-
- SEXIT;
+ (void) tsd_hash_add_key(tsd_hash_table, keyp, dtor);
}
EXPORT_SYMBOL(tsd_create);
tsd_hash_entry_t *dtor_entry, *entry;
tsd_hash_bin_t *dtor_entry_bin, *entry_bin;
ulong_t hash;
- SENTRY;
table = tsd_hash_table;
ASSERT3P(table, !=, NULL);
dtor_entry = tsd_hash_search(table, *keyp, DTOR_PID);
if (dtor_entry == NULL) {
spin_unlock(&table->ht_lock);
- SEXIT;
return;
}
* DTOR_PID entry. They are removed from the hash table and
* linked in to a private working list to be destroyed.
*/
- while (!list_empty(&dtor_entry->he_key_list)) {
+ while (!list_empty(&dtor_entry->he_key_list)) {
entry = list_entry(dtor_entry->he_key_list.next,
- tsd_hash_entry_t, he_key_list);
+ tsd_hash_entry_t, he_key_list);
ASSERT3U(dtor_entry->he_key, ==, entry->he_key);
ASSERT3P(dtor_entry->he_dtor, ==, entry->he_dtor);
hash = hash_long((ulong_t)entry->he_key *
- (ulong_t)entry->he_pid, table->ht_bits);
+ (ulong_t)entry->he_pid, table->ht_bits);
entry_bin = &table->ht_bins[hash];
spin_lock(&entry_bin->hb_lock);
tsd_hash_dtor(&work);
*keyp = 0;
-
- SEXIT;
}
EXPORT_SYMBOL(tsd_destroy);
tsd_hash_entry_t *pid_entry, *entry;
tsd_hash_bin_t *pid_entry_bin, *entry_bin;
ulong_t hash;
- SENTRY;
table = tsd_hash_table;
ASSERT3P(table, !=, NULL);
pid_entry = tsd_hash_search(table, PID_KEY, curthread->pid);
if (pid_entry == NULL) {
spin_unlock(&table->ht_lock);
- SEXIT;
return;
}
* linked in to a private working list to be destroyed.
*/
- while (!list_empty(&pid_entry->he_pid_list)) {
+ while (!list_empty(&pid_entry->he_pid_list)) {
entry = list_entry(pid_entry->he_pid_list.next,
- tsd_hash_entry_t, he_pid_list);
+ tsd_hash_entry_t, he_pid_list);
ASSERT3U(pid_entry->he_pid, ==, entry->he_pid);
hash = hash_long((ulong_t)entry->he_key *
spin_unlock(&table->ht_lock);
tsd_hash_dtor(&work);
-
- SEXIT;
}
EXPORT_SYMBOL(tsd_exit);
int
spl_tsd_init(void)
{
- SENTRY;
-
tsd_hash_table = tsd_hash_table_init(TSD_HASH_TABLE_BITS_DEFAULT);
if (tsd_hash_table == NULL)
- SRETURN(1);
+ return (1);
- SRETURN(0);
+ return (0);
}
void
spl_tsd_fini(void)
{
- SENTRY;
tsd_hash_table_fini(tsd_hash_table);
tsd_hash_table = NULL;
- SEXIT;
}