summaryrefslogtreecommitdiffstats
path: root/module/spl/spl-tsd.c
diff options
context:
space:
mode:
authorChunwei Chen <[email protected]>2015-04-02 01:50:48 +0800
committerBrian Behlendorf <[email protected]>2015-04-24 14:15:22 -0700
commit3d39d0afab44b217d51f7fbd97c3561d39e80f0c (patch)
tree4f4a68a26851f3cbac3d4ba72d59c31ae1536d60 /module/spl/spl-tsd.c
parentd3c677bcd330423c72cd4d4100727e5c1e8c1f38 (diff)
Make tsd_set(key, NULL) remove the tsd entry for current thread
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 <[email protected]> Signed-off-by: Brian Behlendorf <[email protected]> Closes #443
Diffstat (limited to 'module/spl/spl-tsd.c')
-rw-r--r--module/spl/spl-tsd.c63
1 files changed, 63 insertions, 0 deletions
diff --git a/module/spl/spl-tsd.c b/module/spl/spl-tsd.c
index 9a0987527..156a8a633 100644
--- a/module/spl/spl-tsd.c
+++ b/module/spl/spl-tsd.c
@@ -392,6 +392,60 @@ tsd_hash_table_fini(tsd_hash_table_t *table)
}
/*
+ * 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
* @value: value to set
@@ -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) {