]> git.proxmox.com Git - mirror_spl-debian.git/blobdiff - module/spl/spl-tsd.c
New upstream version 0.7.2
[mirror_spl-debian.git] / module / spl / spl-tsd.c
index f5c579578bba42a729b58e33bca9a75a203cecc7..bf8235063be3d4dfd54871cd84658d1566cabbb2 100644 (file)
@@ -1,11 +1,11 @@
-/*****************************************************************************\
+/*
  *  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
@@ -19,7 +19,8 @@
  *
  *  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;
@@ -108,20 +102,20 @@ tsd_hash_search(tsd_hash_table_t *table, uint_t key, pid_t pid)
        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);
 }
 
 /*
@@ -135,7 +129,6 @@ static void
 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);
@@ -144,10 +137,8 @@ tsd_hash_dtor(struct hlist_head *work)
                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;
 }
 
 /*
@@ -169,14 +160,13 @@ tsd_hash_add(tsd_hash_table_t *table, uint_t key, pid_t pid, void *value)
        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;
@@ -208,7 +198,7 @@ tsd_hash_add(tsd_hash_table_t *table, uint_t key, pid_t pid, void *value)
        spin_unlock(&bin->hb_lock);
        spin_unlock(&table->ht_lock);
 
-       SRETURN(rc);
+       return (rc);
 }
 
 /*
@@ -229,14 +219,13 @@ tsd_hash_add_key(tsd_hash_table_t *table, uint_t *keyp, dtor_func_t dtor)
        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);
@@ -248,7 +237,7 @@ tsd_hash_add_key(tsd_hash_table_t *table, uint_t *keyp, dtor_func_t dtor)
                /* 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);
@@ -272,7 +261,7 @@ tsd_hash_add_key(tsd_hash_table_t *table, uint_t *keyp, dtor_func_t dtor)
        spin_unlock(&bin->hb_lock);
        spin_unlock(&table->ht_lock);
 
-       SRETURN(0);
+       return (0);
 }
 
 /*
@@ -290,12 +279,11 @@ tsd_hash_add_pid(tsd_hash_table_t *table, pid_t pid)
        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;
@@ -315,7 +303,7 @@ tsd_hash_add_pid(tsd_hash_table_t *table, pid_t pid)
        spin_unlock(&bin->hb_lock);
        spin_unlock(&table->ht_lock);
 
-       SRETURN(0);
+       return (0);
 }
 
 /*
@@ -327,14 +315,10 @@ tsd_hash_add_pid(tsd_hash_table_t *table, pid_t pid)
 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;
 }
 
 /*
@@ -349,17 +333,15 @@ tsd_hash_table_init(uint_t bits)
 {
        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++) {
@@ -371,7 +353,7 @@ tsd_hash_table_init(uint_t bits)
        table->ht_bits = bits;
        table->ht_key = 1;
 
-       SRETURN(table);
+       return (table);
 }
 
 /*
@@ -389,16 +371,15 @@ tsd_hash_table_fini(tsd_hash_table_t *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);
                }
@@ -407,10 +388,62 @@ tsd_hash_table_fini(tsd_hash_table_t *table)
        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));
+}
 
-       SEXIT;
+/*
+ * 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);
+
+       tsd_hash_dtor(&work);
 }
 
 /*
@@ -431,32 +464,40 @@ tsd_set(uint_t key, void *value)
        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);
 
@@ -472,21 +513,47 @@ void *
 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
@@ -502,17 +569,11 @@ EXPORT_SYMBOL(tsd_get);
 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);
 
@@ -531,7 +592,8 @@ tsd_destroy(uint_t *keyp)
        HLIST_HEAD(work);
        tsd_hash_table_t *table;
        tsd_hash_entry_t *dtor_entry, *entry;
-       SENTRY;
+       tsd_hash_bin_t *dtor_entry_bin, *entry_bin;
+       ulong_t hash;
 
        table = tsd_hash_table;
        ASSERT3P(table, !=, NULL);
@@ -540,7 +602,6 @@ tsd_destroy(uint_t *keyp)
        dtor_entry = tsd_hash_search(table, *keyp, DTOR_PID);
        if (dtor_entry == NULL) {
                spin_unlock(&table->ht_lock);
-               SEXIT;
                return;
        }
 
@@ -549,23 +610,34 @@ tsd_destroy(uint_t *keyp)
         * 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);
+               entry_bin = &table->ht_bins[hash];
+
+               spin_lock(&entry_bin->hb_lock);
                tsd_hash_del(table, entry);
                hlist_add_head(&entry->he_list, &work);
+               spin_unlock(&entry_bin->hb_lock);
        }
 
+       hash = hash_long((ulong_t)dtor_entry->he_key *
+           (ulong_t)dtor_entry->he_pid, table->ht_bits);
+       dtor_entry_bin = &table->ht_bins[hash];
+
+       spin_lock(&dtor_entry_bin->hb_lock);
        tsd_hash_del(table, dtor_entry);
        hlist_add_head(&dtor_entry->he_list, &work);
+       spin_unlock(&dtor_entry_bin->hb_lock);
        spin_unlock(&table->ht_lock);
 
        tsd_hash_dtor(&work);
        *keyp = 0;
-
-       SEXIT;
 }
 EXPORT_SYMBOL(tsd_destroy);
 
@@ -583,7 +655,8 @@ tsd_exit(void)
        HLIST_HEAD(work);
        tsd_hash_table_t *table;
        tsd_hash_entry_t *pid_entry, *entry;
-       SENTRY;
+       tsd_hash_bin_t *pid_entry_bin, *entry_bin;
+       ulong_t hash;
 
        table = tsd_hash_table;
        ASSERT3P(table, !=, NULL);
@@ -592,50 +665,57 @@ tsd_exit(void)
        pid_entry = tsd_hash_search(table, PID_KEY, curthread->pid);
        if (pid_entry == NULL) {
                spin_unlock(&table->ht_lock);
-               SEXIT;
                return;
        }
 
        /*
         * All keys associated with this pid must be linked off of the
         * PID_KEY entry.  They are removed from the hash table and
-        * linked in to a private working to be destroyed.
+        * 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 *
+                   (ulong_t)entry->he_pid, table->ht_bits);
+               entry_bin = &table->ht_bins[hash];
+
+               spin_lock(&entry_bin->hb_lock);
                tsd_hash_del(table, entry);
                hlist_add_head(&entry->he_list, &work);
+               spin_unlock(&entry_bin->hb_lock);
        }
 
+       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);
 
        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;
 }