diff options
Diffstat (limited to 'net/unix/af_unix.c')
-rw-r--r-- | net/unix/af_unix.c | 127 |
1 files changed, 84 insertions, 43 deletions
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 85d3bb7490a..641f2e47f16 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -149,9 +149,10 @@ static inline void unix_set_secdata(struct scm_cookie *scm, struct sk_buff *skb) * each socket state is protected by separate spin lock. */ -static inline unsigned unix_hash_fold(__wsum n) +static inline unsigned int unix_hash_fold(__wsum n) { - unsigned hash = (__force unsigned)n; + unsigned int hash = (__force unsigned int)n; + hash ^= hash>>16; hash ^= hash>>8; return hash&(UNIX_HASH_SIZE-1); @@ -200,7 +201,7 @@ static inline void unix_release_addr(struct unix_address *addr) * - if started by zero, it is abstract name. */ -static int unix_mkname(struct sockaddr_un *sunaddr, int len, unsigned *hashp) +static int unix_mkname(struct sockaddr_un *sunaddr, int len, unsigned int *hashp) { if (len <= sizeof(short) || len > sizeof(*sunaddr)) return -EINVAL; @@ -250,7 +251,7 @@ static inline void unix_insert_socket(struct hlist_head *list, struct sock *sk) static struct sock *__unix_find_socket_byname(struct net *net, struct sockaddr_un *sunname, - int len, int type, unsigned hash) + int len, int type, unsigned int hash) { struct sock *s; struct hlist_node *node; @@ -273,7 +274,7 @@ found: static inline struct sock *unix_find_socket_byname(struct net *net, struct sockaddr_un *sunname, int len, int type, - unsigned hash) + unsigned int hash) { struct sock *s; @@ -293,7 +294,7 @@ static struct sock *unix_find_socket_byinode(struct inode *i) spin_lock(&unix_table_lock); sk_for_each(s, node, &unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) { - struct dentry *dentry = unix_sk(s)->dentry; + struct dentry *dentry = unix_sk(s)->path.dentry; if (dentry && dentry->d_inode == i) { sock_hold(s); @@ -377,8 +378,7 @@ static void unix_sock_destructor(struct sock *sk) static int unix_release_sock(struct sock *sk, int embrion) { struct unix_sock *u = unix_sk(sk); - struct dentry *dentry; - struct vfsmount *mnt; + struct path path; struct sock *skpair; struct sk_buff *skb; int state; @@ -389,10 +389,9 @@ static int unix_release_sock(struct sock *sk, int embrion) unix_state_lock(sk); sock_orphan(sk); sk->sk_shutdown = SHUTDOWN_MASK; - dentry = u->dentry; - u->dentry = NULL; - mnt = u->mnt; - u->mnt = NULL; + path = u->path; + u->path.dentry = NULL; + u->path.mnt = NULL; state = sk->sk_state; sk->sk_state = TCP_CLOSE; unix_state_unlock(sk); @@ -425,10 +424,8 @@ static int unix_release_sock(struct sock *sk, int embrion) kfree_skb(skb); } - if (dentry) { - dput(dentry); - mntput(mnt); - } + if (path.dentry) + path_put(&path); sock_put(sk); @@ -530,6 +527,16 @@ static int unix_seqpacket_sendmsg(struct kiocb *, struct socket *, static int unix_seqpacket_recvmsg(struct kiocb *, struct socket *, struct msghdr *, size_t, int); +static void unix_set_peek_off(struct sock *sk, int val) +{ + struct unix_sock *u = unix_sk(sk); + + mutex_lock(&u->readlock); + sk->sk_peek_off = val; + mutex_unlock(&u->readlock); +} + + static const struct proto_ops unix_stream_ops = { .family = PF_UNIX, .owner = THIS_MODULE, @@ -549,6 +556,7 @@ static const struct proto_ops unix_stream_ops = { .recvmsg = unix_stream_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, + .set_peek_off = unix_set_peek_off, }; static const struct proto_ops unix_dgram_ops = { @@ -570,6 +578,7 @@ static const struct proto_ops unix_dgram_ops = { .recvmsg = unix_dgram_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, + .set_peek_off = unix_set_peek_off, }; static const struct proto_ops unix_seqpacket_ops = { @@ -591,6 +600,7 @@ static const struct proto_ops unix_seqpacket_ops = { .recvmsg = unix_seqpacket_recvmsg, .mmap = sock_no_mmap, .sendpage = sock_no_sendpage, + .set_peek_off = unix_set_peek_off, }; static struct proto unix_proto = { @@ -628,8 +638,8 @@ static struct sock *unix_create1(struct net *net, struct socket *sock) sk->sk_max_ack_backlog = net->unx.sysctl_max_dgram_qlen; sk->sk_destruct = unix_sock_destructor; u = unix_sk(sk); - u->dentry = NULL; - u->mnt = NULL; + u->path.dentry = NULL; + u->path.mnt = NULL; spin_lock_init(&u->lock); atomic_long_set(&u->inflight, 0); INIT_LIST_HEAD(&u->link); @@ -751,7 +761,7 @@ out: mutex_unlock(&u->readlock); static struct sock *unix_find_other(struct net *net, struct sockaddr_un *sunname, int len, - int type, unsigned hash, int *error) + int type, unsigned int hash, int *error) { struct sock *u; struct path path; @@ -775,7 +785,7 @@ static struct sock *unix_find_other(struct net *net, goto put_fail; if (u->sk_type == type) - touch_atime(path.mnt, path.dentry); + touch_atime(&path); path_put(&path); @@ -789,9 +799,9 @@ static struct sock *unix_find_other(struct net *net, u = unix_find_socket_byname(net, sunname, len, type, hash); if (u) { struct dentry *dentry; - dentry = unix_sk(u)->dentry; + dentry = unix_sk(u)->path.dentry; if (dentry) - touch_atime(unix_sk(u)->mnt, dentry); + touch_atime(&unix_sk(u)->path); } else goto fail; } @@ -815,7 +825,7 @@ static int unix_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) struct dentry *dentry = NULL; struct path path; int err; - unsigned hash; + unsigned int hash; struct unix_address *addr; struct hlist_head *list; @@ -897,8 +907,7 @@ out_mknod_drop_write: list = &unix_socket_table[addr->hash]; } else { list = &unix_socket_table[dentry->d_inode->i_ino & (UNIX_HASH_SIZE-1)]; - u->dentry = path.dentry; - u->mnt = path.mnt; + u->path = path; } err = 0; @@ -956,7 +965,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr, struct net *net = sock_net(sk); struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr; struct sock *other; - unsigned hash; + unsigned int hash; int err; if (addr->sa_family != AF_UNSPEC) { @@ -1054,7 +1063,7 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr, struct sock *newsk = NULL; struct sock *other = NULL; struct sk_buff *skb = NULL; - unsigned hash; + unsigned int hash; int st; int err; long timeo; @@ -1180,9 +1189,9 @@ restart: atomic_inc(&otheru->addr->refcnt); newu->addr = otheru->addr; } - if (otheru->dentry) { - newu->dentry = dget(otheru->dentry); - newu->mnt = mntget(otheru->mnt); + if (otheru->path.dentry) { + path_get(&otheru->path); + newu->path = otheru->path; } /* Set credentials */ @@ -1429,11 +1438,12 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, struct sock *other = NULL; int namelen = 0; /* fake GCC */ int err; - unsigned hash; + unsigned int hash; struct sk_buff *skb; long timeo; struct scm_cookie tmp_scm; int max_level; + int data_len = 0; if (NULL == siocb->scm) siocb->scm = &tmp_scm; @@ -1467,7 +1477,13 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, if (len > sk->sk_sndbuf - 32) goto out; - skb = sock_alloc_send_skb(sk, len, msg->msg_flags&MSG_DONTWAIT, &err); + if (len > SKB_MAX_ALLOC) + data_len = min_t(size_t, + len - SKB_MAX_ALLOC, + MAX_SKB_FRAGS * PAGE_SIZE); + + skb = sock_alloc_send_pskb(sk, len - data_len, data_len, + msg->msg_flags & MSG_DONTWAIT, &err); if (skb == NULL) goto out; @@ -1477,8 +1493,10 @@ static int unix_dgram_sendmsg(struct kiocb *kiocb, struct socket *sock, max_level = err + 1; unix_get_secdata(siocb->scm, skb); - skb_reset_transport_header(skb); - err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len); + skb_put(skb, len - data_len); + skb->data_len = data_len; + skb->len = len; + err = skb_copy_datagram_from_iovec(skb, 0, msg->msg_iov, 0, len); if (err) goto out_free; @@ -1756,6 +1774,7 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, int noblock = flags & MSG_DONTWAIT; struct sk_buff *skb; int err; + int peeked, skip; err = -EOPNOTSUPP; if (flags&MSG_OOB) @@ -1769,7 +1788,9 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, goto out; } - skb = skb_recv_datagram(sk, flags, noblock, &err); + skip = sk_peek_offset(sk, flags); + + skb = __skb_recv_datagram(sk, flags, &peeked, &skip, &err); if (!skb) { unix_state_lock(sk); /* Signal EOF on disconnected non-blocking SEQPACKET socket. */ @@ -1786,12 +1807,12 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, if (msg->msg_name) unix_copy_addr(msg, skb->sk); - if (size > skb->len) - size = skb->len; - else if (size < skb->len) + if (size > skb->len - skip) + size = skb->len - skip; + else if (size < skb->len - skip) msg->msg_flags |= MSG_TRUNC; - err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, size); + err = skb_copy_datagram_iovec(skb, skip, msg->msg_iov, size); if (err) goto out_free; @@ -1808,6 +1829,8 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, if (!(flags & MSG_PEEK)) { if (UNIXCB(skb).fp) unix_detach_fds(siocb->scm, skb); + + sk_peek_offset_bwd(sk, skb->len); } else { /* It is questionable: on PEEK we could: - do not return fds - good, but too simple 8) @@ -1821,10 +1844,13 @@ static int unix_dgram_recvmsg(struct kiocb *iocb, struct socket *sock, clearly however! */ + + sk_peek_offset_fwd(sk, size); + if (UNIXCB(skb).fp) siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp); } - err = size; + err = (flags & MSG_TRUNC) ? skb->len - skip : size; scm_recv(sock, msg, siocb->scm, flags); @@ -1884,6 +1910,7 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, int target; int err = 0; long timeo; + int skip; err = -EINVAL; if (sk->sk_state != TCP_ESTABLISHED) @@ -1913,12 +1940,15 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, goto out; } + skip = sk_peek_offset(sk, flags); + do { int chunk; struct sk_buff *skb; unix_state_lock(sk); skb = skb_peek(&sk->sk_receive_queue); +again: if (skb == NULL) { unix_sk(sk)->recursion_level = 0; if (copied >= target) @@ -1953,6 +1983,13 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, unix_state_unlock(sk); break; } + + if (skip >= skb->len) { + skip -= skb->len; + skb = skb_peek_next(skb, &sk->sk_receive_queue); + goto again; + } + unix_state_unlock(sk); if (check_creds) { @@ -1972,8 +2009,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, sunaddr = NULL; } - chunk = min_t(unsigned int, skb->len, size); - if (memcpy_toiovec(msg->msg_iov, skb->data, chunk)) { + chunk = min_t(unsigned int, skb->len - skip, size); + if (memcpy_toiovec(msg->msg_iov, skb->data + skip, chunk)) { if (copied == 0) copied = -EFAULT; break; @@ -1985,6 +2022,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, if (!(flags & MSG_PEEK)) { skb_pull(skb, chunk); + sk_peek_offset_bwd(sk, chunk); + if (UNIXCB(skb).fp) unix_detach_fds(siocb->scm, skb); @@ -2002,6 +2041,8 @@ static int unix_stream_recvmsg(struct kiocb *iocb, struct socket *sock, if (UNIXCB(skb).fp) siocb->scm->fp = scm_fp_dup(UNIXCB(skb).fp); + sk_peek_offset_fwd(sk, chunk); + break; } } while (size); @@ -2175,7 +2216,7 @@ static unsigned int unix_dgram_poll(struct file *file, struct socket *sock, } /* No write status requested, avoid expensive OUT tests. */ - if (wait && !(wait->key & (POLLWRBAND | POLLWRNORM | POLLOUT))) + if (!(poll_requested_events(wait) & (POLLWRBAND|POLLWRNORM|POLLOUT))) return mask; writable = unix_writable(sk); |