diff options
author | Tommy Kardach <thomaskardach@google.com> | 2023-04-27 16:41:54 +0000 |
---|---|---|
committer | Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> | 2023-12-13 16:36:31 +0000 |
commit | f8f9b25ba1a2aba430de0531560c6c21d8f5fdc5 (patch) | |
tree | aef0db150841b19de7eb1ec4f7884efd3901bdb3 | |
parent | b64156b9d2e33a005d07f004f3e9967db2aa6a0c (diff) | |
download | lwis-f8f9b25ba1a2aba430de0531560c6c21d8f5fdc5.tar.gz |
Revert "Optimize I2C Bus manager scheduling"
Revert submission 2462937-I2CBusManager-OptimizedScheduling
Reason for revert: Change causes memory leak b/279860649
Reverted changes: /q/submissionid:2462937-I2CBusManager-OptimizedScheduling
Bug: 299130975
Change-Id: I8ab99b1b8221f1cade6fd485ca9ce5b3bcc2eeee
(cherry picked from commit 7d2d4f7ad26c4ceab65c8e0165bf662aaf179683)
Signed-off-by: Meghana Barkalle <mbarkalle@google.com>
-rw-r--r-- | lwis_device.c | 7 | ||||
-rw-r--r-- | lwis_device_i2c.c | 1 | ||||
-rw-r--r-- | lwis_device_i2c.h | 1 | ||||
-rw-r--r-- | lwis_dt.c | 34 | ||||
-rw-r--r-- | lwis_i2c_bus_manager.c | 293 | ||||
-rw-r--r-- | lwis_i2c_bus_manager.h | 22 | ||||
-rw-r--r-- | lwis_i2c_sched.c | 67 | ||||
-rw-r--r-- | lwis_i2c_sched.h | 10 | ||||
-rw-r--r-- | lwis_periodic_io.c | 8 | ||||
-rw-r--r-- | lwis_transaction.c | 24 |
10 files changed, 154 insertions, 313 deletions
diff --git a/lwis_device.c b/lwis_device.c index 4735f62..60b5dc4 100644 --- a/lwis_device.c +++ b/lwis_device.c @@ -160,11 +160,6 @@ static int lwis_open(struct inode *node, struct file *fp) /* Storing the client handle in fp private_data for easy access */ fp->private_data = lwis_client; - if (lwis_i2c_bus_manager_connect_client(lwis_client)) { - dev_err(lwis_dev->dev, "Failed to connect lwis client to I2C bus manager\n"); - return -EINVAL; - } - lwis_client->is_enabled = false; return 0; } @@ -239,8 +234,6 @@ static int lwis_release_client(struct lwis_client *lwis_client) } spin_unlock_irqrestore(&lwis_dev->lock, flags); - lwis_i2c_bus_manager_disconnect_client(lwis_client); - kfree(lwis_client); return 0; diff --git a/lwis_device_i2c.c b/lwis_device_i2c.c index 877719f..ad513b8 100644 --- a/lwis_device_i2c.c +++ b/lwis_device_i2c.c @@ -7,6 +7,7 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ + #define pr_fmt(fmt) KBUILD_MODNAME "-i2c-dev: " fmt #include "lwis_device_i2c.h" diff --git a/lwis_device_i2c.h b/lwis_device_i2c.h index 393bd32..03c393f 100644 --- a/lwis_device_i2c.h +++ b/lwis_device_i2c.h @@ -36,7 +36,6 @@ struct lwis_i2c_device { struct mutex *group_i2c_lock; /* Pointer to the I2C bus manager for this device */ struct lwis_i2c_bus_manager *i2c_bus_manager; - int device_priority; }; int lwis_i2c_device_deinit(void); @@ -25,7 +25,6 @@ #include "lwis_i2c.h" #include "lwis_ioreg.h" #include "lwis_regulator.h" -#include "lwis_i2c_bus_manager.h" #define SHARED_STRING "shared-" #define PULSE_STRING "pulse-" @@ -1166,33 +1165,6 @@ static int parse_thread_priority(struct lwis_device *lwis_dev) return 0; } -static int parse_i2c_device_priority(struct lwis_i2c_device *i2c_dev) -{ - struct device_node *dev_node; - int ret = 0; - - dev_node = i2c_dev->base_dev.plat_dev->dev.of_node; - /* Set i2c device_priority value to default */ - i2c_dev->device_priority = I2C_DEVICE_HIGH_PRIORITY; - - ret = of_property_read_u32(dev_node, "i2c-device-priority", &i2c_dev->device_priority); - /* If no property in device tree, just return to use default */ - if (ret == -EINVAL) { - return 0; - } - if (ret) { - pr_err("invalid i2c-device-priority value\n"); - return ret; - } - if ((i2c_dev->device_priority < I2C_DEVICE_HIGH_PRIORITY) || - (i2c_dev->device_priority > I2C_DEVICE_LOW_PRIORITY)) { - pr_err("invalid i2c-device-priority value %d\n", i2c_dev->device_priority); - return -EINVAL; - } - - return 0; -} - static int parse_i2c_lock_group_id(struct lwis_i2c_device *i2c_dev) { struct device_node *dev_node; @@ -1392,12 +1364,6 @@ int lwis_i2c_device_parse_dt(struct lwis_i2c_device *i2c_dev) return ret; } - ret = parse_i2c_device_priority(i2c_dev); - if (ret) { - dev_err(i2c_dev->base_dev.dev, "Error parsing i2c device priority\n"); - return ret; - } - return 0; } diff --git a/lwis_i2c_bus_manager.c b/lwis_i2c_bus_manager.c index 04711f7..1b3525e 100644 --- a/lwis_i2c_bus_manager.c +++ b/lwis_i2c_bus_manager.c @@ -208,24 +208,20 @@ static void destroy_i2c_bus_manager(struct lwis_i2c_bus_manager *i2c_bus_manager struct lwis_device *lwis_dev) { unsigned long flags; - int i = 0; - if (!i2c_bus_manager) { - return; - } - - dev_info(lwis_dev->dev, "Destroying I2C Bus Manager: %s\n", i2c_bus_manager->i2c_bus_name); - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); - for (i = 0; i < I2C_MAX_PRIORITY_LEVELS; i++) { - lwis_i2c_process_request_queue_destroy(&i2c_bus_manager->i2c_bus_process_queue[i]); - } - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); + if (i2c_bus_manager) { + dev_info(lwis_dev->dev, "Destroying I2C Bus Manager: %s\n", + i2c_bus_manager->i2c_bus_name); + spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); + lwis_i2c_process_request_queue_destroy(&i2c_bus_manager->i2c_bus_process_queue); + spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); - /* Delete the bus manager instance from the list */ - delete_bus_manager_id_in_list(i2c_bus_manager->i2c_bus_id); + /* Delete the bus manager instance from the list */ + delete_bus_manager_id_in_list(i2c_bus_manager->i2c_bus_id); - /* Free the bus manager */ - kfree(i2c_bus_manager); - i2c_bus_manager = NULL; + /* Free the bus manager */ + kfree(i2c_bus_manager); + i2c_bus_manager = NULL; + } } /* @@ -263,76 +259,42 @@ static int connect_i2c_bus_manager(struct lwis_i2c_bus_manager *i2c_bus_manager, return ret; } -static bool i2c_device_priority_is_valid(int device_priority) -{ - if ((device_priority >= I2C_DEVICE_HIGH_PRIORITY) && - (device_priority <= I2C_DEVICE_LOW_PRIORITY)) { - return true; - } - return false; -} - /* * lwis_i2c_bus_manager_process_worker_queue: * Function to be called by i2c bus manager worker thread to - * pick the next I2C client that is scheduled for transfer. - * The process queue will be processed in order of I2C - * device priority. + * pick the next I2C device that is scheduled for transfer */ void lwis_i2c_bus_manager_process_worker_queue(struct lwis_client *client) { /* Get the correct I2C Bus manager to process it's queue */ struct lwis_device *lwis_dev = NULL; struct lwis_i2c_bus_manager *i2c_bus_manager = NULL; - int i = 0; /* The transfers will be processed in fifo order */ + struct lwis_device **dequeuing_dev = NULL; struct lwis_client *client_to_process = NULL; struct lwis_device *lwis_dev_to_process = NULL; unsigned long flags; - struct lwis_i2c_process_queue *process_queue = NULL; - struct lwis_i2c_process_request *process_request = NULL; - - struct list_head *i2c_client_node, *i2c_client_tmp_node; lwis_dev = client->lwis_dev; i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); - if (!i2c_bus_manager) { - dev_err(lwis_dev->dev, "I2C Bus Manager is null\n"); - return; - } - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); - for (i = 0; i < I2C_MAX_PRIORITY_LEVELS; i++) { - process_queue = &i2c_bus_manager->i2c_bus_process_queue[i]; - list_for_each_safe (i2c_client_node, i2c_client_tmp_node, &process_queue->head) { - process_request = list_entry(i2c_client_node, - struct lwis_i2c_process_request, request_node); - if (!process_request) { - dev_err(lwis_dev->dev, "I2C Bus Worker process_request is null\n"); - break; - } - client_to_process = process_request->requesting_client; - if (!client_to_process) { - dev_err(lwis_dev->dev, - "I2C Bus Worker client_to_process is null\n"); - break; - } - lwis_dev_to_process = client_to_process->lwis_dev; - if (!lwis_dev_to_process) { - dev_err(lwis_dev->dev, - "I2C Bus Worker lwis_dev_to_process is null\n"); - break; - } - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); + if (i2c_bus_manager) { + spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); + dequeuing_dev = lwis_i2c_process_request_queue_dequeue_request( + &i2c_bus_manager->i2c_bus_process_queue); + spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); + + if (dequeuing_dev) { + lwis_dev_to_process = *dequeuing_dev; if (is_valid_connected_device(lwis_dev_to_process, i2c_bus_manager)) { + client_to_process = + container_of(dequeuing_dev, struct lwis_client, lwis_dev); lwis_process_transactions_in_queue(client_to_process); lwis_process_periodic_io_in_queue(client_to_process); } - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); } } - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); } /* @@ -342,7 +304,6 @@ void lwis_i2c_bus_manager_process_worker_queue(struct lwis_client *client) int lwis_i2c_bus_manager_create(struct lwis_i2c_device *i2c_dev) { int ret = 0; - int i = 0; struct lwis_i2c_bus_manager *i2c_bus_manager = NULL; struct lwis_device *i2c_base_device = &i2c_dev->base_dev; @@ -370,10 +331,7 @@ int lwis_i2c_bus_manager_create(struct lwis_i2c_device *i2c_dev) INIT_LIST_HEAD(&i2c_bus_manager->i2c_connected_devices); /* Create a I2C transfer process queue */ - for (i = 0; i < I2C_MAX_PRIORITY_LEVELS; i++) { - lwis_i2c_process_request_queue_initialize( - &i2c_bus_manager->i2c_bus_process_queue[i]); - } + lwis_i2c_process_request_queue_initialize(&i2c_bus_manager->i2c_bus_process_queue); /* Insert this instance of bus manager in the bus manager list */ ret = insert_bus_manager_id_in_list(i2c_bus_manager, i2c_dev->adapter->nr); @@ -463,6 +421,25 @@ void lwis_i2c_bus_manager_disconnect(struct lwis_device *lwis_dev) } } +/* lwis_i2c_bus_manager_enqueue_transfer_request: + * Enqueues I2C transfer request from a requesting device on the I2C Scheduler + */ +int lwis_i2c_bus_manager_enqueue_transfer_request(struct lwis_i2c_bus_manager *i2c_bus_manager, + struct lwis_device **lwis_dev) +{ + int ret = 0; + struct lwis_device *enqueuing_dev = *lwis_dev; + unsigned long flags; + + if (lwis_check_device_type(enqueuing_dev, DEVICE_TYPE_I2C)) { + spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); + ret = lwis_i2c_process_request_queue_enqueue_request( + &i2c_bus_manager->i2c_bus_process_queue, lwis_dev); + spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); + } + return ret; +} + /* lwis_i2c_bus_manager_lock_i2c_bus: * Locks the I2C bus for a given I2C Lwis Device */ @@ -502,23 +479,23 @@ struct lwis_i2c_bus_manager *lwis_i2c_bus_manager_get_manager(struct lwis_device return NULL; } -/* lwis_i2c_bus_manager_flush_i2c_worker: - * Flushes the I2C Bus Manager worker - */ void lwis_i2c_bus_manager_flush_i2c_worker(struct lwis_device *lwis_dev) { + unsigned long process_queue_flags; struct lwis_i2c_bus_manager *i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); if (i2c_bus_manager == NULL) return; kthread_flush_worker(&i2c_bus_manager->i2c_bus_worker); + + /* After flushing the worker the process queue should be empty. + * This destroy is to make sure there are no more requests to be handled. */ + spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, process_queue_flags); + lwis_i2c_process_request_queue_destroy(&i2c_bus_manager->i2c_bus_process_queue); + spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, process_queue_flags); } -/* lwis_i2c_bus_manager_list_initialize: - * Initializes bus manager global list. This is the list that holds - * actual bus manager pointers for a given physical I2C Bus connection - */ void lwis_i2c_bus_manager_list_initialize(void) { /* initialize_i2c_bus_manager_list */ @@ -526,9 +503,6 @@ void lwis_i2c_bus_manager_list_initialize(void) INIT_LIST_HEAD(&i2c_bus_manager_list.i2c_bus_manager_list_head); } -/* lwis_i2c_bus_manager_list_deinitialize: - * Deinitializes bus manager global list - */ void lwis_i2c_bus_manager_list_deinitialize(void) { struct list_head *i2c_bus_manager_list_node, *i2c_bus_manager_list_tmp_node; @@ -547,169 +521,4 @@ void lwis_i2c_bus_manager_list_deinitialize(void) i2c_bus_manager_identifier = NULL; } mutex_unlock(&i2c_bus_manager_list_lock); -} - -/* lwis_i2c_bus_manager_connect_client: - * Connects a lwis client to the bus manager to be processed by the worker. - * The client will be connected to the appropriate priority queue based - * on the I2C device priority specified in the dts for the I2C device node. - * I2C lwis client is always connected when a new instance of client is - * created. - */ -int lwis_i2c_bus_manager_connect_client(struct lwis_client *connecting_client) -{ - int ret = 0; - int device_priority = I2C_MAX_PRIORITY_LEVELS; - unsigned long flags; - bool create_client_node = true; - struct lwis_i2c_process_request *i2c_connecting_client_node; - struct lwis_device *lwis_dev = NULL; - struct lwis_i2c_process_queue *process_queue = NULL; - struct lwis_i2c_device *i2c_dev = NULL; - struct lwis_i2c_bus_manager *i2c_bus_manager = NULL; - struct list_head *request, *request_tmp; - struct lwis_i2c_process_request *search_node; - - if (!connecting_client) { - pr_err("Connecting client pointer for I2C Bus Manager is NULL\n"); - return -EINVAL; - } - - lwis_dev = connecting_client->lwis_dev; - if (!lwis_dev) { - pr_err("Connecting device for I2C Bus Manager is NULL\n"); - return -EINVAL; - } - - if (!lwis_check_device_type(lwis_dev, DEVICE_TYPE_I2C)) { - return ret; - } - - i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); - if (!i2c_bus_manager) { - dev_err(lwis_dev->dev, "I2C bus manager is NULL\n"); - return -EINVAL; - } - - i2c_dev = container_of(lwis_dev, struct lwis_i2c_device, base_dev); - if (!i2c_dev) { - dev_err(lwis_dev->dev, "I2C device is NULL\n"); - return -EINVAL; - } - - device_priority = i2c_dev->device_priority; - if (!i2c_device_priority_is_valid(device_priority)) { - dev_err(lwis_dev->dev, "Invalid I2C device priority %d\n", device_priority); - return -EINVAL; - } - - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); - - // Search for existing client node in the queue, if client is already connected - // to this bus then don't create a new client node - process_queue = &i2c_bus_manager->i2c_bus_process_queue[device_priority]; - if (!lwis_i2c_process_request_queue_is_empty(process_queue)) { - list_for_each_safe (request, request_tmp, &process_queue->head) { - search_node = - list_entry(request, struct lwis_i2c_process_request, request_node); - if (search_node->requesting_client == connecting_client) { - dev_info(lwis_dev->dev, - "I2C client already connected %s(%p) to bus %s \n", - lwis_dev->name, connecting_client, - i2c_bus_manager->i2c_bus_name); - create_client_node = false; - break; - } - } - } - - if (create_client_node) { - i2c_connecting_client_node = - kzalloc(sizeof(struct lwis_i2c_process_request), GFP_ATOMIC); - if (!i2c_connecting_client_node) { - dev_err(lwis_dev->dev, "Failed to connect client to I2C Bus Manager\n"); - return -ENOMEM; - } - i2c_connecting_client_node->requesting_client = connecting_client; - INIT_LIST_HEAD(&i2c_connecting_client_node->request_node); - list_add_tail(&i2c_connecting_client_node->request_node, &process_queue->head); - ++process_queue->number_of_nodes; - dev_info(lwis_dev->dev, "Connecting client %s(%p) to bus %s\n", lwis_dev->name, - connecting_client, i2c_bus_manager->i2c_bus_name); - } - - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); - return ret; -} - -/* lwis_i2c_bus_manager_disconnect_client: - * Disconnects a lwis client to the bus manager. This will make sure that - * the released client is not processed further by the I2C worker. - * The client will be disconnected from the appropriate priority queue based - * on the I2C device priority specified in the dts for the I2C device node. - * I2C lwis client is always disconnected when the instance of client is - * released/destroyed. - */ -void lwis_i2c_bus_manager_disconnect_client(struct lwis_client *disconnecting_client) -{ - int device_priority = I2C_MAX_PRIORITY_LEVELS; - unsigned long flags; - struct lwis_i2c_process_request *i2c_disconnecting_client_node; - struct lwis_device *lwis_dev = NULL; - struct lwis_i2c_process_queue *process_queue = NULL; - struct lwis_i2c_device *i2c_dev = NULL; - struct list_head *request, *request_tmp; - struct lwis_i2c_bus_manager *i2c_bus_manager = NULL; - - if (!disconnecting_client) { - pr_err("Disconnecting client pointer for I2C Bus Manager is NULL\n"); - return; - } - - lwis_dev = disconnecting_client->lwis_dev; - if (!lwis_dev) { - pr_err("Disconnecting device for I2C Bus Manager is NULL\n"); - return; - } - - if (!lwis_check_device_type(lwis_dev, DEVICE_TYPE_I2C)) { - return; - } - - i2c_bus_manager = lwis_i2c_bus_manager_get_manager(lwis_dev); - if (!i2c_bus_manager) { - dev_err(lwis_dev->dev, "I2C bus manager is NULL\n"); - return; - } - - i2c_dev = container_of(lwis_dev, struct lwis_i2c_device, base_dev); - if (!i2c_dev) { - dev_err(lwis_dev->dev, "I2C device is NULL\n"); - return; - } - - device_priority = i2c_dev->device_priority; - if (!i2c_device_priority_is_valid(device_priority)) { - dev_err(lwis_dev->dev, "Invalid I2C device priority %d\n", device_priority); - return; - } - - spin_lock_irqsave(&i2c_bus_manager->i2c_process_queue_lock, flags); - process_queue = &i2c_bus_manager->i2c_bus_process_queue[device_priority]; - list_for_each_safe (request, request_tmp, &process_queue->head) { - i2c_disconnecting_client_node = - list_entry(request, struct lwis_i2c_process_request, request_node); - if (i2c_disconnecting_client_node->requesting_client == disconnecting_client) { - dev_info(lwis_dev->dev, "Disconnecting I2C client %s(%p) from bus %s\n", - lwis_dev->name, disconnecting_client, - i2c_bus_manager->i2c_bus_name); - list_del(&i2c_disconnecting_client_node->request_node); - i2c_disconnecting_client_node->requesting_client = NULL; - kfree(i2c_disconnecting_client_node); - i2c_disconnecting_client_node = NULL; - --process_queue->number_of_nodes; - break; - } - } - spin_unlock_irqrestore(&i2c_bus_manager->i2c_process_queue_lock, flags); }
\ No newline at end of file diff --git a/lwis_i2c_bus_manager.h b/lwis_i2c_bus_manager.h index 5e236e1..ca40ae8 100644 --- a/lwis_i2c_bus_manager.h +++ b/lwis_i2c_bus_manager.h @@ -17,17 +17,6 @@ #include "lwis_periodic_io.h" #include "lwis_transaction.h" -/* enum lwis_i2c_device_priority_level: - * Defines the I2C device priority level - * in which the requests will be executed - */ -enum lwis_i2c_device_priority_level { - I2C_DEVICE_HIGH_PRIORITY = 0, - I2C_DEVICE_MEDIUM_PRIORITY = 1, - I2C_DEVICE_LOW_PRIORITY = 2, - I2C_MAX_PRIORITY_LEVELS = 3 -}; - // Forward declaration struct lwis_i2c_device; @@ -56,7 +45,7 @@ struct lwis_i2c_process_queue { /* Head node for the process queue */ struct list_head head; /* Total number of devices that are queued to be processed */ - int number_of_nodes; + int number_of_requests; }; /* @@ -78,7 +67,7 @@ struct lwis_i2c_bus_manager { struct kthread_worker i2c_bus_worker; struct task_struct *i2c_bus_worker_thread; /* Queue of all I2C devices that have data to transfer in their process queues */ - struct lwis_i2c_process_queue i2c_bus_process_queue[I2C_MAX_PRIORITY_LEVELS]; + struct lwis_i2c_process_queue i2c_bus_process_queue; /* List of I2C devices using this bus */ struct list_head i2c_connected_devices; /* Total number of physically connected devices to the bus @@ -105,14 +94,13 @@ void lwis_i2c_bus_manager_disconnect(struct lwis_device *lwis_dev); void lwis_i2c_bus_manager_process_worker_queue(struct lwis_client *client); +int lwis_i2c_bus_manager_enqueue_transfer_request(struct lwis_i2c_bus_manager *i2c_bus_manager, + struct lwis_device **lwis_dev); + void lwis_i2c_bus_manager_flush_i2c_worker(struct lwis_device *lwis_dev); void lwis_i2c_bus_manager_list_initialize(void); void lwis_i2c_bus_manager_list_deinitialize(void); -int lwis_i2c_bus_manager_connect_client(struct lwis_client *connecting_client); - -void lwis_i2c_bus_manager_disconnect_client(struct lwis_client *disconnecting_client); - #endif /* LWIS_I2C_BUS_MANAGER_H */ diff --git a/lwis_i2c_sched.c b/lwis_i2c_sched.c index 1ca802f..3c61fcf 100644 --- a/lwis_i2c_sched.c +++ b/lwis_i2c_sched.c @@ -16,9 +16,9 @@ * lwis_i2c_process_request_queue_is_empty: * Checks if the I2C process request queue is empty */ -bool lwis_i2c_process_request_queue_is_empty(struct lwis_i2c_process_queue *process_queue) +static bool lwis_i2c_process_request_queue_is_empty(struct lwis_i2c_process_queue *process_queue) { - if ((!process_queue) || ((process_queue) && (process_queue->number_of_nodes == 0))) { + if ((!process_queue) || ((process_queue) && (process_queue->number_of_requests == 0))) { return true; } return false; @@ -30,7 +30,7 @@ bool lwis_i2c_process_request_queue_is_empty(struct lwis_i2c_process_queue *proc */ void lwis_i2c_process_request_queue_initialize(struct lwis_i2c_process_queue *process_queue) { - process_queue->number_of_nodes = 0; + process_queue->number_of_requests = 0; INIT_LIST_HEAD(&process_queue->head); } @@ -54,9 +54,66 @@ void lwis_i2c_process_request_queue_destroy(struct lwis_i2c_process_queue *proce process_request = list_entry(request, struct lwis_i2c_process_request, request_node); list_del(&process_request->request_node); - process_request->requesting_client = NULL; + process_request->requesting_device = NULL; kfree(process_request); process_request = NULL; - --process_queue->number_of_nodes; + --process_queue->number_of_requests; } +} + +/* + * lwis_i2c_process_request_queue_enqueue_request: + * Enqueues a requesting device on tail of the I2C Scheduler +*/ +int lwis_i2c_process_request_queue_enqueue_request(struct lwis_i2c_process_queue *process_queue, + struct lwis_device **requesting_device) +{ + int ret = 0; + struct lwis_i2c_process_request *request; + struct lwis_device *lwis_dev = *requesting_device; + + if ((!process_queue) || (!requesting_device) || (!lwis_dev)) { + pr_err("Invalid pointer\n"); + return -EINVAL; + } + + // Atomic allocation needed here since this memory is allocated within + // transition and periodic io locks of various I2C devices + request = kzalloc(sizeof(struct lwis_i2c_process_request), GFP_ATOMIC); + if (!request) { + dev_err(lwis_dev->dev, "Failed to allocate I2C Process Request Node memory\n"); + return -ENOMEM; + } + request->requesting_device = requesting_device; + INIT_LIST_HEAD(&request->request_node); + list_add_tail(&request->request_node, &process_queue->head); + process_queue->number_of_requests++; + + return ret; +} + +/* + * lwis_i2c_process_request_queue_dequeue_request: + * Dequeues a lwis device from head of the I2C Scheduler +*/ +struct lwis_device ** +lwis_i2c_process_request_queue_dequeue_request(struct lwis_i2c_process_queue *process_queue) +{ + struct lwis_i2c_process_request *request; + struct lwis_device **requested_device = NULL; + + if (lwis_i2c_process_request_queue_is_empty(process_queue)) { + return requested_device; + } + + request = list_first_entry_or_null(&process_queue->head, struct lwis_i2c_process_request, + request_node); + if (request) { + requested_device = request->requesting_device; + list_del(&request->request_node); + kfree(request); + request = NULL; + process_queue->number_of_requests--; + } + return requested_device; }
\ No newline at end of file diff --git a/lwis_i2c_sched.h b/lwis_i2c_sched.h index 22073af..de3390d 100644 --- a/lwis_i2c_sched.h +++ b/lwis_i2c_sched.h @@ -20,14 +20,18 @@ struct lwis_i2c_process_queue; * This maintains the node to identify the devices that * have a request to be processed on a given I2C bus */ struct lwis_i2c_process_request { - struct lwis_client *requesting_client; + struct lwis_device **requesting_device; struct list_head request_node; }; -bool lwis_i2c_process_request_queue_is_empty(struct lwis_i2c_process_queue *process_queue); - void lwis_i2c_process_request_queue_initialize(struct lwis_i2c_process_queue *process_queue); void lwis_i2c_process_request_queue_destroy(struct lwis_i2c_process_queue *process_queue); +int lwis_i2c_process_request_queue_enqueue_request(struct lwis_i2c_process_queue *process_queue, + struct lwis_device **requesting_device); + +struct lwis_device ** +lwis_i2c_process_request_queue_dequeue_request(struct lwis_i2c_process_queue *process_queue); + #endif /* LWIS_I2C_SCHED_H_ */
\ No newline at end of file diff --git a/lwis_periodic_io.c b/lwis_periodic_io.c index ad5ef83..bdd86e5 100644 --- a/lwis_periodic_io.c +++ b/lwis_periodic_io.c @@ -37,6 +37,7 @@ static enum hrtimer_restart periodic_io_timer_func(struct hrtimer *timer) bool active_periodic_io_present = false; struct lwis_device *lwis_dev; struct lwis_i2c_bus_manager *i2c_bus_manager = NULL; + int ret = 0; periodic_io_list = container_of(timer, struct lwis_periodic_io_list, hr_timer); client = periodic_io_list->client; @@ -64,7 +65,12 @@ static enum hrtimer_restart periodic_io_timer_func(struct hrtimer *timer) } if (active_periodic_io_present) { if (i2c_bus_manager) { - kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, &client->i2c_work); + ret = lwis_i2c_bus_manager_enqueue_transfer_request(i2c_bus_manager, + &client->lwis_dev); + if (!ret) { + kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, + &client->i2c_work); + } } else { kthread_queue_work(&client->lwis_dev->transaction_worker, &client->transaction_work); diff --git a/lwis_transaction.c b/lwis_transaction.c index db3c094..983cd5c 100644 --- a/lwis_transaction.c +++ b/lwis_transaction.c @@ -744,7 +744,13 @@ static int queue_transaction_locked(struct lwis_client *client, /* Immediate trigger. */ list_add_tail(&transaction->process_queue_node, &client->transaction_process_queue); if (i2c_bus_manager) { - kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, &client->i2c_work); + if (!lwis_i2c_bus_manager_enqueue_transfer_request(i2c_bus_manager, + &client->lwis_dev)) { + kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, + &client->i2c_work); + } else { + dev_err(client->lwis_dev->dev, "Cannot queue I2C transfer\n"); + } } else { kthread_queue_work(&client->lwis_dev->transaction_worker, &client->transaction_work); @@ -941,7 +947,13 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id, /* Schedule deferred transactions */ if (!list_empty(&client->transaction_process_queue)) { if (i2c_bus_manager) { - kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, &client->i2c_work); + if (!lwis_i2c_bus_manager_enqueue_transfer_request(i2c_bus_manager, + &client->lwis_dev)) { + kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, + &client->i2c_work); + } else { + dev_err(client->lwis_dev->dev, "Cannot queue I2C transfer\n"); + } } else { kthread_queue_work(&client->lwis_dev->transaction_worker, &client->transaction_work); @@ -1014,7 +1026,13 @@ void lwis_transaction_fence_trigger(struct lwis_client *client, struct lwis_fenc /* Schedule deferred transactions */ if (!list_empty(&client->transaction_process_queue)) { if (i2c_bus_manager) { - kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, &client->i2c_work); + if (!lwis_i2c_bus_manager_enqueue_transfer_request(i2c_bus_manager, + &client->lwis_dev)) { + kthread_queue_work(&i2c_bus_manager->i2c_bus_worker, + &client->i2c_work); + } else { + dev_err(client->lwis_dev->dev, "Cannot queue I2C transfer\n"); + } } else { kthread_queue_work(&client->lwis_dev->transaction_worker, &client->transaction_work); |