Skip to content

Commit f76c745

Browse files
committed
Merge tag 'scmi-fixes-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux into arm/fixes
Arm SCMI fixes for v6.1 A bunch of fixes to handle: 1. A possible resource leak in scmi_remove(). The returned error value gets ignored by the driver core and can remove the device and free the devm-allocated resources. As a simple solution to be able to easily backport, the bind attributes in the driver is suppressed as there is no need to support it. Additionally the remove path is cleaned up by adding device links between the core and the protocol devices so that a proper and complete unbinding happens. 2. A possible spin-loop in the SCMI transmit path in case of misbehaving platform firmware. A timeout is added to the existing loop so that the SCMI stack can bailout aborting the transmission with warnings. 3. Optional Rx channel correctly by reporting any memory errors instead of ignoring the same with other allowed errors. 4. The use of proper device for all the device managed allocations in the virtio transport. 5. Incorrect deferred_tx_wq release on the error paths by using devres API(devm_add_action_or_reset) to manage the release in the error path. * tag 'scmi-fixes-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/sudeep.holla/linux: firmware: arm_scmi: Fix deferred_tx_wq release on error paths firmware: arm_scmi: Fix devres allocation device in virtio transport firmware: arm_scmi: Make Rx chan_setup fail on memory errors firmware: arm_scmi: Make tx_prepare time out eventually firmware: arm_scmi: Suppress the driver's bind attributes firmware: arm_scmi: Cleanup the core driver removal callback Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Arnd Bergmann <[email protected]>
2 parents a0e2150 + 1eff692 commit f76c745

File tree

8 files changed

+88
-32
lines changed

8 files changed

+88
-32
lines changed

drivers/firmware/arm_scmi/bus.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,9 +216,20 @@ void scmi_device_destroy(struct scmi_device *scmi_dev)
216216
device_unregister(&scmi_dev->dev);
217217
}
218218

219+
void scmi_device_link_add(struct device *consumer, struct device *supplier)
220+
{
221+
struct device_link *link;
222+
223+
link = device_link_add(consumer, supplier, DL_FLAG_AUTOREMOVE_CONSUMER);
224+
225+
WARN_ON(!link);
226+
}
227+
219228
void scmi_set_handle(struct scmi_device *scmi_dev)
220229
{
221230
scmi_dev->handle = scmi_handle_get(&scmi_dev->dev);
231+
if (scmi_dev->handle)
232+
scmi_device_link_add(&scmi_dev->dev, scmi_dev->handle->dev);
222233
}
223234

224235
int scmi_protocol_register(const struct scmi_protocol *proto)

drivers/firmware/arm_scmi/common.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ static inline void unpack_scmi_header(u32 msg_hdr, struct scmi_msg_hdr *hdr)
9797
struct scmi_revision_info *
9898
scmi_revision_area_get(const struct scmi_protocol_handle *ph);
9999
int scmi_handle_put(const struct scmi_handle *handle);
100+
void scmi_device_link_add(struct device *consumer, struct device *supplier);
100101
struct scmi_handle *scmi_handle_get(struct device *dev);
101102
void scmi_set_handle(struct scmi_device *scmi_dev);
102103
void scmi_setup_protocol_implemented(const struct scmi_protocol_handle *ph,
@@ -117,6 +118,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
117118
*
118119
* @dev: Reference to device in the SCMI hierarchy corresponding to this
119120
* channel
121+
* @rx_timeout_ms: The configured RX timeout in milliseconds.
120122
* @handle: Pointer to SCMI entity handle
121123
* @no_completion_irq: Flag to indicate that this channel has no completion
122124
* interrupt mechanism for synchronous commands.
@@ -126,6 +128,7 @@ void scmi_protocol_release(const struct scmi_handle *handle, u8 protocol_id);
126128
*/
127129
struct scmi_chan_info {
128130
struct device *dev;
131+
unsigned int rx_timeout_ms;
129132
struct scmi_handle *handle;
130133
bool no_completion_irq;
131134
void *transport_info;
@@ -232,7 +235,7 @@ void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id);
232235
struct scmi_shared_mem;
233236

234237
void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
235-
struct scmi_xfer *xfer);
238+
struct scmi_xfer *xfer, struct scmi_chan_info *cinfo);
236239
u32 shmem_read_header(struct scmi_shared_mem __iomem *shmem);
237240
void shmem_fetch_response(struct scmi_shared_mem __iomem *shmem,
238241
struct scmi_xfer *xfer);

drivers/firmware/arm_scmi/driver.c

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2013,6 +2013,7 @@ static int scmi_chan_setup(struct scmi_info *info, struct device *dev,
20132013
return -ENOMEM;
20142014

20152015
cinfo->dev = dev;
2016+
cinfo->rx_timeout_ms = info->desc->max_rx_timeout_ms;
20162017

20172018
ret = info->desc->ops->chan_setup(cinfo, info->dev, tx);
20182019
if (ret)
@@ -2044,8 +2045,12 @@ scmi_txrx_setup(struct scmi_info *info, struct device *dev, int prot_id)
20442045
{
20452046
int ret = scmi_chan_setup(info, dev, prot_id, true);
20462047

2047-
if (!ret) /* Rx is optional, hence no error check */
2048-
scmi_chan_setup(info, dev, prot_id, false);
2048+
if (!ret) {
2049+
/* Rx is optional, report only memory errors */
2050+
ret = scmi_chan_setup(info, dev, prot_id, false);
2051+
if (ret && ret != -ENOMEM)
2052+
ret = 0;
2053+
}
20492054

20502055
return ret;
20512056
}
@@ -2273,10 +2278,16 @@ int scmi_protocol_device_request(const struct scmi_device_id *id_table)
22732278
sdev = scmi_get_protocol_device(child, info,
22742279
id_table->protocol_id,
22752280
id_table->name);
2276-
/* Set handle if not already set: device existed */
2277-
if (sdev && !sdev->handle)
2278-
sdev->handle =
2279-
scmi_handle_get_from_info_unlocked(info);
2281+
if (sdev) {
2282+
/* Set handle if not already set: device existed */
2283+
if (!sdev->handle)
2284+
sdev->handle =
2285+
scmi_handle_get_from_info_unlocked(info);
2286+
/* Relink consumer and suppliers */
2287+
if (sdev->handle)
2288+
scmi_device_link_add(&sdev->dev,
2289+
sdev->handle->dev);
2290+
}
22802291
} else {
22812292
dev_err(info->dev,
22822293
"Failed. SCMI protocol %d not active.\n",
@@ -2475,20 +2486,17 @@ void scmi_free_channel(struct scmi_chan_info *cinfo, struct idr *idr, int id)
24752486

24762487
static int scmi_remove(struct platform_device *pdev)
24772488
{
2478-
int ret = 0, id;
2489+
int ret, id;
24792490
struct scmi_info *info = platform_get_drvdata(pdev);
24802491
struct device_node *child;
24812492

24822493
mutex_lock(&scmi_list_mutex);
24832494
if (info->users)
2484-
ret = -EBUSY;
2485-
else
2486-
list_del(&info->node);
2495+
dev_warn(&pdev->dev,
2496+
"Still active SCMI users will be forcibly unbound.\n");
2497+
list_del(&info->node);
24872498
mutex_unlock(&scmi_list_mutex);
24882499

2489-
if (ret)
2490-
return ret;
2491-
24922500
scmi_notification_exit(&info->handle);
24932501

24942502
mutex_lock(&info->protocols_mtx);
@@ -2500,7 +2508,11 @@ static int scmi_remove(struct platform_device *pdev)
25002508
idr_destroy(&info->active_protocols);
25012509

25022510
/* Safe to free channels since no more users */
2503-
return scmi_cleanup_txrx_channels(info);
2511+
ret = scmi_cleanup_txrx_channels(info);
2512+
if (ret)
2513+
dev_warn(&pdev->dev, "Failed to cleanup SCMI channels.\n");
2514+
2515+
return 0;
25042516
}
25052517

25062518
static ssize_t protocol_version_show(struct device *dev,
@@ -2571,6 +2583,7 @@ MODULE_DEVICE_TABLE(of, scmi_of_match);
25712583
static struct platform_driver scmi_driver = {
25722584
.driver = {
25732585
.name = "arm-scmi",
2586+
.suppress_bind_attrs = true,
25742587
.of_match_table = scmi_of_match,
25752588
.dev_groups = versions_groups,
25762589
},

drivers/firmware/arm_scmi/mailbox.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ static void tx_prepare(struct mbox_client *cl, void *m)
3636
{
3737
struct scmi_mailbox *smbox = client_to_scmi_mailbox(cl);
3838

39-
shmem_tx_prepare(smbox->shmem, m);
39+
shmem_tx_prepare(smbox->shmem, m, smbox->cinfo);
4040
}
4141

4242
static void rx_callback(struct mbox_client *cl, void *m)

drivers/firmware/arm_scmi/optee.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -498,7 +498,7 @@ static int scmi_optee_send_message(struct scmi_chan_info *cinfo,
498498
msg_tx_prepare(channel->req.msg, xfer);
499499
ret = invoke_process_msg_channel(channel, msg_command_size(xfer));
500500
} else {
501-
shmem_tx_prepare(channel->req.shmem, xfer);
501+
shmem_tx_prepare(channel->req.shmem, xfer, cinfo);
502502
ret = invoke_process_smt_channel(channel);
503503
}
504504

drivers/firmware/arm_scmi/shmem.c

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,13 @@
55
* Copyright (C) 2019 ARM Ltd.
66
*/
77

8+
#include <linux/ktime.h>
89
#include <linux/io.h>
910
#include <linux/processor.h>
1011
#include <linux/types.h>
1112

13+
#include <asm-generic/bug.h>
14+
1215
#include "common.h"
1316

1417
/*
@@ -30,16 +33,36 @@ struct scmi_shared_mem {
3033
};
3134

3235
void shmem_tx_prepare(struct scmi_shared_mem __iomem *shmem,
33-
struct scmi_xfer *xfer)
36+
struct scmi_xfer *xfer, struct scmi_chan_info *cinfo)
3437
{
38+
ktime_t stop;
39+
3540
/*
3641
* Ideally channel must be free by now unless OS timeout last
3742
* request and platform continued to process the same, wait
3843
* until it releases the shared memory, otherwise we may endup
39-
* overwriting its response with new message payload or vice-versa
44+
* overwriting its response with new message payload or vice-versa.
45+
* Giving up anyway after twice the expected channel timeout so as
46+
* not to bail-out on intermittent issues where the platform is
47+
* occasionally a bit slower to answer.
48+
*
49+
* Note that after a timeout is detected we bail-out and carry on but
50+
* the transport functionality is probably permanently compromised:
51+
* this is just to ease debugging and avoid complete hangs on boot
52+
* due to a misbehaving SCMI firmware.
4053
*/
41-
spin_until_cond(ioread32(&shmem->channel_status) &
42-
SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE);
54+
stop = ktime_add_ms(ktime_get(), 2 * cinfo->rx_timeout_ms);
55+
spin_until_cond((ioread32(&shmem->channel_status) &
56+
SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE) ||
57+
ktime_after(ktime_get(), stop));
58+
if (!(ioread32(&shmem->channel_status) &
59+
SCMI_SHMEM_CHAN_STAT_CHANNEL_FREE)) {
60+
WARN_ON_ONCE(1);
61+
dev_err(cinfo->dev,
62+
"Timeout waiting for a free TX channel !\n");
63+
return;
64+
}
65+
4366
/* Mark channel busy + clear error */
4467
iowrite32(0x0, &shmem->channel_status);
4568
iowrite32(xfer->hdr.poll_completion ? 0 : SCMI_SHMEM_FLAG_INTR_ENABLED,

drivers/firmware/arm_scmi/smc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ static int smc_send_message(struct scmi_chan_info *cinfo,
188188
*/
189189
smc_channel_lock_acquire(scmi_info, xfer);
190190

191-
shmem_tx_prepare(scmi_info->shmem, xfer);
191+
shmem_tx_prepare(scmi_info->shmem, xfer, cinfo);
192192

193193
arm_smccc_1_1_invoke(scmi_info->func_id, 0, 0, 0, 0, 0, 0, 0, &res);
194194

drivers/firmware/arm_scmi/virtio.c

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,6 @@ static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch)
148148
{
149149
unsigned long flags;
150150
DECLARE_COMPLETION_ONSTACK(vioch_shutdown_done);
151-
void *deferred_wq = NULL;
152151

153152
/*
154153
* Prepare to wait for the last release if not already released
@@ -162,16 +161,11 @@ static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch)
162161

163162
vioch->shutdown_done = &vioch_shutdown_done;
164163
virtio_break_device(vioch->vqueue->vdev);
165-
if (!vioch->is_rx && vioch->deferred_tx_wq) {
166-
deferred_wq = vioch->deferred_tx_wq;
164+
if (!vioch->is_rx && vioch->deferred_tx_wq)
167165
/* Cannot be kicked anymore after this...*/
168166
vioch->deferred_tx_wq = NULL;
169-
}
170167
spin_unlock_irqrestore(&vioch->lock, flags);
171168

172-
if (deferred_wq)
173-
destroy_workqueue(deferred_wq);
174-
175169
scmi_vio_channel_release(vioch);
176170

177171
/* Let any possibly concurrent RX path release the channel */
@@ -416,6 +410,11 @@ static bool virtio_chan_available(struct device *dev, int idx)
416410
return vioch && !vioch->cinfo;
417411
}
418412

413+
static void scmi_destroy_tx_workqueue(void *deferred_tx_wq)
414+
{
415+
destroy_workqueue(deferred_tx_wq);
416+
}
417+
419418
static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
420419
bool tx)
421420
{
@@ -430,26 +429,33 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
430429

431430
/* Setup a deferred worker for polling. */
432431
if (tx && !vioch->deferred_tx_wq) {
432+
int ret;
433+
433434
vioch->deferred_tx_wq =
434435
alloc_workqueue(dev_name(&scmi_vdev->dev),
435436
WQ_UNBOUND | WQ_FREEZABLE | WQ_SYSFS,
436437
0);
437438
if (!vioch->deferred_tx_wq)
438439
return -ENOMEM;
439440

441+
ret = devm_add_action_or_reset(dev, scmi_destroy_tx_workqueue,
442+
vioch->deferred_tx_wq);
443+
if (ret)
444+
return ret;
445+
440446
INIT_WORK(&vioch->deferred_tx_work,
441447
scmi_vio_deferred_tx_worker);
442448
}
443449

444450
for (i = 0; i < vioch->max_msg; i++) {
445451
struct scmi_vio_msg *msg;
446452

447-
msg = devm_kzalloc(cinfo->dev, sizeof(*msg), GFP_KERNEL);
453+
msg = devm_kzalloc(dev, sizeof(*msg), GFP_KERNEL);
448454
if (!msg)
449455
return -ENOMEM;
450456

451457
if (tx) {
452-
msg->request = devm_kzalloc(cinfo->dev,
458+
msg->request = devm_kzalloc(dev,
453459
VIRTIO_SCMI_MAX_PDU_SIZE,
454460
GFP_KERNEL);
455461
if (!msg->request)
@@ -458,7 +464,7 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
458464
refcount_set(&msg->users, 1);
459465
}
460466

461-
msg->input = devm_kzalloc(cinfo->dev, VIRTIO_SCMI_MAX_PDU_SIZE,
467+
msg->input = devm_kzalloc(dev, VIRTIO_SCMI_MAX_PDU_SIZE,
462468
GFP_KERNEL);
463469
if (!msg->input)
464470
return -ENOMEM;

0 commit comments

Comments
 (0)