diff options
author | Ge Bian <bian@google.com> | 2022-04-27 21:13:32 -0700 |
---|---|---|
committer | Ge Bian <bian@google.com> | 2022-05-04 18:59:08 -0700 |
commit | 8184d61b777888730df8f6417408b4301ff61dca (patch) | |
tree | 77b7596c68b6daa918cacbef2b2b3e6a0521a3a7 /lwis_fence.c | |
parent | 590d3818f98cd42b3b2d4c67c3ca8a90fd89075e (diff) | |
download | lwis-8184d61b777888730df8f6417408b4301ff61dca.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
Change-Id: I7536218c49b732061ed2dc85a425428fa7670c79
Diffstat (limited to 'lwis_fence.c')
-rw-r--r-- | lwis_fence.c | 134 |
1 files changed, 128 insertions, 6 deletions
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 |