* [nfs-utils PATCH] gssd: clean up threads on shutdown
@ 2026-08-31 23:02 Scott Mayhew
2026-09-16 21:57 ` Steve Dickson
0 siblings, 1 reply; 2+ messages in thread
From: Scott Mayhew @ 2026-08-31 23:02 UTC (permalink / raw)
To: steved; +Cc: linux-nfs
Valgrind reports leaks such as the following:
==1781== 736 bytes in 2 blocks are possibly lost in loss record 831 of 926
==1781== at 0x487F36B: calloc (vg_replace_malloc.c:1616)
==1781== by 0x403A75F: calloc (rtld-malloc.h:44)
==1781== by 0x403A75F: allocate_dtv (dl-tls.c:477)
==1781== by 0x403B2E5: _dl_allocate_tls (dl-tls.c:734)
==1781== by 0x4B6579E: pthread_create@@GLIBC_2.34 (in /usr/lib64/libc.so.6)
==1781== by 0x400E531: start_upcall_thread.constprop.0 (gssd_proc.c:971)
==1781== by 0x400EC34: handle_gssd_upcall (gssd_proc.c:1117)
==1781== by 0x48B9D9B: event_persist_closure (event.c:1623)
==1781== by 0x48B9D9B: event_process_active_single_queue (event.c:1682)
==1781== by 0x48BA5BE: event_process_active (event.c:1783)
==1781== by 0x48BA5BE: event_base_loop (event.c:2006)
==1781== by 0x400A431: main (gssd.c:1292)
==1781==
This is due to the thread-local storage allocated by pthread_create() as
well as the upcall_thread_info struct allocated by
start_upcall_thread(). Those do eventually get freed by the watchdog
thread in scan_active_thread_list(). The thread-local storage gets
freed when pthread_tryjoin_np() succeeds and the upcall_thread_info gets
freed via TAILQ_REMOVE() + free(). But since the watchdog thread sleeps
between iterations (30 seconds by default), from the perspective of
tools like valgrind there’s a leak.
Add logic to clean up the threads on shutdown to make tools like
valgrind happy.
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
---
utils/gssd/gssd.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 60 insertions(+)
diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
index 6a41fc5d..e381c0f2 100644
--- a/utils/gssd/gssd.c
+++ b/utils/gssd/gssd.c
@@ -98,6 +98,7 @@ static struct event_base *evbase = NULL;
int upcall_timeout = DEF_UPCALL_TIMEOUT;
static bool cancel_timed_out_upcalls = false;
+pthread_t watchdog_tid;
TAILQ_HEAD(topdir_list_head, topdir) topdir_list;
@@ -498,6 +499,61 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data)
handle_krb5_upcall(clp);
}
+static void
+cleanup_active_thread_list(void)
+{
+ struct upcall_thread_info *info;
+ bool cancelled = false;
+ void *tret, *saveprev;
+ int err;
+
+ pthread_mutex_lock(&active_thread_list_lock);
+ TAILQ_FOREACH(info, &active_thread_list, list) {
+ err = pthread_tryjoin_np(info->tid, &tret);
+ switch (err) {
+ case 0:
+ saveprev = info->list.tqe_prev;
+ TAILQ_REMOVE(&active_thread_list, info, list);
+ free(info);
+ info = saveprev;
+ break;
+ case EBUSY:
+ pthread_cancel(info->tid);
+ do_error_downcall(info->fd, info->uid, -ETIMEDOUT);
+ cancelled = true;
+ break;
+ default:
+ /* EDEADLK, EINVAL, and ESRCH... none of which should happen! */
+ printerr(0, "watchdog: attempt to join thread id 0x%lx returned %d (%s)!\n",
+ info->tid, err, strerror(err));
+ break;
+ }
+ }
+ if (cancelled) {
+ TAILQ_FOREACH(info, &active_thread_list, list) {
+ err = pthread_tryjoin_np(info->tid, &tret);
+ switch (err) {
+ case EBUSY:
+ printerr(0, "watchdog: thread id 0x%lx still busy on shutdown\n",
+ info->tid);
+ /* fall through */
+ default:
+ /* EDEADLK, EINVAL, and ESRCH... none of which should happen! */
+ printerr(0, "watchdog: attempt to join thread id 0x%lx returned %d (%s)!\n",
+ info->tid, err, strerror(err));
+ /* fall through */
+ case 0:
+ saveprev = info->list.tqe_prev;
+ TAILQ_REMOVE(&active_thread_list, info, list);
+ free(info);
+ info = saveprev;
+ break;
+ }
+ }
+ }
+ pthread_mutex_unlock(&active_thread_list_lock);
+}
+
/*
* scan_active_thread_list:
*
@@ -628,6 +684,7 @@ start_watchdog_thread(void)
printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
ret, strerror(errno));
}
+ watchdog_tid = th;
return ret;
}
@@ -1290,6 +1347,9 @@ main(int argc, char *argv[])
printerr(0, "event_dispatch() returned %i!\n", rc);
+ pthread_cancel(watchdog_tid);
+ cleanup_active_thread_list();
+
gssd_destroy_krb5_principals(root_uses_machine_creds);
while (!TAILQ_EMPTY(&topdir_list)) {
--
2.55.0
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [nfs-utils PATCH] gssd: clean up threads on shutdown
2026-08-31 23:02 [nfs-utils PATCH] gssd: clean up threads on shutdown Scott Mayhew
@ 2026-09-16 21:57 ` Steve Dickson
0 siblings, 0 replies; 2+ messages in thread
From: Steve Dickson @ 2026-09-16 21:57 UTC (permalink / raw)
To: Scott Mayhew; +Cc: linux-nfs
On 8/31/26 7:02 PM, Scott Mayhew wrote:
> Valgrind reports leaks such as the following:
>
> ==1781== 736 bytes in 2 blocks are possibly lost in loss record 831 of 926
> ==1781== at 0x487F36B: calloc (vg_replace_malloc.c:1616)
> ==1781== by 0x403A75F: calloc (rtld-malloc.h:44)
> ==1781== by 0x403A75F: allocate_dtv (dl-tls.c:477)
> ==1781== by 0x403B2E5: _dl_allocate_tls (dl-tls.c:734)
> ==1781== by 0x4B6579E: pthread_create@@GLIBC_2.34 (in /usr/lib64/libc.so.6)
> ==1781== by 0x400E531: start_upcall_thread.constprop.0 (gssd_proc.c:971)
> ==1781== by 0x400EC34: handle_gssd_upcall (gssd_proc.c:1117)
> ==1781== by 0x48B9D9B: event_persist_closure (event.c:1623)
> ==1781== by 0x48B9D9B: event_process_active_single_queue (event.c:1682)
> ==1781== by 0x48BA5BE: event_process_active (event.c:1783)
> ==1781== by 0x48BA5BE: event_base_loop (event.c:2006)
> ==1781== by 0x400A431: main (gssd.c:1292)
> ==1781==
>
> This is due to the thread-local storage allocated by pthread_create() as
> well as the upcall_thread_info struct allocated by
> start_upcall_thread(). Those do eventually get freed by the watchdog
> thread in scan_active_thread_list(). The thread-local storage gets
> freed when pthread_tryjoin_np() succeeds and the upcall_thread_info gets
> freed via TAILQ_REMOVE() + free(). But since the watchdog thread sleeps
> between iterations (30 seconds by default), from the perspective of
> tools like valgrind there’s a leak.
>
> Add logic to clean up the threads on shutdown to make tools like
> valgrind happy.
>
> Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Committed... (tag: nfs-utils-2-9-3-rc5)
steved.
> ---
> utils/gssd/gssd.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 60 insertions(+)
>
> diff --git a/utils/gssd/gssd.c b/utils/gssd/gssd.c
> index 6a41fc5d..e381c0f2 100644
> --- a/utils/gssd/gssd.c
> +++ b/utils/gssd/gssd.c
> @@ -98,6 +98,7 @@ static struct event_base *evbase = NULL;
>
> int upcall_timeout = DEF_UPCALL_TIMEOUT;
> static bool cancel_timed_out_upcalls = false;
> +pthread_t watchdog_tid;
>
> TAILQ_HEAD(topdir_list_head, topdir) topdir_list;
>
> @@ -498,6 +499,61 @@ gssd_clnt_krb5_cb(int UNUSED(fd), short UNUSED(which), void *data)
> handle_krb5_upcall(clp);
> }
>
> +static void
> +cleanup_active_thread_list(void)
> +{
> + struct upcall_thread_info *info;
> + bool cancelled = false;
> + void *tret, *saveprev;
> + int err;
> +
> + pthread_mutex_lock(&active_thread_list_lock);
> + TAILQ_FOREACH(info, &active_thread_list, list) {
> + err = pthread_tryjoin_np(info->tid, &tret);
> + switch (err) {
> + case 0:
> + saveprev = info->list.tqe_prev;
> + TAILQ_REMOVE(&active_thread_list, info, list);
> + free(info);
> + info = saveprev;
> + break;
> + case EBUSY:
> + pthread_cancel(info->tid);
> + do_error_downcall(info->fd, info->uid, -ETIMEDOUT);
> + cancelled = true;
> + break;
> + default:
> + /* EDEADLK, EINVAL, and ESRCH... none of which should happen! */
> + printerr(0, "watchdog: attempt to join thread id 0x%lx returned %d (%s)!\n",
> + info->tid, err, strerror(err));
> + break;
> + }
> + }
> + if (cancelled) {
> + TAILQ_FOREACH(info, &active_thread_list, list) {
> + err = pthread_tryjoin_np(info->tid, &tret);
> + switch (err) {
> + case EBUSY:
> + printerr(0, "watchdog: thread id 0x%lx still busy on shutdown\n",
> + info->tid);
> + /* fall through */
> + default:
> + /* EDEADLK, EINVAL, and ESRCH... none of which should happen! */
> + printerr(0, "watchdog: attempt to join thread id 0x%lx returned %d (%s)!\n",
> + info->tid, err, strerror(err));
> + /* fall through */
> + case 0:
> + saveprev = info->list.tqe_prev;
> + TAILQ_REMOVE(&active_thread_list, info, list);
> + free(info);
> + info = saveprev;
> + break;
> + }
> + }
> + }
> + pthread_mutex_unlock(&active_thread_list_lock);
> +}
> +
> /*
> * scan_active_thread_list:
> *
> @@ -628,6 +684,7 @@ start_watchdog_thread(void)
> printerr(0, "ERROR: pthread_create failed: ret %d: %s\n",
> ret, strerror(errno));
> }
> + watchdog_tid = th;
> return ret;
> }
>
> @@ -1290,6 +1347,9 @@ main(int argc, char *argv[])
>
> printerr(0, "event_dispatch() returned %i!\n", rc);
>
> + pthread_cancel(watchdog_tid);
> + cleanup_active_thread_list();
> +
> gssd_destroy_krb5_principals(root_uses_machine_creds);
>
> while (!TAILQ_EMPTY(&topdir_list)) {
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-09-16 21:57 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-31 23:02 [nfs-utils PATCH] gssd: clean up threads on shutdown Scott Mayhew
2026-09-16 21:57 ` Steve Dickson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox