Home | History | Annotate | Download | only in src
      1 #define	JEMALLOC_QUARANTINE_C_
      2 #include "jemalloc/internal/jemalloc_internal.h"
      3 
      4 /*
      5  * Quarantine pointers close to NULL are used to encode state information that
      6  * is used for cleaning up during thread shutdown.
      7  */
      8 #define	QUARANTINE_STATE_REINCARNATED	((quarantine_t *)(uintptr_t)1)
      9 #define	QUARANTINE_STATE_PURGATORY	((quarantine_t *)(uintptr_t)2)
     10 #define	QUARANTINE_STATE_MAX		QUARANTINE_STATE_PURGATORY
     11 
     12 /******************************************************************************/
     13 /* Function prototypes for non-inline static functions. */
     14 
     15 static quarantine_t	*quarantine_grow(tsd_t *tsd, quarantine_t *quarantine);
     16 static void	quarantine_drain_one(tsdn_t *tsdn, quarantine_t *quarantine);
     17 static void	quarantine_drain(tsdn_t *tsdn, quarantine_t *quarantine,
     18     size_t upper_bound);
     19 
     20 /******************************************************************************/
     21 
     22 static quarantine_t *
     23 quarantine_init(tsdn_t *tsdn, size_t lg_maxobjs)
     24 {
     25 	quarantine_t *quarantine;
     26 	size_t size;
     27 
     28 	size = offsetof(quarantine_t, objs) + ((ZU(1) << lg_maxobjs) *
     29 	    sizeof(quarantine_obj_t));
     30 	quarantine = (quarantine_t *)iallocztm(tsdn, size, size2index(size),
     31 	    false, NULL, true, arena_get(TSDN_NULL, 0, true), true);
     32 	if (quarantine == NULL)
     33 		return (NULL);
     34 	quarantine->curbytes = 0;
     35 	quarantine->curobjs = 0;
     36 	quarantine->first = 0;
     37 	quarantine->lg_maxobjs = lg_maxobjs;
     38 
     39 	return (quarantine);
     40 }
     41 
     42 void
     43 quarantine_alloc_hook_work(tsd_t *tsd)
     44 {
     45 	quarantine_t *quarantine;
     46 
     47 	if (!tsd_nominal(tsd))
     48 		return;
     49 
     50 	quarantine = quarantine_init(tsd_tsdn(tsd), LG_MAXOBJS_INIT);
     51 	/*
     52 	 * Check again whether quarantine has been initialized, because
     53 	 * quarantine_init() may have triggered recursive initialization.
     54 	 */
     55 	if (tsd_quarantine_get(tsd) == NULL)
     56 		tsd_quarantine_set(tsd, quarantine);
     57 	else
     58 		idalloctm(tsd_tsdn(tsd), quarantine, NULL, true, true);
     59 }
     60 
     61 static quarantine_t *
     62 quarantine_grow(tsd_t *tsd, quarantine_t *quarantine)
     63 {
     64 	quarantine_t *ret;
     65 
     66 	ret = quarantine_init(tsd_tsdn(tsd), quarantine->lg_maxobjs + 1);
     67 	if (ret == NULL) {
     68 		quarantine_drain_one(tsd_tsdn(tsd), quarantine);
     69 		return (quarantine);
     70 	}
     71 
     72 	ret->curbytes = quarantine->curbytes;
     73 	ret->curobjs = quarantine->curobjs;
     74 	if (quarantine->first + quarantine->curobjs <= (ZU(1) <<
     75 	    quarantine->lg_maxobjs)) {
     76 		/* objs ring buffer data are contiguous. */
     77 		memcpy(ret->objs, &quarantine->objs[quarantine->first],
     78 		    quarantine->curobjs * sizeof(quarantine_obj_t));
     79 	} else {
     80 		/* objs ring buffer data wrap around. */
     81 		size_t ncopy_a = (ZU(1) << quarantine->lg_maxobjs) -
     82 		    quarantine->first;
     83 		size_t ncopy_b = quarantine->curobjs - ncopy_a;
     84 
     85 		memcpy(ret->objs, &quarantine->objs[quarantine->first], ncopy_a
     86 		    * sizeof(quarantine_obj_t));
     87 		memcpy(&ret->objs[ncopy_a], quarantine->objs, ncopy_b *
     88 		    sizeof(quarantine_obj_t));
     89 	}
     90 	idalloctm(tsd_tsdn(tsd), quarantine, NULL, true, true);
     91 
     92 	tsd_quarantine_set(tsd, ret);
     93 	return (ret);
     94 }
     95 
     96 static void
     97 quarantine_drain_one(tsdn_t *tsdn, quarantine_t *quarantine)
     98 {
     99 	quarantine_obj_t *obj = &quarantine->objs[quarantine->first];
    100 	assert(obj->usize == isalloc(tsdn, obj->ptr, config_prof));
    101 	idalloctm(tsdn, obj->ptr, NULL, false, true);
    102 	quarantine->curbytes -= obj->usize;
    103 	quarantine->curobjs--;
    104 	quarantine->first = (quarantine->first + 1) & ((ZU(1) <<
    105 	    quarantine->lg_maxobjs) - 1);
    106 }
    107 
    108 static void
    109 quarantine_drain(tsdn_t *tsdn, quarantine_t *quarantine, size_t upper_bound)
    110 {
    111 
    112 	while (quarantine->curbytes > upper_bound && quarantine->curobjs > 0)
    113 		quarantine_drain_one(tsdn, quarantine);
    114 }
    115 
    116 void
    117 quarantine(tsd_t *tsd, void *ptr)
    118 {
    119 	quarantine_t *quarantine;
    120 	size_t usize = isalloc(tsd_tsdn(tsd), ptr, config_prof);
    121 
    122 	cassert(config_fill);
    123 	assert(opt_quarantine);
    124 
    125 	if ((quarantine = tsd_quarantine_get(tsd)) == NULL) {
    126 		idalloctm(tsd_tsdn(tsd), ptr, NULL, false, true);
    127 		return;
    128 	}
    129 	/*
    130 	 * Drain one or more objects if the quarantine size limit would be
    131 	 * exceeded by appending ptr.
    132 	 */
    133 	if (quarantine->curbytes + usize > opt_quarantine) {
    134 		size_t upper_bound = (opt_quarantine >= usize) ? opt_quarantine
    135 		    - usize : 0;
    136 		quarantine_drain(tsd_tsdn(tsd), quarantine, upper_bound);
    137 	}
    138 	/* Grow the quarantine ring buffer if it's full. */
    139 	if (quarantine->curobjs == (ZU(1) << quarantine->lg_maxobjs))
    140 		quarantine = quarantine_grow(tsd, quarantine);
    141 	/* quarantine_grow() must free a slot if it fails to grow. */
    142 	assert(quarantine->curobjs < (ZU(1) << quarantine->lg_maxobjs));
    143 	/* Append ptr if its size doesn't exceed the quarantine size. */
    144 	if (quarantine->curbytes + usize <= opt_quarantine) {
    145 		size_t offset = (quarantine->first + quarantine->curobjs) &
    146 		    ((ZU(1) << quarantine->lg_maxobjs) - 1);
    147 		quarantine_obj_t *obj = &quarantine->objs[offset];
    148 		obj->ptr = ptr;
    149 		obj->usize = usize;
    150 		quarantine->curbytes += usize;
    151 		quarantine->curobjs++;
    152 		if (config_fill && unlikely(opt_junk_free)) {
    153 			/*
    154 			 * Only do redzone validation if Valgrind isn't in
    155 			 * operation.
    156 			 */
    157 			if ((!config_valgrind || likely(!in_valgrind))
    158 			    && usize <= SMALL_MAXCLASS)
    159 				arena_quarantine_junk_small(ptr, usize);
    160 			else
    161 				memset(ptr, JEMALLOC_FREE_JUNK, usize);
    162 		}
    163 	} else {
    164 		assert(quarantine->curbytes == 0);
    165 		idalloctm(tsd_tsdn(tsd), ptr, NULL, false, true);
    166 	}
    167 }
    168 
    169 void
    170 quarantine_cleanup(tsd_t *tsd)
    171 {
    172 	quarantine_t *quarantine;
    173 
    174 	if (!config_fill)
    175 		return;
    176 
    177 	quarantine = tsd_quarantine_get(tsd);
    178 	if (quarantine != NULL) {
    179 		quarantine_drain(tsd_tsdn(tsd), quarantine, 0);
    180 		idalloctm(tsd_tsdn(tsd), quarantine, NULL, true, true);
    181 		tsd_quarantine_set(tsd, NULL);
    182 	}
    183 }
    184