Home | History | Annotate | Download | only in pppd
      1 /*
      2    Unix SMB/CIFS implementation.
      3 
      4    trivial database library
      5 
      6    Copyright (C) Anton Blanchard                   2001
      7 
      8      ** NOTE! The following LGPL license applies to the tdb
      9      ** library. This does NOT imply that all of Samba is released
     10      ** under the LGPL
     11 
     12    This library is free software; you can redistribute it and/or
     13    modify it under the terms of the GNU Lesser General Public
     14    License as published by the Free Software Foundation; either
     15    version 2 of the License, or (at your option) any later version.
     16 
     17    This library is distributed in the hope that it will be useful,
     18    but WITHOUT ANY WARRANTY; without even the implied warranty of
     19    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     20    Lesser General Public License for more details.
     21 
     22    You should have received a copy of the GNU Lesser General Public
     23    License along with this library; if not, write to the Free Software
     24    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
     25 */
     26 #include <stdlib.h>
     27 #include <stdio.h>
     28 #include <unistd.h>
     29 #include <string.h>
     30 #include <fcntl.h>
     31 #include <errno.h>
     32 #include <sys/stat.h>
     33 #include <time.h>
     34 #include <signal.h>
     35 #include "tdb.h"
     36 #include "spinlock.h"
     37 
     38 #define DEBUG
     39 
     40 #ifdef USE_SPINLOCKS
     41 
     42 /*
     43  * ARCH SPECIFIC
     44  */
     45 
     46 #if defined(SPARC_SPINLOCKS)
     47 
     48 static inline int __spin_trylock(spinlock_t *lock)
     49 {
     50 	unsigned int result;
     51 
     52 	asm volatile("ldstub    [%1], %0"
     53 		: "=r" (result)
     54 		: "r" (lock)
     55 		: "memory");
     56 
     57 	return (result == 0) ? 0 : EBUSY;
     58 }
     59 
     60 static inline void __spin_unlock(spinlock_t *lock)
     61 {
     62 	asm volatile("":::"memory");
     63 	*lock = 0;
     64 }
     65 
     66 static inline void __spin_lock_init(spinlock_t *lock)
     67 {
     68 	*lock = 0;
     69 }
     70 
     71 static inline int __spin_is_locked(spinlock_t *lock)
     72 {
     73 	return (*lock != 0);
     74 }
     75 
     76 #elif defined(POWERPC_SPINLOCKS)
     77 
     78 static inline int __spin_trylock(spinlock_t *lock)
     79 {
     80 	unsigned int result;
     81 
     82 	__asm__ __volatile__(
     83 "1:	lwarx		%0,0,%1\n\
     84 	cmpwi		0,%0,0\n\
     85 	li		%0,0\n\
     86 	bne-		2f\n\
     87 	li		%0,1\n\
     88 	stwcx.		%0,0,%1\n\
     89 	bne-		1b\n\
     90 	isync\n\
     91 2:"	: "=&r"(result)
     92 	: "r"(lock)
     93 	: "cr0", "memory");
     94 
     95 	return (result == 1) ? 0 : EBUSY;
     96 }
     97 
     98 static inline void __spin_unlock(spinlock_t *lock)
     99 {
    100 	asm volatile("eieio":::"memory");
    101 	*lock = 0;
    102 }
    103 
    104 static inline void __spin_lock_init(spinlock_t *lock)
    105 {
    106 	*lock = 0;
    107 }
    108 
    109 static inline int __spin_is_locked(spinlock_t *lock)
    110 {
    111 	return (*lock != 0);
    112 }
    113 
    114 #elif defined(INTEL_SPINLOCKS)
    115 
    116 static inline int __spin_trylock(spinlock_t *lock)
    117 {
    118 	int oldval;
    119 
    120 	asm volatile("xchgl %0,%1"
    121 		: "=r" (oldval), "=m" (*lock)
    122 		: "0" (0)
    123 		: "memory");
    124 
    125 	return oldval > 0 ? 0 : EBUSY;
    126 }
    127 
    128 static inline void __spin_unlock(spinlock_t *lock)
    129 {
    130 	asm volatile("":::"memory");
    131 	*lock = 1;
    132 }
    133 
    134 static inline void __spin_lock_init(spinlock_t *lock)
    135 {
    136 	*lock = 1;
    137 }
    138 
    139 static inline int __spin_is_locked(spinlock_t *lock)
    140 {
    141 	return (*lock != 1);
    142 }
    143 
    144 #elif defined(MIPS_SPINLOCKS) && defined(sgi) && (_COMPILER_VERSION >= 730)
    145 
    146 /* Implement spinlocks on IRIX using the MIPSPro atomic fetch operations. See
    147  * sync(3) for the details of the intrinsic operations.
    148  *
    149  * "sgi" and "_COMPILER_VERSION" are always defined by MIPSPro.
    150  */
    151 
    152 #ifdef STANDALONE
    153 
    154 /* MIPSPro 7.3 has "__inline" as an extension, but not "inline. */
    155 #define inline __inline
    156 
    157 #endif /* STANDALONE */
    158 
    159 /* Returns 0 if the lock is acquired, EBUSY otherwise. */
    160 static inline int __spin_trylock(spinlock_t *lock)
    161 {
    162         unsigned int val;
    163         val = __lock_test_and_set(lock, 1);
    164         return val == 0 ? 0 : EBUSY;
    165 }
    166 
    167 static inline void __spin_unlock(spinlock_t *lock)
    168 {
    169         __lock_release(lock);
    170 }
    171 
    172 static inline void __spin_lock_init(spinlock_t *lock)
    173 {
    174         __lock_release(lock);
    175 }
    176 
    177 /* Returns 1 if the lock is held, 0 otherwise. */
    178 static inline int __spin_is_locked(spinlock_t *lock)
    179 {
    180         unsigned int val;
    181         val = __add_and_fetch(lock, 0);
    182 	return val;
    183 }
    184 
    185 #elif defined(MIPS_SPINLOCKS)
    186 
    187 static inline unsigned int load_linked(unsigned long addr)
    188 {
    189 	unsigned int res;
    190 
    191 	__asm__ __volatile__("ll\t%0,(%1)"
    192 		: "=r" (res)
    193 		: "r" (addr));
    194 
    195 	return res;
    196 }
    197 
    198 static inline unsigned int store_conditional(unsigned long addr, unsigned int value)
    199 {
    200 	unsigned int res;
    201 
    202 	__asm__ __volatile__("sc\t%0,(%2)"
    203 		: "=r" (res)
    204 		: "0" (value), "r" (addr));
    205 	return res;
    206 }
    207 
    208 static inline int __spin_trylock(spinlock_t *lock)
    209 {
    210 	unsigned int mw;
    211 
    212 	do {
    213 		mw = load_linked(lock);
    214 		if (mw)
    215 			return EBUSY;
    216 	} while (!store_conditional(lock, 1));
    217 
    218 	asm volatile("":::"memory");
    219 
    220 	return 0;
    221 }
    222 
    223 static inline void __spin_unlock(spinlock_t *lock)
    224 {
    225 	asm volatile("":::"memory");
    226 	*lock = 0;
    227 }
    228 
    229 static inline void __spin_lock_init(spinlock_t *lock)
    230 {
    231 	*lock = 0;
    232 }
    233 
    234 static inline int __spin_is_locked(spinlock_t *lock)
    235 {
    236 	return (*lock != 0);
    237 }
    238 
    239 #else
    240 #error Need to implement spinlock code in spinlock.c
    241 #endif
    242 
    243 /*
    244  * OS SPECIFIC
    245  */
    246 
    247 static void yield_cpu(void)
    248 {
    249 	struct timespec tm;
    250 
    251 #ifdef USE_SCHED_YIELD
    252 	sched_yield();
    253 #else
    254 	/* Linux will busy loop for delays < 2ms on real time tasks */
    255 	tm.tv_sec = 0;
    256 	tm.tv_nsec = 2000000L + 1;
    257 	nanosleep(&tm, NULL);
    258 #endif
    259 }
    260 
    261 static int this_is_smp(void)
    262 {
    263 #if defined(HAVE_SYSCONF) && defined(SYSCONF_SC_NPROC_ONLN)
    264         return (sysconf(_SC_NPROC_ONLN) > 1) ? 1 : 0;
    265 #else
    266 	return 0;
    267 #endif
    268 }
    269 
    270 /*
    271  * GENERIC
    272  */
    273 
    274 static int smp_machine = 0;
    275 
    276 static inline void __spin_lock(spinlock_t *lock)
    277 {
    278 	int ntries = 0;
    279 
    280 	while(__spin_trylock(lock)) {
    281 		while(__spin_is_locked(lock)) {
    282 			if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
    283 				continue;
    284 			yield_cpu();
    285 		}
    286 	}
    287 }
    288 
    289 static void __read_lock(tdb_rwlock_t *rwlock)
    290 {
    291 	int ntries = 0;
    292 
    293 	while(1) {
    294 		__spin_lock(&rwlock->lock);
    295 
    296 		if (!(rwlock->count & RWLOCK_BIAS)) {
    297 			rwlock->count++;
    298 			__spin_unlock(&rwlock->lock);
    299 			return;
    300 		}
    301 
    302 		__spin_unlock(&rwlock->lock);
    303 
    304 		while(rwlock->count & RWLOCK_BIAS) {
    305 			if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
    306 				continue;
    307 			yield_cpu();
    308 		}
    309 	}
    310 }
    311 
    312 static void __write_lock(tdb_rwlock_t *rwlock)
    313 {
    314 	int ntries = 0;
    315 
    316 	while(1) {
    317 		__spin_lock(&rwlock->lock);
    318 
    319 		if (rwlock->count == 0) {
    320 			rwlock->count |= RWLOCK_BIAS;
    321 			__spin_unlock(&rwlock->lock);
    322 			return;
    323 		}
    324 
    325 		__spin_unlock(&rwlock->lock);
    326 
    327 		while(rwlock->count != 0) {
    328 			if (smp_machine && ntries++ < MAX_BUSY_LOOPS)
    329 				continue;
    330 			yield_cpu();
    331 		}
    332 	}
    333 }
    334 
    335 static void __write_unlock(tdb_rwlock_t *rwlock)
    336 {
    337 	__spin_lock(&rwlock->lock);
    338 
    339 #ifdef DEBUG
    340 	if (!(rwlock->count & RWLOCK_BIAS))
    341 		fprintf(stderr, "bug: write_unlock\n");
    342 #endif
    343 
    344 	rwlock->count &= ~RWLOCK_BIAS;
    345 	__spin_unlock(&rwlock->lock);
    346 }
    347 
    348 static void __read_unlock(tdb_rwlock_t *rwlock)
    349 {
    350 	__spin_lock(&rwlock->lock);
    351 
    352 #ifdef DEBUG
    353 	if (!rwlock->count)
    354 		fprintf(stderr, "bug: read_unlock\n");
    355 
    356 	if (rwlock->count & RWLOCK_BIAS)
    357 		fprintf(stderr, "bug: read_unlock\n");
    358 #endif
    359 
    360 	rwlock->count--;
    361 	__spin_unlock(&rwlock->lock);
    362 }
    363 
    364 /* TDB SPECIFIC */
    365 
    366 /* lock a list in the database. list -1 is the alloc list */
    367 int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type)
    368 {
    369 	tdb_rwlock_t *rwlocks;
    370 
    371 	if (!tdb->map_ptr) return -1;
    372 	rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
    373 
    374 	switch(rw_type) {
    375 	case F_RDLCK:
    376 		__read_lock(&rwlocks[list+1]);
    377 		break;
    378 
    379 	case F_WRLCK:
    380 		__write_lock(&rwlocks[list+1]);
    381 		break;
    382 
    383 	default:
    384 		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
    385 	}
    386 	return 0;
    387 }
    388 
    389 /* unlock the database. */
    390 int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type)
    391 {
    392 	tdb_rwlock_t *rwlocks;
    393 
    394 	if (!tdb->map_ptr) return -1;
    395 	rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
    396 
    397 	switch(rw_type) {
    398 	case F_RDLCK:
    399 		__read_unlock(&rwlocks[list+1]);
    400 		break;
    401 
    402 	case F_WRLCK:
    403 		__write_unlock(&rwlocks[list+1]);
    404 		break;
    405 
    406 	default:
    407 		return TDB_ERRCODE(TDB_ERR_LOCK, -1);
    408 	}
    409 
    410 	return 0;
    411 }
    412 
    413 int tdb_create_rwlocks(int fd, unsigned int hash_size)
    414 {
    415 	unsigned size, i;
    416 	tdb_rwlock_t *rwlocks;
    417 
    418 	size = TDB_SPINLOCK_SIZE(hash_size);
    419 	rwlocks = malloc(size);
    420 	if (!rwlocks)
    421 		return -1;
    422 
    423 	for(i = 0; i < hash_size+1; i++) {
    424 		__spin_lock_init(&rwlocks[i].lock);
    425 		rwlocks[i].count = 0;
    426 	}
    427 
    428 	/* Write it out (appending to end) */
    429 	if (write(fd, rwlocks, size) != size) {
    430 		free(rwlocks);
    431 		return -1;
    432 	}
    433 	smp_machine = this_is_smp();
    434 	free(rwlocks);
    435 	return 0;
    436 }
    437 
    438 int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
    439 {
    440 	tdb_rwlock_t *rwlocks;
    441 	unsigned i;
    442 
    443 	if (tdb->header.rwlocks == 0) return 0;
    444 	if (!tdb->map_ptr) return -1;
    445 
    446 	/* We're mmapped here */
    447 	rwlocks = (tdb_rwlock_t *)((char *)tdb->map_ptr + tdb->header.rwlocks);
    448 	for(i = 0; i < tdb->header.hash_size+1; i++) {
    449 		__spin_lock_init(&rwlocks[i].lock);
    450 		rwlocks[i].count = 0;
    451 	}
    452 	return 0;
    453 }
    454 #else
    455 int tdb_create_rwlocks(int fd, unsigned int hash_size) { return 0; }
    456 int tdb_spinlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
    457 int tdb_spinunlock(TDB_CONTEXT *tdb, int list, int rw_type) { return -1; }
    458 
    459 /* Non-spinlock version: remove spinlock pointer */
    460 int tdb_clear_spinlocks(TDB_CONTEXT *tdb)
    461 {
    462 	tdb_off off = (tdb_off)((char *)&tdb->header.rwlocks
    463 				- (char *)&tdb->header);
    464 
    465 	tdb->header.rwlocks = 0;
    466 	if (lseek(tdb->fd, off, SEEK_SET) != off
    467 	    || write(tdb->fd, (void *)&tdb->header.rwlocks,
    468 		     sizeof(tdb->header.rwlocks))
    469 	    != sizeof(tdb->header.rwlocks))
    470 		return -1;
    471 	return 0;
    472 }
    473 #endif
    474