Home | History | Annotate | Line # | Download | only in internal
      1  1.1  christos #ifndef JEMALLOC_INTERNAL_HPDATA_H
      2  1.1  christos #define JEMALLOC_INTERNAL_HPDATA_H
      3  1.1  christos 
      4  1.1  christos #include "jemalloc/internal/fb.h"
      5  1.1  christos #include "jemalloc/internal/ph.h"
      6  1.1  christos #include "jemalloc/internal/ql.h"
      7  1.1  christos #include "jemalloc/internal/typed_list.h"
      8  1.1  christos 
      9  1.1  christos /*
     10  1.1  christos  * The metadata representation we use for extents in hugepages.  While the PAC
     11  1.1  christos  * uses the edata_t to represent both active and inactive extents, the HP only
     12  1.1  christos  * uses the edata_t for active ones; instead, inactive extent state is tracked
     13  1.1  christos  * within hpdata associated with the enclosing hugepage-sized, hugepage-aligned
     14  1.1  christos  * region of virtual address space.
     15  1.1  christos  *
     16  1.1  christos  * An hpdata need not be "truly" backed by a hugepage (which is not necessarily
     17  1.1  christos  * an observable property of any given region of address space).  It's just
     18  1.1  christos  * hugepage-sized and hugepage-aligned; it's *potentially* huge.
     19  1.1  christos  */
     20  1.1  christos typedef struct hpdata_s hpdata_t;
     21  1.1  christos ph_structs(hpdata_age_heap, hpdata_t);
     22  1.1  christos struct hpdata_s {
     23  1.1  christos 	/*
     24  1.1  christos 	 * We likewise follow the edata convention of mangling names and forcing
     25  1.1  christos 	 * the use of accessors -- this lets us add some consistency checks on
     26  1.1  christos 	 * access.
     27  1.1  christos 	 */
     28  1.1  christos 
     29  1.1  christos 	/*
     30  1.1  christos 	 * The address of the hugepage in question.  This can't be named h_addr,
     31  1.1  christos 	 * since that conflicts with a macro defined in Windows headers.
     32  1.1  christos 	 */
     33  1.1  christos 	void *h_address;
     34  1.1  christos 	/* Its age (measured in psset operations). */
     35  1.1  christos 	uint64_t h_age;
     36  1.1  christos 	/* Whether or not we think the hugepage is mapped that way by the OS. */
     37  1.1  christos 	bool h_huge;
     38  1.1  christos 
     39  1.1  christos 	/*
     40  1.1  christos 	 * For some properties, we keep parallel sets of bools; h_foo_allowed
     41  1.1  christos 	 * and h_in_psset_foo_container.  This is a decoupling mechanism to
     42  1.1  christos 	 * avoid bothering the hpa (which manages policies) from the psset
     43  1.1  christos 	 * (which is the mechanism used to enforce those policies).  This allows
     44  1.1  christos 	 * all the container management logic to live in one place, without the
     45  1.1  christos 	 * HPA needing to know or care how that happens.
     46  1.1  christos 	 */
     47  1.1  christos 
     48  1.1  christos 	/*
     49  1.1  christos 	 * Whether or not the hpdata is allowed to be used to serve allocations,
     50  1.1  christos 	 * and whether or not the psset is currently tracking it as such.
     51  1.1  christos 	 */
     52  1.1  christos 	bool h_alloc_allowed;
     53  1.1  christos 	bool h_in_psset_alloc_container;
     54  1.1  christos 
     55  1.1  christos 	/*
     56  1.1  christos 	 * The same, but with purging.  There's no corresponding
     57  1.1  christos 	 * h_in_psset_purge_container, because the psset (currently) always
     58  1.1  christos 	 * removes hpdatas from their containers during updates (to implement
     59  1.1  christos 	 * LRU for purging).
     60  1.1  christos 	 */
     61  1.1  christos 	bool h_purge_allowed;
     62  1.1  christos 
     63  1.1  christos 	/* And with hugifying. */
     64  1.1  christos 	bool h_hugify_allowed;
     65  1.1  christos 	/* When we became a hugification candidate. */
     66  1.1  christos 	nstime_t h_time_hugify_allowed;
     67  1.1  christos 	bool h_in_psset_hugify_container;
     68  1.1  christos 
     69  1.1  christos 	/* Whether or not a purge or hugify is currently happening. */
     70  1.1  christos 	bool h_mid_purge;
     71  1.1  christos 	bool h_mid_hugify;
     72  1.1  christos 
     73  1.1  christos 	/*
     74  1.1  christos 	 * Whether or not the hpdata is being updated in the psset (i.e. if
     75  1.1  christos 	 * there has been a psset_update_begin call issued without a matching
     76  1.1  christos 	 * psset_update_end call).  Eventually this will expand to other types
     77  1.1  christos 	 * of updates.
     78  1.1  christos 	 */
     79  1.1  christos 	bool h_updating;
     80  1.1  christos 
     81  1.1  christos 	/* Whether or not the hpdata is in a psset. */
     82  1.1  christos 	bool h_in_psset;
     83  1.1  christos 
     84  1.1  christos 	union {
     85  1.1  christos 		/* When nonempty (and also nonfull), used by the psset bins. */
     86  1.1  christos 		hpdata_age_heap_link_t age_link;
     87  1.1  christos 		/*
     88  1.1  christos 		 * When empty (or not corresponding to any hugepage), list
     89  1.1  christos 		 * linkage.
     90  1.1  christos 		 */
     91  1.1  christos 		ql_elm(hpdata_t) ql_link_empty;
     92  1.1  christos 	};
     93  1.1  christos 
     94  1.1  christos 	/*
     95  1.1  christos 	 * Linkage for the psset to track candidates for purging and hugifying.
     96  1.1  christos 	 */
     97  1.1  christos 	ql_elm(hpdata_t) ql_link_purge;
     98  1.1  christos 	ql_elm(hpdata_t) ql_link_hugify;
     99  1.1  christos 
    100  1.1  christos 	/* The length of the largest contiguous sequence of inactive pages. */
    101  1.1  christos 	size_t h_longest_free_range;
    102  1.1  christos 
    103  1.1  christos 	/* Number of active pages. */
    104  1.1  christos 	size_t h_nactive;
    105  1.1  christos 
    106  1.1  christos 	/* A bitmap with bits set in the active pages. */
    107  1.1  christos 	fb_group_t active_pages[FB_NGROUPS(HUGEPAGE_PAGES)];
    108  1.1  christos 
    109  1.1  christos 	/*
    110  1.1  christos 	 * Number of dirty or active pages, and a bitmap tracking them.  One
    111  1.1  christos 	 * way to think of this is as which pages are dirty from the OS's
    112  1.1  christos 	 * perspective.
    113  1.1  christos 	 */
    114  1.1  christos 	size_t h_ntouched;
    115  1.1  christos 
    116  1.1  christos 	/* The touched pages (using the same definition as above). */
    117  1.1  christos 	fb_group_t touched_pages[FB_NGROUPS(HUGEPAGE_PAGES)];
    118  1.1  christos };
    119  1.1  christos 
    120  1.1  christos TYPED_LIST(hpdata_empty_list, hpdata_t, ql_link_empty)
    121  1.1  christos TYPED_LIST(hpdata_purge_list, hpdata_t, ql_link_purge)
    122  1.1  christos TYPED_LIST(hpdata_hugify_list, hpdata_t, ql_link_hugify)
    123  1.1  christos 
    124  1.1  christos ph_proto(, hpdata_age_heap, hpdata_t);
    125  1.1  christos 
    126  1.1  christos static inline void *
    127  1.1  christos hpdata_addr_get(const hpdata_t *hpdata) {
    128  1.1  christos 	return hpdata->h_address;
    129  1.1  christos }
    130  1.1  christos 
    131  1.1  christos static inline void
    132  1.1  christos hpdata_addr_set(hpdata_t *hpdata, void *addr) {
    133  1.1  christos 	assert(HUGEPAGE_ADDR2BASE(addr) == addr);
    134  1.1  christos 	hpdata->h_address = addr;
    135  1.1  christos }
    136  1.1  christos 
    137  1.1  christos static inline uint64_t
    138  1.1  christos hpdata_age_get(const hpdata_t *hpdata) {
    139  1.1  christos 	return hpdata->h_age;
    140  1.1  christos }
    141  1.1  christos 
    142  1.1  christos static inline void
    143  1.1  christos hpdata_age_set(hpdata_t *hpdata, uint64_t age) {
    144  1.1  christos 	hpdata->h_age = age;
    145  1.1  christos }
    146  1.1  christos 
    147  1.1  christos static inline bool
    148  1.1  christos hpdata_huge_get(const hpdata_t *hpdata) {
    149  1.1  christos 	return hpdata->h_huge;
    150  1.1  christos }
    151  1.1  christos 
    152  1.1  christos static inline bool
    153  1.1  christos hpdata_alloc_allowed_get(const hpdata_t *hpdata) {
    154  1.1  christos 	return hpdata->h_alloc_allowed;
    155  1.1  christos }
    156  1.1  christos 
    157  1.1  christos static inline void
    158  1.1  christos hpdata_alloc_allowed_set(hpdata_t *hpdata, bool alloc_allowed) {
    159  1.1  christos 	hpdata->h_alloc_allowed = alloc_allowed;
    160  1.1  christos }
    161  1.1  christos 
    162  1.1  christos static inline bool
    163  1.1  christos hpdata_in_psset_alloc_container_get(const hpdata_t *hpdata) {
    164  1.1  christos 	return hpdata->h_in_psset_alloc_container;
    165  1.1  christos }
    166  1.1  christos 
    167  1.1  christos static inline void
    168  1.1  christos hpdata_in_psset_alloc_container_set(hpdata_t *hpdata, bool in_container) {
    169  1.1  christos 	assert(in_container != hpdata->h_in_psset_alloc_container);
    170  1.1  christos 	hpdata->h_in_psset_alloc_container = in_container;
    171  1.1  christos }
    172  1.1  christos 
    173  1.1  christos static inline bool
    174  1.1  christos hpdata_purge_allowed_get(const hpdata_t *hpdata) {
    175  1.1  christos 	return hpdata->h_purge_allowed;
    176  1.1  christos }
    177  1.1  christos 
    178  1.1  christos static inline void
    179  1.1  christos hpdata_purge_allowed_set(hpdata_t *hpdata, bool purge_allowed) {
    180  1.1  christos        assert(purge_allowed == false || !hpdata->h_mid_purge);
    181  1.1  christos        hpdata->h_purge_allowed = purge_allowed;
    182  1.1  christos }
    183  1.1  christos 
    184  1.1  christos static inline bool
    185  1.1  christos hpdata_hugify_allowed_get(const hpdata_t *hpdata) {
    186  1.1  christos 	return hpdata->h_hugify_allowed;
    187  1.1  christos }
    188  1.1  christos 
    189  1.1  christos static inline void
    190  1.1  christos hpdata_allow_hugify(hpdata_t *hpdata, nstime_t now) {
    191  1.1  christos 	assert(!hpdata->h_mid_hugify);
    192  1.1  christos 	hpdata->h_hugify_allowed = true;
    193  1.1  christos 	hpdata->h_time_hugify_allowed = now;
    194  1.1  christos }
    195  1.1  christos 
    196  1.1  christos static inline nstime_t
    197  1.1  christos hpdata_time_hugify_allowed(hpdata_t *hpdata) {
    198  1.1  christos 	return hpdata->h_time_hugify_allowed;
    199  1.1  christos }
    200  1.1  christos 
    201  1.1  christos static inline void
    202  1.1  christos hpdata_disallow_hugify(hpdata_t *hpdata) {
    203  1.1  christos 	hpdata->h_hugify_allowed = false;
    204  1.1  christos }
    205  1.1  christos 
    206  1.1  christos static inline bool
    207  1.1  christos hpdata_in_psset_hugify_container_get(const hpdata_t *hpdata) {
    208  1.1  christos 	return hpdata->h_in_psset_hugify_container;
    209  1.1  christos }
    210  1.1  christos 
    211  1.1  christos static inline void
    212  1.1  christos hpdata_in_psset_hugify_container_set(hpdata_t *hpdata, bool in_container) {
    213  1.1  christos 	assert(in_container != hpdata->h_in_psset_hugify_container);
    214  1.1  christos 	hpdata->h_in_psset_hugify_container = in_container;
    215  1.1  christos }
    216  1.1  christos 
    217  1.1  christos static inline bool
    218  1.1  christos hpdata_mid_purge_get(const hpdata_t *hpdata) {
    219  1.1  christos 	return hpdata->h_mid_purge;
    220  1.1  christos }
    221  1.1  christos 
    222  1.1  christos static inline void
    223  1.1  christos hpdata_mid_purge_set(hpdata_t *hpdata, bool mid_purge) {
    224  1.1  christos 	assert(mid_purge != hpdata->h_mid_purge);
    225  1.1  christos 	hpdata->h_mid_purge = mid_purge;
    226  1.1  christos }
    227  1.1  christos 
    228  1.1  christos static inline bool
    229  1.1  christos hpdata_mid_hugify_get(const hpdata_t *hpdata) {
    230  1.1  christos 	return hpdata->h_mid_hugify;
    231  1.1  christos }
    232  1.1  christos 
    233  1.1  christos static inline void
    234  1.1  christos hpdata_mid_hugify_set(hpdata_t *hpdata, bool mid_hugify) {
    235  1.1  christos 	assert(mid_hugify != hpdata->h_mid_hugify);
    236  1.1  christos 	hpdata->h_mid_hugify = mid_hugify;
    237  1.1  christos }
    238  1.1  christos 
    239  1.1  christos static inline bool
    240  1.1  christos hpdata_changing_state_get(const hpdata_t *hpdata) {
    241  1.1  christos 	return hpdata->h_mid_purge || hpdata->h_mid_hugify;
    242  1.1  christos }
    243  1.1  christos 
    244  1.1  christos 
    245  1.1  christos static inline bool
    246  1.1  christos hpdata_updating_get(const hpdata_t *hpdata) {
    247  1.1  christos 	return hpdata->h_updating;
    248  1.1  christos }
    249  1.1  christos 
    250  1.1  christos static inline void
    251  1.1  christos hpdata_updating_set(hpdata_t *hpdata, bool updating) {
    252  1.1  christos 	assert(updating != hpdata->h_updating);
    253  1.1  christos 	hpdata->h_updating = updating;
    254  1.1  christos }
    255  1.1  christos 
    256  1.1  christos static inline bool
    257  1.1  christos hpdata_in_psset_get(const hpdata_t *hpdata) {
    258  1.1  christos 	return hpdata->h_in_psset;
    259  1.1  christos }
    260  1.1  christos 
    261  1.1  christos static inline void
    262  1.1  christos hpdata_in_psset_set(hpdata_t *hpdata, bool in_psset) {
    263  1.1  christos 	assert(in_psset != hpdata->h_in_psset);
    264  1.1  christos 	hpdata->h_in_psset = in_psset;
    265  1.1  christos }
    266  1.1  christos 
    267  1.1  christos static inline size_t
    268  1.1  christos hpdata_longest_free_range_get(const hpdata_t *hpdata) {
    269  1.1  christos 	return hpdata->h_longest_free_range;
    270  1.1  christos }
    271  1.1  christos 
    272  1.1  christos static inline void
    273  1.1  christos hpdata_longest_free_range_set(hpdata_t *hpdata, size_t longest_free_range) {
    274  1.1  christos 	assert(longest_free_range <= HUGEPAGE_PAGES);
    275  1.1  christos 	hpdata->h_longest_free_range = longest_free_range;
    276  1.1  christos }
    277  1.1  christos 
    278  1.1  christos static inline size_t
    279  1.1  christos hpdata_nactive_get(hpdata_t *hpdata) {
    280  1.1  christos 	return hpdata->h_nactive;
    281  1.1  christos }
    282  1.1  christos 
    283  1.1  christos static inline size_t
    284  1.1  christos hpdata_ntouched_get(hpdata_t *hpdata) {
    285  1.1  christos 	return hpdata->h_ntouched;
    286  1.1  christos }
    287  1.1  christos 
    288  1.1  christos static inline size_t
    289  1.1  christos hpdata_ndirty_get(hpdata_t *hpdata) {
    290  1.1  christos 	return hpdata->h_ntouched - hpdata->h_nactive;
    291  1.1  christos }
    292  1.1  christos 
    293  1.1  christos static inline size_t
    294  1.1  christos hpdata_nretained_get(hpdata_t *hpdata) {
    295  1.1  christos 	return HUGEPAGE_PAGES - hpdata->h_ntouched;
    296  1.1  christos }
    297  1.1  christos 
    298  1.1  christos static inline void
    299  1.1  christos hpdata_assert_empty(hpdata_t *hpdata) {
    300  1.1  christos 	assert(fb_empty(hpdata->active_pages, HUGEPAGE_PAGES));
    301  1.1  christos 	assert(hpdata->h_nactive == 0);
    302  1.1  christos }
    303  1.1  christos 
    304  1.1  christos /*
    305  1.1  christos  * Only used in tests, and in hpdata_assert_consistent, below.  Verifies some
    306  1.1  christos  * consistency properties of the hpdata (e.g. that cached counts of page stats
    307  1.1  christos  * match computed ones).
    308  1.1  christos  */
    309  1.1  christos static inline bool
    310  1.1  christos hpdata_consistent(hpdata_t *hpdata) {
    311  1.1  christos 	if(fb_urange_longest(hpdata->active_pages, HUGEPAGE_PAGES)
    312  1.1  christos 	    != hpdata_longest_free_range_get(hpdata)) {
    313  1.1  christos 		return false;
    314  1.1  christos 	}
    315  1.1  christos 	if (fb_scount(hpdata->active_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES)
    316  1.1  christos 	    != hpdata->h_nactive) {
    317  1.1  christos 		return false;
    318  1.1  christos 	}
    319  1.1  christos 	if (fb_scount(hpdata->touched_pages, HUGEPAGE_PAGES, 0, HUGEPAGE_PAGES)
    320  1.1  christos 	    != hpdata->h_ntouched) {
    321  1.1  christos 		return false;
    322  1.1  christos 	}
    323  1.1  christos 	if (hpdata->h_ntouched < hpdata->h_nactive) {
    324  1.1  christos 		return false;
    325  1.1  christos 	}
    326  1.1  christos 	if (hpdata->h_huge && hpdata->h_ntouched != HUGEPAGE_PAGES) {
    327  1.1  christos 		return false;
    328  1.1  christos 	}
    329  1.1  christos 	if (hpdata_changing_state_get(hpdata)
    330  1.1  christos 	    && ((hpdata->h_purge_allowed) || hpdata->h_hugify_allowed)) {
    331  1.1  christos 		return false;
    332  1.1  christos 	}
    333  1.1  christos 	if (hpdata_hugify_allowed_get(hpdata)
    334  1.1  christos 	    != hpdata_in_psset_hugify_container_get(hpdata)) {
    335  1.1  christos 		return false;
    336  1.1  christos 	}
    337  1.1  christos 	return true;
    338  1.1  christos }
    339  1.1  christos 
    340  1.1  christos static inline void
    341  1.1  christos hpdata_assert_consistent(hpdata_t *hpdata) {
    342  1.1  christos 	assert(hpdata_consistent(hpdata));
    343  1.1  christos }
    344  1.1  christos 
    345  1.1  christos static inline bool
    346  1.1  christos hpdata_empty(hpdata_t *hpdata) {
    347  1.1  christos 	return hpdata->h_nactive == 0;
    348  1.1  christos }
    349  1.1  christos 
    350  1.1  christos static inline bool
    351  1.1  christos hpdata_full(hpdata_t *hpdata) {
    352  1.1  christos 	return hpdata->h_nactive == HUGEPAGE_PAGES;
    353  1.1  christos }
    354  1.1  christos 
    355  1.1  christos void hpdata_init(hpdata_t *hpdata, void *addr, uint64_t age);
    356  1.1  christos 
    357  1.1  christos /*
    358  1.1  christos  * Given an hpdata which can serve an allocation request, pick and reserve an
    359  1.1  christos  * offset within that allocation.
    360  1.1  christos  */
    361  1.1  christos void *hpdata_reserve_alloc(hpdata_t *hpdata, size_t sz);
    362  1.1  christos void hpdata_unreserve(hpdata_t *hpdata, void *begin, size_t sz);
    363  1.1  christos 
    364  1.1  christos /*
    365  1.1  christos  * The hpdata_purge_prepare_t allows grabbing the metadata required to purge
    366  1.1  christos  * subranges of a hugepage while holding a lock, drop the lock during the actual
    367  1.1  christos  * purging of them, and reacquire it to update the metadata again.
    368  1.1  christos  */
    369  1.1  christos typedef struct hpdata_purge_state_s hpdata_purge_state_t;
    370  1.1  christos struct hpdata_purge_state_s {
    371  1.1  christos 	size_t npurged;
    372  1.1  christos 	size_t ndirty_to_purge;
    373  1.1  christos 	fb_group_t to_purge[FB_NGROUPS(HUGEPAGE_PAGES)];
    374  1.1  christos 	size_t next_purge_search_begin;
    375  1.1  christos };
    376  1.1  christos 
    377  1.1  christos /*
    378  1.1  christos  * Initializes purge state.  The access to hpdata must be externally
    379  1.1  christos  * synchronized with other hpdata_* calls.
    380  1.1  christos  *
    381  1.1  christos  * You can tell whether or not a thread is purging or hugifying a given hpdata
    382  1.1  christos  * via hpdata_changing_state_get(hpdata).  Racing hugification or purging
    383  1.1  christos  * operations aren't allowed.
    384  1.1  christos  *
    385  1.1  christos  * Once you begin purging, you have to follow through and call hpdata_purge_next
    386  1.1  christos  * until you're done, and then end.  Allocating out of an hpdata undergoing
    387  1.1  christos  * purging is not allowed.
    388  1.1  christos  *
    389  1.1  christos  * Returns the number of dirty pages that will be purged.
    390  1.1  christos  */
    391  1.1  christos size_t hpdata_purge_begin(hpdata_t *hpdata, hpdata_purge_state_t *purge_state);
    392  1.1  christos 
    393  1.1  christos /*
    394  1.1  christos  * If there are more extents to purge, sets *r_purge_addr and *r_purge_size to
    395  1.1  christos  * true, and returns true.  Otherwise, returns false to indicate that we're
    396  1.1  christos  * done.
    397  1.1  christos  *
    398  1.1  christos  * This requires exclusive access to the purge state, but *not* to the hpdata.
    399  1.1  christos  * In particular, unreserve calls are allowed while purging (i.e. you can dalloc
    400  1.1  christos  * into one part of the hpdata while purging a different part).
    401  1.1  christos  */
    402  1.1  christos bool hpdata_purge_next(hpdata_t *hpdata, hpdata_purge_state_t *purge_state,
    403  1.1  christos     void **r_purge_addr, size_t *r_purge_size);
    404  1.1  christos /*
    405  1.1  christos  * Updates the hpdata metadata after all purging is done.  Needs external
    406  1.1  christos  * synchronization.
    407  1.1  christos  */
    408  1.1  christos void hpdata_purge_end(hpdata_t *hpdata, hpdata_purge_state_t *purge_state);
    409  1.1  christos 
    410  1.1  christos void hpdata_hugify(hpdata_t *hpdata);
    411  1.1  christos void hpdata_dehugify(hpdata_t *hpdata);
    412  1.1  christos 
    413  1.1  christos #endif /* JEMALLOC_INTERNAL_HPDATA_H */
    414