diff options
author | Chunwei Chen <[email protected]> | 2015-04-02 01:50:48 +0800 |
---|---|---|
committer | Brian Behlendorf <[email protected]> | 2015-04-24 14:15:22 -0700 |
commit | 3d39d0afab44b217d51f7fbd97c3561d39e80f0c (patch) | |
tree | 4f4a68a26851f3cbac3d4ba72d59c31ae1536d60 /module/spl/spl-tsd.c | |
parent | d3c677bcd330423c72cd4d4100727e5c1e8c1f38 (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.c | 63 |
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) { |