From: Alex Elder Date: Tue, 2 Dec 2014 14:30:35 +0000 (-0600) Subject: greybus: create a slab cache for simple messages X-Git-Tag: v4.9-rc1~119^2~378^2~21^2~1795 X-Git-Url: https://git.karo-electronics.de/?a=commitdiff_plain;h=0cffcac3051fa1447d8a452ab5e0029bbe100777;p=karo-tx-linux.git 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: Alex Elder Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/greybus/operation.c b/drivers/staging/greybus/operation.c index d335bd2454ae..2a6f361b877f 100644 --- a/drivers/staging/greybus/operation.c +++ b/drivers/staging/greybus/operation.c @@ -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) - return NULL; + /* 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); + 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) { - kfree(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) { - kmem_cache_destroy(gb_operation_cache); - gb_operation_cache = NULL; - return -ENOMEM; - } + if (!gb_operation_workqueue) + goto err_operation; return 0; +err_operation: + kmem_cache_destroy(gb_operation_cache); + gb_operation_cache = NULL; +err_simple: + kmem_cache_destroy(gb_simple_message_cache); + gb_simple_message_cache = NULL; + + 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; }