struct list avail; /* list for active and free blocks */
unsigned int nbav; /* number of available blocks */
unsigned int max_obj_size; /* maximum object size (in bytes). */
+ /* Optional callback invoked when shctx is about to take a cold row's
+ * blocks. The consumer is expected to free blocks using
+ * shctx_row_truncate(). It must return 1 only if it freed at least one
+ * block, or 0 otherwise: shctx_row_reserve_hot() re-invokes it as long
+ * as it returns 1, so returning 1 without freeing anything loops
+ * forever under the write lock. Called with the shctx write lock held.
+ * If not specified, the default eviction strategy is used.
+ */
+ int (*make_room)(struct shared_context *shctx);
void (*free_block)(struct shared_block *first, void *data);
void (*reserve_finish)(struct shared_context *shctx);
void *cb_data;
struct shared_block *shctx_row_reserve_hot(struct shared_context *shctx,
struct shared_block *first, int data_len)
{
- struct shared_block *last = NULL, *block, *sblock;
+ struct shared_block *last = NULL, *block;
struct shared_block *ret = first;
int remain = 1;
goto out;
}
- list_for_each_entry_safe(block, sblock, &shctx->avail, list) {
+ while (data_len > 0) {
+ block = LIST_NEXT(&shctx->avail, struct shared_block *, list);
+ if (&block->list == &shctx->avail)
+ break;
+
+ /* Head of avail is a cold row: give the consumer a chance to
+ * free room first. If make_room succeeds, freed blocks are
+ * now at the head of avail and we re-read the head next loop.
+ */
+ if (block->len && shctx->make_room) {
+ if (shctx->make_room(shctx))
+ continue;
+ }
/* release callback */
if (block->len && shctx->free_block)
}
++ret->block_count;
+ ret->last_reserved = block;
data_len -= shctx->block_size;
-
- if (data_len <= 0) {
- ret->last_reserved = block;
- break;
- }
}
shctx_wrunlock(shctx);
return ret;
}
+/*
+ * Truncate a cold row to <new_len> bytes of content. The row keeps the minimum
+ * number of blocks needed to hold <new_len> bytes; tail blocks beyond that
+ * become free blocks. If <new_len> is 0, the row no longer exists and all of
+ * its blocks become free. Freed blocks are moved to the head of the avail
+ * list so the next reservation finds them immediately.
+ *
+ * The row must be cold (refcount 0, thus in the avail list) and the shctx
+ * write lock must be held by the caller.
+ */
+void shctx_row_truncate(struct shared_context *shctx, struct shared_block *first,
+ int new_len)
+{
+ struct shared_block *new_last = first;
+ struct shared_block *freed_first = NULL;
+ int new_block_count = 1;
+ int i;
+
+ BUG_ON(new_len < 0 || new_len > first->len);
+ BUG_ON(first->refcount > 0);
+
+ if (new_len == 0) {
+ if (shctx->free_block)
+ shctx->free_block(first, shctx->cb_data);
+ freed_first = first;
+ } else {
+ new_block_count = (new_len + shctx->block_size - 1) / shctx->block_size;
+ for (i = 1; i < new_block_count; i++)
+ new_last = LIST_NEXT(&new_last->list, struct shared_block *, list);
+
+ if (new_block_count < first->block_count)
+ freed_first = LIST_NEXT(&new_last->list,
+ struct shared_block *, list);
+ }
+
+ if (freed_first) {
+ /* Move the freed chain freed_first..first->last_reserved to the
+ * head of avail.
+ */
+ struct shared_block *freed_last = first->last_reserved;
+ struct list *prev = freed_first->list.p;
+ struct list *next = freed_last->list.n;
+
+ prev->n = next;
+ next->p = prev;
+ freed_first->list.p = &shctx->avail;
+ freed_last->list.n = shctx->avail.n;
+ shctx->avail.n->p = &freed_last->list;
+ shctx->avail.n = &freed_first->list;
+ }
+
+ first->len = new_len;
+ first->block_count = new_block_count;
+ first->last_reserved = new_last;
+
+ /* Appending must resume in the partially filled tail block. When
+ * <new_len> is a multiple of the block size (or 0) there is no such
+ * block; a NULL <last_append> is correct then because the next
+ * shctx_row_reserve_hot() call will set it to the first newly
+ * reserved block (see its "!remain" handling).
+ */
+ first->last_append = (new_len % shctx->block_size) ? new_last : NULL;
+}
+
/*
* if the refcount is 0 move the row to the hot list. Increment the refcount
*/