On 12/06/2017 09:03 PM, Jon Maloy wrote:
> 
> 
>> -----Original Message-----
>> From: Ying Xue [mailto:[email protected]]
>> Sent: Wednesday, December 06, 2017 03:29
>> To: Jon Maloy <[email protected]>; [email protected]
>> Cc: [email protected]
>> Subject: Re: [net-next PATCH 1/2] tipc: prevent swork item from queuing
>> workqueue after connection is closed
>>
>>
>>
>> On 12/06/2017 03:31 AM, Jon Maloy wrote:
>>>> If you in remove the connection item from the idr list *before* you
>>>> flush the queues and remove the socket, there is no risk that this
>>>> will happen.
>>>> If tipc_conn_sendmsg() obtains a pointer, we can be certain that the
>>>> queue has not been flushed yet, and that this will not happen until
>>>> conn_put() is called.
>>>> And, you won't need to use the callback lock or touch the socket at all
>> here.
>>> It's even better: if you introduce the !kref_get_unless_zero(&con->kref))
>> test, there is no way tipc_conn_sendmsg() will obtain a pointer while
>> tipc_conn_kref_release() is being executed, i.e., after the queue is flushed.
>>> I suggest you make both these changes.
>>
>>
>> Please let me use below call chains to show what the race condition is:
>>
>>   CPU 1:                                       CPU 2:
>> ==========                                 ===============
>> T1: tipc_conn_sendmsg
>> T2:  con = tipc_conn_lookup()
>> T3:  test_bit(CF_CONNECTED, &con->flags)
>> T4:   =====> interrupted by another thread;
>> T5:                                        tipc_close_conn()
>> T6:                                        test_and_clear_bit(con->flag)
>> T7:                                        tipc_unregister_callbacks()
>> T8:                                        flush_work(&con->swork)
>> T9:                                        flush_work(&con->rwork)
>> T10: queue_work(s->send_wq, &con->swork)
>>
>>
>> The key point is that the whole process of identifying whether conn is/being
>> closed with test_bit(CF_CONNECTED, &con->flags) or
>> sk->sk_user_data and queuing swork item into workqueue is not atomic.
> 
> That is true. But looking at the kref value is, because if that value is zero 
> nobody else has a pointer to the connection item.

Yes, but it's unrelated to the case.

 And vice versa, if it is non-zero the queues cannot not have been
flushed yet.

In the case, at T4, for example, we has used
kref_get_unless_zero(&con->kref) to confirm that con refcnt is really
not zero, so the checking result shows that we can queue a swork into
workqueue. But at the moment, tipc_conn_sendmsg() is interrupted. At T5
tipc_close_conn() is called on CPU2, and con CF_CONNECTED flag is
cleared, sk_user_data is set to null, and works have been flushed on the
CPU subsequently.

When tipc_conn_sendmsg() comes back at T10, as it is totally unaware
that CF_CONNECTED con->flags has been cleared and works have been
flushed, a swork item will still be queued to a flushed workqueue. As a
result, con may be finally destroyed in the context of calling
ipc_conn_sendmsg() instead of tipc module exit. It means that name table
soft lockup solved by 9dc3abdd1f7e may occurred again.


> I am not even sure that the CF_CONNECTED flag is needed anymore, but I need 
> to think more about that.

CF_CONNECTED flag is still useful for us because we use it to identify
whether connection is broken or not in many places.

> What I am suggesting is that we move most of the functionality that is 
> currently in close_conn() to keref_release(). Something like this:
> 
> static void tipc_close_conn(struct tipc_conn *con, bool flush)
> {
>         if (test_and_clear_bit(CF_CONNECTED, &con->flags))
>                 conn_put(con);
> } 
> 
> static void tipc_conn_kref_release(struct kref *kref)
> {
>         struct tipc_conn *con = container_of(kref, struct tipc_conn, kref);
>         struct tipc_server *s = con->server;
>         struct sockaddr_tipc *saddr = s->saddr;
>         struct socket *sock = con->sock;
>         struct sock *sk;
> 
>         spin_lock_bh(&s->idr_lock);
>         idr_remove(&s->conn_idr, con->conid);
>         s->idr_in_use--;
>         spin_unlock_bh(&s->idr_lock);
> 
>         if (sock) {
>                 sk = sock->sk;
>                 if (test_bit(CF_SERVER, &con->flags)) {
>                         __module_get(sock->ops->owner);
>                         __module_get(sk->sk_prot_creator->owner);
>                 }
>                 flush_work(&con->swork);
>                 flush_work(&con->rwork);

Sorry, we cannot move flush works to tipc_conn_kref_release(),
otherwise, that name table soft lockup solved by 9dc3abdd1f7e ("tipc:
fix nametbl_lock soft lockup at module exit") may occur again because
tipc_conn_kref_release may be called in tipc_nametbl_withdraw() rather
than tipc_topsrv_stop() context.

Moreover, before a work item is queued into workqueue, con refcnt needs
to be first held. So when tipc_conn_kref_release() is called, it means
there is no any user for a con. As a consequence, there is no any work
item which are still queued in workqueues, so we don't need to flush
works at all.

>                 saddr->scope = -TIPC_NODE_SCOPE;
>                 kernel_bind(sock, (struct sockaddr *)saddr, sizeof(*saddr));
>                 if (con->conid)
>                         s->tipc_conn_release(con->conid, con->usr_data);
>                 sock_release(sock);
>                 con->sock = NULL;
>         }
>         tipc_clean_outqueues(con);
>         kfree(con);
> }
> 
> ///jon
> 
>> Even if all checking for connection status are well done and connection is 
>> still
>> connected before swork item is queued, if the thread of running
>> tipc_conn_sendmsg() is interrupted at T4 and it's backed at T10, a swork
>> might be queued into workqueue after its queue has been flushed in
>> tipc_close_conn() on CPU 2.
>>
>> That's why we need use sk_callback_lock read lock to guarantee that the
>> entire process of identifying connection status and queuing swork item
>> cannot be interrupted.
>>
>>>
>>> ///jon
>>>

------------------------------------------------------------------------------
Check out the vibrant tech community on one of the world's most
engaging tech sites, Slashdot.org! http://sdm.link/slashdot
_______________________________________________
tipc-discussion mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/tipc-discussion

Reply via email to