Subversion Repositories HelenOS-historic

Rev

Rev 759 | Rev 763 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
759 palkovsky 1
/*
2
 * Copyright (C) 2006 Ondrej Palkovsky
3
 * All rights reserved.
4
 *
5
 * Redistribution and use in source and binary forms, with or without
6
 * modification, are permitted provided that the following conditions
7
 * are met:
8
 *
9
 * - Redistributions of source code must retain the above copyright
10
 *   notice, this list of conditions and the following disclaimer.
11
 * - Redistributions in binary form must reproduce the above copyright
12
 *   notice, this list of conditions and the following disclaimer in the
13
 *   documentation and/or other materials provided with the distribution.
14
 * - The name of the author may not be used to endorse or promote products
15
 *   derived from this software without specific prior written permission.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
 */
28
 
29
#include <synch/spinlock.h>
30
#include <mm/slab.h>
31
#include <list.h>
32
#include <memstr.h>
33
#include <align.h>
34
#include <mm/heap.h>
762 palkovsky 35
#include <mm/frame.h>
759 palkovsky 36
#include <config.h>
37
#include <print.h>
38
#include <arch.h>
39
#include <panic.h>
762 palkovsky 40
#include <debug.h>
759 palkovsky 41
 
42
SPINLOCK_INITIALIZE(slab_cache_lock);
43
LIST_INITIALIZE(slab_cache_list);
44
 
45
slab_cache_t mag_cache;
46
 
762 palkovsky 47
 
48
typedef struct {
49
	slab_cache_t *cache; /**< Pointer to parent cache */
50
	link_t link;       /* List of full/partial slabs */
51
	void *start;       /**< Start address of first available item */
52
	count_t available; /**< Count of available items in this slab */
53
	index_t nextavail; /**< The index of next available item */
54
}slab_t;
55
 
759 palkovsky 56
/**************************************/
762 palkovsky 57
/* SLAB allocation functions          */
759 palkovsky 58
 
762 palkovsky 59
/**
60
 * Allocate frames for slab space and initialize
61
 *
62
 * TODO: Change slab_t allocation to slab_alloc(????), malloc with flags!!
63
 */
64
static slab_t * slab_space_alloc(slab_cache_t *cache, int flags)
65
{
66
	void *data;
67
	slab_t *slab;
68
	size_t fsize;
69
	int i;
70
	zone_t *zone = NULL;
71
	int status;
759 palkovsky 72
 
762 palkovsky 73
	data = (void *)frame_alloc(FRAME_KA | flags, cache->order, &status, &zone);
74
	if (status != FRAME_OK)
75
		return NULL;
76
 
77
	if (! cache->flags & SLAB_CACHE_SLINSIDE) {
78
		slab = malloc(sizeof(*slab)); // , flags);
79
		if (!slab) {
80
			frame_free((__address)data);
81
			return NULL;
82
		}
83
	} else {
84
		fsize = (PAGE_SIZE << cache->order);
85
		slab = data + fsize - sizeof(*slab);
86
	}
87
 
88
	/* Fill in slab structures */
89
	/* TODO: some better way of accessing the frame, although 
90
	 * the optimizer might optimize the division out :-/ */
91
	for (i=0; i< (1<<cache->order); i++) {
92
		ADDR2FRAME(zone, (__address)(data+i*PAGE_SIZE))->parent = slab;
93
	}
94
 
95
	slab->start = data;
96
	slab->available = cache->objects;
97
	slab->nextavail = 0;
98
 
99
	for (i=0; i<cache->objects;i++)
100
		*((int *) (slab->start + i*cache->size)) = i+1;
101
	return slab;
102
}
103
 
759 palkovsky 104
/**
762 palkovsky 105
 * Free space associated with SLAB
106
 *
107
 * @return number of freed frames
108
 */
109
static count_t slab_space_free(slab_cache_t *cache, slab_t *slab)
110
{
111
	frame_free((__address)slab->start);
112
	if (! cache->flags & SLAB_CACHE_SLINSIDE)
113
		free(slab);
114
	return 1 << cache->order;
115
}
116
 
117
/** Map object to slab structure */
118
static slab_t * obj2slab(void *obj)
119
{
120
	frame_t *frame; 
121
 
122
	frame = frame_addr2frame((__address)obj);
123
	return (slab_t *)frame->parent;
124
}
125
 
126
/**************************************/
127
/* SLAB functions */
128
 
129
 
130
/**
759 palkovsky 131
 * Return object to slab and call a destructor
132
 *
762 palkovsky 133
 * Assume the cache->lock is held;
134
 *
135
 * @param slab If the caller knows directly slab of the object, otherwise NULL
136
 *
759 palkovsky 137
 * @return Number of freed pages
138
 */
762 palkovsky 139
static count_t slab_obj_destroy(slab_cache_t *cache, void *obj,
140
				slab_t *slab)
759 palkovsky 141
{
762 palkovsky 142
	count_t frames = 0;
143
 
144
	if (!slab)
145
		slab = obj2slab(obj);
146
 
147
	spinlock_lock(cache->lock);
148
 
149
	*((int *)obj) = slab->nextavail;
150
	slab->nextavail = (obj - slab->start)/cache->size;
151
	slab->available++;
152
 
153
	/* Move it to correct list */
154
	if (slab->available == 1) {
155
		/* It was in full, move to partial */
156
		list_remove(&slab->link);
157
		list_prepend(&cache->partial_slabs, &slab->link);
158
	}
159
	if (slab->available == cache->objects) {
160
		/* Free associated memory */
161
		list_remove(&slab->link);
162
		/* Avoid deadlock */
163
		spinlock_unlock(&cache->lock);
164
		frames = slab_space_free(cache, slab);
165
		spinlock_lock(&cache->lock);
166
	}
167
 
168
	spinlock_unlock(cache->lock);
169
 
170
	return frames;
759 palkovsky 171
}
172
 
173
/**
174
 * Take new object from slab or create new if needed
175
 *
762 palkovsky 176
 * Assume cache->lock is held. 
177
 *
759 palkovsky 178
 * @return Object address or null
179
 */
180
static void * slab_obj_create(slab_cache_t *cache, int flags)
181
{
762 palkovsky 182
	slab_t *slab;
183
	void *obj;
184
 
185
	if (list_empty(&cache->partial_slabs)) {
186
		/* Allow recursion and reclaiming
187
		 * - this should work, as the SLAB control structures
188
		 *   are small and do not need to allocte with anything
189
		 *   other ten frame_alloc when they are allocating,
190
		 *   that's why we should get recursion at most 1-level deep
191
		 */
192
		spinlock_unlock(&cache->lock);
193
		slab = slab_space_alloc(cache, flags);
194
		spinlock_lock(&cache->lock);
195
		if (!slab)
196
			return NULL;
197
	} else {
198
		slab = list_get_instance(cache->partial_slabs.next,
199
					 slab_t,
200
					 link);
201
		list_remove(&slab->link);
202
	}
203
	obj = slab->start + slab->nextavail * cache->size;
204
	slab->nextavail = *((int *)obj);
205
	slab->available--;
206
	if (! slab->available)
207
		list_prepend(&cache->full_slabs, &slab->link);
208
	else
209
		list_prepend(&cache->partial_slabs, &slab->link);
210
	return obj;
759 palkovsky 211
}
212
 
213
/**************************************/
214
/* CPU-Cache slab functions */
215
 
216
/**
217
 * Free all objects in magazine and free memory associated with magazine
218
 *
762 palkovsky 219
 * Assume mag_cache[cpu].lock is locked 
759 palkovsky 220
 *
221
 * @return Number of freed pages
222
 */
223
static count_t magazine_destroy(slab_cache_t *cache, 
224
				slab_magazine_t *mag)
225
{
226
	int i;
227
	count_t frames = 0;
228
 
229
	for (i=0;i < mag->busy; i++)
762 palkovsky 230
		frames += slab_obj_destroy(cache, mag->objs[i], NULL);
759 palkovsky 231
 
232
	slab_free(&mag_cache, mag);
233
 
234
	return frames;
235
}
236
 
237
/**
238
 * Try to find object in CPU-cache magazines
239
 *
240
 * @return Pointer to object or NULL if not available
241
 */
242
static void * magazine_obj_get(slab_cache_t *cache)
243
{
244
	slab_magazine_t *mag;
245
 
246
	spinlock_lock(&cache->mag_cache[CPU->id].lock);
247
 
248
	mag = cache->mag_cache[CPU->id].current;
249
	if (!mag)
250
		goto out;
251
 
252
	if (!mag->busy) {
253
		/* If current is empty && last exists && not empty, exchange */
254
		if (cache->mag_cache[CPU->id].last \
255
		    && cache->mag_cache[CPU->id].last->busy) {
256
			cache->mag_cache[CPU->id].current = cache->mag_cache[CPU->id].last;
257
			cache->mag_cache[CPU->id].last = mag;
258
			mag = cache->mag_cache[CPU->id].current;
259
			goto gotit;
260
		}
762 palkovsky 261
		/* If still not busy, exchange current with some from
759 palkovsky 262
		 * other full magazines */
263
		spinlock_lock(&cache->lock);
264
		if (list_empty(&cache->magazines)) {
265
			spinlock_unlock(&cache->lock);
266
			goto out;
267
		}
268
		/* Free current magazine and take one from list */
269
		slab_free(&mag_cache, mag);
270
		mag = list_get_instance(cache->magazines.next,
271
					slab_magazine_t,
272
					link);
273
		list_remove(&mag->link);
274
 
275
		spinlock_unlock(&cache->lock);
276
	}
277
gotit:
278
	spinlock_unlock(&cache->mag_cache[CPU->id].lock);
279
	return mag->objs[--mag->busy];
280
out:	
281
	spinlock_unlock(&cache->mag_cache[CPU->id].lock);
282
	return NULL;
283
}
284
 
285
/**
286
 * Put object into CPU-cache magazine
287
 *
288
 * We have 2 magazines bound to processor. 
289
 * First try the current. 
290
 *  If full, try the last.
291
 *   If full, put to magazines list.
292
 *   allocate new, exchange last & current
293
 *
294
 * @return 0 - success, -1 - could not get memory
295
 */
296
static int magazine_obj_put(slab_cache_t *cache, void *obj)
297
{
298
	slab_magazine_t *mag;
299
 
300
	spinlock_lock(&cache->mag_cache[CPU->id].lock);
301
 
302
	mag = cache->mag_cache[CPU->id].current;
303
	if (!mag) {
304
		/* We do not want to sleep just because of caching */
305
		/* Especially we do not want reclaiming to start, as 
306
		 * this would deadlock */
762 palkovsky 307
		mag = slab_alloc(&mag_cache, FRAME_ATOMIC | FRAME_NO_RECLAIM);
759 palkovsky 308
		if (!mag) /* Allocation failed, give up on caching */
309
			goto errout;
310
 
311
		cache->mag_cache[CPU->id].current = mag;
312
		mag->size = SLAB_MAG_SIZE;
313
		mag->busy = 0;
314
	} else if (mag->busy == mag->size) {
315
		/* If the last is full | empty, allocate new */
316
		mag = cache->mag_cache[CPU->id].last;
317
		if (!mag || mag->size == mag->busy) {
318
			if (mag) 
319
				list_prepend(&cache->magazines, &mag->link);
320
 
762 palkovsky 321
			mag = slab_alloc(&mag_cache, FRAME_ATOMIC | FRAME_NO_RECLAIM);
759 palkovsky 322
			if (!mag)
323
				goto errout;
324
 
325
			mag->size = SLAB_MAG_SIZE;
326
			mag->busy = 0;
327
			cache->mag_cache[CPU->id].last = mag;
328
		} 
329
		/* Exchange the 2 */
330
		cache->mag_cache[CPU->id].last = cache->mag_cache[CPU->id].current;
331
		cache->mag_cache[CPU->id].current = mag;
332
	}
333
	mag->objs[mag->busy++] = obj;
334
 
335
	spinlock_unlock(&cache->mag_cache[CPU->id].lock);
336
	return 0;
337
errout:
338
	spinlock_unlock(&cache->mag_cache[CPU->id].lock);
339
	return -1;
340
}
341
 
342
 
343
/**************************************/
762 palkovsky 344
/* SLAB CACHE functions */
759 palkovsky 345
 
762 palkovsky 346
/** Return number of objects that fit in certain cache size */
347
static int comp_objects(slab_cache_t *cache)
348
{
349
	if (cache->flags & SLAB_CACHE_SLINSIDE)
350
		return ((PAGE_SIZE << cache->order) - sizeof(slab_t)) / cache->size;
351
	else 
352
		return (PAGE_SIZE << cache->order) / cache->size;
353
}
354
 
355
/** Return wasted space in slab */
356
static int badness(slab_cache_t *cache)
357
{
358
	int objects;
359
	int ssize;
360
 
361
	objects = comp_objects(cache);
362
	ssize = PAGE_SIZE << cache->order;
363
	if (cache->flags & SLAB_CACHE_SLINSIDE)
364
		ssize -= sizeof(slab_t);
365
	return ssize - objects*cache->size;
366
}
367
 
759 palkovsky 368
/** Initialize allocated memory as a slab cache */
369
static void
370
_slab_cache_create(slab_cache_t *cache,
371
		   char *name,
372
		   size_t size,
373
		   size_t align,
374
		   int (*constructor)(void *obj, int kmflag),
375
		   void (*destructor)(void *obj),
376
		   int flags)
377
{
378
	int i;
379
 
380
	memsetb((__address)cache, sizeof(*cache), 0);
381
	cache->name = name;
382
 
762 palkovsky 383
	if (align)
384
		size = ALIGN_UP(size, align);
385
	cache->size = size;
759 palkovsky 386
 
387
	cache->constructor = constructor;
388
	cache->destructor = destructor;
389
	cache->flags = flags;
390
 
391
	list_initialize(&cache->full_slabs);
392
	list_initialize(&cache->partial_slabs);
393
	list_initialize(&cache->magazines);
394
	spinlock_initialize(&cache->lock, "cachelock");
395
	if (! cache->flags & SLAB_CACHE_NOMAGAZINE) {
396
		for (i=0; i< config.cpu_count; i++)
397
			spinlock_initialize(&cache->mag_cache[i].lock, 
398
					    "cpucachelock");
399
	}
400
 
401
	/* Compute slab sizes, object counts in slabs etc. */
402
	if (cache->size < SLAB_INSIDE_SIZE)
403
		cache->flags |= SLAB_CACHE_SLINSIDE;
404
 
762 palkovsky 405
	/* Minimum slab order */
406
	cache->order = (cache->size / PAGE_SIZE) + 1;
407
 
408
	while (badness(cache) > SLAB_MAX_BADNESS(cache)) {
409
		cache->order += 1;
410
	}
759 palkovsky 411
 
762 palkovsky 412
	cache->objects = comp_objects(cache);
413
 
759 palkovsky 414
	spinlock_lock(&slab_cache_lock);
415
 
416
	list_append(&cache->link, &slab_cache_list);
417
 
418
	spinlock_unlock(&slab_cache_lock);
419
}
420
 
421
/** Create slab cache  */
422
slab_cache_t * slab_cache_create(char *name,
423
				 size_t size,
424
				 size_t align,
425
				 int (*constructor)(void *obj, int kmflag),
426
				 void (*destructor)(void *obj),
427
				 int flags)
428
{
429
	slab_cache_t *cache;
430
 
431
	cache = malloc(sizeof(*cache) + config.cpu_count*sizeof(cache->mag_cache[0]));
432
	_slab_cache_create(cache, name, size, align, constructor, destructor,
433
			   flags);
434
	return cache;
435
}
436
 
437
/** 
438
 * Reclaim space occupied by objects that are already free
439
 *
440
 * @param flags If contains SLAB_RECLAIM_ALL, do aggressive freeing
441
 * @return Number of freed pages
762 palkovsky 442
 *
443
 * TODO: Add light reclaim
759 palkovsky 444
 */
445
static count_t _slab_reclaim(slab_cache_t *cache, int flags)
446
{
447
	int i;
448
	slab_magazine_t *mag;
449
	link_t *cur;
450
	count_t frames = 0;
451
 
452
	if (cache->flags & SLAB_CACHE_NOMAGAZINE)
453
		return 0; /* Nothing to do */
454
 
455
	/* First lock all cpu caches, then the complete cache lock */
456
	for (i=0; i < config.cpu_count; i++)
457
		spinlock_lock(&cache->mag_cache[i].lock);
458
	spinlock_lock(&cache->lock);
459
 
460
	if (flags & SLAB_RECLAIM_ALL) {
762 palkovsky 461
		/* Aggressive memfree */
462
 
759 palkovsky 463
		/* Destroy CPU magazines */
464
		for (i=0; i<config.cpu_count; i++) {
465
			mag = cache->mag_cache[i].current;
466
			if (mag)
467
				frames += magazine_destroy(cache, mag);
468
			cache->mag_cache[i].current = NULL;
469
 
470
			mag = cache->mag_cache[i].last;
471
			if (mag)
472
				frames += magazine_destroy(cache, mag);
473
			cache->mag_cache[i].last = NULL;
474
		}
475
	}
762 palkovsky 476
	/* Destroy full magazines */
477
	cur=cache->magazines.prev;
478
	while (cur!=&cache->magazines) {
479
		mag = list_get_instance(cur, slab_magazine_t, link);
480
 
481
		cur = cur->prev;
482
		list_remove(cur->next);
483
		frames += magazine_destroy(cache,mag);
484
		/* If we do not do full reclaim, break
485
		 * as soon as something is freed */
486
		if (!(flags & SLAB_RECLAIM_ALL) && frames)
487
			break;
488
	}
759 palkovsky 489
 
490
	spinlock_unlock(&cache->lock);
491
	for (i=0; i < config.cpu_count; i++)
492
		spinlock_unlock(&cache->mag_cache[i].lock);
493
 
494
	return frames;
495
}
496
 
497
/** Check that there are no slabs and remove cache from system  */
498
void slab_cache_destroy(slab_cache_t *cache)
499
{
500
	/* Do not lock anything, we assume the software is correct and
501
	 * does not touch the cache when it decides to destroy it */
502
 
503
	/* Destroy all magazines */
504
	_slab_reclaim(cache, SLAB_RECLAIM_ALL);
505
 
506
	/* All slabs must be empty */
507
	if (!list_empty(&cache->full_slabs) \
508
	    || !list_empty(&cache->partial_slabs))
509
		panic("Destroying cache that is not empty.");
510
 
511
	spinlock_lock(&slab_cache_lock);
512
	list_remove(&cache->link);
513
	spinlock_unlock(&slab_cache_lock);
514
 
515
	free(cache);
516
}
517
 
518
/** Allocate new object from cache - if no flags given, always returns 
519
    memory */
520
void * slab_alloc(slab_cache_t *cache, int flags)
521
{
522
	ipl_t ipl;
523
	void *result = NULL;
524
 
525
	/* Disable interrupts to avoid deadlocks with interrupt handlers */
526
	ipl = interrupts_disable();
527
 
528
	if (!cache->flags & SLAB_CACHE_NOMAGAZINE)
529
		result = magazine_obj_get(cache);
530
 
762 palkovsky 531
	if (!result) {
532
		spinlock_lock(&cache->lock);
759 palkovsky 533
		result = slab_obj_create(cache, flags);
762 palkovsky 534
		spinlock_unlock(&cache->lock);
535
	}
759 palkovsky 536
 
537
	interrupts_restore(ipl);
538
 
539
	return result;
540
}
541
 
542
/** Return object to cache  */
543
void slab_free(slab_cache_t *cache, void *obj)
544
{
545
	ipl_t ipl;
546
 
547
	ipl = interrupts_disable();
548
 
762 palkovsky 549
	if ((cache->flags & SLAB_CACHE_NOMAGAZINE) \
550
	    || magazine_obj_put(cache, obj)) {
551
 
552
		spinlock_lock(&cache->lock);
553
		slab_obj_destroy(cache, obj, NULL);
554
		spinlock_unlock(&cache->lock);
759 palkovsky 555
	}
556
	interrupts_restore(ipl);
557
}
558
 
559
/* Go through all caches and reclaim what is possible */
560
count_t slab_reclaim(int flags)
561
{
562
	slab_cache_t *cache;
563
	link_t *cur;
564
	count_t frames = 0;
565
 
566
	spinlock_lock(&slab_cache_lock);
567
 
568
	for (cur = slab_cache_list.next;cur!=&slab_cache_list; cur=cur->next) {
569
		cache = list_get_instance(cur, slab_cache_t, link);
570
		frames += _slab_reclaim(cache, flags);
571
	}
572
 
573
	spinlock_unlock(&slab_cache_lock);
574
 
575
	return frames;
576
}
577
 
578
 
579
/* Print list of slabs */
580
void slab_print_list(void)
581
{
582
	slab_cache_t *cache;
583
	link_t *cur;
584
 
585
	spinlock_lock(&slab_cache_lock);
762 palkovsky 586
	printf("SLAB name\tOsize\tOrder\n");
759 palkovsky 587
	for (cur = slab_cache_list.next;cur!=&slab_cache_list; cur=cur->next) {
588
		cache = list_get_instance(cur, slab_cache_t, link);
762 palkovsky 589
		printf("%s\t%d\t%d\n", cache->name, cache->size, cache->order);
759 palkovsky 590
	}
591
	spinlock_unlock(&slab_cache_lock);
592
}
593
 
594
void slab_cache_init(void)
595
{
596
	/* Initialize magazine cache */
597
	_slab_cache_create(&mag_cache,
598
			   "slab_magazine",
599
			   sizeof(slab_magazine_t)+SLAB_MAG_SIZE*sizeof(void*),
600
			   sizeof(__address),
601
			   NULL, NULL,
602
			   SLAB_CACHE_NOMAGAZINE);
603
 
604
	/* Initialize structures for malloc */
605
}