summaryrefslogtreecommitdiffstats
path: root/mgmt.c
blob: 74d3da01c6631e14f810e6e6a77d63e5f446a52e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/* ----------------------------------------------------------------------- *
 *   
 *   Copyright 2000-2008 H. Peter Anvin - All Rights Reserved
 *
 *   This program is free software; you can redistribute it and/or modify
 *   it under the terms of the GNU Lesser General Public License as
 *   published by the Free Software Foundation, Inc.,
 *   59 Temple Place Ste 330, Boston MA 02111-1307, USA, version 2.1,
 *   incorporated herein by reference.
 *
 * ----------------------------------------------------------------------- */

/*
 * mgmt.c
 *
 * Provide persistent storage management (basic infrastructure.)
 *
 * This code uses a modified buddy system allocator for large allocations
 * and a SLAB allocator for small allocations.
 *
 * This generally assumes 8-bit bytes and 2's-complement arithmetric,
 * and that an all-zero bit pattern is a valid NULL.  These days, that's
 * oh-such-a-strech...
 */

#include <assert.h>
#include <stdlib.h>
#include <inttypes.h>
#include <limits.h>
#include <string.h>
#include <errno.h>

#include "lpsm.h"
#include "internals.h"
#include "bitops.h"

struct arena_header *lpsm_arena_header;

static const unsigned char lpsm_arena_magic[16] =
"\x4f\xd4\x5a\x16\xa0\xaf\xd5\x52\x56\xf1\x1c\x9c\x1c\xed\xcc";
static const unsigned char lpsm_arena_zero[16] =
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";

/* Change these if BUDDY_ORDER_MIN or MINIMUM_ALIGN is changed */
/* These MUST be ordered in order of decreasing size.  0 is end of list. */
/* This particular set of slab sizes should work well for both a
   16- and 24-byte header size. */
static int slabsizes[] =
{
  2032, 1344, 1016, 800, 576, 448, 256, 192, 128, 96, 64, 48, 32, 16, 8, 4, 2, 1, 0
};

/*
 * Initalize the object store arena allocator.  This returns NULL
 * on failure, or a pointer to the "root data pointer" array if
 * successful.
 */
void **lpsm_init(const char *main_file, const char *log_file)
{
  size_t arena_len = sizeof(struct arena_header); /* Minimum initial size */

  if ( !(AH = lpsm_arena_init(main_file, log_file, &arena_len, NULL)) )
    return NULL;		/* Failed to initialize arena */

  if ( memcmp(AH->arena_magic, lpsm_arena_magic, 16) != 0 ) {
    if ( memcmp(AH->arena_magic, lpsm_arena_zero, 16) == 0 ) {
      size_t total_size, bitmap_size;
      int i, header_pad;

      memset(AH, 0, sizeof(*AH)); /* Just in case */

      /* Uninitialized arena */
      memcpy(AH->arena_magic, lpsm_arena_magic, 16);
      AH->arena_byteorder = 0x12345678;
      AH->arena_bytesize  = CHAR_BIT;
      AH->arena_ptrsize   = sizeof(void *);
      AH->arena_base      = (void *)AH;
      AH->buddy_order_min = BUDDY_ORDER_MIN;

      /* Header size must be padded to a multiple of BUDDY_SIZE_MIN
	 as well as the hardware page size; this better be a power of 2! */
      header_pad = (PM->pagesize > BUDDY_SIZE_MIN)
	? PM->pagesize : BUDDY_SIZE_MIN;
      assert((header_pad & (header_pad-1)) == 0);
      AH->header_size = (sizeof(struct arena_header) + header_pad - 1)
	& ~(header_pad-1);

      AH->data_start      = (char *)AH + AH->header_size;
      AH->arena_size_lg2  = ARENA_START_SIZE_LG2;
      bitmap_size =  1UL << (ARENA_START_SIZE_LG2-BUDDY_ORDER_MIN-1);
      total_size = AH->header_size + ARENA_START_SIZE + (bitmap_size << 1);
      AH->free_bitmap  = (char *)AH->data_start + ARENA_START_SIZE;
      AH->alloc_bitmap = (char *)AH->free_bitmap + bitmap_size;

      if ( lpsm_extend(total_size) < 0 )
	return NULL;		/* Failed to establish initial arena */

      /* Compute SLAB definitions */
      for ( i = 0 ; slabsizes[i] ; i++ ) {
	int header_size = sizeof(struct slab_header);
	int bytes = BUDDY_SIZE_MIN - header_size;
	int size = slabsizes[i];
	int count = bytes/size;
	int data_offset;

	while ( 1 ) {
	  /* Compute header+bitmap size in bytes; rounded up to MINIMUM_ALIGN */
	  data_offset = header_size + ((count+7) >> 3);
	  data_offset = ((data_offset+MINIMUM_ALIGN-1) & ~MINIMUM_ALIGN_MASK);
	  if ( data_offset+count*size <= BUDDY_SIZE_MIN )
	    break;

	  count--;
	}
	AH->slab[i].size = size;
	AH->slab[i].count = count;
	AH->slab[i].data_offset = data_offset;
      }

      /* The bitmasks contains zeroes already; set the free bit on the topmost order. */
      set_bit(AH->free_bitmap, 1);
    }
  }

  if ( memcmp(AH->arena_magic, lpsm_arena_magic, 16) != 0 ||
       AH->arena_byteorder != 0x12345678 ||
       AH->arena_bytesize  != CHAR_BIT   ||
       AH->arena_ptrsize   != sizeof(void *) ||
       AH->buddy_order_min != BUDDY_ORDER_MIN ) {
    errno = EINVAL;
    return NULL;		/* Incompatible or corrupt arena */
  }

  if ( AH->arena_base != (void *)AH ) {
    void *arena_base = AH->arena_base;
    /* Arena mapped in the wrong place */
    lpsm_shutdown();		/* Nothing written... */
    if ( !(AH = lpsm_arena_init(main_file, log_file, &arena_len, arena_base)) )
      return NULL;		/* Failed to initialize arena */
    if ( AH->arena_base != (void *)AH ) {
      lpsm_shutdown();
      errno = EINVAL;
      return NULL;		/* Something went weird... */
    }
  }

  return AH->root_data_ptrs;
}