summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGe Bian <bian@google.com>2022-04-27 21:13:32 -0700
committerHolmes Chou <holmeschou@google.com>2023-02-01 14:19:55 +0000
commit988870a84109b8efbe4f4747f4af6e3776d0cfea (patch)
tree284f9502a651407cd7b691fbd00dfa3c2ba3e0dc
parent39c7f614f3cde8b0f6fd54770ceba30a23688415 (diff)
downloadlwis-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.h33
-rw-r--r--lwis_fence.c134
-rw-r--r--lwis_fence.h30
-rw-r--r--lwis_transaction.c84
-rw-r--r--lwis_transaction.h4
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);