linux-sctp.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v4] sctp: fix refcount bug in sctp_wfree
@ 2020-03-22  9:04 Qiujun Huang
  2020-03-26  0:14 ` Marcelo Ricardo Leitner
  0 siblings, 1 reply; 6+ messages in thread
From: Qiujun Huang @ 2020-03-22  9:04 UTC (permalink / raw)
  To: marcelo.leitner, davem
  Cc: vyasevich, nhorman, kuba, linux-sctp, netdev, linux-kernel,
	anenbupt, Qiujun Huang

sctp_sock_migrate should iterate over the datamsgs to modify
all trunks(skbs) to newsk. For this, out_msg_list is added to
sctp_outq to maintain datamsgs list.

The following case cause the bug:

for the trouble SKB, it was in outq->transmitted list

sctp_outq_sack
        sctp_check_transmitted
                SKB was moved to outq->sacked list
        then throw away the sack queue
                SKB was deleted from outq->sacked
(but it was held by datamsg at sctp_datamsg_to_asoc
So, sctp_wfree was not called here)

then migrate happened

        sctp_for_each_tx_datachunk(
        sctp_clear_owner_w);
        sctp_assoc_migrate();
        sctp_for_each_tx_datachunk(
        sctp_set_owner_w);
SKB was not in the outq, and was not changed to newsk

finally

__sctp_outq_teardown
        sctp_chunk_put (for another skb)
                sctp_datamsg_put
                        __kfree_skb(msg->frag_list)
                                sctp_wfree (for SKB)
	SKB->sk was still oldsk (skb->sk != asoc->base.sk).

Reported-and-tested-by: syzbot+cea71eec5d6de256d54d@syzkaller.appspotmail.com
Signed-off-by: Qiujun Huang <hqjagain@gmail.com>
---
 include/net/sctp/structs.h |  5 +++++
 net/sctp/chunk.c           |  4 ++++
 net/sctp/outqueue.c        |  1 +
 net/sctp/sm_sideeffect.c   |  1 +
 net/sctp/socket.c          | 27 +++++++--------------------
 5 files changed, 18 insertions(+), 20 deletions(-)

diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h
index 314a2fa21d6b..f72ba7418230 100644
--- a/include/net/sctp/structs.h
+++ b/include/net/sctp/structs.h
@@ -522,6 +522,8 @@ struct sctp_pf {
 struct sctp_datamsg {
 	/* Chunks waiting to be submitted to lower layer. */
 	struct list_head chunks;
+	/* List in outq. */
+	struct list_head list;
 	/* Reference counting. */
 	refcount_t refcnt;
 	/* When is this message no longer interesting to the peer? */
@@ -1063,6 +1065,9 @@ struct sctp_outq {
 	/* Data pending that has never been transmitted.  */
 	struct list_head out_chunk_list;
 
+	/* Data msg list. */
+	struct list_head out_msg_list;
+
 	/* Stream scheduler being used */
 	struct sctp_sched_ops *sched;
 
diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c
index ab6a997e222f..17b38e9a8a7b 100644
--- a/net/sctp/chunk.c
+++ b/net/sctp/chunk.c
@@ -41,6 +41,7 @@ static void sctp_datamsg_init(struct sctp_datamsg *msg)
 	msg->abandoned = 0;
 	msg->expires_at = 0;
 	INIT_LIST_HEAD(&msg->chunks);
+	INIT_LIST_HEAD(&msg->list);
 }
 
 /* Allocate and initialize datamsg. */
@@ -111,6 +112,9 @@ static void sctp_datamsg_destroy(struct sctp_datamsg *msg)
 		sctp_chunk_put(chunk);
 	}
 
+	if (!list_empty(&msg->list))
+		list_del_init(&msg->list);
+
 	SCTP_DBG_OBJCNT_DEC(datamsg);
 	kfree(msg);
 }
diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c
index 577e3bc4ee6f..3bbcb140c887 100644
--- a/net/sctp/outqueue.c
+++ b/net/sctp/outqueue.c
@@ -194,6 +194,7 @@ void sctp_outq_init(struct sctp_association *asoc, struct sctp_outq *q)
 
 	q->asoc = asoc;
 	INIT_LIST_HEAD(&q->out_chunk_list);
+	INIT_LIST_HEAD(&q->out_msg_list);
 	INIT_LIST_HEAD(&q->control_chunk_list);
 	INIT_LIST_HEAD(&q->retransmit);
 	INIT_LIST_HEAD(&q->sacked);
diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c
index 2bc29463e1dc..93cc911256f6 100644
--- a/net/sctp/sm_sideeffect.c
+++ b/net/sctp/sm_sideeffect.c
@@ -1099,6 +1099,7 @@ static void sctp_cmd_send_msg(struct sctp_association *asoc,
 	list_for_each_entry(chunk, &msg->chunks, frag_list)
 		sctp_outq_tail(&asoc->outqueue, chunk, gfp);
 
+	list_add_tail(&msg->list, &asoc->outqueue.out_msg_list);
 	asoc->outqueue.sched->enqueue(&asoc->outqueue, msg);
 }
 
diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index 1b56fc440606..32f6111bccbf 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -147,29 +147,16 @@ static void sctp_clear_owner_w(struct sctp_chunk *chunk)
 	skb_orphan(chunk->skb);
 }
 
-static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
-				       void (*cb)(struct sctp_chunk *))
+static void sctp_for_each_tx_datamsg(struct sctp_association *asoc,
+				     void (*cb)(struct sctp_chunk *))
 
 {
-	struct sctp_outq *q = &asoc->outqueue;
-	struct sctp_transport *t;
 	struct sctp_chunk *chunk;
+	struct sctp_datamsg *msg;
 
-	list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
-		list_for_each_entry(chunk, &t->transmitted, transmitted_list)
+	list_for_each_entry(msg, &asoc->outqueue.out_msg_list, list)
+		list_for_each_entry(chunk, &msg->chunks, frag_list)
 			cb(chunk);
-
-	list_for_each_entry(chunk, &q->retransmit, transmitted_list)
-		cb(chunk);
-
-	list_for_each_entry(chunk, &q->sacked, transmitted_list)
-		cb(chunk);
-
-	list_for_each_entry(chunk, &q->abandoned, transmitted_list)
-		cb(chunk);
-
-	list_for_each_entry(chunk, &q->out_chunk_list, list)
-		cb(chunk);
 }
 
 static void sctp_for_each_rx_skb(struct sctp_association *asoc, struct sock *sk,
@@ -9574,9 +9561,9 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
 	 * paths won't try to lock it and then oldsk.
 	 */
 	lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
-	sctp_for_each_tx_datachunk(assoc, sctp_clear_owner_w);
+	sctp_for_each_tx_datamsg(assoc, sctp_clear_owner_w);
 	sctp_assoc_migrate(assoc, newsk);
-	sctp_for_each_tx_datachunk(assoc, sctp_set_owner_w);
+	sctp_for_each_tx_datamsg(assoc, sctp_set_owner_w);
 
 	/* If the association on the newsk is already closed before accept()
 	 * is called, set RCV_SHUTDOWN flag.
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v4] sctp: fix refcount bug in sctp_wfree
  2020-03-22  9:04 [PATCH v4] sctp: fix refcount bug in sctp_wfree Qiujun Huang
@ 2020-03-26  0:14 ` Marcelo Ricardo Leitner
  2020-03-26  1:30   ` Qiujun Huang
  0 siblings, 1 reply; 6+ messages in thread
From: Marcelo Ricardo Leitner @ 2020-03-26  0:14 UTC (permalink / raw)
  To: Qiujun Huang
  Cc: davem, vyasevich, nhorman, kuba, linux-sctp, netdev,
	linux-kernel, anenbupt

On Sun, Mar 22, 2020 at 05:04:25PM +0800, Qiujun Huang wrote:
> sctp_sock_migrate should iterate over the datamsgs to modify
> all trunks(skbs) to newsk. For this, out_msg_list is added to

s/trunks/chunks/

> sctp_outq to maintain datamsgs list.

It is an interesting approach. It speeds up the migration, yes, but it
will also use more memory per datamsg, for an operation that, when
performed, the socket is usually calm.

It's also another list to be handled, and I'm not seeing the patch
here move the datamsg itself now to the new outq. It would need
something along these lines:
sctp_sock_migrate()
{
...
        /* Move any messages in the old socket's receive queue that are for the
         * peeled off association to the new socket's receive queue.
         */
        sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
                event = sctp_skb2event(skb);
...
                /* Walk through the pd_lobby, looking for skbs that
                 * need moved to the new socket.
                 */
                sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
                        event = sctp_skb2event(skb);

That said, I don't think it's worth this new list.

  Marcelo

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v4] sctp: fix refcount bug in sctp_wfree
  2020-03-26  0:14 ` Marcelo Ricardo Leitner
@ 2020-03-26  1:30   ` Qiujun Huang
  2020-03-26  3:22     ` Marcelo Ricardo Leitner
  0 siblings, 1 reply; 6+ messages in thread
From: Qiujun Huang @ 2020-03-26  1:30 UTC (permalink / raw)
  To: Marcelo Ricardo Leitner
  Cc: David S. Miller, vyasevich, nhorman, Jakub Kicinski, linux-sctp,
	netdev, LKML, anenbupt

On Thu, Mar 26, 2020 at 8:14 AM Marcelo Ricardo Leitner
<marcelo.leitner@gmail.com> wrote:
>
> On Sun, Mar 22, 2020 at 05:04:25PM +0800, Qiujun Huang wrote:
> > sctp_sock_migrate should iterate over the datamsgs to modify
> > all trunks(skbs) to newsk. For this, out_msg_list is added to
>
> s/trunks/chunks/

My :p.

>
> > sctp_outq to maintain datamsgs list.
>
> It is an interesting approach. It speeds up the migration, yes, but it
> will also use more memory per datamsg, for an operation that, when
> performed, the socket is usually calm.
>
> It's also another list to be handled, and I'm not seeing the patch
> here move the datamsg itself now to the new outq. It would need
> something along these lines:

Are all the rx chunks in the rx queues?

> sctp_sock_migrate()
> {
> ...
>         /* Move any messages in the old socket's receive queue that are for the
>          * peeled off association to the new socket's receive queue.
>          */
>         sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
>                 event = sctp_skb2event(skb);
> ...
>                 /* Walk through the pd_lobby, looking for skbs that
>                  * need moved to the new socket.
>                  */
>                 sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
>                         event = sctp_skb2event(skb);
>
> That said, I don't think it's worth this new list.

About this case:
datamsg
                   ->chunk0                       chunk1
       chunk2
 queue          ->transmitted                 ->retransmit
 ->not in any queue

Also need to maintain a datamsg list to record which datamsg is
processed avoiding repetitive
processing.
So, list it to outq. Maybe it will be used sometime.

>
>   Marcelo

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v4] sctp: fix refcount bug in sctp_wfree
  2020-03-26  1:30   ` Qiujun Huang
@ 2020-03-26  3:22     ` Marcelo Ricardo Leitner
  2020-03-26  5:37       ` Qiujun Huang
  2020-03-26  6:13       ` Qiujun Huang
  0 siblings, 2 replies; 6+ messages in thread
From: Marcelo Ricardo Leitner @ 2020-03-26  3:22 UTC (permalink / raw)
  To: Qiujun Huang
  Cc: David S. Miller, vyasevich, nhorman, Jakub Kicinski, linux-sctp,
	netdev, LKML, anenbupt

On Thu, Mar 26, 2020 at 09:30:08AM +0800, Qiujun Huang wrote:
> On Thu, Mar 26, 2020 at 8:14 AM Marcelo Ricardo Leitner
> <marcelo.leitner@gmail.com> wrote:
> >
> > On Sun, Mar 22, 2020 at 05:04:25PM +0800, Qiujun Huang wrote:
> > > sctp_sock_migrate should iterate over the datamsgs to modify
> > > all trunks(skbs) to newsk. For this, out_msg_list is added to
> >
> > s/trunks/chunks/
> 
> My :p.
> 
> >
> > > sctp_outq to maintain datamsgs list.
> >
> > It is an interesting approach. It speeds up the migration, yes, but it
> > will also use more memory per datamsg, for an operation that, when
> > performed, the socket is usually calm.
> >
> > It's also another list to be handled, and I'm not seeing the patch
> > here move the datamsg itself now to the new outq. It would need
> > something along these lines:
> 
> Are all the rx chunks in the rx queues?

Yes, even with GSO.

> 
> > sctp_sock_migrate()
> > {
> > ...
> >         /* Move any messages in the old socket's receive queue that are for the
> >          * peeled off association to the new socket's receive queue.
> >          */
> >         sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
> >                 event = sctp_skb2event(skb);
> > ...
> >                 /* Walk through the pd_lobby, looking for skbs that
> >                  * need moved to the new socket.
> >                  */
> >                 sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
> >                         event = sctp_skb2event(skb);
> >
> > That said, I don't think it's worth this new list.
> 
> About this case:
> datamsg
>                    ->chunk0                       chunk1
>        chunk2
>  queue          ->transmitted                 ->retransmit
>  ->not in any queue

We always can find it through the other chunks, otherwise it's freed.

> 
> Also need to maintain a datamsg list to record which datamsg is
> processed avoiding repetitive
> processing.

Right, but for that we can add a simple check on
sctp_for_each_tx_datamsg() based on a parameter.

> So, list it to outq. Maybe it will be used sometime.

We can change it when the time comes. For now, if we can avoid growing
sctp_datamsg, it's better. With this patch, it grows from 40 to 56
bytes, leaving just 8 left before it starts using a slab of 128 bytes
for it.


The patched list_for_each_entry() can/should be factored out into
__sctp_for_each_tx_datachunk, whose first parameter then is the queue
instead the asoc.

---8<---

diff --git a/net/sctp/socket.c b/net/sctp/socket.c
index fed26a1e9518..62f401799709 100644
--- a/net/sctp/socket.c
+++ b/net/sctp/socket.c
@@ -148,19 +148,30 @@ static void sctp_clear_owner_w(struct sctp_chunk *chunk)
 }
 
 static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
+				       bool clear,
 				       void (*cb)(struct sctp_chunk *))
 
 {
+	struct sctp_datamsg *msg, *prev_msg = NULL;
 	struct sctp_outq *q = &asoc->outqueue;
+	struct sctp_chunk *chunk, *c;
 	struct sctp_transport *t;
-	struct sctp_chunk *chunk;
 
 	list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
 		list_for_each_entry(chunk, &t->transmitted, transmitted_list)
 			cb(chunk);
 
-	list_for_each_entry(chunk, &q->retransmit, transmitted_list)
-		cb(chunk);
+	list_for_each_entry(chunk, &q->sacked, transmitted_list) {
+		msg = chunk->msg;
+		if (msg = prev_msg)
+			continue;
+		list_for_each_entry(c, &msg->chunks, frag_list) {
+			if ((clear && asoc->base.sk = c->skb->sk) ||
+			    (!clear && asoc->base.sk != c->skb->sk))
+				cb(c);
+		}
+		prev_msg = msg;
+	}
 
 	list_for_each_entry(chunk, &q->sacked, transmitted_list)
 		cb(chunk);
@@ -9574,9 +9585,9 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
 	 * paths won't try to lock it and then oldsk.
 	 */
 	lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
-	sctp_for_each_tx_datachunk(assoc, sctp_clear_owner_w);
+	sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w);
 	sctp_assoc_migrate(assoc, newsk);
-	sctp_for_each_tx_datachunk(assoc, sctp_set_owner_w);
+	sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w);
 
 	/* If the association on the newsk is already closed before accept()
 	 * is called, set RCV_SHUTDOWN flag.

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v4] sctp: fix refcount bug in sctp_wfree
  2020-03-26  3:22     ` Marcelo Ricardo Leitner
@ 2020-03-26  5:37       ` Qiujun Huang
  2020-03-26  6:13       ` Qiujun Huang
  1 sibling, 0 replies; 6+ messages in thread
From: Qiujun Huang @ 2020-03-26  5:37 UTC (permalink / raw)
  To: Marcelo Ricardo Leitner
  Cc: David S. Miller, vyasevich, nhorman, Jakub Kicinski, linux-sctp,
	netdev, LKML, anenbupt

On Thu, Mar 26, 2020 at 11:22 AM Marcelo Ricardo Leitner
<marcelo.leitner@gmail.com> wrote:
>
> On Thu, Mar 26, 2020 at 09:30:08AM +0800, Qiujun Huang wrote:
> > On Thu, Mar 26, 2020 at 8:14 AM Marcelo Ricardo Leitner
> > <marcelo.leitner@gmail.com> wrote:
> > >
> > > On Sun, Mar 22, 2020 at 05:04:25PM +0800, Qiujun Huang wrote:
> > > > sctp_sock_migrate should iterate over the datamsgs to modify
> > > > all trunks(skbs) to newsk. For this, out_msg_list is added to
> > >
> > > s/trunks/chunks/
> >
> > My :p.
> >
> > >
> > > > sctp_outq to maintain datamsgs list.
> > >
> > > It is an interesting approach. It speeds up the migration, yes, but it
> > > will also use more memory per datamsg, for an operation that, when
> > > performed, the socket is usually calm.
> > >
> > > It's also another list to be handled, and I'm not seeing the patch
> > > here move the datamsg itself now to the new outq. It would need
> > > something along these lines:
> >
> > Are all the rx chunks in the rx queues?
>
> Yes, even with GSO.
>
> >
> > > sctp_sock_migrate()
> > > {
> > > ...
> > >         /* Move any messages in the old socket's receive queue that are for the
> > >          * peeled off association to the new socket's receive queue.
> > >          */
> > >         sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
> > >                 event = sctp_skb2event(skb);
> > > ...
> > >                 /* Walk through the pd_lobby, looking for skbs that
> > >                  * need moved to the new socket.
> > >                  */
> > >                 sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
> > >                         event = sctp_skb2event(skb);
> > >
> > > That said, I don't think it's worth this new list.
> >
> > About this case:
> > datamsg
> >                    ->chunk0                       chunk1
> >        chunk2
> >  queue          ->transmitted                 ->retransmit
> >  ->not in any queue
>
> We always can find it through the other chunks, otherwise it's freed.

Yes.

datamsg   (chunk0, chunk1, chunk2)
chunk1 in transmiited queue
chunk2 in retransmit queue
chunk0 not in any queue

 We also need to check chunk2->msg processed or not, when we iterate
retransmit queue.

>
> >
> > Also need to maintain a datamsg list to record which datamsg is
> > processed avoiding repetitive
> > processing.
>
> Right, but for that we can add a simple check on
> sctp_for_each_tx_datamsg() based on a parameter.
>
> > So, list it to outq. Maybe it will be used sometime.
>
> We can change it when the time comes. For now, if we can avoid growing
> sctp_datamsg, it's better. With this patch, it grows from 40 to 56
> bytes, leaving just 8 left before it starts using a slab of 128 bytes
> for it.

I get that, thanks.

>
>
> The patched list_for_each_entry() can/should be factored out into
> __sctp_for_each_tx_datachunk, whose first parameter then is the queue
> instead the asoc.
>
> ---8<---
>
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index fed26a1e9518..62f401799709 100644
> --- a/net/sctp/socket.c
> +++ b/net/sctp/socket.c
> @@ -148,19 +148,30 @@ static void sctp_clear_owner_w(struct sctp_chunk *chunk)
>  }
>
>  static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
> +                                      bool clear,
>                                        void (*cb)(struct sctp_chunk *))
>
>  {
> +       struct sctp_datamsg *msg, *prev_msg = NULL;
>         struct sctp_outq *q = &asoc->outqueue;
> +       struct sctp_chunk *chunk, *c;
>         struct sctp_transport *t;
> -       struct sctp_chunk *chunk;
>
>         list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
>                 list_for_each_entry(chunk, &t->transmitted, transmitted_list)
>                         cb(chunk);
>
> -       list_for_each_entry(chunk, &q->retransmit, transmitted_list)
> -               cb(chunk);
> +       list_for_each_entry(chunk, &q->sacked, transmitted_list) {
> +               msg = chunk->msg;
> +               if (msg = prev_msg)
> +                       continue;
> +               list_for_each_entry(c, &msg->chunks, frag_list) {
> +                       if ((clear && asoc->base.sk = c->skb->sk) ||
> +                           (!clear && asoc->base.sk != c->skb->sk))
> +                               cb(c);
> +               }
> +               prev_msg = msg;
> +       }

The case exists?
datamsg   (chunk0, chunk1, chunk2)
chunk1 chunk2 in &q->retransmit
chunk0 not in any queue (deleted in sctp_outq_sack)

>         list_for_each_entry(chunk, &q->sacked, transmitted_list)
>                 cb(chunk);
> @@ -9574,9 +9585,9 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
>          * paths won't try to lock it and then oldsk.
>          */
>         lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
> -       sctp_for_each_tx_datachunk(assoc, sctp_clear_owner_w);
> +       sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w);
>         sctp_assoc_migrate(assoc, newsk);
> -       sctp_for_each_tx_datachunk(assoc, sctp_set_owner_w);
> +       sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w);
>
>         /* If the association on the newsk is already closed before accept()
>          * is called, set RCV_SHUTDOWN flag.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v4] sctp: fix refcount bug in sctp_wfree
  2020-03-26  3:22     ` Marcelo Ricardo Leitner
  2020-03-26  5:37       ` Qiujun Huang
@ 2020-03-26  6:13       ` Qiujun Huang
  1 sibling, 0 replies; 6+ messages in thread
From: Qiujun Huang @ 2020-03-26  6:13 UTC (permalink / raw)
  To: Marcelo Ricardo Leitner
  Cc: David S. Miller, vyasevich, nhorman, Jakub Kicinski, linux-sctp,
	netdev, LKML, anenbupt

On Thu, Mar 26, 2020 at 11:22 AM Marcelo Ricardo Leitner
<marcelo.leitner@gmail.com> wrote:
>
> On Thu, Mar 26, 2020 at 09:30:08AM +0800, Qiujun Huang wrote:
> > On Thu, Mar 26, 2020 at 8:14 AM Marcelo Ricardo Leitner
> > <marcelo.leitner@gmail.com> wrote:
> > >
> > > On Sun, Mar 22, 2020 at 05:04:25PM +0800, Qiujun Huang wrote:
> > > > sctp_sock_migrate should iterate over the datamsgs to modify
> > > > all trunks(skbs) to newsk. For this, out_msg_list is added to
> > >
> > > s/trunks/chunks/
> >
> > My :p.
> >
> > >
> > > > sctp_outq to maintain datamsgs list.
> > >
> > > It is an interesting approach. It speeds up the migration, yes, but it
> > > will also use more memory per datamsg, for an operation that, when
> > > performed, the socket is usually calm.
> > >
> > > It's also another list to be handled, and I'm not seeing the patch
> > > here move the datamsg itself now to the new outq. It would need
> > > something along these lines:
> >
> > Are all the rx chunks in the rx queues?
>
> Yes, even with GSO.
>
> >
> > > sctp_sock_migrate()
> > > {
> > > ...
> > >         /* Move any messages in the old socket's receive queue that are for the
> > >          * peeled off association to the new socket's receive queue.
> > >          */
> > >         sctp_skb_for_each(skb, &oldsk->sk_receive_queue, tmp) {
> > >                 event = sctp_skb2event(skb);
> > > ...
> > >                 /* Walk through the pd_lobby, looking for skbs that
> > >                  * need moved to the new socket.
> > >                  */
> > >                 sctp_skb_for_each(skb, &oldsp->pd_lobby, tmp) {
> > >                         event = sctp_skb2event(skb);
> > >
> > > That said, I don't think it's worth this new list.
> >
> > About this case:
> > datamsg
> >                    ->chunk0                       chunk1
> >        chunk2
> >  queue          ->transmitted                 ->retransmit
> >  ->not in any queue
>
> We always can find it through the other chunks, otherwise it's freed.
>
> >
> > Also need to maintain a datamsg list to record which datamsg is
> > processed avoiding repetitive
> > processing.
>
> Right, but for that we can add a simple check on
> sctp_for_each_tx_datamsg() based on a parameter.

Great! I get it, thanks!

>
> > So, list it to outq. Maybe it will be used sometime.
>
> We can change it when the time comes. For now, if we can avoid growing
> sctp_datamsg, it's better. With this patch, it grows from 40 to 56
> bytes, leaving just 8 left before it starts using a slab of 128 bytes
> for it.
>
>
> The patched list_for_each_entry() can/should be factored out into
> __sctp_for_each_tx_datachunk, whose first parameter then is the queue
> instead the asoc.
>
> ---8<---
>
> diff --git a/net/sctp/socket.c b/net/sctp/socket.c
> index fed26a1e9518..62f401799709 100644
> --- a/net/sctp/socket.c
> +++ b/net/sctp/socket.c
> @@ -148,19 +148,30 @@ static void sctp_clear_owner_w(struct sctp_chunk *chunk)
>  }
>
>  static void sctp_for_each_tx_datachunk(struct sctp_association *asoc,
> +                                      bool clear,
>                                        void (*cb)(struct sctp_chunk *))
>
>  {
> +       struct sctp_datamsg *msg, *prev_msg = NULL;
>         struct sctp_outq *q = &asoc->outqueue;
> +       struct sctp_chunk *chunk, *c;
>         struct sctp_transport *t;
> -       struct sctp_chunk *chunk;
>
>         list_for_each_entry(t, &asoc->peer.transport_addr_list, transports)
>                 list_for_each_entry(chunk, &t->transmitted, transmitted_list)
>                         cb(chunk);
>
> -       list_for_each_entry(chunk, &q->retransmit, transmitted_list)
> -               cb(chunk);
> +       list_for_each_entry(chunk, &q->sacked, transmitted_list) {
> +               msg = chunk->msg;
> +               if (msg = prev_msg)
> +                       continue;
> +               list_for_each_entry(c, &msg->chunks, frag_list) {
> +                       if ((clear && asoc->base.sk = c->skb->sk) ||
> +                           (!clear && asoc->base.sk != c->skb->sk))
> +                               cb(c);
> +               }
> +               prev_msg = msg;
> +       }
>
>         list_for_each_entry(chunk, &q->sacked, transmitted_list)
>                 cb(chunk);
> @@ -9574,9 +9585,9 @@ static int sctp_sock_migrate(struct sock *oldsk, struct sock *newsk,
>          * paths won't try to lock it and then oldsk.
>          */
>         lock_sock_nested(newsk, SINGLE_DEPTH_NESTING);
> -       sctp_for_each_tx_datachunk(assoc, sctp_clear_owner_w);
> +       sctp_for_each_tx_datachunk(assoc, true, sctp_clear_owner_w);
>         sctp_assoc_migrate(assoc, newsk);
> -       sctp_for_each_tx_datachunk(assoc, sctp_set_owner_w);
> +       sctp_for_each_tx_datachunk(assoc, false, sctp_set_owner_w);
>
>         /* If the association on the newsk is already closed before accept()
>          * is called, set RCV_SHUTDOWN flag.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2020-03-26  6:13 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-03-22  9:04 [PATCH v4] sctp: fix refcount bug in sctp_wfree Qiujun Huang
2020-03-26  0:14 ` Marcelo Ricardo Leitner
2020-03-26  1:30   ` Qiujun Huang
2020-03-26  3:22     ` Marcelo Ricardo Leitner
2020-03-26  5:37       ` Qiujun Huang
2020-03-26  6:13       ` Qiujun Huang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).