+ if (p != NULL)
+ {
+ struct block *b = p;
+ struct arena *a = block_to_arena (b);
+ struct desc *d = a->desc;
+
+ if (d != NULL)
+ {
+ /* It's a normal block. We handle it here. */
+
+#ifndef NDEBUG
+ /* Clear the block to help detect use-after-free bugs. */
+ memset (b, 0xcc, d->block_size);
+#endif
+
+ lock_acquire (&d->lock);
+
+ /* Add block to free list. */
+ list_push_front (&d->free_list, &b->free_elem);
+
+ /* If the arena is now entirely unused, free it. */
+ if (++a->free_cnt >= d->blocks_per_arena)
+ {
+ size_t i;
+
+ ASSERT (a->free_cnt == d->blocks_per_arena);
+ for (i = 0; i < d->blocks_per_arena; i++)
+ {
+ struct block *b = arena_to_block (a, i);
+ list_remove (&b->free_elem);
+ }
+ palloc_free_page (a);
+ }
+
+ lock_release (&d->lock);
+ }
+ else
+ {
+ /* It's a big block. Free its pages. */
+ palloc_free_multiple (a, a->free_cnt);
+ return;
+ }
+ }
+}
+\f
+/* Returns the arena that block B is inside. */
+static struct arena *
+block_to_arena (struct block *b)
+{
+ struct arena *a = pg_round_down (b);
+
+ /* Check that the arena is valid. */
+ ASSERT (a != NULL);
+ ASSERT (a->magic == ARENA_MAGIC);
+
+ /* Check that the block is properly aligned for the arena. */
+ ASSERT (a->desc == NULL
+ || (pg_ofs (b) - sizeof *a) % a->desc->block_size == 0);
+ ASSERT (a->desc != NULL || pg_ofs (b) == sizeof *a);