Commit 0cffcac3 authored by Alex Elder's avatar Alex Elder Committed by Greg Kroah-Hartman
Browse files

greybus: create a slab cache for simple messages



A large number of request and response message types have no payload.
Such "simple" messages have a known, fixed maximum size, so we can
preallocate and use a pool (slab cache) of them.

Here are two benefits to doing this:
    - There can be (small) performance and memory utilization
      benefits to using a slab cache.
    - Error responses can be sent with no payload; the cache is
      likely to have a free entry to use for an error response even
      in a low memory situation.

The plan here is that an incoming request handler that has no
response payload to fill will not need to allocate a response
message.  If no message has been allocated when a response is to be
sent, one will be allocated from the cache by the core code.

Signed-off-by: default avatarAlex Elder <elder@linaro.org>
Signed-off-by: default avatarGreg Kroah-Hartman <greg@kroah.com>
parent 835fb5e4
Loading
Loading
Loading
Loading
+49 −11
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@
#define GB_OPERATION_MESSAGE_SIZE_MAX	4096

static struct kmem_cache *gb_operation_cache;
static struct kmem_cache *gb_simple_message_cache;

/* Workqueue to handle Greybus operation completions. */
static struct workqueue_struct *gb_operation_workqueue;
@@ -369,11 +370,19 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type,
		hd->buffer_size_max = GB_OPERATION_MESSAGE_SIZE_MAX;
	}

	if (message_size > hd->buffer_size_max)
	/* Allocate the message.  Use the slab cache for simple messages */
	if (payload_size) {
		if (message_size > hd->buffer_size_max) {
			pr_warn("requested message size too big (%zu > %zu)\n",
				message_size, hd->buffer_size_max);
			return NULL;
		}

		size = sizeof(*message) + hd->buffer_headroom + message_size;
		message = kzalloc(size, gfp_flags);
	} else {
		message = kmem_cache_zalloc(gb_simple_message_cache, gfp_flags);
	}
	if (!message)
		return NULL;

@@ -385,7 +394,10 @@ gb_operation_message_alloc(struct greybus_host_device *hd, u8 type,

static void gb_operation_message_free(struct gb_message *message)
{
	if (message->size > sizeof(message->header))
		kfree(message);
	else
		kmem_cache_free(gb_simple_message_cache, message);
}

/*
@@ -836,22 +848,46 @@ int gb_operation_sync(struct gb_connection *connection, int type,

int gb_operation_init(void)
{
	size_t size;

	BUILD_BUG_ON(GB_OPERATION_MESSAGE_SIZE_MAX >
			U16_MAX - sizeof(struct gb_operation_msg_hdr));

	/*
	 * A message structure with consists of:
	 *  - the message structure itself
	 *  - the headroom set aside for the host device
	 *  - the message header
	 *  - space for the message payload
	 * Messages with no payload are a fairly common case and
	 * have a known fixed maximum size, so we use a slab cache
	 * for them.
	 */
	size = sizeof(struct gb_message) + GB_BUFFER_HEADROOM_MAX +
				sizeof(struct gb_operation_msg_hdr);
	gb_simple_message_cache = kmem_cache_create("gb_simple_message_cache",
							size, 0, 0, NULL);
	if (!gb_simple_message_cache)
		return -ENOMEM;

	gb_operation_cache = kmem_cache_create("gb_operation_cache",
				sizeof(struct gb_operation), 0, 0, NULL);
	if (!gb_operation_cache)
		return -ENOMEM;
		goto err_simple;

	gb_operation_workqueue = alloc_workqueue("greybus_operation", 0, 1);
	if (!gb_operation_workqueue) {
	if (!gb_operation_workqueue)
		goto err_operation;

	return 0;
err_operation:
	kmem_cache_destroy(gb_operation_cache);
	gb_operation_cache = NULL;
		return -ENOMEM;
	}
err_simple:
	kmem_cache_destroy(gb_simple_message_cache);
	gb_simple_message_cache = NULL;

	return 0;
	return -ENOMEM;
}

void gb_operation_exit(void)
@@ -860,4 +896,6 @@ void gb_operation_exit(void)
	gb_operation_workqueue = NULL;
	kmem_cache_destroy(gb_operation_cache);
	gb_operation_cache = NULL;
	kmem_cache_destroy(gb_simple_message_cache);
	gb_simple_message_cache = NULL;
}