diff options
Diffstat (limited to 'lwis_transaction.c')
-rw-r--r-- | lwis_transaction.c | 481 |
1 files changed, 354 insertions, 127 deletions
diff --git a/lwis_transaction.c b/lwis_transaction.c index 434846c..1016af5 100644 --- a/lwis_transaction.c +++ b/lwis_transaction.c @@ -15,11 +15,15 @@ #include <linux/delay.h> #include <linux/kernel.h> #include <linux/mm.h> +#include <linux/preempt.h> #include <linux/slab.h> +#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" @@ -30,6 +34,9 @@ #define EXPLICIT_EVENT_COUNTER(x) \ ((x) != LWIS_EVENT_COUNTER_ON_NEXT_OCCURRENCE && (x) != LWIS_EVENT_COUNTER_EVERY_TIME) +bool lwis_transaction_debug; +module_param(lwis_transaction_debug, bool, 0644); + static struct lwis_transaction_event_list *event_list_find(struct lwis_client *client, int64_t event_id) { @@ -64,8 +71,34 @@ static struct lwis_transaction_event_list *event_list_find_or_create(struct lwis return (list == NULL) ? event_list_create(client, event_id) : list; } +static void add_pending_transaction(struct lwis_client *client, + struct lwis_transaction *transaction) +{ + hash_add(client->pending_transactions, &transaction->pending_map_node, + transaction->info.id); + if (lwis_fence_debug) { + dev_info(client->lwis_dev->dev, + "lwis_fence add transaction id %llu to lwis_client pending map", + transaction->info.id); + } +} + +static struct lwis_transaction *pending_transaction_peek(struct lwis_client *client, + int64_t transaction_id) +{ + struct hlist_node *tmp; + struct lwis_transaction *transaction; + hash_for_each_possible_safe (client->pending_transactions, transaction, tmp, + pending_map_node, transaction_id) { + if (transaction->info.id == transaction_id) { + return transaction; + } + } + return NULL; +} + static void save_transaction_to_history(struct lwis_client *client, - struct lwis_transaction_info *trans_info, + struct lwis_transaction_info_v2 *trans_info, int64_t process_timestamp, int64_t process_duration_ns) { client->debug_info.transaction_hist[client->debug_info.cur_transaction_hist_idx].info = @@ -83,6 +116,28 @@ static void save_transaction_to_history(struct lwis_client *client, void lwis_transaction_free(struct lwis_device *lwis_dev, struct lwis_transaction *transaction) { int i; + struct lwis_fence_pending_signal *pending_fence; + struct list_head *it_fence, *it_fence_tmp; + + if (transaction->is_weak_transaction) { + kfree(transaction); + return; + } + + if (!list_empty(&transaction->completion_fence_list)) { + list_for_each_safe (it_fence, it_fence_tmp, &transaction->completion_fence_list) { + pending_fence = + list_entry(it_fence, struct lwis_fence_pending_signal, node); + list_del(&pending_fence->node); + fput(pending_fence->fp); + kfree(pending_fence); + } + } + + for (i = 0; i < transaction->num_trigger_fences; i++) { + fput(transaction->trigger_fence_fps[i]); + transaction->trigger_fence_fps[i] = NULL; + } for (i = 0; i < transaction->info.num_io_entries; ++i) { if (transaction->info.io_entries[i].type == LWIS_IO_ENTRY_WRITE_BATCH) { @@ -98,22 +153,28 @@ void lwis_transaction_free(struct lwis_device *lwis_dev, struct lwis_transaction } static int process_transaction(struct lwis_client *client, struct lwis_transaction *transaction, - struct list_head *pending_events, bool in_irq, bool skip_err) + struct list_head *pending_events, struct list_head *pending_fences, + bool skip_err) { int i; int ret = 0; + int pending_status; struct lwis_io_entry *entry = NULL; struct lwis_device *lwis_dev = client->lwis_dev; - struct lwis_transaction_info *info = &transaction->info; + struct lwis_transaction_info_v2 *info = &transaction->info; struct lwis_transaction_response_header *resp = transaction->resp; size_t resp_size; uint8_t *read_buf; struct lwis_io_result *io_result; const int reg_value_bytewidth = lwis_dev->native_value_bitwidth / 8; - int64_t process_duration_ns = 0; - int64_t process_timestamp = ktime_to_ns(lwis_get_time()); + int64_t process_duration_ns = -1; + int64_t process_timestamp = -1; + unsigned long flags; + + if (lwis_transaction_debug) { + process_timestamp = ktime_to_ns(lwis_get_time()); + } - LWIS_ATRACE_FUNC_BEGIN(lwis_dev); resp_size = sizeof(struct lwis_transaction_response_header) + resp->results_size_bytes; read_buf = (uint8_t *)resp + sizeof(struct lwis_transaction_response_header); resp->completion_index = -1; @@ -136,9 +197,10 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti if (ret) { resp->error_code = ret; if (skip_err) { - dev_warn(lwis_dev->dev, - "transaction type %d processing failed, skip this error and run the next command\n", - entry->type); + dev_warn( + lwis_dev->dev, + "transaction type %d processing failed, skip this error and run the next command\n", + entry->type); continue; } break; @@ -153,9 +215,10 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti if (ret) { resp->error_code = ret; if (skip_err) { - dev_warn(lwis_dev->dev, - "transaction type %d processing failed, skip this error and run the next command\n", - entry->type); + dev_warn( + lwis_dev->dev, + "transaction type %d processing failed, skip this error and run the next command\n", + entry->type); continue; } break; @@ -173,22 +236,24 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti if (ret) { resp->error_code = ret; if (skip_err) { - dev_warn(lwis_dev->dev, - "transaction type %d processing failed, skip this error and run the next command\n", - entry->type); + dev_warn( + lwis_dev->dev, + "transaction type %d processing failed, skip this error and run the next command\n", + entry->type); continue; } break; } read_buf += sizeof(struct lwis_io_result) + io_result->num_value_bytes; } else if (entry->type == LWIS_IO_ENTRY_POLL) { - ret = lwis_io_entry_poll(lwis_dev, entry, in_irq); + ret = lwis_io_entry_poll(lwis_dev, entry); if (ret) { resp->error_code = ret; if (skip_err) { - dev_warn(lwis_dev->dev, - "transaction type %d processing failed, skip this error and run the next command\n", - entry->type); + dev_warn( + lwis_dev->dev, + "transaction type %d processing failed, skip this error and run the next command\n", + entry->type); continue; } break; @@ -198,9 +263,10 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti if (ret) { resp->error_code = ret; if (skip_err) { - dev_warn(lwis_dev->dev, - "transaction type %d processing failed, skip this error and run the next command\n", - entry->type); + dev_warn( + lwis_dev->dev, + "transaction type %d processing failed, skip this error and run the next command\n", + entry->type); continue; } break; @@ -209,9 +275,10 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti dev_err(lwis_dev->dev, "Unrecognized io_entry command\n"); resp->error_code = -EINVAL; if (skip_err) { - dev_warn(lwis_dev->dev, - "transaction type %d processing failed, skip this error and run the next command\n", - entry->type); + dev_warn( + lwis_dev->dev, + "transaction type %d processing failed, skip this error and run the next command\n", + entry->type); continue; } break; @@ -219,7 +286,9 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti resp->completion_index = i; } - process_duration_ns = ktime_to_ns(lwis_get_time() - process_timestamp); + if (lwis_transaction_debug) { + process_duration_ns = ktime_to_ns(lwis_get_time() - process_timestamp); + } /* Use read memory barrier at the end of I/O entries if the access protocol * allows it */ @@ -230,7 +299,7 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti if (pending_events) { lwis_pending_event_push(pending_events, resp->error_code ? info->emit_error_event_id : - info->emit_success_event_id, + info->emit_success_event_id, (void *)resp, resp_size); } else { /* No pending events indicates it's cleanup io_entries. */ @@ -240,6 +309,13 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti resp->error_code, transaction->info.id, i, entry->type); } } + + spin_lock_irqsave(&client->transaction_lock, flags); + if (pending_fences) { + /* Convert -ECANCELED error code to userspace Cancellation error code */ + pending_status = resp->error_code == -ECANCELED ? 1 : resp->error_code; + lwis_pending_fences_move_all(lwis_dev, transaction, pending_fences, pending_status); + } save_transaction_to_history(client, info, process_timestamp, process_duration_ns); if (info->trigger_event_counter == LWIS_EVENT_COUNTER_EVERY_TIME) { /* Only clean the transaction struct for this iteration. The @@ -249,14 +325,17 @@ static int process_transaction(struct lwis_client *client, struct lwis_transacti } else { lwis_transaction_free(lwis_dev, transaction); } - LWIS_ATRACE_FUNC_END(lwis_dev); + spin_unlock_irqrestore(&client->transaction_lock, flags); + return ret; } static void cancel_transaction(struct lwis_device *lwis_dev, struct lwis_transaction *transaction, - int error_code, struct list_head *pending_events) + int error_code, struct list_head *pending_events, + struct list_head *pending_fences) { - struct lwis_transaction_info *info = &transaction->info; + int pending_status; + struct lwis_transaction_info_v2 *info = &transaction->info; struct lwis_transaction_response_header resp; resp.id = info->id; resp.error_code = error_code; @@ -264,22 +343,33 @@ static void cancel_transaction(struct lwis_device *lwis_dev, struct lwis_transac resp.results_size_bytes = 0; resp.completion_index = -1; + if (transaction->is_weak_transaction) { + lwis_transaction_free(lwis_dev, transaction); + return; + } + if (pending_events) { lwis_pending_event_push(pending_events, info->emit_error_event_id, &resp, sizeof(resp)); } + if (pending_fences) { + /* Convert -ECANCELED error code to userspace Cancellation error code */ + pending_status = error_code == -ECANCELED ? 1 : error_code; + lwis_pending_fences_move_all(lwis_dev, transaction, pending_fences, pending_status); + } lwis_transaction_free(lwis_dev, transaction); } -static void transaction_work_func(struct kthread_work *work) +void lwis_process_transactions_in_queue(struct lwis_client *client) { unsigned long flags; - struct lwis_client *client = container_of(work, struct lwis_client, transaction_work); struct list_head *it_tran, *it_tran_tmp; struct list_head pending_events; + struct list_head pending_fences; struct lwis_transaction *transaction; INIT_LIST_HEAD(&pending_events); + INIT_LIST_HEAD(&pending_fences); spin_lock_irqsave(&client->transaction_lock, flags); list_for_each_safe (it_tran, it_tran_tmp, &client->transaction_process_queue) { @@ -287,27 +377,27 @@ static void transaction_work_func(struct kthread_work *work) list_del(&transaction->process_queue_node); if (transaction->resp->error_code) { cancel_transaction(client->lwis_dev, transaction, - transaction->resp->error_code, &pending_events); + transaction->resp->error_code, &pending_events, + &pending_fences); } else { spin_unlock_irqrestore(&client->transaction_lock, flags); - process_transaction(client, transaction, &pending_events, - /*in_irq=*/false, + process_transaction(client, transaction, &pending_events, &pending_fences, /*skip_err=*/false); spin_lock_irqsave(&client->transaction_lock, flags); } } spin_unlock_irqrestore(&client->transaction_lock, flags); - - lwis_pending_events_emit(client->lwis_dev, &pending_events, /*in_irq=*/false); + lwis_pending_events_emit(client->lwis_dev, &pending_events); + lwis_fences_pending_signal_emit(client->lwis_dev, &pending_fences); } int lwis_transaction_init(struct lwis_client *client) { spin_lock_init(&client->transaction_lock); INIT_LIST_HEAD(&client->transaction_process_queue); - kthread_init_work(&client->transaction_work, transaction_work_func); client->transaction_counter = 0; hash_init(client->transaction_list); + hash_init(client->pending_transactions); return 0; } @@ -325,7 +415,7 @@ int lwis_transaction_clear(struct lwis_client *client) } static void cancel_all_transactions_in_queue_locked(struct lwis_client *client, - struct list_head *transaction_queue) + struct list_head *transaction_queue) { struct lwis_transaction *transaction; struct list_head *it_tran, *it_tran_tmp; @@ -336,7 +426,7 @@ static void cancel_all_transactions_in_queue_locked(struct lwis_client *client, transaction = list_entry(it_tran, struct lwis_transaction, process_queue_node); list_del(&transaction->process_queue_node); - cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL); + cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL, NULL); } } } @@ -364,11 +454,15 @@ int lwis_transaction_client_flush(struct lwis_client *client) list_for_each_safe (it_tran, it_tran_tmp, &it_evt_list->list) { transaction = list_entry(it_tran, struct lwis_transaction, event_list_node); list_del(&transaction->event_list_node); - cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL); + cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL, NULL); } hash_del(&it_evt_list->node); kfree(it_evt_list); } + hash_for_each_safe (client->pending_transactions, i, tmp, transaction, pending_map_node) { + cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL, NULL); + hash_del(&transaction->pending_map_node); + } spin_unlock_irqrestore(&client->transaction_lock, flags); if (client->lwis_dev->transaction_worker_thread) @@ -383,36 +477,12 @@ int lwis_transaction_client_flush(struct lwis_client *client) return 0; } -static void print_buffer(struct lwis_client *client, const char *buf, int size) { - char output_string[80], temp_string[8]; - int i; - - output_string[0] = '\0'; - for (i = 0; i < size; i++) { - if (i % 16 == 0 && i != 0) { - dev_info(client->lwis_dev->dev, "%s\n", output_string); - output_string[0] = '\0'; - } - sprintf(temp_string, "%02X ", buf[i]); - if (i % 4 == 3) { - strcat(temp_string, " "); - } - strcat(output_string, temp_string); - } - dev_info(client->lwis_dev->dev, "%s\n", output_string); -} - int lwis_transaction_client_cleanup(struct lwis_client *client) { unsigned long flags; struct list_head *it_tran, *it_tran_tmp; struct lwis_transaction *transaction; struct lwis_transaction_event_list *it_evt_list; - struct list_head pending_events; - bool in_irq = false; - struct lwis_event_entry *event; - - INIT_LIST_HEAD(&pending_events); spin_lock_irqsave(&client->transaction_lock, flags); /* Perform client defined clean-up routine. */ @@ -426,12 +496,20 @@ int lwis_transaction_client_cleanup(struct lwis_client *client) list_for_each_safe (it_tran, it_tran_tmp, &it_evt_list->list) { transaction = list_entry(it_tran, struct lwis_transaction, event_list_node); + if (!list_empty(&transaction->completion_fence_list)) { + dev_warn( + client->lwis_dev->dev, + "Cleanup transaction with id %llu has tailing fences; cleanup transactions should not have tailing fences", + transaction->info.id); + } list_del(&transaction->event_list_node); if (transaction->resp->error_code || client->lwis_dev->enabled == 0) { - cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL); + cancel_transaction(client->lwis_dev, transaction, -ECANCELED, NULL, NULL); } else { spin_unlock_irqrestore(&client->transaction_lock, flags); - process_transaction(client, transaction, &pending_events, in_irq, + process_transaction(client, transaction, + /*pending_events=*/NULL, + /*pending_fences=*/NULL, /*skip_err=*/true); spin_lock_irqsave(&client->transaction_lock, flags); } @@ -440,52 +518,55 @@ int lwis_transaction_client_cleanup(struct lwis_client *client) kfree(it_evt_list); spin_unlock_irqrestore(&client->transaction_lock, flags); + return 0; +} - while (!list_empty(&pending_events)) { - struct lwis_transaction_response_header *resp; - struct lwis_io_result *result; - char *results_buf; - int i; - - event = list_first_entry(&pending_events, struct lwis_event_entry, node); - /* - If there is no read result, the payload size will less than - sizeof(struct lwis_transaction_response_header). - We can skip it. - */ - if (event->event_info.payload_size <= - sizeof(struct lwis_transaction_response_header)) { - list_del(&event->node); - kfree(event); - continue; - } - resp = (struct lwis_transaction_response_header *) - event->event_info.payload_buffer; - dev_info(client->lwis_dev->dev, "event_id = %llx, num_entries = %ld\n", - event->event_info.event_id, resp->num_entries); - results_buf = (char *)event->event_info.payload_buffer + - sizeof(struct lwis_transaction_response_header); - for (i = 0; i < resp->num_entries; i++) { - dev_info(client->lwis_dev->dev, "entry-%d\n", i); - result = (struct lwis_io_result *)results_buf; - print_buffer(client, result->values, result->num_value_bytes); - results_buf += sizeof(struct lwis_io_result) + - result->num_value_bytes; - } +int lwis_trigger_event_add_weak_transaction(struct lwis_client *client, int64_t transaction_id, + int64_t event_id, int32_t precondition_fence_fd) +{ + struct lwis_transaction *weak_transaction; + struct lwis_transaction_event_list *event_list; - list_del(&event->node); - kfree(event); + weak_transaction = kmalloc(sizeof(struct lwis_transaction), GFP_ATOMIC); + if (!weak_transaction) { + dev_err(client->lwis_dev->dev, "Cannot allocate weak transaction\n"); + return -ENOMEM; + } + weak_transaction->is_weak_transaction = true; + weak_transaction->id = transaction_id; + if (precondition_fence_fd >= 0) { + weak_transaction->precondition_fence_fp = fget(precondition_fence_fd); + if (weak_transaction->precondition_fence_fp == NULL) { + dev_err(client->lwis_dev->dev, + "Precondition fence %d results in NULL file pointer", + precondition_fence_fd); + return -EINVAL; + } + } else { + weak_transaction->precondition_fence_fp = NULL; } + event_list = event_list_find_or_create(client, event_id); + if (!event_list) { + dev_err(client->lwis_dev->dev, "Cannot create transaction event list\n"); + return -EINVAL; + } + list_add_tail(&weak_transaction->event_list_node, &event_list->list); + if (lwis_fence_debug) { + dev_info( + client->lwis_dev->dev, + "lwis_fence add weak transaction for event id-%lld triggered transaction id %llu", + event_id, transaction_id); + } return 0; } static int check_transaction_param_locked(struct lwis_client *client, struct lwis_transaction *transaction, - bool allow_counter_eq) + bool is_level_triggered) { struct lwis_device_event_state *event_state; - struct lwis_transaction_info *info = &transaction->info; + struct lwis_transaction_info_v2 *info = &transaction->info; struct lwis_device *lwis_dev = client->lwis_dev; if (!client) { @@ -498,6 +579,8 @@ static int check_transaction_param_locked(struct lwis_client *client, return -ENODEV; } + /* Assign the transaction id and increment transaction counter */ + info->id = client->transaction_counter++; /* Initialize event counter return value */ info->current_trigger_event_counter = -1LL; @@ -512,6 +595,9 @@ static int check_transaction_param_locked(struct lwis_client *client, /* Event found, return current counter to userspace */ info->current_trigger_event_counter = event_state->event_counter; } + } else if (!lwis_triggered_by_condition(transaction)) { + /* Otherwise it's an immediate transaction */ + transaction->queue_immediately = true; } /* Both trigger event ID and counter are defined */ @@ -519,10 +605,10 @@ static int check_transaction_param_locked(struct lwis_client *client, EXPLICIT_EVENT_COUNTER(info->trigger_event_counter)) { /* Check if event has happened already */ if (info->trigger_event_counter == info->current_trigger_event_counter) { - if (allow_counter_eq) { + if (is_level_triggered) { /* Convert this transaction into an immediate * one */ - info->trigger_event_id = LWIS_EVENT_ID_NONE; + transaction->queue_immediately = true; } else { return -ENOENT; } @@ -547,17 +633,34 @@ static int check_transaction_param_locked(struct lwis_client *client, return 0; } +static int prepare_transaction_fences_locked(struct lwis_client *client, + struct lwis_transaction *transaction) +{ + int ret = 0; + + /* If triggered by trigger_condition */ + if (lwis_triggered_by_condition(transaction)) { + ret = lwis_parse_trigger_condition(client, transaction); + if (ret) { + return ret; + } + } + + /* If transaction contains completion fences, add them to the transaction */ + ret = lwis_add_completion_fence(client, transaction); + + return ret; +} + static int prepare_response_locked(struct lwis_client *client, struct lwis_transaction *transaction) { - struct lwis_transaction_info *info = &transaction->info; + struct lwis_transaction_info_v2 *info = &transaction->info; int i; size_t resp_size; size_t read_buf_size = 0; int read_entries = 0; const int reg_value_bytewidth = client->lwis_dev->native_value_bitwidth / 8; - info->id = client->transaction_counter; - for (i = 0; i < info->num_io_entries; ++i) { struct lwis_io_entry *entry = &info->io_entries[i]; if (entry->type == LWIS_IO_ENTRY_READ) { @@ -595,13 +698,16 @@ static int queue_transaction_locked(struct lwis_client *client, struct lwis_transaction *transaction) { struct lwis_transaction_event_list *event_list; - struct lwis_transaction_info *info = &transaction->info; + struct lwis_transaction_info_v2 *info = &transaction->info; - if (info->trigger_event_id == LWIS_EVENT_ID_NONE) { + if (transaction->queue_immediately) { /* Immediate trigger. */ list_add_tail(&transaction->process_queue_node, &client->transaction_process_queue); kthread_queue_work(&client->lwis_dev->transaction_worker, &client->transaction_work); + } else if (lwis_triggered_by_condition(transaction)) { + /* Trigger by trigger conditions. */ + add_pending_transaction(client, transaction); } else { /* Trigger by event. */ event_list = event_list_find_or_create(client, info->trigger_event_id); @@ -613,18 +719,16 @@ static int queue_transaction_locked(struct lwis_client *client, list_add_tail(&transaction->event_list_node, &event_list->list); } info->submission_timestamp_ns = ktime_to_ns(ktime_get()); - client->transaction_counter++; return 0; } int lwis_transaction_submit_locked(struct lwis_client *client, struct lwis_transaction *transaction) { int ret; - struct lwis_transaction_info *info = &transaction->info; + struct lwis_transaction_info_v2 *info = &transaction->info; ret = check_transaction_param_locked(client, transaction, - /*allow_counter_eq=*/info->allow_counter_eq); - + /*is_level_triggered=*/info->is_level_triggered); if (ret) { return ret; } @@ -634,6 +738,11 @@ int lwis_transaction_submit_locked(struct lwis_client *client, struct lwis_trans return ret; } + ret = prepare_transaction_fences_locked(client, transaction); + if (ret) { + return ret; + } + ret = queue_transaction_locked(client, transaction); return ret; } @@ -652,7 +761,7 @@ new_repeating_transaction_iteration(struct lwis_client *client, "Failed to allocate repeating transaction instance\n"); return NULL; } - memcpy(&new_instance->info, &transaction->info, sizeof(struct lwis_transaction_info)); + memcpy(&new_instance->info, &transaction->info, sizeof(transaction->info)); /* Allocate response buffer */ resp_buf = kmalloc(sizeof(struct lwis_transaction_response_header) + @@ -669,14 +778,15 @@ new_repeating_transaction_iteration(struct lwis_client *client, INIT_LIST_HEAD(&new_instance->event_list_node); INIT_LIST_HEAD(&new_instance->process_queue_node); + INIT_LIST_HEAD(&new_instance->completion_fence_list); return new_instance; } static void defer_transaction_locked(struct lwis_client *client, struct lwis_transaction *transaction, - struct list_head *pending_events, bool in_irq, - bool del_event_list_node) + struct list_head *pending_events, + struct list_head *pending_fences, bool del_event_list_node) { unsigned long flags = 0; if (del_event_list_node) { @@ -684,14 +794,14 @@ static void defer_transaction_locked(struct lwis_client *client, } /* I2C read/write cannot be executed in IRQ context */ - if (in_irq && client->lwis_dev->type == DEVICE_TYPE_I2C) { + if (in_irq() && client->lwis_dev->type == DEVICE_TYPE_I2C) { list_add_tail(&transaction->process_queue_node, &client->transaction_process_queue); return; } if (transaction->info.run_in_event_context) { spin_unlock_irqrestore(&client->transaction_lock, flags); - process_transaction(client, transaction, pending_events, in_irq, + process_transaction(client, transaction, pending_events, pending_fences, /*skip_err=*/false); spin_lock_irqsave(&client->transaction_lock, flags); } else { @@ -700,18 +810,23 @@ static void defer_transaction_locked(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) + int64_t event_counter, struct list_head *pending_events) { unsigned long flags; struct lwis_transaction_event_list *event_list; struct list_head *it_tran, *it_tran_tmp; - struct lwis_transaction *transaction; + struct lwis_transaction *transaction, *weak_transaction = NULL; struct lwis_transaction *new_instance; int64_t trigger_counter = 0; + struct list_head pending_fences; + + INIT_LIST_HEAD(&pending_fences); /* Find event list that matches the trigger event ID. */ spin_lock_irqsave(&client->transaction_lock, flags); + if (event_id & LWIS_OVERFLOW_IRQ_EVENT_FLAG) { + event_id = event_id ^ LWIS_OVERFLOW_IRQ_EVENT_FLAG; + } event_list = event_list_find(client, event_id); /* No event found, just return. */ if (event_list == NULL || list_empty(&event_list->list)) { @@ -722,6 +837,33 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id, /* Go through all transactions under the chosen event list. */ list_for_each_safe (it_tran, it_tran_tmp, &event_list->list) { transaction = list_entry(it_tran, struct lwis_transaction, event_list_node); + + if (transaction->is_weak_transaction) { + weak_transaction = transaction; + transaction = pending_transaction_peek(client, weak_transaction->id); + if (transaction == NULL) { + /* It means the transaction is already executed or is canceled */ + list_del(&weak_transaction->event_list_node); + kfree(weak_transaction); + continue; + } + + if (lwis_event_triggered_condition_ready(transaction, weak_transaction, + event_id, event_counter)) { + if (lwis_fence_debug) { + dev_info( + client->lwis_dev->dev, + "lwis_fence event id-%lld counter-%lld triggered transaction id %llu", + event_id, event_counter, transaction->info.id); + } + hash_del(&transaction->pending_map_node); + defer_transaction_locked(client, transaction, pending_events, + &pending_fences, + /* del_event_list_node */ false); + } + continue; + } + if (transaction->resp->error_code) { list_add_tail(&transaction->process_queue_node, &client->transaction_process_queue); @@ -734,8 +876,8 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id, trigger_counter = transaction->info.trigger_event_counter; if (trigger_counter == LWIS_EVENT_COUNTER_ON_NEXT_OCCURRENCE || trigger_counter == event_counter) { - defer_transaction_locked(client, transaction, pending_events, in_irq, - /* del_event_list_node */ true); + defer_transaction_locked(client, transaction, pending_events, + &pending_fences, /* del_event_list_node */ true); } else if (trigger_counter == LWIS_EVENT_COUNTER_EVERY_TIME) { new_instance = new_repeating_transaction_iteration(client, transaction); if (!new_instance) { @@ -745,8 +887,8 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id, list_del(&transaction->event_list_node); continue; } - defer_transaction_locked(client, new_instance, pending_events, in_irq, - /* del_event_list_node */ false); + defer_transaction_locked(client, new_instance, pending_events, + &pending_fences, /* del_event_list_node */ false); } } @@ -758,9 +900,77 @@ int lwis_transaction_event_trigger(struct lwis_client *client, int64_t event_id, spin_unlock_irqrestore(&client->transaction_lock, flags); + lwis_fences_pending_signal_emit(client->lwis_dev, &pending_fences); + 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_pending_transaction_id *transaction_id; + struct lwis_transaction *transaction; + struct list_head *it_tran, *it_tran_tmp; + struct list_head pending_events; + struct list_head pending_fences; + + if (list_empty(transaction_list)) { + return; + } + + INIT_LIST_HEAD(&pending_events); + INIT_LIST_HEAD(&pending_fences); + + spin_lock_irqsave(&client->transaction_lock, flags); + list_for_each_safe (it_tran, it_tran_tmp, transaction_list) { + transaction_id = list_entry(it_tran, struct lwis_pending_transaction_id, list_node); + list_del(&transaction_id->list_node); + + transaction = pending_transaction_peek(client, transaction_id->id); + if (transaction == NULL) { + /* It means the transaction is already executed or is canceled */ + if (lwis_fence_debug) { + dev_info( + client->lwis_dev->dev, + "lwis_fence fd-%d did NOT triggered transaction id %llu, seems already triggered", + fence->fd, transaction_id->id); + } + } else { + if (lwis_fence_triggered_condition_ready(transaction, fence->status)) { + hash_del(&transaction->pending_map_node); + if (fence->status == 0) { + list_add_tail(&transaction->process_queue_node, + &client->transaction_process_queue); + if (lwis_fence_debug) { + dev_info( + client->lwis_dev->dev, + "lwis_fence fd-%d triggered transaction id %llu", + fence->fd, transaction->info.id); + } + } else { + cancel_transaction(client->lwis_dev, transaction, + -ECANCELED, &pending_events, + &pending_fences); + } + } + } + + kfree(transaction_id); + } + + /* 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); + + lwis_pending_events_emit(client->lwis_dev, &pending_events); + lwis_fences_pending_signal_emit(client->lwis_dev, &pending_fences); +} + /* Calling this function requires holding the client's transaction_lock. */ static int cancel_waiting_transaction_locked(struct lwis_client *client, int64_t id) { @@ -770,6 +980,7 @@ static int cancel_waiting_transaction_locked(struct lwis_client *client, int64_t struct lwis_transaction_event_list *it_evt_list; struct lwis_transaction *transaction; + /* Search transactions triggered by events */ hash_for_each_safe (client->transaction_list, i, tmp, it_evt_list, node) { list_for_each_safe (it_tran, it_tran_tmp, &it_evt_list->list) { transaction = list_entry(it_tran, struct lwis_transaction, event_list_node); @@ -779,6 +990,16 @@ static int cancel_waiting_transaction_locked(struct lwis_client *client, int64_t } } } + + /* Search transactions triggered by trigger_condition */ + hash_for_each_possible_safe (client->pending_transactions, transaction, tmp, + pending_map_node, id) { + if (transaction->info.id == id) { + transaction->resp->error_code = -ECANCELED; + return 0; + } + } + return -ENOENT; } @@ -798,14 +1019,15 @@ int lwis_transaction_replace_locked(struct lwis_client *client, struct lwis_transaction *transaction) { int ret; + int64_t old_transaction_id = transaction->info.id; ret = check_transaction_param_locked(client, transaction, - /*allow_counter_eq=*/false); + /*is_level_triggered=*/false); if (ret) { return ret; } - ret = cancel_waiting_transaction_locked(client, transaction->info.id); + ret = cancel_waiting_transaction_locked(client, old_transaction_id); if (ret) { return ret; } @@ -815,6 +1037,11 @@ int lwis_transaction_replace_locked(struct lwis_client *client, return ret; } + ret = prepare_transaction_fences_locked(client, transaction); + if (ret) { + return ret; + } + ret = queue_transaction_locked(client, transaction); return ret; } |