|
|
| version 1.4, 2004/03/29 14:16:32 | version 1.5, 2004/03/29 16:22:21 |
|---|---|
| Line 41 | Line 41 |
| #define arysize(ary) (sizeof(ary)/sizeof((ary)[0])) | #define arysize(ary) (sizeof(ary)/sizeof((ary)[0])) |
| typedef struct mpipe_buf { | static MALLOC_DEFINE(M_MPIPEARY, "MPipe Array", "Auxillary MPIPE structure"); |
| TAILQ_ENTRY(mpipe_buf) entry; | |
| } *mpipe_buf_t; | |
| /* | /* |
| * Initialize a malloc pipeline for the specified malloc type and allocation | * Initialize a malloc pipeline for the specified malloc type and allocation |
| * size, and immediately allocate nnow buffers and set the nominal maximum | * size. Create an array to cache up to nom_count buffers and preallocate |
| * to nmax. | * them. |
| */ | */ |
| void | void |
| mpipe_init(malloc_pipe_t mpipe, malloc_type_t type, int bytes, | mpipe_init(malloc_pipe_t mpipe, malloc_type_t type, int bytes, |
| int nnow, int nmax) | int nnom, int nmax, |
| int mpflags, void (*deconstruct)(struct malloc_pipe *, void *)) | |
| { | { |
| if (bytes < sizeof(struct mpipe_buf)) | int n; |
| bytes = sizeof(struct mpipe_buf); | |
| if (nnom < 1) | |
| nnom = 1; | |
| if (nmax < 0) | |
| nmax = 0x7FFF0000; /* some very large number */ | |
| if (nmax < nnom) | |
| nmax = nnom; | |
| bzero(mpipe, sizeof(struct malloc_pipe)); | bzero(mpipe, sizeof(struct malloc_pipe)); |
| TAILQ_INIT(&mpipe->queue); | |
| mpipe->type = type; | mpipe->type = type; |
| mpipe->bytes = bytes; | mpipe->bytes = bytes; |
| mpipe->mpflags = mpflags; | |
| mpipe->deconstruct = deconstruct; | |
| if ((mpflags & MPF_NOZERO) == 0) | |
| mpipe->mflags |= M_ZERO; | |
| mpipe->ary_count = nnom; | |
| mpipe->max_count = nmax; | mpipe->max_count = nmax; |
| if (nnow > 0) { | mpipe->array = malloc(nnom * sizeof(mpipe->array[0]), M_MPIPEARY, |
| void *buf; | M_WAITOK | M_ZERO); |
| buf = malloc(bytes, mpipe->type, M_WAITOK); | while (mpipe->free_count < nnom) { |
| KKASSERT(buf != NULL); | n = mpipe->free_count; |
| mpipe->array[n] = malloc(bytes, mpipe->type, M_WAITOK | mpipe->mflags); | |
| ++mpipe->free_count; | |
| ++mpipe->total_count; | ++mpipe->total_count; |
| mpipe_free(mpipe, buf); | |
| while (--nnow > 0) { | |
| buf = malloc(bytes, mpipe->type, M_SYSNOWAIT); | |
| if (buf == NULL) | |
| break; | |
| ++mpipe->total_count; | |
| mpipe_free(mpipe, buf); | |
| } | |
| } | } |
| if (mpipe->max_count < mpipe->total_count) | |
| mpipe->max_count = mpipe->total_count; | |
| } | } |
| void | void |
| mpipe_done(malloc_pipe_t mpipe) | mpipe_done(malloc_pipe_t mpipe) |
| { | { |
| struct mpipe_buf *buf; | void *buf; |
| int n; | |
| KKASSERT(mpipe->free_count == mpipe->total_count); | KKASSERT(mpipe->free_count == mpipe->total_count); /* no outstanding mem */ |
| while (mpipe->free_count) { | while (--mpipe->free_count >= 0) { |
| buf = TAILQ_FIRST(&mpipe->queue); | n = mpipe->free_count; |
| buf = mpipe->array[n]; | |
| mpipe->array[n] = NULL; | |
| KKASSERT(buf != NULL); | KKASSERT(buf != NULL); |
| TAILQ_REMOVE(&mpipe->queue, buf, entry); | |
| --mpipe->free_count; | |
| --mpipe->total_count; | --mpipe->total_count; |
| if (mpipe->deconstruct) | |
| mpipe->deconstruct(mpipe, buf); | |
| free(buf, mpipe->type); | free(buf, mpipe->type); |
| } | } |
| KKASSERT(TAILQ_EMPTY(&mpipe->queue)); | |
| } | } |
| /* | /* |
| * Allocate an entry. flags can be M_RNOWAIT which tells us not to block. | * Allocate an entry, nominally non-blocking. The allocation is guarenteed |
| * Unlike a normal malloc, if we block in mpipe_alloc() no deadlock will occur | * to return non-NULL up to the nominal count after which it may return NULL. |
| * because it will unblock the moment an existing in-use buffer is freed. | * Note that the implementation is defined to be allowed to block for short |
| * periods of time. Use mpipe_alloc_waitok() to guarentee the allocation. | |
| */ | */ |
| void * | void * |
| mpipe_alloc(malloc_pipe_t mpipe, int flags) | mpipe_alloc_nowait(malloc_pipe_t mpipe) |
| { | { |
| mpipe_buf_t buf; | void *buf; |
| int n; | |
| crit_enter(); | crit_enter(); |
| while (mpipe->free_count == 0) { | if ((n = mpipe->free_count) != 0) { |
| if (mpipe->total_count < mpipe->max_count) { | /* |
| * Use a free entry if it exists. | |
| */ | |
| --n; | |
| buf = mpipe->array[n]; | |
| mpipe->array[n] = NULL; /* sanity check, not absolutely needed */ | |
| mpipe->free_count = n; | |
| } else if (mpipe->total_count >= mpipe->max_count) { | |
| /* | |
| * Return NULL if we have hit our limit | |
| */ | |
| buf = NULL; | |
| } else { | |
| /* | |
| * Otherwise try to malloc() non-blocking. | |
| */ | |
| buf = malloc(mpipe->bytes, mpipe->type, M_NOWAIT | mpipe->mflags); | |
| if (buf) | |
| ++mpipe->total_count; | ++mpipe->total_count; |
| if ((buf = malloc(mpipe->bytes, mpipe->type, flags)) != NULL) { | } |
| crit_exit(); | crit_exit(); |
| return(buf); | return(buf); |
| } | } |
| --mpipe->total_count; | |
| } else if (flags & M_RNOWAIT) { | /* |
| crit_exit(); | * Allocate an entry, block until the allocation succeeds. This may cause |
| return(NULL); | * us to block waiting for a prior allocation to be freed. |
| } else { | */ |
| void * | |
| mpipe_alloc_waitok(malloc_pipe_t mpipe) | |
| { | |
| void *buf; | |
| int n; | |
| int mfailed; | |
| crit_enter(); | |
| mfailed = 0; | |
| for (;;) { | |
| if ((n = mpipe->free_count) != 0) { | |
| /* | |
| * Use a free entry if it exists. | |
| */ | |
| --n; | |
| buf = mpipe->array[n]; | |
| mpipe->array[n] = NULL; | |
| mpipe->free_count = n; | |
| break; | |
| } | |
| if (mpipe->total_count >= mpipe->max_count || mfailed) { | |
| /* | |
| * Block if we have hit our limit | |
| */ | |
| mpipe->pending = 1; | mpipe->pending = 1; |
| tsleep(mpipe, 0, "mpipe", 0); | tsleep(mpipe, 0, "mpipe1", 0); |
| continue; | |
| } | } |
| /* | |
| * Otherwise try to malloc() non-blocking. If that fails loop to | |
| * recheck, and block instead of trying to malloc() again. | |
| */ | |
| buf = malloc(mpipe->bytes, mpipe->type, M_NOWAIT | mpipe->mflags); | |
| if (buf) { | |
| ++mpipe->total_count; | |
| break; | |
| } | |
| mfailed = 1; | |
| } | } |
| buf = TAILQ_FIRST(&mpipe->queue); | |
| KKASSERT(buf != NULL); | |
| TAILQ_REMOVE(&mpipe->queue, buf, entry); | |
| --mpipe->free_count; | |
| crit_exit(); | crit_exit(); |
| if (flags & M_ZERO) | |
| bzero(buf, mpipe->bytes); | |
| return(buf); | return(buf); |
| } | } |
| /* | /* |
| * Free an entry, unblock any waiters. | * Free an entry, unblock any waiters. Allow NULL. |
| */ | */ |
| void | void |
| mpipe_free(malloc_pipe_t mpipe, void *vbuf) | mpipe_free(malloc_pipe_t mpipe, void *buf) |
| { | { |
| struct mpipe_buf *buf; | int n; |
| if (buf == NULL) | |
| return; | |
| if ((buf = vbuf) != NULL) { | crit_enter(); |
| crit_enter(); | if ((n = mpipe->free_count) < mpipe->ary_count) { |
| if (mpipe->total_count > mpipe->max_count) { | /* |
| --mpipe->total_count; | * Free slot available in free array (LIFO) |
| crit_exit(); | */ |
| free(buf, mpipe->type); | mpipe->array[n] = buf; |
| } else { | ++mpipe->free_count; |
| TAILQ_INSERT_TAIL(&mpipe->queue, buf, entry); | if ((mpipe->mpflags & (MPF_CACHEDATA|MPF_NOZERO)) == 0) |
| ++mpipe->free_count; | bzero(buf, mpipe->bytes); |
| crit_exit(); | crit_exit(); |
| if (mpipe->free_count >= (mpipe->total_count >> 2) + 1) { | |
| if (mpipe->trigger) { | /* |
| mpipe->trigger(mpipe->trigger_data); | * Wakeup anyone blocked in mpipe_alloc_*(). |
| } | */ |
| if (mpipe->pending) { | if (mpipe->pending) { |
| mpipe->pending = 0; | mpipe->pending = 0; |
| wakeup(mpipe); | wakeup(mpipe); |
| } | |
| } | |
| } | } |
| } else { | |
| /* | |
| * All the free slots are full, free the buffer directly. | |
| */ | |
| --mpipe->total_count; | |
| KKASSERT(mpipe->total_count >= mpipe->free_count); | |
| if (mpipe->deconstruct) | |
| mpipe->deconstruct(mpipe, buf); | |
| crit_exit(); | |
| free(buf, mpipe->type); | |
| } | } |
| } | } |