From: Bruce Momjian Date: Tue, 16 Jun 1998 07:18:16 +0000 (+0000) Subject: Here is the long awaited optimized version of the S_LOCK patch. X-Git-Tag: REL6_4_2~885 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=0d8e7f6381291b85ad6264365e01143357d70a75;p=postgresql Here is the long awaited optimized version of the S_LOCK patch. This incorporates all the precedeing patches and emailed suggestions and the results of the performance testing I posted last week. I would like to get this tested on as many platforms as possible so I can verify it went in correctly (as opposed to the horrorshow last time I sent in a patch). Once this is confirmed, I will make a tarball of files that can be dropped into a 6.3.2 source tree as a few people have asked for this in 6.3.2 as well. David Gould --- diff --git a/src/backend/storage/buffer/Makefile b/src/backend/storage/buffer/Makefile index 8626500a05..298cdbb8d5 100644 --- a/src/backend/storage/buffer/Makefile +++ b/src/backend/storage/buffer/Makefile @@ -4,7 +4,7 @@ # Makefile for storage/buffer # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/storage/buffer/Makefile,v 1.10 1998/05/04 16:58:37 scrappy Exp $ +# $Header: /cvsroot/pgsql/src/backend/storage/buffer/Makefile,v 1.11 1998/06/16 07:18:15 momjian Exp $ # #------------------------------------------------------------------------- @@ -27,7 +27,7 @@ clean: rm -f SUBSYS.o $(OBJS) s_lock_test s_lock_test: s_lock.c - $(CC) $(CFLAGS) -DS_LOCK_TEST=1 -g s_lock.c -o s_lock_test + $(CC) $(CFLAGS) -DS_LOCK_TEST=1 s_lock.c -o s_lock_test ./s_lock_test ifeq (depend,$(wildcard depend)) diff --git a/src/backend/storage/buffer/s_lock.c b/src/backend/storage/buffer/s_lock.c index aa44385fed..23bc0ec2c4 100644 --- a/src/backend/storage/buffer/s_lock.c +++ b/src/backend/storage/buffer/s_lock.c @@ -7,12 +7,13 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.7 1998/05/04 23:49:09 scrappy Exp $ + * $Header: /cvsroot/pgsql/src/backend/storage/buffer/Attic/s_lock.c,v 1.8 1998/06/16 07:18:15 momjian Exp $ * *------------------------------------------------------------------------- */ #include +#include #include "config.h" #include "c.h" @@ -22,25 +23,41 @@ /* * Each time we busy spin we select the next element of this array as the * number of microseconds to wait. This accomplishes pseudo random back-off. - * Values are not critical and are weighted to the low end of the range. They - * were chosen to work even with different select() timer resolutions on - * different platforms. - * note: total time to cycle through all 16 entries might be about .1 second. + * Values are not critical but 10 milliseconds is a common platform + * granularity. + * note: total time to cycle through all 16 entries might be about .07 sec. */ -int s_spincycle[S_NSPINCYCLE] = -{0, 0, 0, 1000, 5000, 0, 10000, 3000, - 0, 10000, 0, 15000, 9000, 21000, 6000, 30000 +#define S_NSPINCYCLE 20 +#define S_MAX_BUSY 500 * S_NSPINCYCLE + +int s_spincycle[S_NSPINCYCLE] = +{ 0, 0, 0, 0, 10000, 0, 0, 0, 10000, 0, + 0, 10000, 0, 0, 10000, 0, 10000, 0, 10000, 10000 }; -#if defined(S_LOCK_DEBUG) /* - * s_lock(lock) - take a spinlock - * add intrumentation code to this and define S_LOCK_DEBUG - * instead of hacking up the macro in s_lock.h + * s_lock_stuck(lock) - complain about a stuck spinlock + */ +static void +s_lock_stuck(volatile slock_t *lock, const char *file, const int line) +{ + fprintf(stderr, + "\nFATAL: s_lock(%08x) at %s:%d, stuck spinlock. Aborting.\n", + (unsigned int) lock, file, line); + fprintf(stdout, + "\nFATAL: s_lock(%08x) at %s:%d, stuck spinlock. Aborting.\n", + (unsigned int) lock, file, line); + abort(); +} + + + +/* + * s_lock(lock) - take a spinlock with backoff */ void -s_lock(slock_t *lock, char *file, int line) +s_lock(volatile slock_t *lock, const char *file, const int line) { int spins = 0; @@ -49,114 +66,39 @@ s_lock(slock_t *lock, char *file, int line) struct timeval delay; delay.tv_sec = 0; - delay.tv_usec = s_spincycle[spins++ % S_NSPINCYCLE]; + delay.tv_usec = s_spincycle[spins % S_NSPINCYCLE]; (void) select(0, NULL, NULL, NULL, &delay); - if (spins > S_MAX_BUSY) + if (++spins > S_MAX_BUSY) { - /* It's been well over a minute... */ + /* It's been over a minute... */ s_lock_stuck(lock, file, line); } } } -#endif /* S_LOCK_DEBUG */ - -/* - * s_lock_stuck(lock) - deal with stuck spinlock - */ -void -s_lock_stuck(slock_t *lock, char *file, int line) -{ - fprintf(stderr, - "\nFATAL: s_lock(%08x) at %s:%d, stuck spinlock. Aborting.\n", - (unsigned int) lock, file, line); - fprintf(stdout, - "\nFATAL: s_lock(%08x) at %s:%d, stuck spinlock. Aborting.\n", - (unsigned int) lock, file, line); - abort(); -} /* - * Various TAS implementations moved from s_lock.h to avoid redundant - * definitions of the same routine. - * RESOLVE: move this to tas.c. Alternatively get rid of tas.[cso] and fold - * all that into this file. + * Various TAS implementations that cannot live in s_lock.h as no inline + * definition exists (yet). + * In the future, get rid of tas.[cso] and fold it into this file. */ -#if defined(linux) +#if defined(__GNUC__) /************************************************************************* - * All the Linux flavors + * All the gcc flavors that are not inlined */ -#if defined(__alpha) -int -tas(slock_t *lock) -{ - slock_t _res; - - __asm__(" ldq $0, %0 \n\ - bne $0, already_set \n\ - ldq_l $0, %0 \n\ - bne $0, already_set \n\ - or $31, 1, $0 \n\ - stq_c $0, %0 \n\ - beq $0, stqc_fail \n\ - success: bis $31, $31, %1 \n\ - mb \n\ - jmp $31, end \n\ - stqc_fail: or $31, 1, $0 \n\ - already_set: bis $0, $0, %1 \n\ - end: nop ": "=m"(*lock), "=r"(_res): :"0"); - - return (_res != 0); -} -#endif /* __alpha */ - - - -#if defined(i386) -int -tas(slock_t *lock) -{ - slock_t _res = 1; - - __asm__("lock; xchgb %0,%1": "=q"(_res), "=m"(*lock):"0"(0x1)); - return (_res != 0); -} -#endif /* i386 */ - - - -#if defined(sparc) - -int -tas(slock_t *lock) -{ - slock_t _res; - slock_t *tmplock = lock; - - __asm__("ldstub [%1], %0" \ - : "=&r"(_res), "=r"(tmplock) \ - : "1"(tmplock)); - return (_res != 0); -} - -#endif /* sparc */ - - #if defined(PPC) - -static int -tas_dummy() +/* Note: need a nice gcc constrained asm version so it can be inlined */ +int +tas(volatile slock_t *lock) { - __asm__(" \n\ -tas: \n\ - lwarx 5,0,3 \n\ + __asm__("lwarx 5,0,3 \n\ cmpwi 5,0 \n\ bne fail \n\ addi 5,5,1 \n\ @@ -169,21 +111,20 @@ success: \n\ blr \n\ "); } - #endif /* PPC */ -#else /* defined(linux) */ +#else /* defined(__GNUC__) */ /*************************************************************************** - * All Non-Linux + * All non gcc */ #if defined(sun3) static void -tas_dummy() /* really means: extern int tas(slock_t *lock); */ +tas_dummy() /* really means: extern int tas(slock_t *lock); */ { asm("LLA0:"); asm(" .data"); @@ -208,26 +149,18 @@ tas_dummy() /* really means: extern int tas(slock_t *lock); */ #if defined(NEED_SPARC_TAS_ASM) /* - * bsd and bsdi sparc machines + * sparc machines not using gcc */ - -/* if we're using -ansi w/ gcc, use __asm__ instead of asm */ -#if defined(__STRICT_ANSI__) -#define asm(x) __asm__(x) -#endif /* __STRICT_ANSI__ */ - static void -tas_dummy() /* really means: extern int tas(slock_t *lock); */ +tas_dummy() /* really means: extern int tas(slock_t *lock); */ { asm(".seg \"data\""); asm(".seg \"text\""); asm("_tas:"); - /* * Sparc atomic test and set (sparc calls it "atomic load-store") */ asm("ldstub [%r8], %r8"); - asm("retl"); asm("nop"); } @@ -237,76 +170,25 @@ tas_dummy() /* really means: extern int tas(slock_t *lock); */ -#if defined(NEED_VAX_TAS_ASM) -/* - * VAXen -- even multiprocessor ones - * (thanks to Tom Ivar Helbekkmo) - */ -typedef unsigned char slock_t; - -int -tas(slock_t *lock) -{ - register ret; - - asm(" movl $1, r0 - bbssi $0, (%1), 1f - clrl r0 - 1: movl r0, %0 " - : "=r"(ret) /* return value, in register */ - : "r"(lock) /* argument, 'lock pointer', in register */ - : "r0"); /* inline code uses this register */ - - return ret; -} - -#endif /* NEED_VAX_TAS_ASM */ - - - #if defined(NEED_I386_TAS_ASM) -/* - * i386 based things - */ - -#if defined(USE_UNIVEL_CC) -asm int -tas(slock_t *s_lock) -{ - %lab locked; -/* Upon entry, %eax will contain the pointer to the lock byte */ - pushl % ebx - xchgl % eax, %ebx - xor % eax, %eax - movb $255, %al - lock - xchgb % al, (%ebx) - popl % ebx -} - - -#else /* USE_UNIVEL_CC */ - -int -tas(slock_t *lock) -{ - slock_t _res = 1; +/* non gcc i386 based things */ +#endif /* NEED_I386_TAS_ASM */ - __asm__("lock; xchgb %0,%1": "=q"(_res), "=m"(*lock):"0"(0x1)); - return (_res != 0); -} -#endif /* USE_UNIVEL_CC */ -#endif /* NEED_I386_TAS_ASM */ +#endif /* not __GNUC__ */ -#endif /* linux */ +/*****************************************************************************/ #if defined(S_LOCK_TEST) -slock_t test_lock; +/* + * test program for verifying a port. + */ + +volatile slock_t test_lock; void main() @@ -330,7 +212,7 @@ main() printf("S_LOCK_TEST: this will hang for a few minutes and then abort\n"); printf(" with a 'stuck spinlock' message if S_LOCK()\n"); printf(" and TAS() are working.\n"); - S_LOCK(&test_lock); + s_lock(&test_lock, __FILE__, __LINE__); printf("S_LOCK_TEST: failed, lock not locked~\n"); exit(3); @@ -338,3 +220,4 @@ main() } #endif /* S_LOCK_TEST */ + diff --git a/src/include/storage/s_lock.h b/src/include/storage/s_lock.h index 369a3d8724..4808d3d493 100644 --- a/src/include/storage/s_lock.h +++ b/src/include/storage/s_lock.h @@ -7,13 +7,14 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.35 1998/06/15 18:40:04 momjian Exp $ + * $Header: /cvsroot/pgsql/src/include/storage/s_lock.h,v 1.36 1998/06/16 07:18:16 momjian Exp $ * *------------------------------------------------------------------------- */ + /* * DESCRIPTION - * The public functions that must be provided are: + * The public macros that must be provided are: * * void S_INIT_LOCK(slock_t *lock) * @@ -45,11 +46,15 @@ * #define TAS(lock) tas(lock) * int tas(slock_t *lock) // True if lock already set * + * There are default implementations for all these macros at the bottom + * of this file. Check if your platform can use these or needs to + * override them. + * + * NOTES * If none of this can be done, POSTGRES will default to using * System V semaphores (and take a large performance hit -- around 40% * of its time on a DS5000/240 is spent in semop(3)...). * - * NOTES * AIX has a test-and-set but the recommended interface is the cs(3) * system call. This provides an 8-instruction (plus system call * overhead) uninterruptible compare-and-set operation. True @@ -57,10 +62,6 @@ * regression test suite by about 25%. I don't have an assembler * manual for POWER in any case. * - * There are default implementations for all these macros at the bottom - * of this file. Check if your platform can use these or needs to - * override them. - * */ #if !defined(S_LOCK_H) #define S_LOCK_H @@ -69,23 +70,104 @@ #if defined(HAS_TEST_AND_SET) -#if defined(linux) -/*************************************************************************** - * All Linux + +#if defined(__GNUC__) +/************************************************************************* + * All the gcc inlines */ #if defined(__alpha) - +#define TAS(lock) tas(lock) #define S_UNLOCK(lock) { __asm__("mb"); *(lock) = 0; } +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res; + + __asm__(" ldq $0, %0 \n\ + bne $0, already_set \n\ + ldq_l $0, %0 \n\ + bne $0, already_set \n\ + or $31, 1, $0 \n\ + stq_c $0, %0 \n\ + beq $0, stqc_fail \n\ + success: bis $31, $31, %1 \n\ + mb \n\ + jmp $31, end \n\ + stqc_fail: or $31, 1, $0 \n\ + already_set: bis $0, $0, %1 \n\ + end: nop " : "=m"(*lock), "=r"(_res) : : "0"); + + return (int) _res; +} #endif /* __alpha */ +#if defined(i386) +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res = 1; + + __asm__("lock; xchgb %0,%1" : "=q"(_res), "=m"(*lock) : "0"(_res) ); + return (int) _res; +} +#endif /* i386 */ + + + +#if defined(sparc) +#define TAS(lock) tas(lock) + +static __inline__ int +tas(volatile slock_t *lock) +{ + register slock_t _res = 1; + + __asm__("ldstub [%1], %0" \ + : "=r"(_res), "=m"(*lock) \ + : "1"(lock)); + return (int) _res; +} +#endif /* sparc */ + + + +#if defined(NEED_VAX_TAS_ASM) +/* + * VAXen -- even multiprocessor ones + * (thanks to Tom Ivar Helbekkmo) + */ +#define TAS(lock) tas(lock) + +typedef unsigned char slock_t; + +static __inline__ int +tas(volatile slock_t *lock) +{ + register _res; + + __asm__(" movl $1, r0 + bbssi $0, (%1), 1f + clrl r0 + 1: movl r0, %0 " + : "=r"(_res) /* return value, in register */ + : "r"(lock) /* argument, 'lock pointer', in register */ + : "r0"); /* inline code uses this register */ + return (int) _res; +} +#endif /* NEED_VAX_TAS_ASM */ + + -#else /* linux */ + +#else /* __GNUC__ */ /*************************************************************************** - * All non Linux + * All non gcc */ #if defined (nextstep) @@ -95,14 +177,10 @@ */ #define S_LOCK(lock) mutex_lock(lock) - #define S_UNLOCK(lock) mutex_unlock(lock) - #define S_INIT_LOCK(lock) mutex_init(lock) - /* For Mach, we have to delve inside the entrails of `struct mutex'. Ick! */ #define S_LOCK_FREE(alock) ((alock)->lock == 0) - #endif /* nextstep */ @@ -118,13 +196,9 @@ * for the R3000 chips out there. */ #define TAS(lock) (!acquire_lock(lock)) - #define S_UNLOCK(lock) release_lock(lock) - #define S_INIT_LOCK(lock) init_lock(lock) - #define S_LOCK_FREE(lock) (stat_lock(lock) == UNLOCKED) - #endif /* __sgi */ @@ -137,13 +211,9 @@ * (see storage/ipc.h). */ #define TAS(lock) (msem_lock((lock), MSEM_IF_NOWAIT) < 0) - #define S_UNLOCK(lock) msem_unlock((lock), 0) - #define S_INIT_LOCK(lock) msem_init((lock), MSEM_UNLOCKED) - #define S_LOCK_FREE(lock) (!(lock)->msem_state) - #endif /* __alpha */ @@ -156,7 +226,6 @@ * (see storage/ipc.h). */ #define TAS(lock) cs((int *) (lock), 0, 1) - #endif /* _AIX */ @@ -175,64 +244,56 @@ static slock_t clear_lock = {-1, -1, -1, -1}; #define S_UNLOCK(lock) (*(lock) = clear_lock) /* struct assignment */ - #define S_LOCK_FREE(lock) ( *(int *) (((long) (lock) + 15) & ~15) != 0) - #endif /* __hpux */ -#endif /* else defined(linux) */ - +#if defined(NEED_I386_TAS_ASM) +/* non gcc i386 based things */ +#if defined(USE_UNIVEL_CC) +#define TAS(lock) tas(lock) -/**************************************************************************** - * Default Definitions - override these above as needed. - */ +asm int +tas(slock_t *s_lock) +{ + %lab locked; + /* Upon entry, %eax will contain the pointer to the lock byte */ + pushl % ebx + xchgl % eax, %ebx + xor % eax, %eax + movb $255, %al + lock + xchgb % al, (%ebx) + popl % ebx +} +#endif /* USE_UNIVEL_CC */ -#if !defined(S_LOCK) -#include +#endif /* NEED_I386_TAS_ASM */ -#define S_NSPINCYCLE 16 -#define S_MAX_BUSY 1000 * S_NSPINCYCLE -extern int s_spincycle[]; -extern void s_lock_stuck(slock_t *lock, char *file, int line); +#endif /* else defined(__GNUC__) */ -#if defined(S_LOCK_DEBUG) -extern void s_lock(slock_t *lock); -#define S_LOCK(lock) s_lock(lock, __FILE__, __LINE__) -#else /* S_LOCK_DEBUG */ +/**************************************************************************** + * Default Definitions - override these above as needed. + */ +#if !defined(S_LOCK) +extern void s_lock(volatile slock_t *lock, const char *file, const int line); #define S_LOCK(lock) \ -do { \ - int spins = 0; \ - while (TAS(lock)) \ - { \ - struct timeval delay; \ - delay.tv_sec = 0; \ - delay.tv_usec = s_spincycle[spins++ % S_NSPINCYCLE]; \ - (void) select(0, NULL, NULL, NULL, &delay); \ - if (spins > S_MAX_BUSY) \ - { \ - /* It's been well over a minute... */ \ - s_lock_stuck(lock, __FILE__, __LINE__); \ - } \ - } \ -} while(0) - -#endif /* S_LOCK_DEBUG */ + if (TAS((volatile slock_t *) lock)) {\ + s_lock((volatile slock_t *) lock, __FILE__, __LINE__); \ + } else #endif /* S_LOCK */ - - #if !defined(S_LOCK_FREE) -#define S_LOCK_FREE(lock) ((*lock) == 0) +#define S_LOCK_FREE(lock) (*(lock) == 0) #endif /* S_LOCK_FREE */ #if !defined(S_UNLOCK) @@ -244,12 +305,11 @@ do { \ #endif /* S_INIT_LOCK */ #if !defined(TAS) -int tas(slock_t *lock); /* port/.../tas.s, or s_lock.c */ - -#define TAS(lock) tas(lock) +int tas(volatile slock_t *lock); /* port/.../tas.s, or s_lock.c */ +#define TAS(lock) tas((volatile slock_t *) lock) #endif /* TAS */ #endif /* HAS_TEST_AND_SET */ - #endif /* S_LOCK_H */ +