cmogstored.git  about / heads / tags
alternative mogstored implementation for MogileFS
blob 105a1c007251e15adac9dc2c92e288561912d875 5109 bytes (raw)
$ git show HEAD:alloc.c	# shows this blob on the CLI

  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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
 
/*
 * Copyright (C) 2012-2020 all contributors <cmogstored-public@yhbt.net>
 * License: GPL-3.0+ <https://www.gnu.org/licenses/gpl-3.0.txt>
 *
 * We use thread-local buffers as much as possible.  mog_rbuf may
 * be detached from the thread-local pointer (and grown) if we have
 * requests trickled to us or large requests.  This is unlikely with
 * MogileFS (which only deals with internal LAN traffic), and unlikely
 * even with normal, untrusted HTTP traffic.
 */
#include "cmogstored.h"
#define L1_CACHE_LINE_MAX 128 /* largest I've seen (Pentium 4) */
static size_t l1_cache_line_size = L1_CACHE_LINE_MAX;

static __thread struct mog_rbuf *tls_rbuf; /* for small reads (headers) */
static __thread unsigned char tls_fsbuf[8192]; /* for filesystem I/O */

#define MOG_MASK(align)        (~((size_t)align - 1))
#define MOG_ALIGN(align,val)   (((val) + (size_t)align - 1) & MOG_MASK(align))

static void l1_cache_line_size_detect(void)
{
#ifdef _SC_LEVEL1_DCACHE_LINESIZE
	long tmp = sysconf(_SC_LEVEL1_DCACHE_LINESIZE);

	if (tmp > 0 && tmp <= L1_CACHE_LINE_MAX)
		l1_cache_line_size = (size_t)tmp;
#endif /* _SC_LEVEL1_DCACHE_LINESIZE */
}

void mog_alloc_quit(void)
{
	struct mog_rbuf *rbuf = tls_rbuf;

	tls_rbuf = NULL;

	mog_rbuf_free(rbuf);
}

__attribute__((constructor)) static void alloc_init(void)
{
	l1_cache_line_size_detect();
	atexit(mog_alloc_quit);
}

void mog_free_and_null(void *ptrptr)
{
	void **tmp = ptrptr;

	free(*tmp);
	*tmp = NULL;
}

_Noreturn void mog_oom(void)
{
	write(STDERR_FILENO, "OOM\n", 4);
	syslog(LOG_CRIT, "Out of memory, aborting");
	abort();
}

void mog_oom_if_null(const void *ptr)
{
	if (!ptr)
		mog_oom();
}

/*
 * Cache alignment is important for sub-pagesized allocations
 * that can be bounced between threads.  We round up the
 * allocation to the cache size
 */
void *mog_cachealign(size_t size)
{
	void *ptr;
	int err = posix_memalign(&ptr, l1_cache_line_size, size);

	switch (err) {
	case 0: return ptr;
	case ENOMEM: mog_oom();
	}

	errno = err; /* most likely EINVAL */
	die_errno("posix_memalign failed");
}


/* allocates a new mog_rbuf of +size+ bytes */
struct mog_rbuf *mog_rbuf_new(size_t size)
{
	struct mog_rbuf *rbuf;
	size_t bytes = size + sizeof(struct mog_rbuf);

	assert(size > 0 && "tried to allocate a zero-byte mog_rbuf");

	rbuf = mog_cachealign(bytes);
	rbuf->rcapa = size;
	/*
	 * do not initialize rsize here, we only need rsize when we detach
	 * a TLS rbuf and associate it with a mog_fd, not in the common
	 * case where the rbuf remains thread-local
	 */

	return rbuf;
}

MOG_NOINLINE static struct mog_rbuf *
rbuf_replace(struct mog_rbuf *rbuf, size_t size)
{
	free(rbuf); /* free(NULL) works on modern systems */
	rbuf = mog_rbuf_new(size);
	tls_rbuf = rbuf;

	return rbuf;
}

/*
 * retrieves the per-thread rbuf belonging to the current thread,
 * ensuring it is at least capable of storing the specified size
 */
struct mog_rbuf *mog_rbuf_get(size_t size)
{
	struct mog_rbuf *rbuf = tls_rbuf;

	if (rbuf && rbuf->rcapa >= size) return rbuf;

	return rbuf_replace(rbuf, size);
}

/* ensures a given rbuf is no longer associated with the current thread */
struct mog_rbuf *mog_rbuf_detach(struct mog_rbuf *rbuf)
{
	struct mog_rbuf *cur = tls_rbuf;

	if (cur == rbuf)
		tls_rbuf = NULL;

	return rbuf;
}

/*
 * Behaves similarly to realloc(), but uses posix_memalign()
 * Returns a detached rbuf with the contents of +cur+
 * (which may be cur itself)
 * Releases memory and returns NULL if rbuf is too big.
 */
struct mog_rbuf *mog_rbuf_grow(struct mog_rbuf *cur)
{
	struct mog_rbuf *ret;
	size_t new_size = cur->rsize + 500; /* grow by 500 bytes or so */

	if (cur->rsize == MOG_RBUF_MAX_SIZE) {
		assert(cur != tls_rbuf && "TLS rbuf is HUGE");
		free(cur);
		return NULL;
	}
	assert(cur->rsize < MOG_RBUF_MAX_SIZE && "rbuf rsize got too big");

	if (new_size > MOG_RBUF_MAX_SIZE)
		new_size = MOG_RBUF_MAX_SIZE;
	if (cur->rcapa < new_size) {
		ret = mog_rbuf_new(new_size);
		memcpy(ret->rptr, cur->rptr, cur->rsize);
		if (cur != tls_rbuf)
			mog_rbuf_free(cur);
	} else {
		/* this may not even happen, just in case: */
		ret = mog_rbuf_detach(cur);
	}

	return ret;
}

void mog_rbuf_free(struct mog_rbuf *rbuf)
{
	assert(((rbuf == NULL) ||
	       (tls_rbuf != rbuf)) &&
	       "trying to free undetached rbuf");
	free(rbuf);
}

/* retrieves the per-thread fsbuf and sets size to the value of fsbuf_size */
void *mog_fsbuf_get(size_t *size)
{
	void *ptr = tls_fsbuf;

	*size = sizeof(tls_fsbuf);

	return ptr;
}

/*
 * attempts to reattach an rbuf belonging to a previously-idle client
 * if it makes sense to reattach.
 *
 * We want to favor rbufs attached to clients if they are bigger than
 * the thread-local one.
 */
void mog_rbuf_reattach_and_null(struct mog_rbuf **ptrptr)
{
	struct mog_rbuf *rbuf = *ptrptr;

	if (!rbuf)
		return;
	*ptrptr = NULL;

	assert(rbuf != tls_rbuf && "cannot reattach, already attached");
	if (tls_rbuf) {
		/* we never want to swap a small buffer for a big buffer */
		if (rbuf->rcapa < tls_rbuf->rcapa) {
			mog_rbuf_free(rbuf);
			return;
		}
		free(tls_rbuf);
	}
	tls_rbuf = rbuf;
}

git clone https://yhbt.net/cmogstored.git