Skip to content

Commit 42e90eb

Browse files
cris-masudeep-holla
authored andcommitted
firmware: arm_scmi: Add a virtio channel refcount
Currently SCMI VirtIO channels are marked with a ready flag and related lock to track channel lifetime and support proper synchronization at shutdown when virtqueues have to be stopped. This leads to some extended spinlocked sections with IRQs off on the RX path to keep hold of the ready flag and does not scale well especially when SCMI VirtIO polling mode will be introduced. Add an SCMI VirtIO channel dedicated refcount to track active users on both the TX and the RX path and properly enforce synchronization and cleanup at shutdown, inhibiting further usage of the channel once freed. Link: https://lore.kernel.org/r/[email protected] Cc: "Michael S. Tsirkin" <[email protected]> Cc: Igor Skalkin <[email protected]> Cc: Peter Hilber <[email protected]> Cc: [email protected] Signed-off-by: Cristian Marussi <[email protected]> Signed-off-by: Sudeep Holla <[email protected]>
1 parent cdf157f commit 42e90eb

File tree

1 file changed

+92
-51
lines changed

1 file changed

+92
-51
lines changed

drivers/firmware/arm_scmi/virtio.c

Lines changed: 92 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
* virtqueue. Access to each virtqueue is protected by spinlocks.
1818
*/
1919

20+
#include <linux/completion.h>
2021
#include <linux/errno.h>
22+
#include <linux/refcount.h>
2123
#include <linux/slab.h>
2224
#include <linux/virtio.h>
2325
#include <linux/virtio_config.h>
@@ -27,6 +29,7 @@
2729

2830
#include "common.h"
2931

32+
#define VIRTIO_MAX_RX_TIMEOUT_MS 60000
3033
#define VIRTIO_SCMI_MAX_MSG_SIZE 128 /* Value may be increased. */
3134
#define VIRTIO_SCMI_MAX_PDU_SIZE \
3235
(VIRTIO_SCMI_MAX_MSG_SIZE + SCMI_MSG_MAX_PROT_OVERHEAD)
@@ -39,23 +42,21 @@
3942
* @cinfo: SCMI Tx or Rx channel
4043
* @free_list: List of unused scmi_vio_msg, maintained for Tx channels only
4144
* @is_rx: Whether channel is an Rx channel
42-
* @ready: Whether transport user is ready to hear about channel
4345
* @max_msg: Maximum number of pending messages for this channel.
44-
* @lock: Protects access to all members except ready.
45-
* @ready_lock: Protects access to ready. If required, it must be taken before
46-
* lock.
46+
* @lock: Protects access to all members except users.
47+
* @shutdown_done: A reference to a completion used when freeing this channel.
48+
* @users: A reference count to currently active users of this channel.
4749
*/
4850
struct scmi_vio_channel {
4951
struct virtqueue *vqueue;
5052
struct scmi_chan_info *cinfo;
5153
struct list_head free_list;
5254
bool is_rx;
53-
bool ready;
5455
unsigned int max_msg;
55-
/* lock to protect access to all members except ready. */
56+
/* lock to protect access to all members except users. */
5657
spinlock_t lock;
57-
/* lock to rotects access to ready flag. */
58-
spinlock_t ready_lock;
58+
struct completion *shutdown_done;
59+
refcount_t users;
5960
};
6061

6162
/**
@@ -76,6 +77,63 @@ struct scmi_vio_msg {
7677
/* Only one SCMI VirtIO device can possibly exist */
7778
static struct virtio_device *scmi_vdev;
7879

80+
static void scmi_vio_channel_ready(struct scmi_vio_channel *vioch,
81+
struct scmi_chan_info *cinfo)
82+
{
83+
unsigned long flags;
84+
85+
spin_lock_irqsave(&vioch->lock, flags);
86+
cinfo->transport_info = vioch;
87+
/* Indirectly setting channel not available any more */
88+
vioch->cinfo = cinfo;
89+
spin_unlock_irqrestore(&vioch->lock, flags);
90+
91+
refcount_set(&vioch->users, 1);
92+
}
93+
94+
static inline bool scmi_vio_channel_acquire(struct scmi_vio_channel *vioch)
95+
{
96+
return refcount_inc_not_zero(&vioch->users);
97+
}
98+
99+
static inline void scmi_vio_channel_release(struct scmi_vio_channel *vioch)
100+
{
101+
if (refcount_dec_and_test(&vioch->users)) {
102+
unsigned long flags;
103+
104+
spin_lock_irqsave(&vioch->lock, flags);
105+
if (vioch->shutdown_done) {
106+
vioch->cinfo = NULL;
107+
complete(vioch->shutdown_done);
108+
}
109+
spin_unlock_irqrestore(&vioch->lock, flags);
110+
}
111+
}
112+
113+
static void scmi_vio_channel_cleanup_sync(struct scmi_vio_channel *vioch)
114+
{
115+
unsigned long flags;
116+
DECLARE_COMPLETION_ONSTACK(vioch_shutdown_done);
117+
118+
/*
119+
* Prepare to wait for the last release if not already released
120+
* or in progress.
121+
*/
122+
spin_lock_irqsave(&vioch->lock, flags);
123+
if (!vioch->cinfo || vioch->shutdown_done) {
124+
spin_unlock_irqrestore(&vioch->lock, flags);
125+
return;
126+
}
127+
vioch->shutdown_done = &vioch_shutdown_done;
128+
virtio_break_device(vioch->vqueue->vdev);
129+
spin_unlock_irqrestore(&vioch->lock, flags);
130+
131+
scmi_vio_channel_release(vioch);
132+
133+
/* Let any possibly concurrent RX path release the channel */
134+
wait_for_completion(vioch->shutdown_done);
135+
}
136+
79137
static bool scmi_vio_have_vq_rx(struct virtio_device *vdev)
80138
{
81139
return virtio_has_feature(vdev, VIRTIO_SCMI_F_P2A_CHANNELS);
@@ -119,7 +177,7 @@ static void scmi_finalize_message(struct scmi_vio_channel *vioch,
119177

120178
static void scmi_vio_complete_cb(struct virtqueue *vqueue)
121179
{
122-
unsigned long ready_flags;
180+
unsigned long flags;
123181
unsigned int length;
124182
struct scmi_vio_channel *vioch;
125183
struct scmi_vio_msg *msg;
@@ -130,27 +188,24 @@ static void scmi_vio_complete_cb(struct virtqueue *vqueue)
130188
vioch = &((struct scmi_vio_channel *)vqueue->vdev->priv)[vqueue->index];
131189

132190
for (;;) {
133-
spin_lock_irqsave(&vioch->ready_lock, ready_flags);
134-
135-
if (!vioch->ready) {
136-
if (!cb_enabled)
137-
(void)virtqueue_enable_cb(vqueue);
138-
goto unlock_ready_out;
139-
}
191+
if (!scmi_vio_channel_acquire(vioch))
192+
return;
140193

141-
/* IRQs already disabled here no need to irqsave */
142-
spin_lock(&vioch->lock);
194+
spin_lock_irqsave(&vioch->lock, flags);
143195
if (cb_enabled) {
144196
virtqueue_disable_cb(vqueue);
145197
cb_enabled = false;
146198
}
147199
msg = virtqueue_get_buf(vqueue, &length);
148200
if (!msg) {
149-
if (virtqueue_enable_cb(vqueue))
150-
goto unlock_out;
201+
if (virtqueue_enable_cb(vqueue)) {
202+
spin_unlock_irqrestore(&vioch->lock, flags);
203+
scmi_vio_channel_release(vioch);
204+
return;
205+
}
151206
cb_enabled = true;
152207
}
153-
spin_unlock(&vioch->lock);
208+
spin_unlock_irqrestore(&vioch->lock, flags);
154209

155210
if (msg) {
156211
msg->rx_len = length;
@@ -161,19 +216,14 @@ static void scmi_vio_complete_cb(struct virtqueue *vqueue)
161216
}
162217

163218
/*
164-
* Release ready_lock and re-enable IRQs between loop iterations
165-
* to allow virtio_chan_free() to possibly kick in and set the
166-
* flag vioch->ready to false even in between processing of
167-
* messages, so as to force outstanding messages to be ignored
168-
* when system is shutting down.
219+
* Release vio channel between loop iterations to allow
220+
* virtio_chan_free() to eventually fully release it when
221+
* shutting down; in such a case, any outstanding message will
222+
* be ignored since this loop will bail out at the next
223+
* iteration.
169224
*/
170-
spin_unlock_irqrestore(&vioch->ready_lock, ready_flags);
225+
scmi_vio_channel_release(vioch);
171226
}
172-
173-
unlock_out:
174-
spin_unlock(&vioch->lock);
175-
unlock_ready_out:
176-
spin_unlock_irqrestore(&vioch->ready_lock, ready_flags);
177227
}
178228

179229
static const char *const scmi_vio_vqueue_names[] = { "tx", "rx" };
@@ -273,35 +323,20 @@ static int virtio_chan_setup(struct scmi_chan_info *cinfo, struct device *dev,
273323
}
274324
}
275325

276-
spin_lock_irqsave(&vioch->lock, flags);
277-
cinfo->transport_info = vioch;
278-
/* Indirectly setting channel not available any more */
279-
vioch->cinfo = cinfo;
280-
spin_unlock_irqrestore(&vioch->lock, flags);
281-
282-
spin_lock_irqsave(&vioch->ready_lock, flags);
283-
vioch->ready = true;
284-
spin_unlock_irqrestore(&vioch->ready_lock, flags);
326+
scmi_vio_channel_ready(vioch, cinfo);
285327

286328
return 0;
287329
}
288330

289331
static int virtio_chan_free(int id, void *p, void *data)
290332
{
291-
unsigned long flags;
292333
struct scmi_chan_info *cinfo = p;
293334
struct scmi_vio_channel *vioch = cinfo->transport_info;
294335

295-
spin_lock_irqsave(&vioch->ready_lock, flags);
296-
vioch->ready = false;
297-
spin_unlock_irqrestore(&vioch->ready_lock, flags);
336+
scmi_vio_channel_cleanup_sync(vioch);
298337

299338
scmi_free_channel(cinfo, data, id);
300339

301-
spin_lock_irqsave(&vioch->lock, flags);
302-
vioch->cinfo = NULL;
303-
spin_unlock_irqrestore(&vioch->lock, flags);
304-
305340
return 0;
306341
}
307342

@@ -316,10 +351,14 @@ static int virtio_send_message(struct scmi_chan_info *cinfo,
316351
int rc;
317352
struct scmi_vio_msg *msg;
318353

354+
if (!scmi_vio_channel_acquire(vioch))
355+
return -EINVAL;
356+
319357
spin_lock_irqsave(&vioch->lock, flags);
320358

321359
if (list_empty(&vioch->free_list)) {
322360
spin_unlock_irqrestore(&vioch->lock, flags);
361+
scmi_vio_channel_release(vioch);
323362
return -EBUSY;
324363
}
325364

@@ -342,6 +381,8 @@ static int virtio_send_message(struct scmi_chan_info *cinfo,
342381

343382
spin_unlock_irqrestore(&vioch->lock, flags);
344383

384+
scmi_vio_channel_release(vioch);
385+
345386
return rc;
346387
}
347388

@@ -416,7 +457,6 @@ static int scmi_vio_probe(struct virtio_device *vdev)
416457
unsigned int sz;
417458

418459
spin_lock_init(&channels[i].lock);
419-
spin_lock_init(&channels[i].ready_lock);
420460
INIT_LIST_HEAD(&channels[i].free_list);
421461
channels[i].vqueue = vqs[i];
422462

@@ -503,7 +543,8 @@ const struct scmi_desc scmi_virtio_desc = {
503543
.transport_init = virtio_scmi_init,
504544
.transport_exit = virtio_scmi_exit,
505545
.ops = &scmi_virtio_ops,
506-
.max_rx_timeout_ms = 60000, /* for non-realtime virtio devices */
546+
/* for non-realtime virtio devices */
547+
.max_rx_timeout_ms = VIRTIO_MAX_RX_TIMEOUT_MS,
507548
.max_msg = 0, /* overridden by virtio_get_max_msg() */
508549
.max_msg_size = VIRTIO_SCMI_MAX_MSG_SIZE,
509550
};

0 commit comments

Comments
 (0)