// SPDX-License-Identifier: GPL-2.0+/* * SSH request transport layer. * * Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com> */ #include <linux/unaligned.h>#include <linux/atomic.h>#include <linux/completion.h>#include <linux/error-injection.h>#include <linux/ktime.h>#include <linux/limits.h>#include <linux/list.h>#include <linux/slab.h>#include <linux/spinlock.h>#include <linux/types.h>#include <linux/workqueue.h> #include <linux/surface_aggregator/serial_hub.h>#include <linux/surface_aggregator/controller.h> #include "ssh_packet_layer.h"#include "ssh_request_layer.h" #include "trace.h" /* * SSH_RTL_REQUEST_TIMEOUT - Request timeout. * * Timeout as ktime_t delta for request responses. If we have not received a * response in this time-frame after finishing the underlying packet * transmission, the request will be completed with %-ETIMEDOUT as status * code. */#define SSH_RTL_REQUEST_TIMEOUT ms_to_ktime(3000) /* * SSH_RTL_REQUEST_TIMEOUT_RESOLUTION - Request timeout granularity. * * Time-resolution for timeouts. Should be larger than one jiffy to avoid * direct re-scheduling of reaper work_struct. */#define SSH_RTL_REQUEST_TIMEOUT_RESOLUTION ms_to_ktime(max(2000 / HZ, 50)) /* * SSH_RTL_MAX_PENDING - Maximum number of pending requests. * * Maximum number of requests concurrently waiting to be completed (i.e. * waiting for the corresponding packet transmission to finish if they don't * have a response or waiting for a response if they have one). */#define SSH_RTL_MAX_PENDING 3 /* * SSH_RTL_TX_BATCH - Maximum number of requests processed per work execution. * Used to prevent livelocking of the workqueue. Value chosen via educated * guess, may be adjusted. */#define SSH_RTL_TX_BATCH 10 #ifdef CONFIG_SURFACE_AGGREGATOR_ERROR_INJECTION /** * ssh_rtl_should_drop_response() - Error injection hook to drop request * responses. * * Useful to cause request transmission timeouts in the driver by dropping the * response to a request. */static noinline bool ssh_rtl_should_drop_response(void){ return false;}ALLOW_ERROR_INJECTION(ssh_rtl_should_drop_response, TRUE); #else static inline bool ssh_rtl_should_drop_response(void){ return false;} #endif static u16 ssh_request_get_rqid(struct ssh_request *rqst){ return get_unaligned_le16(rqst->packet.data.ptr + SSH_MSGOFFSET_COMMAND(rqid));} static u32 ssh_request_get_rqid_safe(struct ssh_request *rqst){ if (!rqst->packet.data.ptr) return U32_MAX; return ssh_request_get_rqid(rqst);} static void ssh_rtl_queue_remove(struct ssh_request *rqst){ struct ssh_rtl *rtl = ssh_request_rtl(rqst); spin_lock(&rtl->queue.lock); if (!test_and_clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &rqst->state)) { spin_unlock(&rtl->queue.lock); return; } list_del(&rqst->node); spin_unlock(&rtl->queue.lock); ssh_request_put(rqst);} static bool ssh_rtl_queue_empty(struct ssh_rtl *rtl){ bool empty; spin_lock(&rtl->queue.lock); empty = list_empty(&rtl->queue.head); spin_unlock(&rtl->queue.lock); return empty;} static void ssh_rtl_pending_remove(struct ssh_request *rqst){ struct ssh_rtl *rtl = ssh_request_rtl(rqst); spin_lock(&rtl->pending.lock); if (!test_and_clear_bit(SSH_REQUEST_SF_PENDING_BIT, &rqst->state)) { spin_unlock(&rtl->pending.lock); return; } atomic_dec(&rtl->pending.count); list_del(&rqst->node); spin_unlock(&rtl->pending.lock); ssh_request_put(rqst);} static int ssh_rtl_tx_pending_push(struct ssh_request *rqst){ struct ssh_rtl *rtl = ssh_request_rtl(rqst); spin_lock(&rtl->pending.lock); if (test_bit(SSH_REQUEST_SF_LOCKED_BIT, &rqst->state)) { spin_unlock(&rtl->pending.lock); return -EINVAL; } if (test_and_set_bit(SSH_REQUEST_SF_PENDING_BIT, &rqst->state)) { spin_unlock(&rtl->pending.lock); return -EALREADY; } atomic_inc(&rtl->pending.count); list_add_tail(&ssh_request_get(rqst)->node, &rtl->pending.head); spin_unlock(&rtl->pending.lock); return 0;} static void ssh_rtl_complete_with_status(struct ssh_request *rqst, int status){ struct ssh_rtl *rtl = ssh_request_rtl(rqst); trace_ssam_request_complete(rqst, status); /* rtl/ptl may not be set if we're canceling before submitting. */ rtl_dbg_cond(rtl, "rtl: completing request (rqid: %#06x, status: %d)\n", ssh_request_get_rqid_safe(rqst), status); rqst->ops->complete(rqst, NULL, NULL, status);} static void ssh_rtl_complete_with_rsp(struct ssh_request *rqst, const struct ssh_command *cmd, const struct ssam_span *data){ struct ssh_rtl *rtl = ssh_request_rtl(rqst); trace_ssam_request_complete(rqst, 0); rtl_dbg(rtl, "rtl: completing request with response (rqid: %#06x)\n", ssh_request_get_rqid(rqst)); rqst->ops->complete(rqst, cmd, data, 0);} static bool ssh_rtl_tx_can_process(struct ssh_request *rqst){ struct ssh_rtl *rtl = ssh_request_rtl(rqst); if (test_bit(SSH_REQUEST_TY_FLUSH_BIT, &rqst->state)) return !atomic_read(&rtl->pending.count); return atomic_read(&rtl->pending.count) < SSH_RTL_MAX_PENDING;} static struct ssh_request *ssh_rtl_tx_next(struct ssh_rtl *rtl){ struct ssh_request *rqst = ERR_PTR(-ENOENT); struct ssh_request *p, *n; spin_lock(&rtl->queue.lock); /* Find first non-locked request and remove it. */ list_for_each_entry_safe(p, n, &rtl->queue.head, node) { if (unlikely(test_bit(SSH_REQUEST_SF_LOCKED_BIT, &p->state))) continue; if (!ssh_rtl_tx_can_process(p)) { rqst = ERR_PTR(-EBUSY); break; } /* Remove from queue and mark as transmitting. */ set_bit(SSH_REQUEST_SF_TRANSMITTING_BIT, &p->state); /* Ensure state never gets zero. */ smp_mb__before_atomic(); clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &p->state); list_del(&p->node); rqst = p; break; } spin_unlock(&rtl->queue.lock); return rqst;} static int ssh_rtl_tx_try_process_one(struct ssh_rtl *rtl){ struct ssh_request *rqst; int status; /* Get and prepare next request for transmit. */ rqst = ssh_rtl_tx_next(rtl); if (IS_ERR(rqst)) return PTR_ERR(rqst); /* Add it to/mark it as pending. */ status = ssh_rtl_tx_pending_push(rqst); if (status) { ssh_request_put(rqst); return -EAGAIN; } /* Submit packet. */ status = ssh_ptl_submit(&rtl->ptl, &rqst->packet); if (status == -ESHUTDOWN) { /* * Packet has been refused due to the packet layer shutting * down. Complete it here. */ set_bit(SSH_REQUEST_SF_LOCKED_BIT, &rqst->state); /* * Note: A barrier is not required here, as there are only two * references in the system at this point: The one that we have, * and the other one that belongs to the pending set. Due to the * request being marked as "transmitting", our process is the * only one allowed to remove the pending node and change the * state. Normally, the task would fall to the packet callback, * but as this is a path where submission failed, this callback * will never be executed. */ ssh_rtl_pending_remove(rqst); ssh_rtl_complete_with_status(rqst, -ESHUTDOWN); ssh_request_put(rqst); return -ESHUTDOWN; } else if (status) { /* * If submitting the packet failed and the packet layer isn't * shutting down, the packet has either been submitted/queued * before (-EALREADY, which cannot happen as we have * guaranteed that requests cannot be re-submitted), or the * packet was marked as locked (-EINVAL). To mark the packet * locked at this stage, the request, and thus the packets * itself, had to have been canceled. Simply drop the * reference. Cancellation itself will remove it from the set * of pending requests. */ WARN_ON(status != -EINVAL); ssh_request_put(rqst); return -EAGAIN; } ssh_request_put(rqst); return 0;} static bool ssh_rtl_tx_schedule(struct ssh_rtl *rtl){ if (atomic_read(&rtl->pending.count) >= SSH_RTL_MAX_PENDING) return false; if (ssh_rtl_queue_empty(rtl)) return false; return schedule_work(&rtl->tx.work);} static void ssh_rtl_tx_work_fn(struct work_struct *work){ struct ssh_rtl *rtl = to_ssh_rtl(work, tx.work); unsigned int iterations = SSH_RTL_TX_BATCH; int status; /* * Try to be nice and not block/live-lock the workqueue: Run a maximum * of 10 tries, then re-submit if necessary. This should not be * necessary for normal execution, but guarantee it anyway. */ do { status = ssh_rtl_tx_try_process_one(rtl); if (status == -ENOENT || status == -EBUSY) return; /* No more requests to process. */ if (status == -ESHUTDOWN) { /* * Packet system shutting down. No new packets can be * transmitted. Return silently, the party initiating * the shutdown should handle the rest. */ return; } WARN_ON(status != 0 && status != -EAGAIN); } while (--iterations); /* Out of tries, reschedule. */ ssh_rtl_tx_schedule(rtl);} /** * ssh_rtl_submit() - Submit a request to the transport layer. * @rtl: The request transport layer. * @rqst: The request to submit. * * Submits a request to the transport layer. A single request may not be * submitted multiple times without reinitializing it. * * Return: Returns zero on success, %-EINVAL if the request type is invalid or * the request has been canceled prior to submission, %-EALREADY if the * request has already been submitted, or %-ESHUTDOWN in case the request * transport layer has been shut down. */int ssh_rtl_submit(struct ssh_rtl *rtl, struct ssh_request *rqst){ trace_ssam_request_submit(rqst); /* * Ensure that requests expecting a response are sequenced. If this * invariant ever changes, see the comment in ssh_rtl_complete() on what * is required to be changed in the code. */ if (test_bit(SSH_REQUEST_TY_HAS_RESPONSE_BIT, &rqst->state)) if (!test_bit(SSH_PACKET_TY_SEQUENCED_BIT, &rqst->packet.state)) return -EINVAL; spin_lock(&rtl->queue.lock); /* * Try to set ptl and check if this request has already been submitted. * * Must be inside lock as we might run into a lost update problem * otherwise: If this were outside of the lock, cancellation in * ssh_rtl_cancel_nonpending() may run after we've set the ptl * reference but before we enter the lock. In that case, we'd detect * that the request is being added to the queue and would try to remove * it from that, but removal might fail because it hasn't actually been * added yet. By putting this cmpxchg in the critical section, we * ensure that the queuing detection only triggers when we are already * in the critical section and the remove process will wait until the * push operation has been completed (via lock) due to that. Only then, * we can safely try to remove it. */ if (cmpxchg(&rqst->packet.ptl, NULL, &rtl->ptl)) { spin_unlock(&rtl->queue.lock); return -EALREADY; } /* * Ensure that we set ptl reference before we continue modifying state. * This is required for non-pending cancellation. This barrier is paired * with the one in ssh_rtl_cancel_nonpending(). * * By setting the ptl reference before we test for "locked", we can * check if the "locked" test may have already run. See comments in * ssh_rtl_cancel_nonpending() for more detail. */ smp_mb__after_atomic(); if (test_bit(SSH_RTL_SF_SHUTDOWN_BIT, &rtl->state)) { spin_unlock(&rtl->queue.lock); return -ESHUTDOWN; } if (test_bit(SSH_REQUEST_SF_LOCKED_BIT, &rqst->state)) { spin_unlock(&rtl->queue.lock); return -EINVAL; } set_bit(SSH_REQUEST_SF_QUEUED_BIT, &rqst->state); list_add_tail(&ssh_request_get(rqst)->node, &rtl->queue.head); spin_unlock(&rtl->queue.lock); ssh_rtl_tx_schedule(rtl); return 0;} static void ssh_rtl_timeout_reaper_mod(struct ssh_rtl *rtl, ktime_t now, ktime_t expires){ unsigned long delta = msecs_to_jiffies(ktime_ms_delta(expires, now)); ktime_t aexp = ktime_add(expires, SSH_RTL_REQUEST_TIMEOUT_RESOLUTION); spin_lock(&rtl->rtx_timeout.lock); /* Re-adjust / schedule reaper only if it is above resolution delta. */ if (ktime_before(aexp, rtl->rtx_timeout.expires)) { rtl->rtx_timeout.expires = expires; mod_delayed_work(system_wq, &rtl->rtx_timeout.reaper, delta); } spin_unlock(&rtl->rtx_timeout.lock);} static void ssh_rtl_timeout_start(struct ssh_request *rqst){ struct ssh_rtl *rtl = ssh_request_rtl(rqst); ktime_t timestamp = ktime_get_coarse_boottime(); ktime_t timeout = rtl->rtx_timeout.timeout; if (test_bit(SSH_REQUEST_SF_LOCKED_BIT, &rqst->state)) return; /* * Note: The timestamp gets set only once. This happens on the packet * callback. All other access to it is read-only. */ WRITE_ONCE(rqst->timestamp, timestamp); /* * Ensure timestamp is set before starting the reaper. Paired with * implicit barrier following check on ssh_request_get_expiration() in * ssh_rtl_timeout_reap. */ smp_mb__after_atomic(); ssh_rtl_timeout_reaper_mod(rtl, timestamp, timestamp + timeout);} static void ssh_rtl_complete(struct ssh_rtl *rtl, const struct ssh_command *command, const struct ssam_span *command_data){ struct ssh_request *r = NULL; struct ssh_request *p, *n; u16 rqid = get_unaligned_le16(&command->rqid); trace_ssam_rx_response_received(command, command_data->len); /* * Get request from pending based on request ID and mark it as response * received and locked. */ spin_lock(&rtl->pending.lock); list_for_each_entry_safe(p, n, &rtl->pending.head, node) { /* We generally expect requests to be processed in order. */ if (unlikely(ssh_request_get_rqid(p) != rqid)) continue; /* Simulate response timeout. */ if (ssh_rtl_should_drop_response()) { spin_unlock(&rtl->pending.lock); trace_ssam_ei_rx_drop_response(p); rtl_info(rtl, "request error injection: dropping response for request %p\n", &p->packet); return; } /* * Mark as "response received" and "locked" as we're going to * complete it. */Lines 1–500 of 1274. Load more
// SPDX-License-Identifier: GPL-2.0+ /* * SSH request transport layer. * * Copyright (C) 2019-2022 Maximilian Luz <luzmaximilian@gmail.com> */ #include <linux/unaligned.h> #include <linux/atomic.h> #include <linux/completion.h> #include <linux/error-injection.h> #include <linux/ktime.h> #include <linux/limits.h> #include <linux/list.h> #include <linux/slab.h> #include <linux/spinlock.h> #include <linux/types.h> #include <linux/workqueue.h> #include <linux/surface_aggregator/serial_hub.h> #include <linux/surface_aggregator/controller.h> #include "ssh_packet_layer.h" #include "ssh_request_layer.h" #include "trace.h" /* * SSH_RTL_REQUEST_TIMEOUT - Request timeout. * * Timeout as ktime_t delta for request responses. If we have not received a * response in this time-frame after finishing the underlying packet * transmission, the request will be completed with %-ETIMEDOUT as status * code. */ #define SSH_RTL_REQUEST_TIMEOUT ms_to_ktime(3000) /* * SSH_RTL_REQUEST_TIMEOUT_RESOLUTION - Request timeout granularity. * * Time-resolution for timeouts. Should be larger than one jiffy to avoid * direct re-scheduling of reaper work_struct. */ #define SSH_RTL_REQUEST_TIMEOUT_RESOLUTION ms_to_ktime(max(2000 / HZ, 50)) /* * SSH_RTL_MAX_PENDING - Maximum number of pending requests. * * Maximum number of requests concurrently waiting to be completed (i.e. * waiting for the corresponding packet transmission to finish if they don't * have a response or waiting for a response if they have one). */ #define SSH_RTL_MAX_PENDING 3 /* * SSH_RTL_TX_BATCH - Maximum number of requests processed per work execution. * Used to prevent livelocking of the workqueue. Value chosen via educated * guess, may be adjusted. */ #define SSH_RTL_TX_BATCH 10 #ifdef CONFIG_SURFACE_AGGREGATOR_ERROR_INJECTION /** * ssh_rtl_should_drop_response() - Error injection hook to drop request * responses. * * Useful to cause request transmission timeouts in the driver by dropping the * response to a request. */ static noinline bool ssh_rtl_should_drop_response(void) { return false; } ALLOW_ERROR_INJECTION(ssh_rtl_should_drop_response, TRUE); #else static inline bool ssh_rtl_should_drop_response(void) { return false; } #endif static u16 ssh_request_get_rqid(struct ssh_request *rqst) { return get_unaligned_le16(rqst->packet.data.ptr + SSH_MSGOFFSET_COMMAND(rqid)); } static u32 ssh_request_get_rqid_safe(struct ssh_request *rqst) { if (!rqst->packet.data.ptr) return U32_MAX; return ssh_request_get_rqid(rqst); } static void ssh_rtl_queue_remove(struct ssh_request *rqst) { struct ssh_rtl *rtl = ssh_request_rtl(rqst); spin_lock(&rtl->queue.lock); if (!test_and_clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &rqst->state)) { spin_unlock(&rtl->queue.lock); return; } list_del(&rqst->node); spin_unlock(&rtl->queue.lock); ssh_request_put(rqst); } static bool ssh_rtl_queue_empty(struct ssh_rtl *rtl) { bool empty; spin_lock(&rtl->queue.lock); empty = list_empty(&rtl->queue.head); spin_unlock(&rtl->queue.lock); return empty; } static void ssh_rtl_pending_remove(struct ssh_request *rqst) { struct ssh_rtl *rtl = ssh_request_rtl(rqst); spin_lock(&rtl->pending.lock); if (!test_and_clear_bit(SSH_REQUEST_SF_PENDING_BIT, &rqst->state)) { spin_unlock(&rtl->pending.lock); return; } atomic_dec(&rtl->pending.count); list_del(&rqst->node); spin_unlock(&rtl->pending.lock); ssh_request_put(rqst); } static int ssh_rtl_tx_pending_push(struct ssh_request *rqst) { struct ssh_rtl *rtl = ssh_request_rtl(rqst); spin_lock(&rtl->pending.lock); if (test_bit(SSH_REQUEST_SF_LOCKED_BIT, &rqst->state)) { spin_unlock(&rtl->pending.lock); return -EINVAL; } if (test_and_set_bit(SSH_REQUEST_SF_PENDING_BIT, &rqst->state)) { spin_unlock(&rtl->pending.lock); return -EALREADY; } atomic_inc(&rtl->pending.count); list_add_tail(&ssh_request_get(rqst)->node, &rtl->pending.head); spin_unlock(&rtl->pending.lock); return 0; } static void ssh_rtl_complete_with_status(struct ssh_request *rqst, int status) { struct ssh_rtl *rtl = ssh_request_rtl(rqst); trace_ssam_request_complete(rqst, status); /* rtl/ptl may not be set if we're canceling before submitting. */ rtl_dbg_cond(rtl, "rtl: completing request (rqid: %#06x, status: %d)\n", ssh_request_get_rqid_safe(rqst), status); rqst->ops->complete(rqst, NULL, NULL, status); } static void ssh_rtl_complete_with_rsp(struct ssh_request *rqst, const struct ssh_command *cmd, const struct ssam_span *data) { struct ssh_rtl *rtl = ssh_request_rtl(rqst); trace_ssam_request_complete(rqst, 0); rtl_dbg(rtl, "rtl: completing request with response (rqid: %#06x)\n", ssh_request_get_rqid(rqst)); rqst->ops->complete(rqst, cmd, data, 0); } static bool ssh_rtl_tx_can_process(struct ssh_request *rqst) { struct ssh_rtl *rtl = ssh_request_rtl(rqst); if (test_bit(SSH_REQUEST_TY_FLUSH_BIT, &rqst->state)) return !atomic_read(&rtl->pending.count); return atomic_read(&rtl->pending.count) < SSH_RTL_MAX_PENDING; } static struct ssh_request *ssh_rtl_tx_next(struct ssh_rtl *rtl) { struct ssh_request *rqst = ERR_PTR(-ENOENT); struct ssh_request *p, *n; spin_lock(&rtl->queue.lock); /* Find first non-locked request and remove it. */ list_for_each_entry_safe(p, n, &rtl->queue.head, node) { if (unlikely(test_bit(SSH_REQUEST_SF_LOCKED_BIT, &p->state))) continue; if (!ssh_rtl_tx_can_process(p)) { rqst = ERR_PTR(-EBUSY); break; } /* Remove from queue and mark as transmitting. */ set_bit(SSH_REQUEST_SF_TRANSMITTING_BIT, &p->state); /* Ensure state never gets zero. */ smp_mb__before_atomic(); clear_bit(SSH_REQUEST_SF_QUEUED_BIT, &p->state); list_del(&p->node); rqst = p; break; } spin_unlock(&rtl->queue.lock); return rqst; } static int ssh_rtl_tx_try_process_one(struct ssh_rtl *rtl) { struct ssh_request *rqst; int status; /* Get and prepare next request for transmit. */ rqst = ssh_rtl_tx_next(rtl); if (IS_ERR(rqst)) return PTR_ERR(rqst); /* Add it to/mark it as pending. */ status = ssh_rtl_tx_pending_push(rqst); if (status) { ssh_request_put(rqst); return -EAGAIN; } /* Submit packet. */ status = ssh_ptl_submit(&rtl->ptl, &rqst->packet); if (status == -ESHUTDOWN) { /* * Packet has been refused due to the packet layer shutting * down. Complete it here. */ set_bit(SSH_REQUEST_SF_LOCKED_BIT, &rqst->state); /* * Note: A barrier is not required here, as there are only two * references in the system at this point: The one that we have, * and the other one that belongs to the pending set. Due to the * request being marked as "transmitting", our process is the * only one allowed to remove the pending node and change the * state. Normally, the task would fall to the packet callback, * but as this is a path where submission failed, this callback * will never be executed. */ ssh_rtl_pending_remove(rqst); ssh_rtl_complete_with_status(rqst, -ESHUTDOWN); ssh_request_put(rqst); return -ESHUTDOWN; } else if (status) { /* * If submitting the packet failed and the packet layer isn't * shutting down, the packet has either been submitted/queued * before (-EALREADY, which cannot happen as we have * guaranteed that requests cannot be re-submitted), or the * packet was marked as locked (-EINVAL). To mark the packet * locked at this stage, the request, and thus the packets * itself, had to have been canceled. Simply drop the * reference. Cancellation itself will remove it from the set * of pending requests. */ WARN_ON(status != -EINVAL); ssh_request_put(rqst); return -EAGAIN; } ssh_request_put(rqst); return 0; } static bool ssh_rtl_tx_schedule(struct ssh_rtl *rtl) { if (atomic_read(&rtl->pending.count) >= SSH_RTL_MAX_PENDING) return false; if (ssh_rtl_queue_empty(rtl)) return false; return schedule_work(&rtl->tx.work); } static void ssh_rtl_tx_work_fn(struct work_struct *work) { struct ssh_rtl *rtl = to_ssh_rtl(work, tx.work); unsigned int iterations = SSH_RTL_TX_BATCH; int status; /* * Try to be nice and not block/live-lock the workqueue: Run a maximum * of 10 tries, then re-submit if necessary. This should not be * necessary for normal execution, but guarantee it anyway. */ do { status = ssh_rtl_tx_try_process_one(rtl); if (status == -ENOENT || status == -EBUSY) return; /* No more requests to process. */ if (status == -ESHUTDOWN) { /* * Packet system shutting down. No new packets can be * transmitted. Return silently, the party initiating * the shutdown should handle the rest. */ return; } WARN_ON(status != 0 && status != -EAGAIN); } while (--iterations); /* Out of tries, reschedule. */ ssh_rtl_tx_schedule(rtl); } /** * ssh_rtl_submit() - Submit a request to the transport layer. * @rtl: The request transport layer. * @rqst: The request to submit. * * Submits a request to the transport layer. A single request may not be * submitted multiple times without reinitializing it. * * Return: Returns zero on success, %-EINVAL if the request type is invalid or * the request has been canceled prior to submission, %-EALREADY if the * request has already been submitted, or %-ESHUTDOWN in case the request * transport layer has been shut down. */ int ssh_rtl_submit(struct ssh_rtl *rtl, struct ssh_request *rqst) { trace_ssam_request_submit(rqst); /* * Ensure that requests expecting a response are sequenced. If this * invariant ever changes, see the comment in ssh_rtl_complete() on what * is required to be changed in the code. */ if (test_bit(SSH_REQUEST_TY_HAS_RESPONSE_BIT, &rqst->state)) if (!test_bit(SSH_PACKET_TY_SEQUENCED_BIT, &rqst->packet.state)) return -EINVAL; spin_lock(&rtl->queue.lock); /* * Try to set ptl and check if this request has already been submitted. * * Must be inside lock as we might run into a lost update problem * otherwise: If this were outside of the lock, cancellation in * ssh_rtl_cancel_nonpending() may run after we've set the ptl * reference but before we enter the lock. In that case, we'd detect * that the request is being added to the queue and would try to remove * it from that, but removal might fail because it hasn't actually been * added yet. By putting this cmpxchg in the critical section, we * ensure that the queuing detection only triggers when we are already * in the critical section and the remove process will wait until the * push operation has been completed (via lock) due to that. Only then, * we can safely try to remove it. */ if (cmpxchg(&rqst->packet.ptl, NULL, &rtl->ptl)) { spin_unlock(&rtl->queue.lock); return -EALREADY; } /* * Ensure that we set ptl reference before we continue modifying state. * This is required for non-pending cancellation. This barrier is paired * with the one in ssh_rtl_cancel_nonpending(). * * By setting the ptl reference before we test for "locked", we can * check if the "locked" test may have already run. See comments in * ssh_rtl_cancel_nonpending() for more detail. */ smp_mb__after_atomic(); if (test_bit(SSH_RTL_SF_SHUTDOWN_BIT, &rtl->state)) { spin_unlock(&rtl->queue.lock); return -ESHUTDOWN; } if (test_bit(SSH_REQUEST_SF_LOCKED_BIT, &rqst->state)) { spin_unlock(&rtl->queue.lock); return -EINVAL; } set_bit(SSH_REQUEST_SF_QUEUED_BIT, &rqst->state); list_add_tail(&ssh_request_get(rqst)->node, &rtl->queue.head); spin_unlock(&rtl->queue.lock); ssh_rtl_tx_schedule(rtl); return 0; } static void ssh_rtl_timeout_reaper_mod(struct ssh_rtl *rtl, ktime_t now, ktime_t expires) { unsigned long delta = msecs_to_jiffies(ktime_ms_delta(expires, now)); ktime_t aexp = ktime_add(expires, SSH_RTL_REQUEST_TIMEOUT_RESOLUTION); spin_lock(&rtl->rtx_timeout.lock); /* Re-adjust / schedule reaper only if it is above resolution delta. */ if (ktime_before(aexp, rtl->rtx_timeout.expires)) { rtl->rtx_timeout.expires = expires; mod_delayed_work(system_wq, &rtl->rtx_timeout.reaper, delta); } spin_unlock(&rtl->rtx_timeout.lock); } static void ssh_rtl_timeout_start(struct ssh_request *rqst) { struct ssh_rtl *rtl = ssh_request_rtl(rqst); ktime_t timestamp = ktime_get_coarse_boottime(); ktime_t timeout = rtl->rtx_timeout.timeout; if (test_bit(SSH_REQUEST_SF_LOCKED_BIT, &rqst->state)) return; /* * Note: The timestamp gets set only once. This happens on the packet * callback. All other access to it is read-only. */ WRITE_ONCE(rqst->timestamp, timestamp); /* * Ensure timestamp is set before starting the reaper. Paired with * implicit barrier following check on ssh_request_get_expiration() in * ssh_rtl_timeout_reap. */ smp_mb__after_atomic(); ssh_rtl_timeout_reaper_mod(rtl, timestamp, timestamp + timeout); } static void ssh_rtl_complete(struct ssh_rtl *rtl, const struct ssh_command *command, const struct ssam_span *command_data) { struct ssh_request *r = NULL; struct ssh_request *p, *n; u16 rqid = get_unaligned_le16(&command->rqid); trace_ssam_rx_response_received(command, command_data->len); /* * Get request from pending based on request ID and mark it as response * received and locked. */ spin_lock(&rtl->pending.lock); list_for_each_entry_safe(p, n, &rtl->pending.head, node) { /* We generally expect requests to be processed in order. */ if (unlikely(ssh_request_get_rqid(p) != rqid)) continue; /* Simulate response timeout. */ if (ssh_rtl_should_drop_response()) { spin_unlock(&rtl->pending.lock); trace_ssam_ei_rx_drop_response(p); rtl_info(rtl, "request error injection: dropping response for request %p\n", &p->packet); return; } /* * Mark as "response received" and "locked" as we're going to * complete it. */