mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-11 04:18:39 +08:00
sctp: add a refcnt in sctp_stream_priorities to avoid a nested loop
With this refcnt added in sctp_stream_priorities, we don't need to
traverse all streams to check if the prio is used by other streams
when freeing one stream's prio in sctp_sched_prio_free_sid(). This
can avoid a nested loop (up to 65535 * 65535), which may cause a
stuck as Ying reported:
watchdog: BUG: soft lockup - CPU#23 stuck for 26s! [ksoftirqd/23:136]
Call Trace:
<TASK>
sctp_sched_prio_free_sid+0xab/0x100 [sctp]
sctp_stream_free_ext+0x64/0xa0 [sctp]
sctp_stream_free+0x31/0x50 [sctp]
sctp_association_free+0xa5/0x200 [sctp]
Note that it doesn't need to use refcount_t type for this counter,
as its accessing is always protected under the sock lock.
v1->v2:
- add a check in sctp_sched_prio_set to avoid the possible prio_head
refcnt overflow.
Fixes: 9ed7bfc795
("sctp: fix memory leak in sctp_stream_outq_migrate()")
Reported-by: Ying Xu <yinxu@redhat.com>
Acked-by: Marcelo Ricardo Leitner <marcelo.leitner@gmail.com>
Signed-off-by: Xin Long <lucien.xin@gmail.com>
Link: https://lore.kernel.org/r/825eb0c905cb864991eba335f4a2b780e543f06b.1677085641.git.lucien.xin@gmail.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
ee0a735fd9
commit
68ba446395
@ -1412,6 +1412,7 @@ struct sctp_stream_priorities {
|
||||
/* The next stream in line */
|
||||
struct sctp_stream_out_ext *next;
|
||||
__u16 prio;
|
||||
__u16 users;
|
||||
};
|
||||
|
||||
struct sctp_stream_out_ext {
|
||||
|
@ -25,6 +25,18 @@
|
||||
|
||||
static void sctp_sched_prio_unsched_all(struct sctp_stream *stream);
|
||||
|
||||
static struct sctp_stream_priorities *sctp_sched_prio_head_get(struct sctp_stream_priorities *p)
|
||||
{
|
||||
p->users++;
|
||||
return p;
|
||||
}
|
||||
|
||||
static void sctp_sched_prio_head_put(struct sctp_stream_priorities *p)
|
||||
{
|
||||
if (p && --p->users == 0)
|
||||
kfree(p);
|
||||
}
|
||||
|
||||
static struct sctp_stream_priorities *sctp_sched_prio_new_head(
|
||||
struct sctp_stream *stream, int prio, gfp_t gfp)
|
||||
{
|
||||
@ -38,6 +50,7 @@ static struct sctp_stream_priorities *sctp_sched_prio_new_head(
|
||||
INIT_LIST_HEAD(&p->active);
|
||||
p->next = NULL;
|
||||
p->prio = prio;
|
||||
p->users = 1;
|
||||
|
||||
return p;
|
||||
}
|
||||
@ -53,7 +66,7 @@ static struct sctp_stream_priorities *sctp_sched_prio_get_head(
|
||||
*/
|
||||
list_for_each_entry(p, &stream->prio_list, prio_sched) {
|
||||
if (p->prio == prio)
|
||||
return p;
|
||||
return sctp_sched_prio_head_get(p);
|
||||
if (p->prio > prio)
|
||||
break;
|
||||
}
|
||||
@ -70,7 +83,7 @@ static struct sctp_stream_priorities *sctp_sched_prio_get_head(
|
||||
*/
|
||||
break;
|
||||
if (p->prio == prio)
|
||||
return p;
|
||||
return sctp_sched_prio_head_get(p);
|
||||
}
|
||||
|
||||
/* If not even there, allocate a new one. */
|
||||
@ -154,32 +167,21 @@ static int sctp_sched_prio_set(struct sctp_stream *stream, __u16 sid,
|
||||
struct sctp_stream_out_ext *soute = sout->ext;
|
||||
struct sctp_stream_priorities *prio_head, *old;
|
||||
bool reschedule = false;
|
||||
int i;
|
||||
|
||||
old = soute->prio_head;
|
||||
if (old && old->prio == prio)
|
||||
return 0;
|
||||
|
||||
prio_head = sctp_sched_prio_get_head(stream, prio, gfp);
|
||||
if (!prio_head)
|
||||
return -ENOMEM;
|
||||
|
||||
reschedule = sctp_sched_prio_unsched(soute);
|
||||
old = soute->prio_head;
|
||||
soute->prio_head = prio_head;
|
||||
if (reschedule)
|
||||
sctp_sched_prio_sched(stream, soute);
|
||||
|
||||
if (!old)
|
||||
/* Happens when we set the priority for the first time */
|
||||
return 0;
|
||||
|
||||
for (i = 0; i < stream->outcnt; i++) {
|
||||
soute = SCTP_SO(stream, i)->ext;
|
||||
if (soute && soute->prio_head == old)
|
||||
/* It's still in use, nothing else to do here. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* No hits, we are good to free it. */
|
||||
kfree(old);
|
||||
|
||||
sctp_sched_prio_head_put(old);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -206,20 +208,8 @@ static int sctp_sched_prio_init_sid(struct sctp_stream *stream, __u16 sid,
|
||||
|
||||
static void sctp_sched_prio_free_sid(struct sctp_stream *stream, __u16 sid)
|
||||
{
|
||||
struct sctp_stream_priorities *prio = SCTP_SO(stream, sid)->ext->prio_head;
|
||||
int i;
|
||||
|
||||
if (!prio)
|
||||
return;
|
||||
|
||||
sctp_sched_prio_head_put(SCTP_SO(stream, sid)->ext->prio_head);
|
||||
SCTP_SO(stream, sid)->ext->prio_head = NULL;
|
||||
for (i = 0; i < stream->outcnt; i++) {
|
||||
if (SCTP_SO(stream, i)->ext &&
|
||||
SCTP_SO(stream, i)->ext->prio_head == prio)
|
||||
return;
|
||||
}
|
||||
|
||||
kfree(prio);
|
||||
}
|
||||
|
||||
static void sctp_sched_prio_enqueue(struct sctp_outq *q,
|
||||
|
Loading…
Reference in New Issue
Block a user