]> granicus.if.org Git - spl/commitdiff
Make tsd_set(key, NULL) remove the tsd entry for current thread
authorChunwei Chen <tuxoko@gmail.com>
Wed, 1 Apr 2015 17:50:48 +0000 (01:50 +0800)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Fri, 24 Apr 2015 21:15:22 +0000 (14:15 -0700)
To prevent leaking tsd entries, we make tsd_set(key, NULL) remove the tsd
entry for the current thread. This is alright since tsd_get() returns NULL
when the entry doesn't exist.

Signed-off-by: Chunwei Chen <tuxoko@gmail.com>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #443

module/spl/spl-tsd.c

index 9a0987527b2705d9bd5a1b0f9be2a8add67ffda6..156a8a633f5753888776053d0560d7a92cb896b6 100644 (file)
@@ -391,6 +391,60 @@ tsd_hash_table_fini(tsd_hash_table_t *table)
        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);
+
+       tsd_hash_dtor(&work);
+}
+
 /*
  * tsd_set - set thread specific data
  * @key: lookup key
@@ -409,6 +463,8 @@ tsd_set(uint_t key, void *value)
        tsd_hash_entry_t *entry;
        pid_t pid;
        int rc;
+       /* mark remove if value is NULL */
+       boolean_t remove = (value == NULL);
 
        table = tsd_hash_table;
        pid = curthread->pid;
@@ -421,9 +477,16 @@ tsd_set(uint_t key, void *value)
        entry = tsd_hash_search(table, key, pid);
        if (entry) {
                entry->he_value = value;
+               /* 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) {