Commit 3a7081f6 authored by Maximilian Luz's avatar Maximilian Luz Committed by Hans de Goede

platform/surface: aggregator: Add event item allocation caching

Event items are used for completing Surface Aggregator EC events, i.e.
placing event command data and payload on a workqueue for later
processing to avoid doing said processing directly on the receiver
thread. This means that event items are allocated for each incoming
event, regardless of that event being transmitted via sequenced or
unsequenced packets.

On the Surface Book 3 and Surface Laptop 3, touchpad HID input events
(unsequenced), can constitute a larger amount of traffic, and therefore
allocation of event items. This warrants caching event items to reduce
memory fragmentation. The size of the cached objects is specifically
tuned to accommodate keyboard and touchpad input events and their
payloads on those devices. As a result, this effectively also covers
most other event types. In case of a larger event payload, event item
allocation will fall back to kzalloc().
Signed-off-by: default avatarMaximilian Luz <luzmaximilian@gmail.com>
Reviewed-by: default avatarHans de Goede <hdegoede@redhat.com>
Link: https://lore.kernel.org/r/20201221183959.1186143-4-luzmaximilian@gmail.comSigned-off-by: default avatarHans de Goede <hdegoede@redhat.com>
parent 44b84ee7
...@@ -513,14 +513,74 @@ static void ssam_nf_destroy(struct ssam_nf *nf) ...@@ -513,14 +513,74 @@ static void ssam_nf_destroy(struct ssam_nf *nf)
*/ */
#define SSAM_CPLT_WQ_BATCH 10 #define SSAM_CPLT_WQ_BATCH 10
/*
* SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN - Maximum payload length for a cached
* &struct ssam_event_item.
*
* This length has been chosen to be accommodate standard touchpad and
* keyboard input events. Events with larger payloads will be allocated
* separately.
*/
#define SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN 32
static struct kmem_cache *ssam_event_item_cache;
/**
* ssam_event_item_cache_init() - Initialize the event item cache.
*/
int ssam_event_item_cache_init(void)
{
const unsigned int size = sizeof(struct ssam_event_item)
+ SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN;
const unsigned int align = __alignof__(struct ssam_event_item);
struct kmem_cache *cache;
cache = kmem_cache_create("ssam_event_item", size, align, 0, NULL);
if (!cache)
return -ENOMEM;
ssam_event_item_cache = cache;
return 0;
}
/**
* ssam_event_item_cache_destroy() - Deinitialize the event item cache.
*/
void ssam_event_item_cache_destroy(void)
{
kmem_cache_destroy(ssam_event_item_cache);
ssam_event_item_cache = NULL;
}
static void __ssam_event_item_free_cached(struct ssam_event_item *item)
{
kmem_cache_free(ssam_event_item_cache, item);
}
static void __ssam_event_item_free_generic(struct ssam_event_item *item)
{
kfree(item);
}
/**
* ssam_event_item_free() - Free the provided event item.
* @item: The event item to free.
*/
static void ssam_event_item_free(struct ssam_event_item *item)
{
item->ops.free(item);
}
/** /**
* ssam_event_item_alloc() - Allocate an event item with the given payload size. * ssam_event_item_alloc() - Allocate an event item with the given payload size.
* @len: The event payload length. * @len: The event payload length.
* @flags: The flags used for allocation. * @flags: The flags used for allocation.
* *
* Allocate an event item with the given payload size. Sets the item * Allocate an event item with the given payload size, preferring allocation
* operations and payload length values. The item free callback (``ops.free``) * from the event item cache if the payload is small enough (i.e. smaller than
* should not be overwritten after this call. * %SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN). Sets the item operations and payload
* length values. The item free callback (``ops.free``) should not be
* overwritten after this call.
* *
* Return: Returns the newly allocated event item. * Return: Returns the newly allocated event item.
*/ */
...@@ -528,9 +588,19 @@ static struct ssam_event_item *ssam_event_item_alloc(size_t len, gfp_t flags) ...@@ -528,9 +588,19 @@ static struct ssam_event_item *ssam_event_item_alloc(size_t len, gfp_t flags)
{ {
struct ssam_event_item *item; struct ssam_event_item *item;
item = kzalloc(struct_size(item, event.data, len), flags); if (len <= SSAM_EVENT_ITEM_CACHE_PAYLOAD_LEN) {
if (!item) item = kmem_cache_alloc(ssam_event_item_cache, flags);
return NULL; if (!item)
return NULL;
item->ops.free = __ssam_event_item_free_cached;
} else {
item = kzalloc(struct_size(item, event.data, len), flags);
if (!item)
return NULL;
item->ops.free = __ssam_event_item_free_generic;
}
item->event.length = len; item->event.length = len;
return item; return item;
...@@ -692,7 +762,7 @@ static void ssam_event_queue_work_fn(struct work_struct *work) ...@@ -692,7 +762,7 @@ static void ssam_event_queue_work_fn(struct work_struct *work)
return; return;
ssam_nf_call(nf, dev, item->rqid, &item->event); ssam_nf_call(nf, dev, item->rqid, &item->event);
kfree(item); ssam_event_item_free(item);
} while (--iterations); } while (--iterations);
if (!ssam_event_queue_is_empty(queue)) if (!ssam_event_queue_is_empty(queue))
...@@ -900,7 +970,7 @@ static void ssam_handle_event(struct ssh_rtl *rtl, ...@@ -900,7 +970,7 @@ static void ssam_handle_event(struct ssh_rtl *rtl,
memcpy(&item->event.data[0], data->ptr, data->len); memcpy(&item->event.data[0], data->ptr, data->len);
if (WARN_ON(ssam_cplt_submit_event(&ctrl->cplt, item))) if (WARN_ON(ssam_cplt_submit_event(&ctrl->cplt, item)))
kfree(item); ssam_event_item_free(item);
} }
static const struct ssh_rtl_ops ssam_rtl_ops = { static const struct ssh_rtl_ops ssam_rtl_ops = {
......
...@@ -80,12 +80,18 @@ struct ssam_cplt; ...@@ -80,12 +80,18 @@ struct ssam_cplt;
* struct ssam_event_item - Struct for event queuing and completion. * struct ssam_event_item - Struct for event queuing and completion.
* @node: The node in the queue. * @node: The node in the queue.
* @rqid: The request ID of the event. * @rqid: The request ID of the event.
* @ops: Instance specific functions.
* @ops.free: Callback for freeing this event item.
* @event: Actual event data. * @event: Actual event data.
*/ */
struct ssam_event_item { struct ssam_event_item {
struct list_head node; struct list_head node;
u16 rqid; u16 rqid;
struct {
void (*free)(struct ssam_event_item *event);
} ops;
struct ssam_event event; /* must be last */ struct ssam_event event; /* must be last */
}; };
...@@ -273,4 +279,7 @@ int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl); ...@@ -273,4 +279,7 @@ int ssam_ctrl_notif_d0_entry(struct ssam_controller *ctrl);
int ssam_controller_suspend(struct ssam_controller *ctrl); int ssam_controller_suspend(struct ssam_controller *ctrl);
int ssam_controller_resume(struct ssam_controller *ctrl); int ssam_controller_resume(struct ssam_controller *ctrl);
int ssam_event_item_cache_init(void);
void ssam_event_item_cache_destroy(void);
#endif /* _SURFACE_AGGREGATOR_CONTROLLER_H */ #endif /* _SURFACE_AGGREGATOR_CONTROLLER_H */
...@@ -790,12 +790,23 @@ static int __init ssam_core_init(void) ...@@ -790,12 +790,23 @@ static int __init ssam_core_init(void)
status = ssh_ctrl_packet_cache_init(); status = ssh_ctrl_packet_cache_init();
if (status) if (status)
return status; goto err_cpkg;
status = ssam_event_item_cache_init();
if (status)
goto err_evitem;
status = serdev_device_driver_register(&ssam_serial_hub); status = serdev_device_driver_register(&ssam_serial_hub);
if (status) if (status)
ssh_ctrl_packet_cache_destroy(); goto err_register;
return 0;
err_register:
ssam_event_item_cache_destroy();
err_evitem:
ssh_ctrl_packet_cache_destroy();
err_cpkg:
return status; return status;
} }
module_init(ssam_core_init); module_init(ssam_core_init);
...@@ -803,6 +814,7 @@ module_init(ssam_core_init); ...@@ -803,6 +814,7 @@ module_init(ssam_core_init);
static void __exit ssam_core_exit(void) static void __exit ssam_core_exit(void)
{ {
serdev_device_driver_unregister(&ssam_serial_hub); serdev_device_driver_unregister(&ssam_serial_hub);
ssam_event_item_cache_destroy();
ssh_ctrl_packet_cache_destroy(); ssh_ctrl_packet_cache_destroy();
} }
module_exit(ssam_core_exit); module_exit(ssam_core_exit);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment