// SPDX-License-Identifier: GPL-2.0+ /* * SSH request transport layer. * * Copyright (C) 2019-2022 Maximilian Luz <[email protected]> */ #include <asm/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 … /* * 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 … /* * 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 … /* * 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 … #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) { … } ALLOW_ERROR_INJECTION(…); #else static inline bool ssh_rtl_should_drop_response(void) { return false; } #endif static u16 ssh_request_get_rqid(struct ssh_request *rqst) { … } static u32 ssh_request_get_rqid_safe(struct ssh_request *rqst) { … } static void ssh_rtl_queue_remove(struct ssh_request *rqst) { … } static bool ssh_rtl_queue_empty(struct ssh_rtl *rtl) { … } static void ssh_rtl_pending_remove(struct ssh_request *rqst) { … } static int ssh_rtl_tx_pending_push(struct ssh_request *rqst) { … } static void ssh_rtl_complete_with_status(struct ssh_request *rqst, int status) { … } static void ssh_rtl_complete_with_rsp(struct ssh_request *rqst, const struct ssh_command *cmd, const struct ssam_span *data) { … } static bool ssh_rtl_tx_can_process(struct ssh_request *rqst) { … } static struct ssh_request *ssh_rtl_tx_next(struct ssh_rtl *rtl) { … } static int ssh_rtl_tx_try_process_one(struct ssh_rtl *rtl) { … } static bool ssh_rtl_tx_schedule(struct ssh_rtl *rtl) { … } static void ssh_rtl_tx_work_fn(struct work_struct *work) { … } /** * 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) { … } static void ssh_rtl_timeout_reaper_mod(struct ssh_rtl *rtl, ktime_t now, ktime_t expires) { … } static void ssh_rtl_timeout_start(struct ssh_request *rqst) { … } static void ssh_rtl_complete(struct ssh_rtl *rtl, const struct ssh_command *command, const struct ssam_span *command_data) { … } static bool ssh_rtl_cancel_nonpending(struct ssh_request *r) { … } static bool ssh_rtl_cancel_pending(struct ssh_request *r) { … } /** * ssh_rtl_cancel() - Cancel request. * @rqst: The request to cancel. * @pending: Whether to also cancel pending requests. * * Cancels the given request. If @pending is %false, this will not cancel * pending requests, i.e. requests that have already been submitted to the * packet layer but not been completed yet. If @pending is %true, this will * cancel the given request regardless of the state it is in. * * If the request has been canceled by calling this function, both completion * and release callbacks of the request will be executed in a reasonable * time-frame. This may happen during execution of this function, however, * there is no guarantee for this. For example, a request currently * transmitting will be canceled/completed only after transmission has * completed, and the respective callbacks will be executed on the transmitter * thread, which may happen during, but also some time after execution of the * cancel function. * * Return: Returns %true if the given request has been canceled or completed, * either by this function or prior to calling this function, %false * otherwise. If @pending is %true, this function will always return %true. */ bool ssh_rtl_cancel(struct ssh_request *rqst, bool pending) { … } static void ssh_rtl_packet_callback(struct ssh_packet *p, int status) { … } static ktime_t ssh_request_get_expiration(struct ssh_request *r, ktime_t timeout) { … } static void ssh_rtl_timeout_reap(struct work_struct *work) { … } static void ssh_rtl_rx_event(struct ssh_rtl *rtl, const struct ssh_command *cmd, const struct ssam_span *data) { … } static void ssh_rtl_rx_command(struct ssh_ptl *p, const struct ssam_span *data) { … } static void ssh_rtl_rx_data(struct ssh_ptl *p, const struct ssam_span *data) { … } static void ssh_rtl_packet_release(struct ssh_packet *p) { … } static const struct ssh_packet_ops ssh_rtl_packet_ops = …; /** * ssh_request_init() - Initialize SSH request. * @rqst: The request to initialize. * @flags: Request flags, determining the type of the request. * @ops: Request operations. * * Initializes the given SSH request and underlying packet. Sets the message * buffer pointer to %NULL and the message buffer length to zero. This buffer * has to be set separately via ssh_request_set_data() before submission and * must contain a valid SSH request message. * * Return: Returns zero on success or %-EINVAL if the given flags are invalid. */ int ssh_request_init(struct ssh_request *rqst, enum ssam_request_flags flags, const struct ssh_request_ops *ops) { … } /** * ssh_rtl_init() - Initialize request transport layer. * @rtl: The request transport layer to initialize. * @serdev: The underlying serial device, i.e. the lower-level transport. * @ops: Request transport layer operations. * * Initializes the given request transport layer and associated packet * transport layer. Transmitter and receiver threads must be started * separately via ssh_rtl_start(), after the request-layer has been * initialized and the lower-level serial device layer has been set up. * * Return: Returns zero on success and a nonzero error code on failure. */ int ssh_rtl_init(struct ssh_rtl *rtl, struct serdev_device *serdev, const struct ssh_rtl_ops *ops) { … } /** * ssh_rtl_destroy() - Deinitialize request transport layer. * @rtl: The request transport layer to deinitialize. * * Deinitializes the given request transport layer and frees resources * associated with it. If receiver and/or transmitter threads have been * started, the layer must first be shut down via ssh_rtl_shutdown() before * this function can be called. */ void ssh_rtl_destroy(struct ssh_rtl *rtl) { … } /** * ssh_rtl_start() - Start request transmitter and receiver. * @rtl: The request transport layer. * * Return: Returns zero on success, a negative error code on failure. */ int ssh_rtl_start(struct ssh_rtl *rtl) { … } struct ssh_flush_request { … }; static void ssh_rtl_flush_request_complete(struct ssh_request *r, const struct ssh_command *cmd, const struct ssam_span *data, int status) { … } static void ssh_rtl_flush_request_release(struct ssh_request *r) { … } static const struct ssh_request_ops ssh_rtl_flush_request_ops = …; /** * ssh_rtl_flush() - Flush the request transport layer. * @rtl: request transport layer * @timeout: timeout for the flush operation in jiffies * * Queue a special flush request and wait for its completion. This request * will be completed after all other currently queued and pending requests * have been completed. Instead of a normal data packet, this request submits * a special flush packet, meaning that upon completion, also the underlying * packet transport layer has been flushed. * * Flushing the request layer guarantees that all previously submitted * requests have been fully completed before this call returns. Additionally, * flushing blocks execution of all later submitted requests until the flush * has been completed. * * If the caller ensures that no new requests are submitted after a call to * this function, the request transport layer is guaranteed to have no * remaining requests when this call returns. The same guarantee does not hold * for the packet layer, on which control packets may still be queued after * this call. * * Return: Returns zero on success, %-ETIMEDOUT if the flush timed out and has * been canceled as a result of the timeout, or %-ESHUTDOWN if the packet * and/or request transport layer has been shut down before this call. May * also return %-EINTR if the underlying packet transmission has been * interrupted. */ int ssh_rtl_flush(struct ssh_rtl *rtl, unsigned long timeout) { … } /** * ssh_rtl_shutdown() - Shut down request transport layer. * @rtl: The request transport layer. * * Shuts down the request transport layer, removing and canceling all queued * and pending requests. Requests canceled by this operation will be completed * with %-ESHUTDOWN as status. Receiver and transmitter threads will be * stopped, the lower-level packet layer will be shutdown. * * As a result of this function, the transport layer will be marked as shut * down. Submission of requests after the transport layer has been shut down * will fail with %-ESHUTDOWN. */ void ssh_rtl_shutdown(struct ssh_rtl *rtl) { … }