greybus: create a slab cache for simple messages
authorAlex Elder <elder@linaro.org>
Tue, 2 Dec 2014 14:30:35 +0000 (08:30 -0600)
committerGreg Kroah-Hartman <greg@kroah.com>
Tue, 2 Dec 2014 22:41:58 +0000 (14:41 -0800)
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 <elder@linaro.org>
Signed-off-by: Greg Kroah-Hartman <greg@kroah.com>
drivers/staging/greybus/operation.c

index d335bd2..2a6f361 100644 (file)
@@ -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;
 }