summaryrefslogtreecommitdiffstats
path: root/net/unix/af_unix.c
diff options
context:
space:
mode:
Diffstat (limited to 'net/unix/af_unix.c')
-rw-r--r--net/unix/af_unix.c127
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);