diff options
author | Ge Bian <bian@google.com> | 2022-04-27 21:13:32 -0700 |
---|---|---|
committer | Holmes Chou <holmeschou@google.com> | 2023-02-01 14:19:55 +0000 |
commit | 988870a84109b8efbe4f4747f4af6e3776d0cfea (patch) | |
tree | 284f9502a651407cd7b691fbd00dfa3c2ba3e0dc | |
parent | 39c7f614f3cde8b0f6fd54770ceba30a23688415 (diff) | |
download | lwis-988870a84109b8efbe4f4747f4af6e3776d0cfea.tar.gz |
LWIS: Support submitting transaction triggered by a lwis_fence.
Support transactions triggered by one fence, whcih can be either can
existing lwis_fence or a placeholder fence.
Bug: 229024993
Test: unit test
Signed-off-by: Ge Bian <bian@google.com>
Change-Id: I7536218c49b732061ed2dc85a425428fa7670c79
-rw-r--r-- | lwis_commands.h | 33 | ||||
-rw-r--r-- | lwis_fence.c | 134 | ||||
-rw-r--r-- | lwis_fence.h | 30 | ||||
-rw-r--r-- | lwis_transaction.c | 84 | ||||
-rw-r--r-- | lwis_transaction.h | 4 |
5 files changed, 279 insertions, 6 deletions
diff --git a/lwis_commands.h b/lwis_commands.h index b4ab870..a024411 100644 --- a/lwis_commands.h +++ b/lwis_commands.h @@ -277,6 +277,38 @@ struct lwis_event_control_list { struct lwis_event_control *event_controls; }; +enum lwis_transaction_trigger_node_types { + LWIS_TRIGGER_EVENT, + LWIS_TRIGGER_FENCE, + LWIS_TRIGGER_FENCE_PLACEHOLDER +}; + +struct lwis_transaction_trigger_event { + int64_t id; + int64_t counter; +}; + +struct lwis_transaction_trigger_node { + int32_t type; //lwis_transaction_trigger_node_types + union { + int32_t fence_fd; + struct lwis_transaction_trigger_event event; + }; +}; + +enum lwis_transaction_trigger_node_operator { + LWIS_TRIGGER_NODE_OPERATOR_NONE, + LWIS_TRIGGER_NODE_OPERATOR_AND, + LWIS_TRIGGER_NODE_OPERATOR_OR, +}; + +#define LWIS_TRIGGER_NODES_MAX_NUM 16 +struct lwis_transaction_trigger_condition { + size_t num_nodes; + int32_t operator_type; //lwis_transaction_trigger_node_operator + struct lwis_transaction_trigger_node trigger_nodes[LWIS_TRIGGER_NODES_MAX_NUM]; +}; + // Invalid ID for Transaction id and Periodic IO id #define LWIS_ID_INVALID (-1LL) #define LWIS_EVENT_COUNTER_ON_NEXT_OCCURRENCE (-1LL) @@ -285,6 +317,7 @@ struct lwis_transaction_info { // Input int64_t trigger_event_id; int64_t trigger_event_counter; + struct lwis_transaction_trigger_condition trigger_condition; size_t num_io_entries; struct lwis_io_entry *io_entries; bool run_in_event_context; diff --git a/lwis_fence.c b/lwis_fence.c index 9cb0b8a..8c6e301 100644 --- a/lwis_fence.c +++ b/lwis_fence.c @@ -16,8 +16,10 @@ #include "lwis_device_top.h" #include "lwis_commands.h" #include "lwis_fence.h" +#include "lwis_transaction.h" #define LWIS_FENCE_DBG +#define HASH_CLIENT(x) hash_ptr(x, LWIS_CLIENTS_HASH_BITS) static int lwis_fence_release(struct inode *node, struct file *fp); static ssize_t lwis_fence_get_status(struct file *fp, char __user *user_buffer, size_t len, @@ -57,6 +59,7 @@ static int lwis_fence_release(struct inode *node, struct file *fp) static ssize_t lwis_fence_get_status(struct file *fp, char __user *user_buffer, size_t len, loff_t *offset) { + int status = 0; struct lwis_fence *lwis_fence = fp->private_data; int max_len, read_len; @@ -65,13 +68,16 @@ static ssize_t lwis_fence_get_status(struct file *fp, char __user *user_buffer, return -EFAULT; } - max_len = sizeof(lwis_fence->status) - *offset; + max_len = sizeof(status) - *offset; if (len > max_len) { len = max_len; } - read_len = len - copy_to_user((void __user *)user_buffer, - (void *)&lwis_fence->status + *offset, len); + mutex_lock(&lwis_fence->lock); + status = lwis_fence->status; + mutex_unlock(&lwis_fence->lock); + + read_len = len - copy_to_user((void __user *)user_buffer, (void *)&status + *offset, len); *offset += read_len; #ifdef LWIS_FENCE_DBG dev_info(lwis_fence->lwis_top_dev->dev, "lwis_fence fd-%d reading status = %d", @@ -86,7 +92,13 @@ static ssize_t lwis_fence_get_status(struct file *fp, char __user *user_buffer, static ssize_t lwis_fence_signal(struct file *fp, const char __user *user_buffer, size_t len, loff_t *offset) { + int status = 0; struct lwis_fence *lwis_fence = fp->private_data; + struct lwis_fence_trigger_transaction_list *tx_list; + /* Temporary vars for hash table traversal */ + struct hlist_node *n; + int i; + if (!lwis_fence) { dev_err(lwis_fence->lwis_top_dev->dev, "Cannot find lwis_fence instance\n"); return -EFAULT; @@ -94,16 +106,40 @@ static ssize_t lwis_fence_signal(struct file *fp, const char __user *user_buffer if (len != sizeof(lwis_fence->status)) { dev_err(lwis_fence->lwis_top_dev->dev, - "Signal lwis_fence with incorrect buffer length\n"); + "Signal lwis_fence fd-%d with incorrect buffer length\n", lwis_fence->fd); return -EINVAL; } - len = len - copy_from_user(&lwis_fence->status, (void __user *)user_buffer, len); + /* Set lwis_fence's status if not signaled */ + len = len - copy_from_user(&status, (void __user *)user_buffer, len); + mutex_lock(&lwis_fence->lock); + if (lwis_fence->status != LWIS_FENCE_STATUS_NOT_SIGNALED) { + /* Return error if fence is already signaled */ + dev_err(lwis_fence->lwis_top_dev->dev, + "Cannot signal a lwis_fence fd-%d already signaled, status is %d\n", + lwis_fence->fd, lwis_fence->status); + mutex_unlock(&lwis_fence->lock); + return -EINVAL; + } + lwis_fence->status = status; + mutex_unlock(&lwis_fence->lock); + wake_up_interruptible(&lwis_fence->status_wait_queue); #ifdef LWIS_FENCE_DBG dev_info(lwis_fence->lwis_top_dev->dev, "lwis_fence fd-%d setting status to %d", lwis_fence->fd, lwis_fence->status); #endif + + hash_for_each_safe (lwis_fence->transaction_list, i, n, tx_list, node) { + lwis_transaction_fence_trigger(tx_list->owner, lwis_fence, &tx_list->list); + if (!list_empty(&tx_list->list)) { + dev_err(lwis_fence->lwis_top_dev->dev, + "Fail to trigger all transactions\n"); + } + hash_del(&tx_list->node); + kfree(tx_list); + } + return len; } @@ -112,6 +148,7 @@ static ssize_t lwis_fence_signal(struct file *fp, const char __user *user_buffer */ static unsigned int lwis_fence_poll(struct file *fp, poll_table *wait) { + int status = 0; struct lwis_fence *lwis_fence = fp->private_data; if (!lwis_fence) { dev_err(lwis_fence->lwis_top_dev->dev, "Cannot find lwis_fence instance\n"); @@ -119,8 +156,13 @@ static unsigned int lwis_fence_poll(struct file *fp, poll_table *wait) } poll_wait(fp, &lwis_fence->status_wait_queue, wait); + + mutex_lock(&lwis_fence->lock); + status = lwis_fence->status; + mutex_unlock(&lwis_fence->lock); + /* Check if the fence is already signaled */ - if (lwis_fence->status != LWIS_FENCE_STATUS_NOT_SIGNALED) { + if (status != LWIS_FENCE_STATUS_NOT_SIGNALED) { #ifdef LWIS_FENCE_DBG dev_info(lwis_fence->lwis_top_dev->dev, "lwis_fence fd-%d poll return POLLIN", lwis_fence->fd); @@ -162,4 +204,84 @@ int lwis_fence_create(struct lwis_device *lwis_dev) dev_info(lwis_dev->dev, "lwis_fence created new LWIS fence fd: %d", new_fence->fd); #endif return fd_or_err; +} + +static struct lwis_fence_trigger_transaction_list *transaction_list_find(struct lwis_fence *fence, + struct lwis_client *owner) +{ + int hash_key = HASH_CLIENT(owner); + struct lwis_fence_trigger_transaction_list *tx_list; + hash_for_each_possible (fence->transaction_list, tx_list, node, hash_key) { + if (tx_list->owner == owner) { + return tx_list; + } + } + return NULL; +} + +static struct lwis_fence_trigger_transaction_list * +transaction_list_create(struct lwis_fence *fence, struct lwis_client *owner) +{ + struct lwis_fence_trigger_transaction_list *tx_list = + kmalloc(sizeof(struct lwis_fence_trigger_transaction_list), GFP_ATOMIC); + if (!tx_list) { + dev_err(fence->lwis_top_dev->dev, "Cannot allocate new event list\n"); + return NULL; + } + tx_list->owner = owner; + INIT_LIST_HEAD(&tx_list->list); + hash_add(fence->transaction_list, &tx_list->node, HASH_CLIENT(owner)); + return tx_list; +} + +static struct lwis_fence_trigger_transaction_list * +transaction_list_find_or_create(struct lwis_fence *fence, struct lwis_client *owner) +{ + struct lwis_fence_trigger_transaction_list *list = transaction_list_find(fence, owner); + return (list == NULL) ? transaction_list_create(fence, owner) : list; +} + +int lwis_trigger_fence_add_transaction(int fence_fd, struct lwis_client *client, + struct lwis_transaction *transaction) +{ + struct file *fp; + struct lwis_fence *lwis_fence; + struct lwis_fence_trigger_transaction_list *tx_list; + int ret = 0; + + fp = fget(fence_fd); + if (fp == NULL) { + dev_err(client->lwis_dev->dev, "Failed to find lwis_fence with fd %d\n", fence_fd); + return -EBADF; + } + lwis_fence = fp->private_data; + if (lwis_fence->fd != fence_fd) { + dev_err(client->lwis_dev->dev, + "Invalid lwis_fence with fd %d. Contains stale data \n", fence_fd); + return -EBADF; + } + + mutex_lock(&lwis_fence->lock); + if (lwis_fence->status == LWIS_FENCE_STATUS_NOT_SIGNALED) { + lwis_fence->fp = fp; + tx_list = transaction_list_find_or_create(lwis_fence, client); + list_add(&transaction->event_list_node, &tx_list->list); +#ifdef LWIS_FENCE_DBG + dev_info(client->lwis_dev->dev, + "lwis_fence transaction id %llu added to its trigger fence fd %d ", + transaction->info.id, lwis_fence->fd); +#endif + } else if (lwis_fence->status == 0) { + fput(fp); + ret = -EALREADY; + } else if (lwis_fence->status) { + fput(fp); + dev_err(client->lwis_dev->dev, + "Bad lwis_fence fd-%d already signaled with error code %d \n", fence_fd, + lwis_fence->status); + ret = -EINVAL; + } + mutex_unlock(&lwis_fence->lock); + + return ret; }
\ No newline at end of file diff --git a/lwis_fence.h b/lwis_fence.h index 33188ed..6b5f389 100644 --- a/lwis_fence.h +++ b/lwis_fence.h @@ -11,15 +11,30 @@ #ifndef LWIS_FENCE_H_ #define LWIS_FENCE_H_ +#include <linux/hashtable.h> +#include <linux/list.h> + #include "lwis_device.h" +#define LWIS_CLIENTS_HASH_BITS 8 + struct lwis_fence { int fd; + struct file *fp; int status; + struct mutex lock; /* Top device for printing logs */ struct lwis_device *lwis_top_dev; /* Status wait queue for waking up userspace */ wait_queue_head_t status_wait_queue; + /* Hash table of transactions that's triggered by this fence */ + DECLARE_HASHTABLE(transaction_list, LWIS_CLIENTS_HASH_BITS); +}; + +struct lwis_fence_trigger_transaction_list { + struct lwis_client *owner; + struct list_head list; + struct hlist_node node; }; /* @@ -27,4 +42,19 @@ struct lwis_fence { */ int lwis_fence_create(struct lwis_device *lwis_dev); +/* + * lwis_fence_get: Get the lwis_fence associated with the fd. + */ +struct lwis_device *lwis_fence_get(int fd); + +/* + * lwis_trigger_fence_add_transaction: Add the transaction to the trigger-fence's transactions list. + * Returns: 0 if fence is not signaled and transaction is added to the list + * -EBADFD if no lwis_fence is found with the fd + * -EALREADY if fence is already signaled OK and transaction should be turned into an immediate transaction + * -EINVAL if fence is signaled with error + */ +int lwis_trigger_fence_add_transaction(int fence_fd, struct lwis_client *client, + struct lwis_transaction *transaction); + #endif /* LWIS_IOCTL_H_ */
\ No newline at end of file diff --git a/lwis_transaction.c b/lwis_transaction.c index aeeb9cb..3300d89 100644 --- a/lwis_transaction.c +++ b/lwis_transaction.c @@ -19,8 +19,10 @@ #include <linux/workqueue.h> #include "lwis_allocator.h" +#include "lwis_commands.h" #include "lwis_device.h" #include "lwis_event.h" +#include "lwis_fence.h" #include "lwis_io_entry.h" #include "lwis_ioreg.h" #include "lwis_util.h" @@ -503,6 +505,19 @@ int lwis_transaction_client_cleanup(struct lwis_client *client) return 0; } +static bool lwis_transaction_triggered_by_fence(struct lwis_transaction *transaction) +{ + if (transaction->info.trigger_condition.num_nodes == 0) { + return false; + } + + if (transaction->info.trigger_condition.num_nodes == 1 && + transaction->info.trigger_condition.trigger_nodes[0].type == LWIS_TRIGGER_EVENT) { + return false; + } + return true; +} + static int check_transaction_param_locked(struct lwis_client *client, struct lwis_transaction *transaction, bool is_level_triggered) @@ -510,6 +525,8 @@ static int check_transaction_param_locked(struct lwis_client *client, struct lwis_device_event_state *event_state; struct lwis_transaction_info *info = &transaction->info; struct lwis_device *lwis_dev = client->lwis_dev; + int32_t fd_or_err; + int ret = 0; if (!client) { pr_err("Client is NULL while checking transaction parameter.\n"); @@ -524,6 +541,31 @@ static int check_transaction_param_locked(struct lwis_client *client, /* Initialize event counter return value */ info->current_trigger_event_counter = -1LL; + /* If triggered by a lwis_fence */ + if (lwis_transaction_triggered_by_fence(transaction)) { + // TODO(bian@): add supports for nested trigger expression. + if (transaction->info.trigger_condition.num_nodes > 1) { + dev_err(lwis_dev->dev, "Nested trigger expression is not supported yet\n"); + return -EINVAL; + } + + if (transaction->info.trigger_condition.trigger_nodes[0].type == + LWIS_TRIGGER_FENCE_PLACEHOLDER) { + fd_or_err = lwis_fence_create(lwis_dev); + if (fd_or_err < 0) { + return fd_or_err; + } + transaction->info.trigger_condition.trigger_nodes[0].fence_fd = fd_or_err; + } + + ret = lwis_trigger_fence_add_transaction( + transaction->info.trigger_condition.trigger_nodes[0].fence_fd, client, + transaction); + if (ret) { + return ret; + } + } + /* Look for the trigger event state, if specified */ if (info->trigger_event_id != LWIS_EVENT_ID_NONE) { event_state = lwis_device_event_state_find(lwis_dev, info->trigger_event_id); @@ -620,6 +662,13 @@ static int queue_transaction_locked(struct lwis_client *client, struct lwis_transaction_event_list *event_list; struct lwis_transaction_info *info = &transaction->info; + if (lwis_transaction_triggered_by_fence(transaction)) { + /* Trigger by lwis_fence, nothing to be done here */ + info->submission_timestamp_ns = ktime_to_ns(ktime_get()); + client->transaction_counter++; + return 0; + } + if (info->trigger_event_id == LWIS_EVENT_ID_NONE) { /* Immediate trigger. */ if (info->run_at_real_time) { @@ -796,6 +845,41 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id, return 0; } +void lwis_transaction_fence_trigger(struct lwis_client *client, struct lwis_fence *fence, + struct list_head *transaction_list) +{ + unsigned long flags = 0; + struct lwis_transaction *transaction; + struct list_head *it_tran, *it_tran_tmp; + + if (list_empty(transaction_list)) { + return; + } + + spin_lock_irqsave(&client->transaction_lock, flags); + list_for_each_safe (it_tran, it_tran_tmp, transaction_list) { + transaction = list_entry(it_tran, struct lwis_transaction, event_list_node); + list_del(&transaction->event_list_node); + if (fence->status == 0) { + /* TODO(bian@): Ignore the transaction context flags for now. */ + list_add_tail(&transaction->process_queue_node, + &client->transaction_process_queue); + } else { + cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL); + } + dev_info(client->lwis_dev->dev, "lwis_fence fd-%d triggered transaction id %llu", + fence->fd, transaction->info.id); + fput(fence->fp); + } + + /* Schedule deferred transactions */ + if (!list_empty(&client->transaction_process_queue)) { + kthread_queue_work(&client->lwis_dev->transaction_worker, + &client->transaction_work); + } + spin_unlock_irqrestore(&client->transaction_lock, flags); +} + /* Calling this function requires holding the client's transaction_lock. */ static int cancel_waiting_transaction_locked(struct lwis_client *client, int64_t id) { diff --git a/lwis_transaction.h b/lwis_transaction.h index 2d6157e..8b645b0 100644 --- a/lwis_transaction.h +++ b/lwis_transaction.h @@ -16,6 +16,7 @@ /* LWIS forward declarations */ struct lwis_device; struct lwis_client; +struct lwis_fence; /* Transaction entry. Each entry belongs to two queues: * 1) Event list: Transactions are sorted by event IDs. This is to search for @@ -53,6 +54,9 @@ int lwis_transaction_client_cleanup(struct lwis_client *client); int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id, int64_t event_counter, struct list_head *pending_events, bool in_irq); +void lwis_transaction_fence_trigger(struct lwis_client *client, struct lwis_fence *fence, + struct list_head *transaction_list); + int lwis_transaction_cancel(struct lwis_client *client, int64_t id); void lwis_transaction_free(struct lwis_device *lwis_dev, struct lwis_transaction *transaction); |