]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - net/tipc/socket.c
net: use skb_queue_empty_lockless() in poll() handlers
[mirror_ubuntu-bionic-kernel.git] / net / tipc / socket.c
index 129d4355cefa80ec2f508c951b726024eb65009a..d6ce720eaa57eafd6f3c678da4d69ff61f49217d 100644 (file)
@@ -377,11 +377,13 @@ static int tipc_sk_sock_err(struct socket *sock, long *timeout)
 
 #define tipc_wait_for_cond(sock_, timeo_, condition_)                         \
 ({                                                                             \
+       DEFINE_WAIT_FUNC(wait_, woken_wake_function);                          \
        struct sock *sk_;                                                      \
        int rc_;                                                               \
                                                                               \
        while ((rc_ = !(condition_))) {                                        \
-               DEFINE_WAIT_FUNC(wait_, woken_wake_function);                  \
+               /* coupled with smp_wmb() in tipc_sk_proto_rcv() */            \
+               smp_rmb();                                                     \
                sk_ = (sock_)->sk;                                             \
                rc_ = tipc_sk_sock_err((sock_), timeo_);                       \
                if (rc_)                                                       \
@@ -728,12 +730,12 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock,
 
        switch (sk->sk_state) {
        case TIPC_ESTABLISHED:
-       case TIPC_CONNECTING:
                if (!tsk->cong_link_cnt && !tsk_conn_cong(tsk))
                        revents |= POLLOUT;
                /* fall thru' */
        case TIPC_LISTEN:
-               if (!skb_queue_empty(&sk->sk_receive_queue))
+       case TIPC_CONNECTING:
+               if (!skb_queue_empty_lockless(&sk->sk_receive_queue))
                        revents |= POLLIN | POLLRDNORM;
                break;
        case TIPC_OPEN:
@@ -742,7 +744,7 @@ static unsigned int tipc_poll(struct file *file, struct socket *sock,
                                revents |= POLLOUT;
                if (!tipc_sk_type_connectionless(sk))
                        break;
-               if (skb_queue_empty(&sk->sk_receive_queue))
+               if (skb_queue_empty_lockless(&sk->sk_receive_queue))
                        break;
                revents |= POLLIN | POLLRDNORM;
                break;
@@ -884,7 +886,6 @@ static int tipc_send_group_unicast(struct socket *sock, struct msghdr *m,
        DECLARE_SOCKADDR(struct sockaddr_tipc *, dest, m->msg_name);
        int blks = tsk_blocks(GROUP_H_SIZE + dlen);
        struct tipc_sock *tsk = tipc_sk(sk);
-       struct tipc_group *grp = tsk->group;
        struct net *net = sock_net(sk);
        struct tipc_member *mb = NULL;
        u32 node, port;
@@ -898,7 +899,9 @@ static int tipc_send_group_unicast(struct socket *sock, struct msghdr *m,
        /* Block or return if destination link or member is congested */
        rc = tipc_wait_for_cond(sock, &timeout,
                                !tipc_dest_find(&tsk->cong_links, node, 0) &&
-                               !tipc_group_cong(grp, node, port, blks, &mb));
+                               tsk->group &&
+                               !tipc_group_cong(tsk->group, node, port, blks,
+                                                &mb));
        if (unlikely(rc))
                return rc;
 
@@ -928,7 +931,6 @@ static int tipc_send_group_anycast(struct socket *sock, struct msghdr *m,
        struct tipc_sock *tsk = tipc_sk(sk);
        struct list_head *cong_links = &tsk->cong_links;
        int blks = tsk_blocks(GROUP_H_SIZE + dlen);
-       struct tipc_group *grp = tsk->group;
        struct tipc_member *first = NULL;
        struct tipc_member *mbr = NULL;
        struct net *net = sock_net(sk);
@@ -944,9 +946,10 @@ static int tipc_send_group_anycast(struct socket *sock, struct msghdr *m,
        type = dest->addr.name.name.type;
        inst = dest->addr.name.name.instance;
        domain = addr_domain(net, dest->scope);
-       exclude = tipc_group_exclude(grp);
 
        while (++lookups < 4) {
+               exclude = tipc_group_exclude(tsk->group);
+
                first = NULL;
 
                /* Look for a non-congested destination member, if any */
@@ -955,7 +958,8 @@ static int tipc_send_group_anycast(struct socket *sock, struct msghdr *m,
                                                 &dstcnt, exclude, false))
                                return -EHOSTUNREACH;
                        tipc_dest_pop(&dsts, &node, &port);
-                       cong = tipc_group_cong(grp, node, port, blks, &mbr);
+                       cong = tipc_group_cong(tsk->group, node, port, blks,
+                                              &mbr);
                        if (!cong)
                                break;
                        if (mbr == first)
@@ -974,7 +978,8 @@ static int tipc_send_group_anycast(struct socket *sock, struct msghdr *m,
                /* Block or return if destination link or member is congested */
                rc = tipc_wait_for_cond(sock, &timeout,
                                        !tipc_dest_find(cong_links, node, 0) &&
-                                       !tipc_group_cong(grp, node, port,
+                                       tsk->group &&
+                                       !tipc_group_cong(tsk->group, node, port,
                                                         blks, &mbr));
                if (unlikely(rc))
                        return rc;
@@ -1009,8 +1014,7 @@ static int tipc_send_group_bcast(struct socket *sock, struct msghdr *m,
        struct sock *sk = sock->sk;
        struct net *net = sock_net(sk);
        struct tipc_sock *tsk = tipc_sk(sk);
-       struct tipc_group *grp = tsk->group;
-       struct tipc_nlist *dsts = tipc_group_dests(grp);
+       struct tipc_nlist *dsts;
        struct tipc_mc_method *method = &tsk->mc_method;
        bool ack = method->mandatory && method->rcast;
        int blks = tsk_blocks(MCAST_H_SIZE + dlen);
@@ -1019,15 +1023,17 @@ static int tipc_send_group_bcast(struct socket *sock, struct msghdr *m,
        struct sk_buff_head pkts;
        int rc = -EHOSTUNREACH;
 
-       if (!dsts->local && !dsts->remote)
-               return -EHOSTUNREACH;
-
        /* Block or return if any destination link or member is congested */
-       rc = tipc_wait_for_cond(sock, &timeout, !tsk->cong_link_cnt &&
-                               !tipc_group_bc_cong(grp, blks));
+       rc = tipc_wait_for_cond(sock, &timeout,
+                               !tsk->cong_link_cnt && tsk->group &&
+                               !tipc_group_bc_cong(tsk->group, blks));
        if (unlikely(rc))
                return rc;
 
+       dsts = tipc_group_dests(tsk->group);
+       if (!dsts->local && !dsts->remote)
+               return -EHOSTUNREACH;
+
        /* Complete message header */
        if (dest) {
                msg_set_type(hdr, TIPC_GRP_MCAST_MSG);
@@ -1039,7 +1045,7 @@ static int tipc_send_group_bcast(struct socket *sock, struct msghdr *m,
        msg_set_hdr_sz(hdr, GROUP_H_SIZE);
        msg_set_destport(hdr, 0);
        msg_set_destnode(hdr, 0);
-       msg_set_grp_bc_seqno(hdr, tipc_group_bc_snd_nxt(grp));
+       msg_set_grp_bc_seqno(hdr, tipc_group_bc_snd_nxt(tsk->group));
 
        /* Avoid getting stuck with repeated forced replicasts */
        msg_set_grp_bc_ack_req(hdr, ack);
@@ -1294,7 +1300,7 @@ static int __tipc_sendmsg(struct socket *sock, struct msghdr *m, size_t dlen)
 
        if (unlikely(!dest)) {
                dest = &tsk->peer;
-               if (!syn || dest->family != AF_TIPC)
+               if (!syn && dest->family != AF_TIPC)
                        return -EDESTADDRREQ;
        }
 
@@ -1527,16 +1533,17 @@ static void tipc_sk_set_orig_addr(struct msghdr *m, struct sk_buff *skb)
 /**
  * tipc_sk_anc_data_recv - optionally capture ancillary data for received message
  * @m: descriptor for message info
- * @msg: received message header
+ * @skb: received message buffer
  * @tsk: TIPC port associated with message
  *
  * Note: Ancillary data is not captured if not requested by receiver.
  *
  * Returns 0 if successful, otherwise errno
  */
-static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
+static int tipc_sk_anc_data_recv(struct msghdr *m, struct sk_buff *skb,
                                 struct tipc_sock *tsk)
 {
+       struct tipc_msg *msg;
        u32 anc_data[3];
        u32 err;
        u32 dest_type;
@@ -1545,6 +1552,7 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
 
        if (likely(m->msg_controllen == 0))
                return 0;
+       msg = buf_msg(skb);
 
        /* Optionally capture errored message object(s) */
        err = msg ? msg_errcode(msg) : 0;
@@ -1555,6 +1563,9 @@ static int tipc_sk_anc_data_recv(struct msghdr *m, struct tipc_msg *msg,
                if (res)
                        return res;
                if (anc_data[1]) {
+                       if (skb_linearize(skb))
+                               return -ENOMEM;
+                       msg = buf_msg(skb);
                        res = put_cmsg(m, SOL_TIPC, TIPC_RETDATA, anc_data[1],
                                       msg_data(msg));
                        if (res)
@@ -1716,9 +1727,10 @@ static int tipc_recvmsg(struct socket *sock, struct msghdr *m,
 
        /* Collect msg meta data, including error code and rejected data */
        tipc_sk_set_orig_addr(m, skb);
-       rc = tipc_sk_anc_data_recv(m, hdr, tsk);
+       rc = tipc_sk_anc_data_recv(m, skb, tsk);
        if (unlikely(rc))
                goto exit;
+       hdr = buf_msg(skb);
 
        /* Capture data if non-error msg, otherwise just set return value */
        if (likely(!err)) {
@@ -1828,9 +1840,10 @@ static int tipc_recvstream(struct socket *sock, struct msghdr *m,
                /* Collect msg meta data, incl. error code and rejected data */
                if (!copied) {
                        tipc_sk_set_orig_addr(m, skb);
-                       rc = tipc_sk_anc_data_recv(m, hdr, tsk);
+                       rc = tipc_sk_anc_data_recv(m, skb, tsk);
                        if (rc)
                                break;
+                       hdr = buf_msg(skb);
                }
 
                /* Copy data if msg ok, otherwise return error/partial data */
@@ -1929,6 +1942,8 @@ static void tipc_sk_proto_rcv(struct sock *sk,
                return;
        case SOCK_WAKEUP:
                tipc_dest_del(&tsk->cong_links, msg_orignode(hdr), 0);
+               /* coupled with smp_rmb() in tipc_wait_for_cond() */
+               smp_wmb();
                tsk->cong_link_cnt--;
                wakeup = true;
                break;
@@ -2004,7 +2019,7 @@ static bool tipc_sk_filter_connect(struct tipc_sock *tsk, struct sk_buff *skb)
                        return true;
 
                /* If empty 'ACK-' message, wake up sleeping connect() */
-               sk->sk_data_ready(sk);
+               sk->sk_state_change(sk);
 
                /* 'ACK-' message is neither accepted nor rejected: */
                msg_set_dest_droppable(hdr, 1);
@@ -2649,11 +2664,15 @@ void tipc_sk_reinit(struct net *net)
                        goto walk_stop;
 
                while ((tsk = rhashtable_walk_next(&iter)) && !IS_ERR(tsk)) {
-                       spin_lock_bh(&tsk->sk.sk_lock.slock);
+                       sock_hold(&tsk->sk);
+                       rhashtable_walk_stop(&iter);
+                       lock_sock(&tsk->sk);
                        msg = &tsk->phdr;
                        msg_set_prevnode(msg, tn->own_addr);
                        msg_set_orignode(msg, tn->own_addr);
-                       spin_unlock_bh(&tsk->sk.sk_lock.slock);
+                       release_sock(&tsk->sk);
+                       rhashtable_walk_start(&iter);
+                       sock_put(&tsk->sk);
                }
 walk_stop:
                rhashtable_walk_stop(&iter);