]> git.ipfire.org Git - thirdparty/kernel/linux.git/blobdiff - net/unix/af_unix.c
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
[thirdparty/kernel/linux.git] / net / unix / af_unix.c
index e94839d89b09d8510184edb8c6b55968afa3f35d..fa906ec5e657b6648ec6add6fadf6adc25ed28e6 100644 (file)
@@ -546,7 +546,7 @@ static void unix_write_space(struct sock *sk)
                if (skwq_has_sleeper(wq))
                        wake_up_interruptible_sync_poll(&wq->wait,
                                EPOLLOUT | EPOLLWRNORM | EPOLLWRBAND);
-               sk_wake_async(sk, SOCK_WAKE_SPACE, POLL_OUT);
+               sk_wake_async_rcu(sk, SOCK_WAKE_SPACE, POLL_OUT);
        }
        rcu_read_unlock();
 }
@@ -979,11 +979,11 @@ static struct sock *unix_create1(struct net *net, struct socket *sock, int kern,
        sk->sk_max_ack_backlog  = net->unx.sysctl_max_dgram_qlen;
        sk->sk_destruct         = unix_sock_destructor;
        u = unix_sk(sk);
-       u->inflight = 0;
+       u->listener = NULL;
+       u->vertex = NULL;
        u->path.dentry = NULL;
        u->path.mnt = NULL;
        spin_lock_init(&u->lock);
-       INIT_LIST_HEAD(&u->link);
        mutex_init(&u->iolock); /* single task reading lock */
        mutex_init(&u->bindlock); /* single task binding lock */
        init_waitqueue_head(&u->peer_wait);
@@ -1597,6 +1597,7 @@ restart:
        newsk->sk_type          = sk->sk_type;
        init_peercred(newsk);
        newu = unix_sk(newsk);
+       newu->listener = other;
        RCU_INIT_POINTER(newsk->sk_wq, &newu->peer_wq);
        otheru = unix_sk(other);
 
@@ -1692,8 +1693,8 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags,
                       bool kern)
 {
        struct sock *sk = sock->sk;
-       struct sock *tsk;
        struct sk_buff *skb;
+       struct sock *tsk;
        int err;
 
        err = -EOPNOTSUPP;
@@ -1723,6 +1724,7 @@ static int unix_accept(struct socket *sock, struct socket *newsock, int flags,
 
        /* attach accepted sock to socket */
        unix_state_lock(tsk);
+       unix_update_edges(unix_sk(tsk));
        newsock->state = SS_CONNECTED;
        unix_sock_inherit_flags(sock, newsock);
        sock_graft(tsk, newsock);
@@ -1789,81 +1791,29 @@ static inline bool too_many_unix_fds(struct task_struct *p)
 
 static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb)
 {
-       int i;
-
        if (too_many_unix_fds(current))
                return -ETOOMANYREFS;
 
-       /* Need to duplicate file references for the sake of garbage
-        * collection.  Otherwise a socket in the fps might become a
-        * candidate for GC while the skb is not yet queued.
-        */
-       UNIXCB(skb).fp = scm_fp_dup(scm->fp);
-       if (!UNIXCB(skb).fp)
-               return -ENOMEM;
+       UNIXCB(skb).fp = scm->fp;
+       scm->fp = NULL;
 
-       for (i = scm->fp->count - 1; i >= 0; i--)
-               unix_inflight(scm->fp->user, scm->fp->fp[i]);
+       if (unix_prepare_fpl(UNIXCB(skb).fp))
+               return -ENOMEM;
 
        return 0;
 }
 
 static void unix_detach_fds(struct scm_cookie *scm, struct sk_buff *skb)
 {
-       int i;
-
        scm->fp = UNIXCB(skb).fp;
        UNIXCB(skb).fp = NULL;
 
-       for (i = scm->fp->count - 1; i >= 0; i--)
-               unix_notinflight(scm->fp->user, scm->fp->fp[i]);
+       unix_destroy_fpl(scm->fp);
 }
 
 static void unix_peek_fds(struct scm_cookie *scm, struct sk_buff *skb)
 {
        scm->fp = scm_fp_dup(UNIXCB(skb).fp);
-
-       /*
-        * Garbage collection of unix sockets starts by selecting a set of
-        * candidate sockets which have reference only from being in flight
-        * (total_refs == inflight_refs).  This condition is checked once during
-        * the candidate collection phase, and candidates are marked as such, so
-        * that non-candidates can later be ignored.  While inflight_refs is
-        * protected by unix_gc_lock, total_refs (file count) is not, hence this
-        * is an instantaneous decision.
-        *
-        * Once a candidate, however, the socket must not be reinstalled into a
-        * file descriptor while the garbage collection is in progress.
-        *
-        * If the above conditions are met, then the directed graph of
-        * candidates (*) does not change while unix_gc_lock is held.
-        *
-        * Any operations that changes the file count through file descriptors
-        * (dup, close, sendmsg) does not change the graph since candidates are
-        * not installed in fds.
-        *
-        * Dequeing a candidate via recvmsg would install it into an fd, but
-        * that takes unix_gc_lock to decrement the inflight count, so it's
-        * serialized with garbage collection.
-        *
-        * MSG_PEEK is special in that it does not change the inflight count,
-        * yet does install the socket into an fd.  The following lock/unlock
-        * pair is to ensure serialization with garbage collection.  It must be
-        * done between incrementing the file count and installing the file into
-        * an fd.
-        *
-        * If garbage collection starts after the barrier provided by the
-        * lock/unlock, then it will see the elevated refcount and not mark this
-        * as a candidate.  If a garbage collection is already in progress
-        * before the file count was incremented, then the lock/unlock pair will
-        * ensure that garbage collection is finished before progressing to
-        * installing the fd.
-        *
-        * (*) A -> B where B is on the queue of A or B is on the queue of C
-        * which is on the queue of listening socket A.
-        */
-       spin_lock(&unix_gc_lock);
-       spin_unlock(&unix_gc_lock);
 }
 
 static void unix_destruct_scm(struct sk_buff *skb)
@@ -1937,8 +1887,10 @@ static void scm_stat_add(struct sock *sk, struct sk_buff *skb)
        struct scm_fp_list *fp = UNIXCB(skb).fp;
        struct unix_sock *u = unix_sk(sk);
 
-       if (unlikely(fp && fp->count))
+       if (unlikely(fp && fp->count)) {
                atomic_add(fp->count, &u->scm_stat.nr_fds);
+               unix_add_edges(fp, u);
+       }
 }
 
 static void scm_stat_del(struct sock *sk, struct sk_buff *skb)
@@ -1946,8 +1898,10 @@ static void scm_stat_del(struct sock *sk, struct sk_buff *skb)
        struct scm_fp_list *fp = UNIXCB(skb).fp;
        struct unix_sock *u = unix_sk(sk);
 
-       if (unlikely(fp && fp->count))
+       if (unlikely(fp && fp->count)) {
                atomic_sub(fp->count, &u->scm_stat.nr_fds);
+               unix_del_edges(fp);
+       }
 }
 
 /*