Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 5 Aug 2004 09:40:36 +0100
From:      Doug Rabson <dfr@nlsystems.com>
To:        freebsd-threads@freebsd.org
Subject:   Pthread patches for TLS
Message-ID:  <200408050940.36506.dfr@nlsystems.com>

next in thread | raw e-mail | index | archive | help
This is the current state of my patches to add TLS support to libpthread 
and libthr. I have added code for i386, amd64 and ia64 but I've only 
tested i386. I believe the code for amd64 is right. I don't much like 
what I've done to the ia64 version though. I would like to commit only 
the i386 version at this stage. The amd64 code could be committed if 
someone tests it.

The current code in rtld for variant I TLS assumes that the size of the 
TCB is fixed at 2*sizeof(Elf_Addr). The original code in libpthread 
assumed that it was possible to extend that by allocating extra storage 
to live below the thread pointer. Since I don't support that in rtld, 
I've just allocated the extra space with malloc() and used the spare 
pointer in the architecturally defined TCB to point at it.

This could be fixed in two ways. The first (which requires no changes to 
rtld) would be to add a '__thread struct tcp _pthread_tcb' variable and 
arrange for ia64_tp::tp_tcp to point at that in each thread. The 
advantage there is that rtld stays simple. The alternative is to change 
the semantics of _rtld_allocate_tls() for variant I to allow the caller 
to request a larger tcb size - it would allocate the extra space below 
the 16 byte architectural TCB.

Trying out either of these will have to wait until I have time to fire 
up my horrible noisy old itanium1 system and update it to current.

Index: libthr/arch/i386/i386/_curthread.S
===================================================================
RCS file: /home/ncvs/src/lib/libthr/arch/i386/i386/_curthread.S,v
retrieving revision 1.2
diff -u -r1.2 _curthread.S
--- libthr/arch/i386/i386/_curthread.S	2 Jun 2003 02:32:56 -0000	1.2
+++ libthr/arch/i386/i386/_curthread.S	5 Aug 2004 08:17:40 -0000
@@ -5,7 +5,7 @@
 ENTRY(_get_curthread)
 	cmpl	$0, _thread_initial
 	je	nothreads
-	movl	%gs:0, %eax
+	movl	%gs:8, %eax
 	ret
 nothreads:
 	xor	%eax, %eax
Index: libthr/arch/i386/i386/_setcurthread.c
===================================================================
RCS file: /home/ncvs/src/lib/libthr/arch/i386/i386/_setcurthread.c,v
retrieving revision 1.13
diff -u -r1.13 _setcurthread.c
--- libthr/arch/i386/i386/_setcurthread.c	5 Mar 2004 08:10:18 -0000	1.13
+++ libthr/arch/i386/i386/_setcurthread.c	5 Aug 2004 08:17:40 -0000
@@ -38,112 +38,67 @@
 #include <machine/segments.h>
 
 #include "thr_private.h"
-
-#define	MAXTHR	8192
-
-#define	LDT_INDEX(x)	(((long)(x) - (long)ldt_entries) / 
sizeof(ldt_entries[0]))
-
-void		**ldt_free = NULL;
-void		 *ldt_entries[MAXTHR];
-static int	  ldt_inited = 0;
-static spinlock_t ldt_lock = _SPINLOCK_INITIALIZER;
-
-static void ldt_init(void);
+#include "rtld_tls.h"
 
 /* in _curthread.S */
 extern void _set_gs(int);
 
-/*
- * Initialize the array of ldt_entries and the next free slot.
- * This routine must be called with the global ldt lock held.
- */
-static void
-ldt_init(void)
-{
-	int i;
-
-	ldt_free = &ldt_entries[NLDT];
-
-	for (i = 0; i < MAXTHR - 1; i++)
-		ldt_entries[i] = (void *)&ldt_entries[i + 1];
-
-	ldt_entries[MAXTHR - 1] = NULL;
-
-	ldt_inited = 1;
-}
+struct tcb {
+	struct tcb		*tcb_self;	/* required by rtld */
+	void			*tcb_dtv;	/* required by rtld */
+	struct pthread		*tcb_thread;
+};
 
 void
 _retire_thread(void *entry)
 {
-	_spinlock(&ldt_lock);
-	if (ldt_free == NULL)
-		*(void **)entry = NULL;
-	else
-		*(void **)entry = *ldt_free;
-	ldt_free = entry;
-	_spinunlock(&ldt_lock);
+	_rtld_free_tls(entry, sizeof(struct tcb), 16);
+	/* XXX free ldt descriptor here */
 }
 
 void *
 _set_curthread(ucontext_t *uc, struct pthread *thr, int *err)
 {
 	union descriptor desc;
-	void **ldt_entry;
+	struct tcb *tcb;
+	void *oldtls;
 	int ldt_index;
 
 	*err = 0;
 
-	/*
-	 * If we are setting up the initial thread, the gs register
-	 * won't be setup for the current thread. In any case, we
-	 * don't need protection from re-entrancy at this point in
-	 * the life of the program.
-	 */
-	if (thr != _thread_initial)
-		_SPINLOCK(&ldt_lock);
-
-	if (ldt_inited == 0)
-		ldt_init();
-
-	if (ldt_free == NULL) {
-		/* Concurrent thread limit reached */
-		*err = curthread->error = EAGAIN;
-		if (thr != _thread_initial)
-			_SPINUNLOCK(&ldt_lock);
-		return (NULL);
+	if (uc == NULL) {
+		__asm __volatile("movl %%gs:0, %0" : "=r" (oldtls));
+	} else {
+		oldtls = NULL;
 	}
 
 	/*
-	 * Pull one off of the free list and update the free list pointer.
+	 * Allocate and initialise a new TLS block with enough extra
+	 * space for our self pointer.
 	 */
-	ldt_entry = ldt_free;
-	ldt_free = (void **)*ldt_entry;
-
-	if (thr != _thread_initial)
-		_SPINUNLOCK(&ldt_lock);
+	tcb = _rtld_allocate_tls(oldtls, sizeof(struct tcb), 16);
 
 	/*
-	 * Cache the address of the thread structure here.  This is
-	 * what the gs register will point to.
+	 * Cache the address of the thread structure here, after
+	 * rtld's two words of private space.
 	 */
-	*ldt_entry = (void *)thr;
+	tcb->tcb_thread = thr;
 
 	bzero(&desc, sizeof(desc));
 
 	/*
-	 * Set up the descriptor to point into the ldt table which contains
-	 * only a pointer to the thread.
+	 * Set up the descriptor to point at the TLS block.
 	 */
-	desc.sd.sd_lolimit = sizeof(*ldt_entry);
-	desc.sd.sd_lobase = (unsigned int)ldt_entry & 0xFFFFFF;
-	desc.sd.sd_type = SDT_MEMRO;
+	desc.sd.sd_lolimit = 0xFFFF;
+	desc.sd.sd_lobase = (unsigned int)tcb & 0xFFFFFF;
+	desc.sd.sd_type = SDT_MEMRW;
 	desc.sd.sd_dpl = SEL_UPL;
 	desc.sd.sd_p = 1;
-	desc.sd.sd_hilimit = 0;
+	desc.sd.sd_hilimit = 0xF;
 	desc.sd.sd_xx = 0;
 	desc.sd.sd_def32 = 1;
-	desc.sd.sd_gran = 0;
-	desc.sd.sd_hibase = (unsigned int)ldt_entry >> 24;
+	desc.sd.sd_gran = 1;
+	desc.sd.sd_hibase = (unsigned int)tcb >> 24;
 
 	/* Get a slot from the process' LDT list */
 	ldt_index = i386_set_ldt(LDT_AUTO_ALLOC, &desc, 1);
@@ -158,5 +113,5 @@
 	else
 		_set_gs(LSEL(ldt_index, SEL_UPL));
 
-	return (ldt_entry);
+	return (tcb);
 }
Index: libthr/arch/ia64/ia64/_curthread.c
===================================================================
RCS file: /home/ncvs/src/lib/libthr/arch/ia64/ia64/_curthread.c,v
retrieving revision 1.3
diff -u -r1.3 _curthread.c
--- libthr/arch/ia64/ia64/_curthread.c	25 May 2003 22:40:57 -0000	1.3
+++ libthr/arch/ia64/ia64/_curthread.c	5 Aug 2004 08:22:06 -0000
@@ -33,27 +33,38 @@
 #include <pthread.h>
 #include "thr_private.h"
 
-register struct pthread *_tp __asm("%r13");
+struct tcb {
+	void		*tcb_dtv;
+	struct pthread	*tcb_thread;
+};
+
+register struct tcb *_tp __asm("%r13");
 
 struct pthread *
 _get_curthread(void)
 {
 
-	return (_tp);
+	return (_tp->tcb_thread);
 }
 
 void
 _retire_thread(void *v)
 {
+
+	_rtld_free_tls(v, sizeof(struct tcb), 16);
 }
 
 void *
 _set_curthread(ucontext_t *uc, struct pthread *thread, int *err)
 {
+	struct tcb *tcb;
+
+	tcb = _rtld_allocate_tls(sizeof(struct tcb), 16);
+	tcb->tcb_thread = thread;
 	*err = 0;
 	if (uc != NULL)
-		uc->uc_mcontext.mc_special.tp = (uint64_t)thread;
+		uc->uc_mcontext.mc_special.tp = (uint64_t)tcb;
 	else
-		_tp = thread;
-	return (NULL);
+		_tp = tcb;
+	return (tcb);
 }
Index: libpthread/arch/amd64/amd64/pthread_md.c
===================================================================
RCS file: /home/ncvs/src/lib/libpthread/arch/amd64/amd64/pthread_md.c,v
retrieving revision 1.2
diff -u -r1.2 pthread_md.c
--- libpthread/arch/amd64/amd64/pthread_md.c	12 Aug 2003 22:13:06 -0000	
1.2
+++ libpthread/arch/amd64/amd64/pthread_md.c	5 Aug 2004 08:27:37 -0000
@@ -28,29 +28,37 @@
 
 #include <stdlib.h>
 #include <strings.h>
+#include "rtld_tls.h"
 #include "pthread_md.h"
 
 /*
  * The constructors.
  */
 struct tcb *
-_tcb_ctor(struct pthread *thread)
+_tcb_ctor(struct pthread *thread, int initial)
 {
 	struct tcb *tcb;
+	void *oldtls;
 
-	if ((tcb = malloc(sizeof(struct tcb))) != NULL) {
-		bzero(tcb, sizeof(struct tcb));
+	if (initial) {
+		__asm __volatile("movq %%fs:0, %0" : "=r" (oldtls));
+	} else {
+		oldtls = NULL;
+	}
+
+	tcb = _rtld_allocate_tls(oldtls, sizeof(struct tcb), 16);
+	if (tcb) {
 		tcb->tcb_thread = thread;
-		/* Allocate TDV */
+		bzero(&tcb->tcb_tmbx, sizeof(tcb->tcb_tmbx));
 	}
+
 	return (tcb);
 }
 
 void
 _tcb_dtor(struct tcb *tcb)
 {
-	/* Free TDV */
-	free(tcb);
+	_rtld_free_tls(tcb, sizeof(struct tcb), 16);
 }
 
 struct kcb *
Index: libpthread/arch/amd64/include/pthread_md.h
===================================================================
RCS 
file: /home/ncvs/src/lib/libpthread/arch/amd64/include/pthread_md.h,v
retrieving revision 1.7
diff -u -r1.7 pthread_md.h
--- libpthread/arch/amd64/include/pthread_md.h	31 Jul 2004 14:14:55 
-0000	1.7
+++ libpthread/arch/amd64/include/pthread_md.h	5 Aug 2004 08:27:02 -0000
@@ -62,8 +62,10 @@
 };
 
 struct tcb {
-	struct tdv		*tcb_tdv;
+	struct tcb		*tcb_self;	/* required by rtld */
+	void			*tcb_dtv;	/* required by rtld */
 	struct pthread		*tcb_thread;
+	void			*tcb_spare[1];	/* align tcb_tmbx to 16 bytes */
 	struct kse_thr_mailbox	tcb_tmbx;
 };
 
@@ -138,7 +140,7 @@
 /*
  * The constructors.
  */
-struct tcb	*_tcb_ctor(struct pthread *);
+struct tcb	*_tcb_ctor(struct pthread *, int);
 void		_tcb_dtor(struct tcb *tcb);
 struct kcb	*_kcb_ctor(struct kse *);
 void		_kcb_dtor(struct kcb *);
Index: libpthread/arch/i386/i386/pthread_md.c
===================================================================
RCS file: /home/ncvs/src/lib/libpthread/arch/i386/i386/pthread_md.c,v
retrieving revision 1.2
diff -u -r1.2 pthread_md.c
--- libpthread/arch/i386/i386/pthread_md.c	5 Aug 2003 23:09:22 -0000	1.2
+++ libpthread/arch/i386/i386/pthread_md.c	5 Aug 2004 08:27:24 -0000
@@ -39,35 +39,35 @@
 #include <string.h>
 #include <ucontext.h>
 
+#include "rtld_tls.h"
 #include "pthread_md.h"
 
 struct tcb *
-_tcb_ctor(struct pthread *thread)
+_tcb_ctor(struct pthread *thread, int initial)
 {
 	struct tcb *tcb;
-	void *addr;
+	void *oldtls;
 
-	addr = malloc(sizeof(struct tcb) + 15);
-	if (addr == NULL)
-		tcb = NULL;
-	else {
-		tcb = (struct tcb *)(((uintptr_t)(addr) + 15) & ~15);
-		bzero(tcb, sizeof(struct tcb));
-		tcb->tcb_addr = addr;
+	if (initial) {
+		__asm __volatile("movl %%gs:0, %0" : "=r" (oldtls));
+	} else {
+		oldtls = NULL;
+	}
+
+	tcb = _rtld_allocate_tls(oldtls, sizeof(struct tcb), 16);
+	if (tcb) {
 		tcb->tcb_thread = thread;
-		/* XXX - Allocate tdv/tls */
+		tcb->tcb_spare = 0;
+		bzero(&tcb->tcb_tmbx, sizeof(tcb->tcb_tmbx));
 	}
+
 	return (tcb);
 }
 
 void
 _tcb_dtor(struct tcb *tcb)
 {
-	void *addr;
-
-	addr = tcb->tcb_addr;
-	tcb->tcb_addr = NULL;
-	free(addr);
+	_rtld_free_tls(tcb, sizeof(struct tcb), 16);
 }
 
 /*
Index: libpthread/arch/i386/include/pthread_md.h
===================================================================
RCS file: /home/ncvs/src/lib/libpthread/arch/i386/include/pthread_md.h,v
retrieving revision 1.8
diff -u -r1.8 pthread_md.h
--- libpthread/arch/i386/include/pthread_md.h	13 Jul 2004 22:54:23 -0000	
1.8
+++ libpthread/arch/i386/include/pthread_md.h	5 Aug 2004 08:26:51 -0000
@@ -47,7 +47,6 @@
 
 struct kse;
 struct pthread;
-struct tdv;
 
 /*
  * %gs points to a struct kcb.
@@ -61,9 +60,9 @@
 };
 
 struct tcb {
-	struct tdv		*tcb_tdv;
+	struct tcb		*tcb_self;	/* required by rtld */
+	void			*tcb_dtv;	/* required by rtld */
 	struct pthread		*tcb_thread;
-	void			*tcb_addr;	/* allocated tcb address */
 	void			*tcb_spare;	/* align tcb_tmbx to 16 bytes */
 	struct kse_thr_mailbox	tcb_tmbx;
 };
@@ -140,7 +139,7 @@
 /*
  * The constructors.
  */
-struct tcb	*_tcb_ctor(struct pthread *);
+struct tcb	*_tcb_ctor(struct pthread *, int);
 void		_tcb_dtor(struct tcb *tcb);
 struct kcb	*_kcb_ctor(struct kse *);
 void		_kcb_dtor(struct kcb *);
Index: libpthread/arch/ia64/ia64/pthread_md.c
===================================================================
RCS file: /home/ncvs/src/lib/libpthread/arch/ia64/ia64/pthread_md.c,v
retrieving revision 1.2
diff -u -r1.2 pthread_md.c
--- libpthread/arch/ia64/ia64/pthread_md.c	6 Aug 2003 04:17:42 -0000	1.2
+++ libpthread/arch/ia64/ia64/pthread_md.c	5 Aug 2004 08:27:46 -0000
@@ -29,19 +29,25 @@
 #include <stdlib.h>
 #include <strings.h>
 #include "pthread_md.h"
+#include "rtld_tls.h"
 
 /*
  * The constructors.
  */
 struct tcb *
-_tcb_ctor(struct pthread *thread)
+_tcb_ctor(struct pthread *thread, int initial)
 {
-	struct tcb *tcb;
+	struct ia64_tp *tp;
+
+	tp = _rtld_allocate_tls(initial ? _tp : NULL,
+	    sizeof(struct ia64_tp), 16);
+	if (tp == NULL)
+		return (NULL);
 
 	if ((tcb = malloc(sizeof(struct tcb))) != NULL) {
 		bzero(tcb, sizeof(struct tcb));
 		tcb->tcb_thread = thread;
-		/* Allocate TDV */
+		tcb->tcb_tp = tp;
 	}
 	return (tcb);
 }
@@ -49,7 +55,8 @@
 void
 _tcb_dtor(struct tcb *tcb)
 {
-	/* Free TDV */
+
+	_rtld_free_tls(tcb->tcb_tp, sizeof(struct ia64_tp), 16);
 	free(tcb);
 }
 
Index: libpthread/arch/ia64/include/pthread_md.h
===================================================================
RCS file: /home/ncvs/src/lib/libpthread/arch/ia64/include/pthread_md.h,v
retrieving revision 1.13
diff -u -r1.13 pthread_md.h
--- libpthread/arch/ia64/include/pthread_md.h	12 Jul 2004 07:41:01 -0000	
1.13
+++ libpthread/arch/ia64/include/pthread_md.h	5 Aug 2004 08:27:08 -0000
@@ -44,19 +44,13 @@
 struct kse;
 struct pthread;
 struct tcb;
-struct tdv;	/* We don't know what this is yet? */
 
 /*
- * tp points to one of these. We define the static TLS as an array
- * of long double to enforce 16-byte alignment of the TLS memory,
- * struct ia64_tp, struct tcb and also struct kcb. Both static and
- * dynamic allocation of any of these structures will result in a
- * valid, well-aligned thread pointer.
+ * tp points to one of these.
  */
 struct ia64_tp {
-	struct tdv		*tp_tdv;	/* dynamic TLS */
-	uint64_t		_reserved_;
-	long double		tp_tls[0];	/* static TLS */
+	void			*tp_dtv;	/* required by rtld */
+	struct tcb		*tp_tcb;	/* real tcb for thread */
 };
 
 struct tcb {
@@ -64,7 +58,7 @@
 	struct pthread		*tcb_thread;
 	struct kcb		*tcb_curkcb;
 	long			tcb_isfake;
-	struct ia64_tp		tcb_tp;
+	struct ia64_tp		*tcb_tp;
 };
 
 struct kcb {
@@ -76,12 +70,12 @@
 
 register struct ia64_tp *_tp __asm("%r13");
 
-#define	_tcb	((struct tcb*)((char*)(_tp) - offsetof(struct tcb, 
tcb_tp)))
+#define	_tcb	(_tp->tp_tcb)
 
 /*
  * The kcb and tcb constructors.
  */
-struct tcb	*_tcb_ctor(struct pthread *);
+struct tcb	*_tcb_ctor(struct pthread *, int);
 void		_tcb_dtor(struct tcb *);
 struct kcb	*_kcb_ctor(struct kse *kse);
 void		_kcb_dtor(struct kcb *);
@@ -90,8 +84,6 @@
 static __inline void
 _kcb_set(struct kcb *kcb)
 {
-	/* There is no thread yet; use the fake tcb. */
-	_tp = &kcb->kcb_faketcb.tcb_tp;
 }
 
 /*
@@ -103,6 +95,7 @@
 static __inline struct kcb *
 _kcb_get(void)
 {
+
 	return (_tcb->tcb_curkcb);
 }
 
@@ -165,22 +158,25 @@
 static __inline void
 _tcb_set(struct kcb *kcb, struct tcb *tcb)
 {
+
 	if (tcb == NULL)
 		tcb = &kcb->kcb_faketcb;
 	kcb->kcb_curtcb = tcb;
 	tcb->tcb_curkcb = kcb;
-	_tp = &tcb->tcb_tp;
+	_tp = tcb->tcb_tp;
 }
 
 static __inline struct tcb *
 _tcb_get(void)
 {
+
 	return (_tcb);
 }
 
 static __inline struct pthread *
 _get_curthread(void)
 {
+
 	return (_tcb->tcb_thread);
 }
 
@@ -204,6 +200,7 @@
 static __inline int
 _thread_enter_uts(struct tcb *tcb, struct kcb *kcb)
 {
+
 	if (_ia64_save_context(&tcb->tcb_tmbx.tm_context.uc_mcontext) == 0) {
 		/* Make the fake tcb the current thread. */
 		kcb->kcb_curtcb = &kcb->kcb_faketcb;
Index: libpthread/thread/thr_kern.c
===================================================================
RCS file: /home/ncvs/src/lib/libpthread/thread/thr_kern.c,v
retrieving revision 1.108
diff -u -r1.108 thr_kern.c
--- libpthread/thread/thr_kern.c	3 Aug 2004 02:22:25 -0000	1.108
+++ libpthread/thread/thr_kern.c	5 Aug 2004 08:29:54 -0000
@@ -2384,7 +2384,7 @@
 	if ((thread == NULL) &&
 	    ((thread = malloc(sizeof(struct pthread))) != NULL)) {
 		bzero(thread, sizeof(struct pthread));
-		if ((thread->tcb = _tcb_ctor(thread)) == NULL) {
+		if ((thread->tcb = _tcb_ctor(thread, curthread == NULL)) == NULL) {
 			free(thread);
 			thread = NULL;
 		} else {



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200408050940.36506.dfr>