]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
firmware: stratix10-svc: Add Multi SVC clients support
authorMuhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
Thu, 5 Mar 2026 09:31:51 +0000 (01:31 -0800)
committerDinh Nguyen <dinguyen@kernel.org>
Tue, 10 Mar 2026 16:32:31 +0000 (11:32 -0500)
In the current implementation, SVC client drivers such as socfpga-hwmon,
intel_fcs, stratix10-soc, stratix10-rsu each send an SMC command that
triggers a single thread in the stratix10-svc driver. Upon receiving a
callback, the initiating client driver sends a stratix10-svc-done signal,
terminating the thread without waiting for other pending SMC commands to
complete. This leads to a timeout issue in the firmware SVC mailbox service
when multiple client drivers send SMC commands concurrently.

To resolve this issue, a dedicated thread is now created per channel. The
stratix10-svc driver will support up to the number of channels defined by
SVC_NUM_CHANNEL. Thread synchronization is handled using a mutex to prevent
simultaneous issuance of SMC commands by multiple threads.

SVC_NUM_DATA_IN_FIFO is reduced from 32 to 8, since each channel now has
its own dedicated FIFO and the SDM processes commands one at a time.
8 entries per channel is sufficient while keeping the total aggregate
capacity the same (4 channels x 8 = 32 entries).

Additionally, a thread task is now validated before invoking kthread_stop
when the user aborts, ensuring safe termination.

Timeout values have also been adjusted to accommodate the increased load
from concurrent client driver activity.

Fixes: 7ca5ce896524 ("firmware: add Intel Stratix10 service layer driver")
Cc: stable@vger.kernel.org
Signed-off-by: Ang Tien Sung <tien.sung.ang@altera.com>
Signed-off-by: Fong, Yan Kei <yankei.fong@altera.com>
Signed-off-by: Muhammad Amirul Asyraf Mohamad Jamian <muhammad.amirul.asyraf.mohamad.jamian@altera.com>
Link: https://lore.kernel.org/all/20260305093151.2678-1-muhammad.amirul.asyraf.mohamad.jamian@altera.com
Signed-off-by: Dinh Nguyen <dinguyen@kernel.org>
drivers/firmware/stratix10-svc.c
include/linux/firmware/intel/stratix10-svc-client.h

index 6f5c298582abde1a64ca259affab29a623f70f1f..e9e35d67ef966af101ee96bbdf60a14b0154b114 100644 (file)
  * service layer will return error to FPGA manager when timeout occurs,
  * timeout is set to 30 seconds (30 * 1000) at Intel Stratix10 SoC.
  */
-#define SVC_NUM_DATA_IN_FIFO                   32
+#define SVC_NUM_DATA_IN_FIFO                   8
 #define SVC_NUM_CHANNEL                                4
-#define FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS      200
+#define FPGA_CONFIG_DATA_CLAIM_TIMEOUT_MS      2000
 #define FPGA_CONFIG_STATUS_TIMEOUT_SEC         30
 #define BYTE_TO_WORD_SIZE              4
 
 /* stratix10 service layer clients */
 #define STRATIX10_RSU                          "stratix10-rsu"
-#define INTEL_FCS                              "intel-fcs"
 
 /* Maximum number of SDM client IDs. */
 #define MAX_SDM_CLIENT_IDS                     16
@@ -105,11 +104,9 @@ struct stratix10_svc_chan;
 /**
  * struct stratix10_svc - svc private data
  * @stratix10_svc_rsu: pointer to stratix10 RSU device
- * @intel_svc_fcs: pointer to the FCS device
  */
 struct stratix10_svc {
        struct platform_device *stratix10_svc_rsu;
-       struct platform_device *intel_svc_fcs;
 };
 
 /**
@@ -251,12 +248,10 @@ struct stratix10_async_ctrl {
  * @num_active_client: number of active service client
  * @node: list management
  * @genpool: memory pool pointing to the memory region
- * @task: pointer to the thread task which handles SMC or HVC call
- * @svc_fifo: a queue for storing service message data
  * @complete_status: state for completion
- * @svc_fifo_lock: protect access to service message data queue
  * @invoke_fn: function to issue secure monitor call or hypervisor call
  * @svc: manages the list of client svc drivers
+ * @sdm_lock: only allows a single command single response to SDM
  * @actrl: async control structure
  *
  * This struct is used to create communication channels for service clients, to
@@ -269,12 +264,10 @@ struct stratix10_svc_controller {
        int num_active_client;
        struct list_head node;
        struct gen_pool *genpool;
-       struct task_struct *task;
-       struct kfifo svc_fifo;
        struct completion complete_status;
-       spinlock_t svc_fifo_lock;
        svc_invoke_fn *invoke_fn;
        struct stratix10_svc *svc;
+       struct mutex sdm_lock;
        struct stratix10_async_ctrl actrl;
 };
 
@@ -283,6 +276,9 @@ struct stratix10_svc_controller {
  * @ctrl: pointer to service controller which is the provider of this channel
  * @scl: pointer to service client which owns the channel
  * @name: service client name associated with the channel
+ * @task: pointer to the thread task which handles SMC or HVC call
+ * @svc_fifo: a queue for storing service message data (separate fifo for every channel)
+ * @svc_fifo_lock: protect access to service message data queue (locking pending fifo)
  * @lock: protect access to the channel
  * @async_chan: reference to asynchronous channel object for this channel
  *
@@ -293,6 +289,9 @@ struct stratix10_svc_chan {
        struct stratix10_svc_controller *ctrl;
        struct stratix10_svc_client *scl;
        char *name;
+       struct task_struct *task;
+       struct kfifo svc_fifo;
+       spinlock_t svc_fifo_lock;
        spinlock_t lock;
        struct stratix10_async_chan *async_chan;
 };
@@ -527,10 +526,10 @@ static void svc_thread_recv_status_ok(struct stratix10_svc_data *p_data,
  */
 static int svc_normal_to_secure_thread(void *data)
 {
-       struct stratix10_svc_controller
-                       *ctrl = (struct stratix10_svc_controller *)data;
-       struct stratix10_svc_data *pdata;
-       struct stratix10_svc_cb_data *cbdata;
+       struct stratix10_svc_chan *chan = (struct stratix10_svc_chan *)data;
+       struct stratix10_svc_controller *ctrl = chan->ctrl;
+       struct stratix10_svc_data *pdata = NULL;
+       struct stratix10_svc_cb_data *cbdata = NULL;
        struct arm_smccc_res res;
        unsigned long a0, a1, a2, a3, a4, a5, a6, a7;
        int ret_fifo = 0;
@@ -555,12 +554,12 @@ static int svc_normal_to_secure_thread(void *data)
        a6 = 0;
        a7 = 0;
 
-       pr_debug("smc_hvc_shm_thread is running\n");
+       pr_debug("%s: %s: Thread is running!\n", __func__, chan->name);
 
        while (!kthread_should_stop()) {
-               ret_fifo = kfifo_out_spinlocked(&ctrl->svc_fifo,
+               ret_fifo = kfifo_out_spinlocked(&chan->svc_fifo,
                                                pdata, sizeof(*pdata),
-                                               &ctrl->svc_fifo_lock);
+                                               &chan->svc_fifo_lock);
 
                if (!ret_fifo)
                        continue;
@@ -569,9 +568,25 @@ static int svc_normal_to_secure_thread(void *data)
                         (unsigned int)pdata->paddr, pdata->command,
                         (unsigned int)pdata->size);
 
+               /* SDM can only process one command at a time */
+               pr_debug("%s: %s: Thread is waiting for mutex!\n",
+                        __func__, chan->name);
+               if (mutex_lock_interruptible(&ctrl->sdm_lock)) {
+                       /* item already dequeued; notify client to unblock it */
+                       cbdata->status = BIT(SVC_STATUS_ERROR);
+                       cbdata->kaddr1 = NULL;
+                       cbdata->kaddr2 = NULL;
+                       cbdata->kaddr3 = NULL;
+                       if (pdata->chan->scl)
+                               pdata->chan->scl->receive_cb(pdata->chan->scl,
+                                                            cbdata);
+                       break;
+               }
+
                switch (pdata->command) {
                case COMMAND_RECONFIG_DATA_CLAIM:
                        svc_thread_cmd_data_claim(ctrl, pdata, cbdata);
+                       mutex_unlock(&ctrl->sdm_lock);
                        continue;
                case COMMAND_RECONFIG:
                        a0 = INTEL_SIP_SMC_FPGA_CONFIG_START;
@@ -700,10 +715,11 @@ static int svc_normal_to_secure_thread(void *data)
                        break;
                default:
                        pr_warn("it shouldn't happen\n");
-                       break;
+                       mutex_unlock(&ctrl->sdm_lock);
+                       continue;
                }
-               pr_debug("%s: before SMC call -- a0=0x%016x a1=0x%016x",
-                        __func__,
+               pr_debug("%s: %s: before SMC call -- a0=0x%016x a1=0x%016x",
+                        __func__, chan->name,
                         (unsigned int)a0,
                         (unsigned int)a1);
                pr_debug(" a2=0x%016x\n", (unsigned int)a2);
@@ -712,8 +728,8 @@ static int svc_normal_to_secure_thread(void *data)
                pr_debug(" a5=0x%016x\n", (unsigned int)a5);
                ctrl->invoke_fn(a0, a1, a2, a3, a4, a5, a6, a7, &res);
 
-               pr_debug("%s: after SMC call -- res.a0=0x%016x",
-                        __func__, (unsigned int)res.a0);
+               pr_debug("%s: %s: after SMC call -- res.a0=0x%016x",
+                        __func__, chan->name, (unsigned int)res.a0);
                pr_debug(" res.a1=0x%016x, res.a2=0x%016x",
                         (unsigned int)res.a1, (unsigned int)res.a2);
                pr_debug(" res.a3=0x%016x\n", (unsigned int)res.a3);
@@ -728,6 +744,7 @@ static int svc_normal_to_secure_thread(void *data)
                        cbdata->kaddr2 = NULL;
                        cbdata->kaddr3 = NULL;
                        pdata->chan->scl->receive_cb(pdata->chan->scl, cbdata);
+                       mutex_unlock(&ctrl->sdm_lock);
                        continue;
                }
 
@@ -801,6 +818,8 @@ static int svc_normal_to_secure_thread(void *data)
                        break;
 
                }
+
+               mutex_unlock(&ctrl->sdm_lock);
        }
 
        kfree(cbdata);
@@ -1696,22 +1715,33 @@ int stratix10_svc_send(struct stratix10_svc_chan *chan, void *msg)
        if (!p_data)
                return -ENOMEM;
 
-       /* first client will create kernel thread */
-       if (!chan->ctrl->task) {
-               chan->ctrl->task =
-                       kthread_run_on_cpu(svc_normal_to_secure_thread,
-                                          (void *)chan->ctrl,
-                                          cpu, "svc_smc_hvc_thread");
-               if (IS_ERR(chan->ctrl->task)) {
+       /* first caller creates the per-channel kthread */
+       if (!chan->task) {
+               struct task_struct *task;
+
+               task = kthread_run_on_cpu(svc_normal_to_secure_thread,
+                                         (void *)chan,
+                                         cpu, "svc_smc_hvc_thread");
+               if (IS_ERR(task)) {
                        dev_err(chan->ctrl->dev,
                                "failed to create svc_smc_hvc_thread\n");
                        kfree(p_data);
                        return -EINVAL;
                }
+
+               spin_lock(&chan->lock);
+               if (chan->task) {
+                       /* another caller won the race; discard our thread */
+                       spin_unlock(&chan->lock);
+                       kthread_stop(task);
+               } else {
+                       chan->task = task;
+                       spin_unlock(&chan->lock);
+               }
        }
 
-       pr_debug("%s: sent P-va=%p, P-com=%x, P-size=%u\n", __func__,
-                p_msg->payload, p_msg->command,
+       pr_debug("%s: %s: sent P-va=%p, P-com=%x, P-size=%u\n", __func__,
+                chan->name, p_msg->payload, p_msg->command,
                 (unsigned int)p_msg->payload_length);
 
        if (list_empty(&svc_data_mem)) {
@@ -1747,12 +1777,16 @@ int stratix10_svc_send(struct stratix10_svc_chan *chan, void *msg)
        p_data->arg[2] = p_msg->arg[2];
        p_data->size = p_msg->payload_length;
        p_data->chan = chan;
-       pr_debug("%s: put to FIFO pa=0x%016x, cmd=%x, size=%u\n", __func__,
-              (unsigned int)p_data->paddr, p_data->command,
-              (unsigned int)p_data->size);
-       ret = kfifo_in_spinlocked(&chan->ctrl->svc_fifo, p_data,
+       pr_debug("%s: %s: put to FIFO pa=0x%016x, cmd=%x, size=%u\n",
+                __func__,
+                chan->name,
+                (unsigned int)p_data->paddr,
+                p_data->command,
+                (unsigned int)p_data->size);
+
+       ret = kfifo_in_spinlocked(&chan->svc_fifo, p_data,
                                  sizeof(*p_data),
-                                 &chan->ctrl->svc_fifo_lock);
+                                 &chan->svc_fifo_lock);
 
        kfree(p_data);
 
@@ -1773,11 +1807,12 @@ EXPORT_SYMBOL_GPL(stratix10_svc_send);
  */
 void stratix10_svc_done(struct stratix10_svc_chan *chan)
 {
-       /* stop thread when thread is running AND only one active client */
-       if (chan->ctrl->task && chan->ctrl->num_active_client <= 1) {
-               pr_debug("svc_smc_hvc_shm_thread is stopped\n");
-               kthread_stop(chan->ctrl->task);
-               chan->ctrl->task = NULL;
+       /* stop thread when thread is running */
+       if (chan->task) {
+               pr_debug("%s: %s: svc_smc_hvc_shm_thread is stopping\n",
+                        __func__, chan->name);
+               kthread_stop(chan->task);
+               chan->task = NULL;
        }
 }
 EXPORT_SYMBOL_GPL(stratix10_svc_done);
@@ -1817,8 +1852,8 @@ void *stratix10_svc_allocate_memory(struct stratix10_svc_chan *chan,
        pmem->paddr = pa;
        pmem->size = s;
        list_add_tail(&pmem->node, &svc_data_mem);
-       pr_debug("%s: va=%p, pa=0x%016x\n", __func__,
-                pmem->vaddr, (unsigned int)pmem->paddr);
+       pr_debug("%s: %s: va=%p, pa=0x%016x\n", __func__,
+                chan->name, pmem->vaddr, (unsigned int)pmem->paddr);
 
        return (void *)va;
 }
@@ -1855,6 +1890,13 @@ static const struct of_device_id stratix10_svc_drv_match[] = {
        {},
 };
 
+static const char * const chan_names[SVC_NUM_CHANNEL] = {
+       SVC_CLIENT_FPGA,
+       SVC_CLIENT_RSU,
+       SVC_CLIENT_FCS,
+       SVC_CLIENT_HWMON
+};
+
 static int stratix10_svc_drv_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -1862,11 +1904,11 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
        struct stratix10_svc_chan *chans;
        struct gen_pool *genpool;
        struct stratix10_svc_sh_memory *sh_memory;
-       struct stratix10_svc *svc;
+       struct stratix10_svc *svc = NULL;
 
        svc_invoke_fn *invoke_fn;
        size_t fifo_size;
-       int ret;
+       int ret, i = 0;
 
        /* get SMC or HVC function */
        invoke_fn = get_invoke_func(dev);
@@ -1905,8 +1947,8 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
        controller->num_active_client = 0;
        controller->chans = chans;
        controller->genpool = genpool;
-       controller->task = NULL;
        controller->invoke_fn = invoke_fn;
+       INIT_LIST_HEAD(&controller->node);
        init_completion(&controller->complete_status);
 
        ret = stratix10_svc_async_init(controller);
@@ -1917,32 +1959,20 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
        }
 
        fifo_size = sizeof(struct stratix10_svc_data) * SVC_NUM_DATA_IN_FIFO;
-       ret = kfifo_alloc(&controller->svc_fifo, fifo_size, GFP_KERNEL);
-       if (ret) {
-               dev_err(dev, "failed to allocate FIFO\n");
-               goto err_async_exit;
-       }
-       spin_lock_init(&controller->svc_fifo_lock);
-
-       chans[0].scl = NULL;
-       chans[0].ctrl = controller;
-       chans[0].name = SVC_CLIENT_FPGA;
-       spin_lock_init(&chans[0].lock);
+       mutex_init(&controller->sdm_lock);
 
-       chans[1].scl = NULL;
-       chans[1].ctrl = controller;
-       chans[1].name = SVC_CLIENT_RSU;
-       spin_lock_init(&chans[1].lock);
-
-       chans[2].scl = NULL;
-       chans[2].ctrl = controller;
-       chans[2].name = SVC_CLIENT_FCS;
-       spin_lock_init(&chans[2].lock);
-
-       chans[3].scl = NULL;
-       chans[3].ctrl = controller;
-       chans[3].name = SVC_CLIENT_HWMON;
-       spin_lock_init(&chans[3].lock);
+       for (i = 0; i < SVC_NUM_CHANNEL; i++) {
+               chans[i].scl = NULL;
+               chans[i].ctrl = controller;
+               chans[i].name = (char *)chan_names[i];
+               spin_lock_init(&chans[i].lock);
+               ret = kfifo_alloc(&chans[i].svc_fifo, fifo_size, GFP_KERNEL);
+               if (ret) {
+                       dev_err(dev, "failed to allocate FIFO %d\n", i);
+                       goto err_free_fifos;
+               }
+               spin_lock_init(&chans[i].svc_fifo_lock);
+       }
 
        list_add_tail(&controller->node, &svc_ctrl);
        platform_set_drvdata(pdev, controller);
@@ -1951,7 +1981,7 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
        svc = devm_kzalloc(dev, sizeof(*svc), GFP_KERNEL);
        if (!svc) {
                ret = -ENOMEM;
-               goto err_free_kfifo;
+               goto err_free_fifos;
        }
        controller->svc = svc;
 
@@ -1959,51 +1989,43 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
        if (!svc->stratix10_svc_rsu) {
                dev_err(dev, "failed to allocate %s device\n", STRATIX10_RSU);
                ret = -ENOMEM;
-               goto err_free_kfifo;
+               goto err_free_fifos;
        }
 
        ret = platform_device_add(svc->stratix10_svc_rsu);
-       if (ret) {
-               platform_device_put(svc->stratix10_svc_rsu);
-               goto err_free_kfifo;
-       }
-
-       svc->intel_svc_fcs = platform_device_alloc(INTEL_FCS, 1);
-       if (!svc->intel_svc_fcs) {
-               dev_err(dev, "failed to allocate %s device\n", INTEL_FCS);
-               ret = -ENOMEM;
-               goto err_unregister_rsu_dev;
-       }
-
-       ret = platform_device_add(svc->intel_svc_fcs);
-       if (ret) {
-               platform_device_put(svc->intel_svc_fcs);
-               goto err_unregister_rsu_dev;
-       }
+       if (ret)
+               goto err_put_device;
 
        ret = of_platform_default_populate(dev_of_node(dev), NULL, dev);
        if (ret)
-               goto err_unregister_fcs_dev;
+               goto err_unregister_rsu_dev;
 
        pr_info("Intel Service Layer Driver Initialized\n");
 
        return 0;
 
-err_unregister_fcs_dev:
-       platform_device_unregister(svc->intel_svc_fcs);
 err_unregister_rsu_dev:
        platform_device_unregister(svc->stratix10_svc_rsu);
-err_free_kfifo:
-       kfifo_free(&controller->svc_fifo);
-err_async_exit:
+       goto err_free_fifos;
+err_put_device:
+       platform_device_put(svc->stratix10_svc_rsu);
+err_free_fifos:
+       /* only remove from list if list_add_tail() was reached */
+       if (!list_empty(&controller->node))
+               list_del(&controller->node);
+       /* free only the FIFOs that were successfully allocated */
+       while (i--)
+               kfifo_free(&chans[i].svc_fifo);
        stratix10_svc_async_exit(controller);
 err_destroy_pool:
        gen_pool_destroy(genpool);
+
        return ret;
 }
 
 static void stratix10_svc_drv_remove(struct platform_device *pdev)
 {
+       int i;
        struct stratix10_svc_controller *ctrl = platform_get_drvdata(pdev);
        struct stratix10_svc *svc = ctrl->svc;
 
@@ -2011,14 +2033,16 @@ static void stratix10_svc_drv_remove(struct platform_device *pdev)
 
        of_platform_depopulate(ctrl->dev);
 
-       platform_device_unregister(svc->intel_svc_fcs);
        platform_device_unregister(svc->stratix10_svc_rsu);
 
-       kfifo_free(&ctrl->svc_fifo);
-       if (ctrl->task) {
-               kthread_stop(ctrl->task);
-               ctrl->task = NULL;
+       for (i = 0; i < SVC_NUM_CHANNEL; i++) {
+               if (ctrl->chans[i].task) {
+                       kthread_stop(ctrl->chans[i].task);
+                       ctrl->chans[i].task = NULL;
+               }
+               kfifo_free(&ctrl->chans[i].svc_fifo);
        }
+
        if (ctrl->genpool)
                gen_pool_destroy(ctrl->genpool);
        list_del(&ctrl->node);
index d290060f4c73d16dfcea9510831ce6bf0c1677a7..91013161e9db9897466b705b3250846759df3af2 100644 (file)
  * timeout value used in Stratix10 FPGA manager driver.
  * timeout value used in RSU driver
  */
-#define SVC_RECONFIG_REQUEST_TIMEOUT_MS         300
-#define SVC_RECONFIG_BUFFER_TIMEOUT_MS          720
-#define SVC_RSU_REQUEST_TIMEOUT_MS              300
+#define SVC_RECONFIG_REQUEST_TIMEOUT_MS         5000
+#define SVC_RECONFIG_BUFFER_TIMEOUT_MS          5000
+#define SVC_RSU_REQUEST_TIMEOUT_MS              2000
 #define SVC_FCS_REQUEST_TIMEOUT_MS             2000
 #define SVC_COMPLETED_TIMEOUT_MS               30000
-#define SVC_HWMON_REQUEST_TIMEOUT_MS           300
+#define SVC_HWMON_REQUEST_TIMEOUT_MS           2000
 
 struct stratix10_svc_chan;