Home | History | Annotate | Download | only in threads
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 
     22 /*
     23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
     24  * Use is subject to license terms.
     25  */
     26 
     27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
     28 
     29 /*
     30  * This file contains most of the functionality
     31  * required to support the threads portion of libc_db.
     32  */
     33 
     34 #include "lint.h"
     35 #include "thr_uberdata.h"
     36 
     37 static void
     38 tdb_event_ready(void) {}
     39 
     40 static void
     41 tdb_event_sleep(void) {}
     42 
     43 static void
     44 tdb_event_switchto(void) {}
     45 
     46 static void
     47 tdb_event_switchfrom(void) {}
     48 
     49 static void
     50 tdb_event_lock_try(void) {}
     51 
     52 static void
     53 tdb_event_catchsig(void) {}
     54 
     55 static void
     56 tdb_event_idle(void) {}
     57 
     58 static void
     59 tdb_event_create(void) {}
     60 
     61 static void
     62 tdb_event_death(void) {}
     63 
     64 static void
     65 tdb_event_preempt(void) {}
     66 
     67 static void
     68 tdb_event_pri_inherit(void) {}
     69 
     70 static void
     71 tdb_event_reap(void) {}
     72 
     73 static void
     74 tdb_event_concurrency(void) {}
     75 
     76 static void
     77 tdb_event_timeout(void) {}
     78 
     79 /*
     80  * uberflags.uf_tdb_register_sync is set to REGISTER_SYNC_ENABLE by a debugger
     81  * to empty the table and then enable synchronization object registration.
     82  *
     83  * uberflags.uf_tdb_register_sync is set to REGISTER_SYNC_DISABLE by a debugger
     84  * to empty the table and then disable synchronization object registration.
     85  */
     86 
     87 const tdb_ev_func_t tdb_events[TD_MAX_EVENT_NUM - TD_MIN_EVENT_NUM + 1] = {
     88 	tdb_event_ready,
     89 	tdb_event_sleep,
     90 	tdb_event_switchto,
     91 	tdb_event_switchfrom,
     92 	tdb_event_lock_try,
     93 	tdb_event_catchsig,
     94 	tdb_event_idle,
     95 	tdb_event_create,
     96 	tdb_event_death,
     97 	tdb_event_preempt,
     98 	tdb_event_pri_inherit,
     99 	tdb_event_reap,
    100 	tdb_event_concurrency,
    101 	tdb_event_timeout
    102 };
    103 
    104 #if TDB_HASH_SHIFT != 15
    105 #error "this is all broken because TDB_HASH_SHIFT is not 15"
    106 #endif
    107 
    108 static uint_t
    109 tdb_addr_hash(void *addr)
    110 {
    111 	/*
    112 	 * This knows for a fact that the hash table has
    113 	 * 32K entries; that is, that TDB_HASH_SHIFT is 15.
    114 	 */
    115 #ifdef	_LP64
    116 	uint64_t value60 = ((uintptr_t)addr >> 4);	/* 60 bits */
    117 	uint32_t value30 = (value60 >> 30) ^ (value60 & 0x3fffffff);
    118 #else
    119 	uint32_t value30 = ((uintptr_t)addr >> 2);	/* 30 bits */
    120 #endif
    121 	return ((value30 >> 15) ^ (value30 & 0x7fff));
    122 }
    123 
    124 static tdb_sync_stats_t *
    125 alloc_sync_addr(void *addr)
    126 {
    127 	uberdata_t *udp = curthread->ul_uberdata;
    128 	tdb_t *tdbp = &udp->tdb;
    129 	tdb_sync_stats_t *sap;
    130 
    131 	ASSERT(MUTEX_OWNED(&udp->tdb_hash_lock, curthread));
    132 
    133 	if ((sap = tdbp->tdb_sync_addr_free) == NULL) {
    134 		void *vaddr;
    135 		int i;
    136 
    137 		/*
    138 		 * Don't keep trying after mmap() has already failed.
    139 		 */
    140 		if (tdbp->tdb_hash_alloc_failed)
    141 			return (NULL);
    142 
    143 		/* double the allocation each time */
    144 		tdbp->tdb_sync_alloc *= 2;
    145 		if ((vaddr = mmap(NULL,
    146 		    tdbp->tdb_sync_alloc * sizeof (tdb_sync_stats_t),
    147 		    PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,
    148 		    -1, (off_t)0)) == MAP_FAILED) {
    149 			tdbp->tdb_hash_alloc_failed = 1;
    150 			return (NULL);
    151 		}
    152 		sap = tdbp->tdb_sync_addr_free = vaddr;
    153 		for (i = 1; i < tdbp->tdb_sync_alloc; sap++, i++)
    154 			sap->next = (uintptr_t)(sap + 1);
    155 		sap->next = (uintptr_t)0;
    156 		tdbp->tdb_sync_addr_last = sap;
    157 
    158 		sap = tdbp->tdb_sync_addr_free;
    159 	}
    160 
    161 	tdbp->tdb_sync_addr_free = (tdb_sync_stats_t *)(uintptr_t)sap->next;
    162 	sap->next = (uintptr_t)0;
    163 	sap->sync_addr = (uintptr_t)addr;
    164 	(void) memset(&sap->un, 0, sizeof (sap->un));
    165 	return (sap);
    166 }
    167 
    168 static void
    169 initialize_sync_hash()
    170 {
    171 	uberdata_t *udp = curthread->ul_uberdata;
    172 	tdb_t *tdbp = &udp->tdb;
    173 	uint64_t *addr_hash;
    174 	tdb_sync_stats_t *sap;
    175 	void *vaddr;
    176 	int i;
    177 
    178 	if (tdbp->tdb_hash_alloc_failed)
    179 		return;
    180 	lmutex_lock(&udp->tdb_hash_lock);
    181 	if (udp->uberflags.uf_tdb_register_sync == REGISTER_SYNC_DISABLE) {
    182 		/*
    183 		 * There is no point allocating the hash table
    184 		 * if we are disabling registration.
    185 		 */
    186 		udp->uberflags.uf_tdb_register_sync = REGISTER_SYNC_OFF;
    187 		lmutex_unlock(&udp->tdb_hash_lock);
    188 		return;
    189 	}
    190 	if (tdbp->tdb_sync_addr_hash != NULL || tdbp->tdb_hash_alloc_failed) {
    191 		lmutex_unlock(&udp->tdb_hash_lock);
    192 		return;
    193 	}
    194 	/* start with a free list of 2k elements */
    195 	tdbp->tdb_sync_alloc = 2*1024;
    196 	if ((vaddr = mmap(NULL, TDB_HASH_SIZE * sizeof (uint64_t) +
    197 	    tdbp->tdb_sync_alloc * sizeof (tdb_sync_stats_t),
    198 	    PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON,
    199 	    -1, (off_t)0)) == MAP_FAILED) {
    200 		tdbp->tdb_hash_alloc_failed = 1;
    201 		return;
    202 	}
    203 	addr_hash = vaddr;
    204 
    205 	/* initialize the free list */
    206 	tdbp->tdb_sync_addr_free = sap =
    207 	    (tdb_sync_stats_t *)&addr_hash[TDB_HASH_SIZE];
    208 	for (i = 1; i < tdbp->tdb_sync_alloc; sap++, i++)
    209 		sap->next = (uintptr_t)(sap + 1);
    210 	sap->next = (uintptr_t)0;
    211 	tdbp->tdb_sync_addr_last = sap;
    212 
    213 	/* insert &udp->tdb_hash_lock itself into the new (empty) table */
    214 	udp->tdb_hash_lock_stats.next = (uintptr_t)0;
    215 	udp->tdb_hash_lock_stats.sync_addr = (uintptr_t)&udp->tdb_hash_lock;
    216 	addr_hash[tdb_addr_hash(&udp->tdb_hash_lock)] =
    217 	    (uintptr_t)&udp->tdb_hash_lock_stats;
    218 
    219 	tdbp->tdb_register_count = 1;
    220 	/* assign to tdb_sync_addr_hash only after fully initialized */
    221 	membar_producer();
    222 	tdbp->tdb_sync_addr_hash = addr_hash;
    223 	lmutex_unlock(&udp->tdb_hash_lock);
    224 }
    225 
    226 tdb_sync_stats_t *
    227 tdb_sync_obj_register(void *addr, int *new)
    228 {
    229 	ulwp_t *self = curthread;
    230 	uberdata_t *udp = self->ul_uberdata;
    231 	tdb_t *tdbp = &udp->tdb;
    232 	uint64_t *sapp;
    233 	tdb_sync_stats_t *sap = NULL;
    234 	int locked = 0;
    235 	int i;
    236 
    237 	/*
    238 	 * Don't start statistics collection until
    239 	 * we have initialized the primary link map.
    240 	 */
    241 	if (!self->ul_primarymap)
    242 		return (NULL);
    243 
    244 	if (new)
    245 		*new = 0;
    246 	/*
    247 	 * To avoid recursion problems, we must do two things:
    248 	 * 1. Make a special case for tdb_hash_lock (we use it internally).
    249 	 * 2. Deal with the dynamic linker's lock interface:
    250 	 *    When calling any external function, we may invoke the
    251 	 *    dynamic linker.  It grabs a lock, which calls back here.
    252 	 *    This only happens on the first call to the external
    253 	 *    function, so we can just return NULL if we are called
    254 	 *    recursively (and miss the first count).
    255 	 */
    256 	if (addr == (void *)&udp->tdb_hash_lock)
    257 		return (&udp->tdb_hash_lock_stats);
    258 	if (self->ul_sync_obj_reg)		/* recursive call */
    259 		return (NULL);
    260 	self->ul_sync_obj_reg = 1;
    261 
    262 	/*
    263 	 * On the first time through, initialize the hash table and free list.
    264 	 */
    265 	if (tdbp->tdb_sync_addr_hash == NULL) {
    266 		initialize_sync_hash();
    267 		if (tdbp->tdb_sync_addr_hash == NULL) {	/* utter failure */
    268 			udp->uberflags.uf_tdb_register_sync = REGISTER_SYNC_OFF;
    269 			goto out;
    270 		}
    271 	}
    272 	membar_consumer();
    273 
    274 	sapp = &tdbp->tdb_sync_addr_hash[tdb_addr_hash(addr)];
    275 	if (udp->uberflags.uf_tdb_register_sync == REGISTER_SYNC_ON) {
    276 		/*
    277 		 * Look up an address in the synchronization object hash table.
    278 		 * No lock is required since it can only deliver a false
    279 		 * negative, in which case we fall into the locked case below.
    280 		 */
    281 		for (sap = (tdb_sync_stats_t *)(uintptr_t)*sapp; sap != NULL;
    282 		    sap = (tdb_sync_stats_t *)(uintptr_t)sap->next) {
    283 			if (sap->sync_addr == (uintptr_t)addr)
    284 				goto out;
    285 		}
    286 	}
    287 
    288 	/*
    289 	 * The search with no lock held failed or a special action is required.
    290 	 * Grab tdb_hash_lock to do special actions and/or get a precise result.
    291 	 */
    292 	lmutex_lock(&udp->tdb_hash_lock);
    293 	locked = 1;
    294 
    295 	switch (udp->uberflags.uf_tdb_register_sync) {
    296 	case REGISTER_SYNC_ON:
    297 		break;
    298 	case REGISTER_SYNC_OFF:
    299 		goto out;
    300 	default:
    301 		/*
    302 		 * For all debugger actions, first zero out the
    303 		 * statistics block of every element in the hash table.
    304 		 */
    305 		for (i = 0; i < TDB_HASH_SIZE; i++)
    306 			for (sap = (tdb_sync_stats_t *)
    307 			    (uintptr_t)tdbp->tdb_sync_addr_hash[i];
    308 			    sap != NULL;
    309 			    sap = (tdb_sync_stats_t *)(uintptr_t)sap->next)
    310 				(void) memset(&sap->un, 0, sizeof (sap->un));
    311 
    312 		switch (udp->uberflags.uf_tdb_register_sync) {
    313 		case REGISTER_SYNC_ENABLE:
    314 			udp->uberflags.uf_tdb_register_sync = REGISTER_SYNC_ON;
    315 			break;
    316 		case REGISTER_SYNC_DISABLE:
    317 		default:
    318 			udp->uberflags.uf_tdb_register_sync = REGISTER_SYNC_OFF;
    319 			goto out;
    320 		}
    321 		break;
    322 	}
    323 
    324 	/*
    325 	 * Perform the search while holding tdb_hash_lock.
    326 	 * Keep track of the insertion point.
    327 	 */
    328 	while ((sap = (tdb_sync_stats_t *)(uintptr_t)*sapp) != NULL) {
    329 		if (sap->sync_addr == (uintptr_t)addr)
    330 			break;
    331 		sapp = &sap->next;
    332 	}
    333 
    334 	/*
    335 	 * Insert a new element if necessary.
    336 	 */
    337 	if (sap == NULL && (sap = alloc_sync_addr(addr)) != NULL) {
    338 		*sapp = (uintptr_t)sap;
    339 		tdbp->tdb_register_count++;
    340 		if (new)
    341 			*new = 1;
    342 	}
    343 
    344 out:
    345 	if (locked)
    346 		lmutex_unlock(&udp->tdb_hash_lock);
    347 	self->ul_sync_obj_reg = 0;
    348 	return (sap);
    349 }
    350 
    351 void
    352 tdb_sync_obj_deregister(void *addr)
    353 {
    354 	uberdata_t *udp = curthread->ul_uberdata;
    355 	tdb_t *tdbp = &udp->tdb;
    356 	uint64_t *sapp;
    357 	tdb_sync_stats_t *sap;
    358 	uint_t hash;
    359 
    360 	/*
    361 	 * tdb_hash_lock is never destroyed.
    362 	 */
    363 	ASSERT(addr != &udp->tdb_hash_lock);
    364 
    365 	/*
    366 	 * Avoid acquiring tdb_hash_lock if lock statistics gathering has
    367 	 * never been initiated or there is nothing in the hash bucket.
    368 	 * (Once the hash table is allocated, it is never deallocated.)
    369 	 */
    370 	if (tdbp->tdb_sync_addr_hash == NULL ||
    371 	    tdbp->tdb_sync_addr_hash[hash = tdb_addr_hash(addr)] == NULL)
    372 		return;
    373 
    374 	lmutex_lock(&udp->tdb_hash_lock);
    375 	sapp = &tdbp->tdb_sync_addr_hash[hash];
    376 	while ((sap = (tdb_sync_stats_t *)(uintptr_t)*sapp) != NULL) {
    377 		if (sap->sync_addr == (uintptr_t)addr) {
    378 			/* remove it from the hash table */
    379 			*sapp = sap->next;
    380 			tdbp->tdb_register_count--;
    381 			/* clear it */
    382 			sap->next = (uintptr_t)0;
    383 			sap->sync_addr = (uintptr_t)0;
    384 			/* insert it on the tail of the free list */
    385 			if (tdbp->tdb_sync_addr_free == NULL) {
    386 				tdbp->tdb_sync_addr_free = sap;
    387 				tdbp->tdb_sync_addr_last = sap;
    388 			} else {
    389 				tdbp->tdb_sync_addr_last->next = (uintptr_t)sap;
    390 				tdbp->tdb_sync_addr_last = sap;
    391 			}
    392 			break;
    393 		}
    394 		sapp = &sap->next;
    395 	}
    396 	lmutex_unlock(&udp->tdb_hash_lock);
    397 }
    398 
    399 /*
    400  * Return a mutex statistics block for the given mutex.
    401  */
    402 tdb_mutex_stats_t *
    403 tdb_mutex_stats(mutex_t *mp)
    404 {
    405 	tdb_sync_stats_t *tssp;
    406 
    407 	/* avoid stealing the cache line unnecessarily */
    408 	if (mp->mutex_magic != MUTEX_MAGIC)
    409 		mp->mutex_magic = MUTEX_MAGIC;
    410 	if ((tssp = tdb_sync_obj_register(mp, NULL)) == NULL)
    411 		return (NULL);
    412 	tssp->un.type = TDB_MUTEX;
    413 	return (&tssp->un.mutex);
    414 }
    415 
    416 /*
    417  * Return a condvar statistics block for the given condvar.
    418  */
    419 tdb_cond_stats_t *
    420 tdb_cond_stats(cond_t *cvp)
    421 {
    422 	tdb_sync_stats_t *tssp;
    423 
    424 	/* avoid stealing the cache line unnecessarily */
    425 	if (cvp->cond_magic != COND_MAGIC)
    426 		cvp->cond_magic = COND_MAGIC;
    427 	if ((tssp = tdb_sync_obj_register(cvp, NULL)) == NULL)
    428 		return (NULL);
    429 	tssp->un.type = TDB_COND;
    430 	return (&tssp->un.cond);
    431 }
    432 
    433 /*
    434  * Return an rwlock statistics block for the given rwlock.
    435  */
    436 tdb_rwlock_stats_t *
    437 tdb_rwlock_stats(rwlock_t *rwlp)
    438 {
    439 	tdb_sync_stats_t *tssp;
    440 
    441 	/* avoid stealing the cache line unnecessarily */
    442 	if (rwlp->magic != RWL_MAGIC)
    443 		rwlp->magic = RWL_MAGIC;
    444 	if ((tssp = tdb_sync_obj_register(rwlp, NULL)) == NULL)
    445 		return (NULL);
    446 	tssp->un.type = TDB_RWLOCK;
    447 	return (&tssp->un.rwlock);
    448 }
    449 
    450 /*
    451  * Return a semaphore statistics block for the given semaphore.
    452  */
    453 tdb_sema_stats_t *
    454 tdb_sema_stats(sema_t *sp)
    455 {
    456 	tdb_sync_stats_t *tssp;
    457 	int new;
    458 
    459 	/* avoid stealing the cache line unnecessarily */
    460 	if (sp->magic != SEMA_MAGIC)
    461 		sp->magic = SEMA_MAGIC;
    462 	if ((tssp = tdb_sync_obj_register(sp, &new)) == NULL)
    463 		return (NULL);
    464 	tssp->un.type = TDB_SEMA;
    465 	if (new) {
    466 		tssp->un.sema.sema_max_count = sp->count;
    467 		tssp->un.sema.sema_min_count = sp->count;
    468 	}
    469 	return (&tssp->un.sema);
    470 }
    471