* [PATCH 1/5] AF_UNIX: add getsockopt and setsockopt on stream sockets
[not found] <20100924182257.11abd9a6@chocolatine.cbg.collabora.co.uk>
@ 2010-09-24 17:25 ` Alban Crequy
2010-09-24 17:25 ` [PATCH 2/5] AF_UNIX: enable/disable multicast with getsockopt/setsockopt Alban Crequy
` (3 subsequent siblings)
4 siblings, 0 replies; 9+ messages in thread
From: Alban Crequy @ 2010-09-24 17:25 UTC (permalink / raw)
To: Alban Crequy
Cc: David S. Miller, Eric Dumazet, Stephen Hemminger, Cyrill Gorcunov,
Alexey Dobriyan, Lennart Poettering, Kay Sievers, Ian Molton,
netdev, linux-kernel, dbus, Alban Crequy
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
net/unix/af_unix.c | 21 +++++++++++++++++++--
1 files changed, 19 insertions(+), 2 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index fef2cc5..47d9f77 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -492,6 +492,10 @@ static unsigned int unix_dgram_poll(struct file *, struct socket *,
poll_table *);
static int unix_ioctl(struct socket *, unsigned int, unsigned long);
static int unix_shutdown(struct socket *, int);
+static int unix_stream_setsockopt(struct socket *, int, int,
+ char __user *, unsigned int);
+static int unix_stream_getsockopt(struct socket *, int, int,
+ char __user *, int __user *);
static int unix_stream_sendmsg(struct kiocb *, struct socket *,
struct msghdr *, size_t);
static int unix_stream_recvmsg(struct kiocb *, struct socket *,
@@ -518,8 +522,8 @@ static const struct proto_ops unix_stream_ops = {
.ioctl = unix_ioctl,
.listen = unix_listen,
.shutdown = unix_shutdown,
- .setsockopt = sock_no_setsockopt,
- .getsockopt = sock_no_getsockopt,
+ .setsockopt = unix_stream_setsockopt,
+ .getsockopt = unix_stream_getsockopt,
.sendmsg = unix_stream_sendmsg,
.recvmsg = unix_stream_recvmsg,
.mmap = sock_no_mmap,
@@ -1494,6 +1498,19 @@ out:
}
+static int unix_stream_setsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, unsigned int optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+int unix_stream_getsockopt(struct socket *sock, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ return -EOPNOTSUPP;
+}
+
+
static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 2/5] AF_UNIX: enable/disable multicast with getsockopt/setsockopt
[not found] <20100924182257.11abd9a6@chocolatine.cbg.collabora.co.uk>
2010-09-24 17:25 ` [PATCH 1/5] AF_UNIX: add getsockopt and setsockopt on stream sockets Alban Crequy
@ 2010-09-24 17:25 ` Alban Crequy
2010-09-24 17:46 ` Eric Dumazet
2010-09-24 17:25 ` [PATCH 3/5] AF_UNIX: implement connect() on multicast Unix stream socket Alban Crequy
` (2 subsequent siblings)
4 siblings, 1 reply; 9+ messages in thread
From: Alban Crequy @ 2010-09-24 17:25 UTC (permalink / raw)
To: Alban Crequy
Cc: David S. Miller, Eric Dumazet, Stephen Hemminger, Cyrill Gorcunov,
Alexey Dobriyan, Lennart Poettering, Kay Sievers, Ian Molton,
netdev, linux-kernel, dbus, Alban Crequy
Multicast can be enabled or disabled after a socket is allocated but this
cannot be changed once the socket is bound or connected.
Userspace applications can enable multicast on an Unix stream socket:
sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
#define UNIX_MULTICAST 1
val = 1;
len = sizeof(val);
ret = setsockopt(sockfd, 0, UNIX_MULTICAST, &val, len);
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
include/net/af_unix.h | 4 +++
net/unix/af_unix.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 59 insertions(+), 2 deletions(-)
diff --git a/include/net/af_unix.h b/include/net/af_unix.h
index 20725e2..4c77c69 100644
--- a/include/net/af_unix.h
+++ b/include/net/af_unix.h
@@ -40,6 +40,9 @@ struct unix_skb_parms {
spin_lock_nested(&unix_sk(s)->lock, \
SINGLE_DEPTH_NESTING)
+/* UNIX socket options */
+#define UNIX_MULTICAST 1 /* Enable multicast Unix sockets */
+
#ifdef __KERNEL__
/* The AF_UNIX socket */
struct unix_sock {
@@ -56,6 +59,7 @@ struct unix_sock {
spinlock_t lock;
unsigned int gc_candidate : 1;
unsigned int gc_maybe_cycle : 1;
+ unsigned int multicast : 1;
struct socket_wq peer_wq;
};
#define unix_sk(__sk) ((struct unix_sock *)__sk)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index 47d9f77..c766e88 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1501,13 +1501,66 @@ out:
static int unix_stream_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
- return -EOPNOTSUPP;
+ struct unix_sock *u = unix_sk(sock->sk);
+ int val;
+ int err = 0;
+
+ if (optlen < sizeof(int))
+ return -EINVAL;
+
+ if (get_user(val, (int __user *)optval))
+ return -EFAULT;
+
+ switch (optname) {
+ case UNIX_MULTICAST:
+ /* Multicast feature can only be changed when the socket is
+ * not used yet */
+ if (u->addr || sock->sk->sk_state != TCP_CLOSE)
+ return -EINVAL;
+
+ if (val != 0) {
+ u->multicast = 1;
+ } else {
+ u->multicast = 0;
+ }
+ break;
+
+ default:
+ err = -ENOPROTOOPT;
+ break;
+ }
+
+ return err;
}
int unix_stream_getsockopt(struct socket *sock, int level, int optname,
char __user *optval, int __user *optlen)
{
- return -EOPNOTSUPP;
+ struct unix_sock *u = unix_sk(sock->sk);
+ int val, len;
+
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ len = min_t(unsigned int, len, sizeof(int));
+
+ if (len < 0)
+ return -EINVAL;
+
+ switch (optname) {
+ case UNIX_MULTICAST:
+ val = u->multicast;
+ break;
+
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (copy_to_user(optval, &val, len))
+ return -EFAULT;
+ return 0;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 3/5] AF_UNIX: implement connect() on multicast Unix stream socket
[not found] <20100924182257.11abd9a6@chocolatine.cbg.collabora.co.uk>
2010-09-24 17:25 ` [PATCH 1/5] AF_UNIX: add getsockopt and setsockopt on stream sockets Alban Crequy
2010-09-24 17:25 ` [PATCH 2/5] AF_UNIX: enable/disable multicast with getsockopt/setsockopt Alban Crequy
@ 2010-09-24 17:25 ` Alban Crequy
2010-09-24 17:25 ` [PATCH 4/5] AF_UNIX: find peers on multicast Unix stream sockets Alban Crequy
2010-09-24 17:25 ` [PATCH 5/5] AF_UNIX: deliver the data to all the multicast peers Alban Crequy
4 siblings, 0 replies; 9+ messages in thread
From: Alban Crequy @ 2010-09-24 17:25 UTC (permalink / raw)
To: Alban Crequy
Cc: David S. Miller, Eric Dumazet, Stephen Hemminger, Cyrill Gorcunov,
Alexey Dobriyan, Lennart Poettering, Kay Sievers, Ian Molton,
netdev, linux-kernel, dbus, Alban Crequy
All multicast peers use connect() without any bound socket.
Multicast Unix socket addresses and non-multicast Unix socket addresses live in
the same namespace. An userspace application cannot connect() a non-multicast
socket if the address is already used for multicast sockets.
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
net/unix/af_unix.c | 22 ++++++++++++++++++++--
1 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index c766e88..a8d9de7 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1064,9 +1064,20 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
restart:
/* Find listening sock. */
other = unix_find_other(net, sunaddr, addr_len, sk->sk_type, hash, &err);
- if (!other)
+ if (!other && !u->multicast)
goto out;
+ if (other && u->multicast) {
+ err = -ECONNREFUSED;
+ goto out;
+ }
+
+ if (u->multicast) {
+ sock->state = SS_CONNECTED;
+ sk->sk_state = TCP_ESTABLISHED;
+ return 0;
+ }
+
/* Latch state of peer */
unix_state_lock(other);
@@ -1567,6 +1578,7 @@ int unix_stream_getsockopt(struct socket *sock, int level, int optname,
static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
struct msghdr *msg, size_t len)
{
+ struct unix_sock *u = unix_sk(sock->sk);
struct sock_iocb *siocb = kiocb_to_siocb(kiocb);
struct sock *sk = sock->sk;
struct sock *other = NULL;
@@ -1591,12 +1603,18 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
if (msg->msg_namelen) {
err = sk->sk_state == TCP_ESTABLISHED ? -EISCONN : -EOPNOTSUPP;
goto out_err;
- } else {
+ } else if (!u->multicast) {
sunaddr = NULL;
err = -ENOTCONN;
other = unix_peer(sk);
if (!other)
goto out_err;
+ } else {
+ sunaddr = NULL;
+ err = -ENOTCONN;
+ other = NULL; /* FIXME: get the list of other connection */
+ if (!other)
+ goto out_err;
}
if (sk->sk_shutdown & SEND_SHUTDOWN)
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 4/5] AF_UNIX: find peers on multicast Unix stream sockets
[not found] <20100924182257.11abd9a6@chocolatine.cbg.collabora.co.uk>
` (2 preceding siblings ...)
2010-09-24 17:25 ` [PATCH 3/5] AF_UNIX: implement connect() on multicast Unix stream socket Alban Crequy
@ 2010-09-24 17:25 ` Alban Crequy
2010-09-24 18:00 ` Eric Dumazet
2010-09-24 17:25 ` [PATCH 5/5] AF_UNIX: deliver the data to all the multicast peers Alban Crequy
4 siblings, 1 reply; 9+ messages in thread
From: Alban Crequy @ 2010-09-24 17:25 UTC (permalink / raw)
To: Alban Crequy
Cc: David S. Miller, Eric Dumazet, Stephen Hemminger, Cyrill Gorcunov,
Alexey Dobriyan, Lennart Poettering, Kay Sievers, Ian Molton,
netdev, linux-kernel, dbus, Alban Crequy
Multicast sockets are stored in the hash table unix_multicast_socket_table.
unix_find_socket_byname() is extended to return an array of sockets matching
the name instead of only one socket. Then unix_stream_sendmsg() can find all
the multicast peers.
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
net/unix/af_unix.c | 134 ++++++++++++++++++++++++++++++++++++++--------------
1 files changed, 99 insertions(+), 35 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index a8d9de7..f259849 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -115,11 +115,13 @@
#include <net/checksum.h>
#include <linux/security.h>
-static struct hlist_head unix_socket_table[UNIX_HASH_SIZE + 1];
+static struct hlist_head unix_socket_table[2 * UNIX_HASH_SIZE + 1];
static DEFINE_SPINLOCK(unix_table_lock);
static atomic_t unix_nr_socks = ATOMIC_INIT(0);
+static atomic_t unix_nr_multicast_socks = ATOMIC_INIT(0);
-#define unix_sockets_unbound (&unix_socket_table[UNIX_HASH_SIZE])
+#define unix_multicast_socket_table (&unix_socket_table[UNIX_HASH_SIZE])
+#define unix_sockets_unbound (&unix_socket_table[2 * UNIX_HASH_SIZE])
#define UNIX_ABSTRACT(sk) (unix_sk(sk)->addr->hash != UNIX_HASH_SIZE)
@@ -227,7 +229,6 @@ static void __unix_remove_socket(struct sock *sk)
static void __unix_insert_socket(struct hlist_head *list, struct sock *sk)
{
- WARN_ON(!sk_unhashed(sk));
sk_add_node(sk, list);
}
@@ -247,12 +248,14 @@ 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 hash, int multicast)
{
struct sock *s;
struct hlist_node *node;
+ unsigned int index = (multicast ? UNIX_HASH_SIZE : 0) + (hash ^ type);
- sk_for_each(s, node, &unix_socket_table[hash ^ type]) {
+ sk_for_each(s, node, &unix_socket_table[index]) {
struct unix_sock *u = unix_sk(s);
if (!net_eq(sock_net(s), net))
@@ -267,29 +270,50 @@ found:
return s;
}
-static inline struct sock *unix_find_socket_byname(struct net *net,
- struct sockaddr_un *sunname,
- int len, int type,
- unsigned hash)
+static inline void unix_find_socket_byname(struct net *net,
+ struct sockaddr_un *sunname,
+ int len, int type,
+ unsigned hash,
+ int multicast,
+ struct sock **others, int max_others)
{
struct sock *s;
+ struct hlist_node *node;
+ int i = 0;
+ unsigned int index = (multicast ? UNIX_HASH_SIZE : 0) + (hash ^ type);
spin_lock(&unix_table_lock);
- s = __unix_find_socket_byname(net, sunname, len, type, hash);
- if (s)
- sock_hold(s);
+
+ sk_for_each(s, node, &unix_socket_table[index]) {
+ struct unix_sock *u = unix_sk(s);
+
+ if (!net_eq(sock_net(s), net))
+ continue;
+
+ if (u->addr->len == len &&
+ !memcmp(u->addr->name, sunname, len)) {
+
+ others[i++] = s;
+ sock_hold(s);
+ if (i == max_others)
+ break;
+ }
+ }
+
spin_unlock(&unix_table_lock);
- return s;
}
-static struct sock *unix_find_socket_byinode(struct net *net, struct inode *i)
+static struct sock *unix_find_socket_byinode(struct net *net, struct inode *i,
+ int multicast)
{
struct sock *s;
struct hlist_node *node;
+ unsigned int index = (multicast ? UNIX_HASH_SIZE : 0)
+ + (i->i_ino & (UNIX_HASH_SIZE - 1));
spin_lock(&unix_table_lock);
sk_for_each(s, node,
- &unix_socket_table[i->i_ino & (UNIX_HASH_SIZE - 1)]) {
+ &unix_socket_table[index]) {
struct dentry *dentry = unix_sk(s)->dentry;
if (!net_eq(sock_net(s), net))
@@ -363,6 +387,9 @@ static void unix_sock_destructor(struct sock *sk)
if (u->addr)
unix_release_addr(u->addr);
+ if (u->multicast)
+ atomic_dec(&unix_nr_multicast_socks);
+
atomic_dec(&unix_nr_socks);
local_bh_disable();
sock_prot_inuse_add(sock_net(sk), sk->sk_prot, -1);
@@ -700,7 +727,7 @@ retry:
ordernum = (ordernum+1)&0xFFFFF;
if (__unix_find_socket_byname(net, addr->name, addr->len, sock->type,
- addr->hash)) {
+ addr->hash, 0)) {
spin_unlock(&unix_table_lock);
/* Sanity yield. It is unusual case, but yet... */
if (!(ordernum&0xFF))
@@ -719,9 +746,11 @@ out: mutex_unlock(&u->readlock);
return err;
}
-static struct sock *unix_find_other(struct net *net,
- struct sockaddr_un *sunname, int len,
- int type, unsigned hash, int *error)
+static void unix_find_other(struct net *net,
+ struct sockaddr_un *sunname, int len,
+ int type, unsigned hash, int multicast,
+ struct sock **others, int max_others,
+ int *error)
{
struct sock *u;
struct path path;
@@ -740,7 +769,7 @@ static struct sock *unix_find_other(struct net *net,
err = -ECONNREFUSED;
if (!S_ISSOCK(inode->i_mode))
goto put_fail;
- u = unix_find_socket_byinode(net, inode);
+ u = unix_find_socket_byinode(net, inode, multicast);
if (!u)
goto put_fail;
@@ -754,24 +783,26 @@ static struct sock *unix_find_other(struct net *net,
sock_put(u);
goto fail;
}
+ others[0] = u;
} else {
+ int i;
err = -ECONNREFUSED;
- u = unix_find_socket_byname(net, sunname, len, type, hash);
- if (u) {
+ unix_find_socket_byname(net, sunname, len, type, hash,
+ multicast, others, max_others);
+ for(i = 0 ; i < max_others && others[i] != NULL ; i++) {
struct dentry *dentry;
- dentry = unix_sk(u)->dentry;
+ dentry = unix_sk(others[i])->dentry;
if (dentry)
- touch_atime(unix_sk(u)->mnt, dentry);
- } else
- goto fail;
+ touch_atime(unix_sk(others[i])->mnt, dentry);
+ }
}
- return u;
+ return;
put_fail:
path_put(&path);
fail:
*error = err;
- return NULL;
+ return;
}
@@ -862,7 +893,7 @@ out_mknod_drop_write:
if (!sunaddr->sun_path[0]) {
err = -EADDRINUSE;
if (__unix_find_socket_byname(net, sunaddr, addr_len,
- sk->sk_type, hash)) {
+ sk->sk_type, hash, 0)) {
unix_release_addr(addr);
goto out_unlock;
}
@@ -929,7 +960,7 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
struct sock *sk = sock->sk;
struct net *net = sock_net(sk);
struct sockaddr_un *sunaddr = (struct sockaddr_un *)addr;
- struct sock *other;
+ struct sock *other = NULL;
unsigned hash;
int err;
@@ -944,7 +975,8 @@ static int unix_dgram_connect(struct socket *sock, struct sockaddr *addr,
goto out;
restart:
- other = unix_find_other(net, sunaddr, alen, sock->type, hash, &err);
+ unix_find_other(net, sunaddr, alen, sock->type, hash,
+ 0, &other, 1, &err);
if (!other)
goto out;
@@ -1063,7 +1095,8 @@ static int unix_stream_connect(struct socket *sock, struct sockaddr *uaddr,
restart:
/* Find listening sock. */
- other = unix_find_other(net, sunaddr, addr_len, sk->sk_type, hash, &err);
+ unix_find_other(net, sunaddr, addr_len, sk->sk_type, hash, 0, &other,
+ 1, &err);
if (!other && !u->multicast)
goto out;
@@ -1075,6 +1108,26 @@ restart:
if (u->multicast) {
sock->state = SS_CONNECTED;
sk->sk_state = TCP_ESTABLISHED;
+
+ unix_find_other(net, sunaddr, addr_len, sk->sk_type,
+ hash, 1, &other, 1, &err);
+ if (other) {
+ otheru = unix_sk(other);
+ atomic_inc(&otheru->addr->refcnt);
+ u->addr = otheru->addr;
+ } else {
+ err = -ENOMEM;
+ u->addr = kmalloc(sizeof(*u->addr)+addr_len, GFP_KERNEL);
+ if (!u->addr)
+ return err;
+
+ memcpy(u->addr->name, sunaddr, addr_len);
+ u->addr->len = addr_len;
+ u->addr->hash = hash ^ sk->sk_type;
+ atomic_set(&u->addr->refcnt, 1);
+ }
+
+ unix_insert_socket(&unix_multicast_socket_table[u->addr->hash], sk);
return 0;
}
@@ -1427,8 +1480,8 @@ restart:
if (sunaddr == NULL)
goto out_free;
- other = unix_find_other(net, sunaddr, namelen, sk->sk_type,
- hash, &err);
+ unix_find_other(net, sunaddr, namelen, sk->sk_type,
+ hash, 0, &other, 1, &err);
if (other == NULL)
goto out_free;
}
@@ -1530,8 +1583,12 @@ static int unix_stream_setsockopt(struct socket *sock, int level, int optname,
return -EINVAL;
if (val != 0) {
+ if (!u->multicast)
+ atomic_inc(&unix_nr_multicast_socks);
u->multicast = 1;
} else {
+ if (u->multicast)
+ atomic_dec(&unix_nr_multicast_socks);
u->multicast = 0;
}
break;
@@ -1582,6 +1639,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
struct sock_iocb *siocb = kiocb_to_siocb(kiocb);
struct sock *sk = sock->sk;
struct sock *other = NULL;
+ struct sock **others = NULL;
+ int max_others;
struct sockaddr_un *sunaddr = msg->msg_name;
int err, size;
struct sk_buff *skb;
@@ -1612,7 +1671,12 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
} else {
sunaddr = NULL;
err = -ENOTCONN;
- other = NULL; /* FIXME: get the list of other connection */
+ max_others = atomic_read(&unix_nr_multicast_socks);
+ others = kzalloc((max_others + 1) * sizeof(void *), GFP_KERNEL);
+ unix_find_other(sock_net(sk), u->addr->name,
+ u->addr->len, 0, u->addr->hash, 1, others, max_others, &err);
+ other = others[0];
+ kfree(others);
if (!other)
goto out_err;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCH 5/5] AF_UNIX: deliver the data to all the multicast peers
[not found] <20100924182257.11abd9a6@chocolatine.cbg.collabora.co.uk>
` (3 preceding siblings ...)
2010-09-24 17:25 ` [PATCH 4/5] AF_UNIX: find peers on multicast Unix stream sockets Alban Crequy
@ 2010-09-24 17:25 ` Alban Crequy
4 siblings, 0 replies; 9+ messages in thread
From: Alban Crequy @ 2010-09-24 17:25 UTC (permalink / raw)
To: Alban Crequy
Cc: David S. Miller, Eric Dumazet, Stephen Hemminger, Cyrill Gorcunov,
Alexey Dobriyan, Lennart Poettering, Kay Sievers, Ian Molton,
netdev, linux-kernel, dbus, Alban Crequy
With multicast sockets, unix_stream_sendmsg() needs to deliver the data to
zero, one or several recipients. skb_clone() is used to get as many sk_buff as
recipients.
If a multicast peer is too slow to receive the packets, it will block the
sender at some point in sock_alloc_send_skb().
A sender can receive SIGPIPE caused by an unique peer on the multicast group.
This is probably not what we want.
I tested this with a few peers and either sending data, receiving data or just
sleeping.
Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
---
net/unix/af_unix.c | 77 +++++++++++++++++++++++++++++++++++----------------
1 files changed, 53 insertions(+), 24 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index f259849..01cb603 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -1640,10 +1640,10 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
struct sock *sk = sock->sk;
struct sock *other = NULL;
struct sock **others = NULL;
+ struct sock **others_cur = NULL;
int max_others;
struct sockaddr_un *sunaddr = msg->msg_name;
int err, size;
- struct sk_buff *skb;
int sent = 0;
struct scm_cookie tmp_scm;
bool fds_sent = false;
@@ -1668,15 +1668,22 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
other = unix_peer(sk);
if (!other)
goto out_err;
+ err = -ENOMEM;
+ others = kzalloc(2 * sizeof(void *), GFP_KERNEL);
+ if (!others)
+ goto out_err;
+ others[0] = other;
} else {
sunaddr = NULL;
- err = -ENOTCONN;
max_others = atomic_read(&unix_nr_multicast_socks);
+ err = -ENOMEM;
others = kzalloc((max_others + 1) * sizeof(void *), GFP_KERNEL);
+ if (!others)
+ goto out_err;
unix_find_other(sock_net(sk), u->addr->name,
u->addr->len, 0, u->addr->hash, 1, others, max_others, &err);
+ err = -ENOTCONN;
other = others[0];
- kfree(others);
if (!other)
goto out_err;
}
@@ -1685,6 +1692,8 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
goto pipe_err;
while (sent < len) {
+ struct sk_buff *skb;
+ struct sk_buff *skb_cloned;
/*
* Optimisation for the fact that under 0.01% of X
* messages typically need breaking up.
@@ -1718,43 +1727,61 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
*/
size = min_t(int, size, skb_tailroom(skb));
- memcpy(UNIXCREDS(skb), &siocb->scm->creds, sizeof(struct ucred));
- /* Only send the fds in the first buffer */
- if (siocb->scm->fp && !fds_sent) {
- err = unix_attach_fds(siocb->scm, skb);
- if (err) {
- kfree_skb(skb);
- goto out_err;
- }
- fds_sent = true;
- }
-
err = memcpy_fromiovec(skb_put(skb, size), msg->msg_iov, size);
if (err) {
kfree_skb(skb);
goto out_err;
}
- unix_state_lock(other);
+ others_cur = others;
+ while (*others_cur != NULL) {
+ skb_cloned = skb_clone(skb, GFP_KERNEL);
+ if (!skb_cloned) {
+ kfree_skb(skb);
+ goto out_err;
+ }
+ skb_set_owner_w(skb_cloned, sk);
+
+ memcpy(UNIXCREDS(skb_cloned), &siocb->scm->creds, sizeof(struct ucred));
+ /* Only send the fds in the first buffer of each
+ * recipient */
+ if (siocb->scm->fp && !fds_sent) {
+ err = unix_attach_fds(siocb->scm, skb_cloned);
+ if (err) {
+ kfree_skb(skb);
+ kfree_skb(skb_cloned);
+ goto out_err;
+ }
+ }
+
+ unix_state_lock(*others_cur);
- if (sock_flag(other, SOCK_DEAD) ||
- (other->sk_shutdown & RCV_SHUTDOWN))
- goto pipe_err_free;
+ if (sock_flag(*others_cur, SOCK_DEAD) ||
+ ((*others_cur)->sk_shutdown & RCV_SHUTDOWN)) {
+ unix_state_unlock(*others_cur);
+ kfree_skb(skb);
+ kfree_skb(skb_cloned);
+ goto pipe_err;
+ }
- skb_queue_tail(&other->sk_receive_queue, skb);
- unix_state_unlock(other);
- other->sk_data_ready(other, size);
+ skb_queue_tail(&(*others_cur)->sk_receive_queue,
+ skb_cloned);
+ unix_state_unlock(*others_cur);
+ (*others_cur)->sk_data_ready(*others_cur, size);
+ others_cur++;
+ }
+ kfree_skb(skb);
+ fds_sent = true;
sent += size;
}
scm_destroy(siocb->scm);
siocb->scm = NULL;
+ if (others)
+ kfree(others);
return sent;
-pipe_err_free:
- unix_state_unlock(other);
- kfree_skb(skb);
pipe_err:
if (sent == 0 && !(msg->msg_flags&MSG_NOSIGNAL))
send_sig(SIGPIPE, current, 0);
@@ -1762,6 +1789,8 @@ pipe_err:
out_err:
scm_destroy(siocb->scm);
siocb->scm = NULL;
+ if (others)
+ kfree(others);
return sent ? : err;
}
--
1.7.1
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH 2/5] AF_UNIX: enable/disable multicast with getsockopt/setsockopt
2010-09-24 17:25 ` [PATCH 2/5] AF_UNIX: enable/disable multicast with getsockopt/setsockopt Alban Crequy
@ 2010-09-24 17:46 ` Eric Dumazet
2010-09-24 18:02 ` Eric Dumazet
0 siblings, 1 reply; 9+ messages in thread
From: Eric Dumazet @ 2010-09-24 17:46 UTC (permalink / raw)
To: Alban Crequy
Cc: David S. Miller, Stephen Hemminger, Cyrill Gorcunov,
Alexey Dobriyan, Lennart Poettering, Kay Sievers, Ian Molton,
netdev, linux-kernel, dbus
Le vendredi 24 septembre 2010 à 18:25 +0100, Alban Crequy a écrit :
> Multicast can be enabled or disabled after a socket is allocated but this
> cannot be changed once the socket is bound or connected.
>
> Userspace applications can enable multicast on an Unix stream socket:
> sockfd = socket(AF_UNIX, SOCK_STREAM, 0);
> #define UNIX_MULTICAST 1
> val = 1;
> len = sizeof(val);
> ret = setsockopt(sockfd, 0, UNIX_MULTICAST, &val, len);
>
> Signed-off-by: Alban Crequy <alban.crequy@collabora.co.uk>
> + if (val != 0) {
> + u->multicast = 1;
> + } else {
> + u->multicast = 0;
> + }
u->multicast = !!val;
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 4/5] AF_UNIX: find peers on multicast Unix stream sockets
2010-09-24 17:25 ` [PATCH 4/5] AF_UNIX: find peers on multicast Unix stream sockets Alban Crequy
@ 2010-09-24 18:00 ` Eric Dumazet
2010-09-30 19:24 ` Alban Crequy
0 siblings, 1 reply; 9+ messages in thread
From: Eric Dumazet @ 2010-09-24 18:00 UTC (permalink / raw)
To: Alban Crequy
Cc: David S. Miller, Stephen Hemminger, Cyrill Gorcunov,
Alexey Dobriyan, Lennart Poettering, Kay Sievers, Ian Molton,
netdev, linux-kernel, dbus
Le vendredi 24 septembre 2010 à 18:25 +0100, Alban Crequy a écrit :
> @@ -1612,7 +1671,12 @@ static int unix_stream_sendmsg(struct kiocb *kiocb, struct socket *sock,
> } else {
> sunaddr = NULL;
> err = -ENOTCONN;
> - other = NULL; /* FIXME: get the list of other connection */
> + max_others = atomic_read(&unix_nr_multicast_socks);
> + others = kzalloc((max_others + 1) * sizeof(void *), GFP_KERNEL);
> + unix_find_other(sock_net(sk), u->addr->name,
> + u->addr->len, 0, u->addr->hash, 1, others, max_others, &err);
> + other = others[0];
> + kfree(others);
> if (!other)
> goto out_err;
> }
Seriously, this block sizing against unix_nr_multicast_socks is not
scalable. What happens if we have 1000 sockets ?
kzalloc() to clear 8000 bytes ?
Its also unsafe.
(say you kzalloc() a buffer for 2 sockets, and another cpu inserts a new
socket. unix_find_socket_byname() can overflow the buffer)
You should use a list, and allocates elements in
unix_find_socket_byname()
struct item {
struct item *next;
struct sock *s;
};
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 2/5] AF_UNIX: enable/disable multicast with getsockopt/setsockopt
2010-09-24 17:46 ` Eric Dumazet
@ 2010-09-24 18:02 ` Eric Dumazet
0 siblings, 0 replies; 9+ messages in thread
From: Eric Dumazet @ 2010-09-24 18:02 UTC (permalink / raw)
To: Alban Crequy
Cc: David S. Miller, Stephen Hemminger, Cyrill Gorcunov,
Alexey Dobriyan, Lennart Poettering, Kay Sievers, Ian Molton,
netdev, linux-kernel
Le vendredi 24 septembre 2010 à 19:46 +0200, Eric Dumazet a écrit :
Hmm
dbus-owner@lists.freedesktop.org is a subscriber only list ....
Please dont use it
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH 4/5] AF_UNIX: find peers on multicast Unix stream sockets
2010-09-24 18:00 ` Eric Dumazet
@ 2010-09-30 19:24 ` Alban Crequy
0 siblings, 0 replies; 9+ messages in thread
From: Alban Crequy @ 2010-09-30 19:24 UTC (permalink / raw)
To: Eric Dumazet
Cc: David S. Miller, Stephen Hemminger, Cyrill Gorcunov,
Alexey Dobriyan, Lennart Poettering, Kay Sievers, Ian Molton,
netdev, linux-kernel
Le Fri, 24 Sep 2010 20:00:37 +0200,
Eric Dumazet <eric.dumazet@gmail.com> a écrit :
> Le vendredi 24 septembre 2010 à 18:25 +0100, Alban Crequy a écrit :
>
> > @@ -1612,7 +1671,12 @@ static int unix_stream_sendmsg(struct kiocb
> > *kiocb, struct socket *sock, } else {
> > sunaddr = NULL;
> > err = -ENOTCONN;
> > - other = NULL; /* FIXME: get the list of other
> > connection */
> > + max_others = atomic_read(&unix_nr_multicast_socks);
> > + others = kzalloc((max_others + 1) * sizeof(void
> > *), GFP_KERNEL);
> > + unix_find_other(sock_net(sk), u->addr->name,
> > + u->addr->len, 0, u->addr->hash, 1, others,
> > max_others, &err);
> > + other = others[0];
> > + kfree(others);
> > if (!other)
> > goto out_err;
> > }
>
> Seriously, this block sizing against unix_nr_multicast_socks is not
> scalable. What happens if we have 1000 sockets ?
> kzalloc() to clear 8000 bytes ?
> Its also unsafe.
>
> (say you kzalloc() a buffer for 2 sockets, and another cpu inserts a
> new socket. unix_find_socket_byname() can overflow the buffer)
>
>
> You should use a list, and allocates elements in
> unix_find_socket_byname()
>
> struct item {
> struct item *next;
> struct sock *s;
> };
Thanks for your review.
I cannot allocate elements directly in unix_find_socket_byname()
iteration after iteration because the spinlock "unix_table_lock" is
held. If I release the spinlock to allocate, the number of sockets in
the table may change.
I changed the code to count the sockets with the lock held and then
allocate. In the unfortunate case where the allocation is not big
enough (if another process inserts a new socket), it just tries again.
The code is available here. Please pull from:
git://git.collabora.co.uk/git/user/alban/linux-2.6.35.y/.git unix-multicast2
It is still a work in progress. Missing pieces:
- The flow control does not work correctly: poll/select does not match
the reality
- Atomic delivery: if a process is killed or interrupted in the middle
of a delivery, only a subset of the recipients will get the message
- Some locking to provide the same delivery order to all the recipients
when several senders run concurrently.
Feedback welcome,
Alban Crequy
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2010-09-30 19:24 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20100924182257.11abd9a6@chocolatine.cbg.collabora.co.uk>
2010-09-24 17:25 ` [PATCH 1/5] AF_UNIX: add getsockopt and setsockopt on stream sockets Alban Crequy
2010-09-24 17:25 ` [PATCH 2/5] AF_UNIX: enable/disable multicast with getsockopt/setsockopt Alban Crequy
2010-09-24 17:46 ` Eric Dumazet
2010-09-24 18:02 ` Eric Dumazet
2010-09-24 17:25 ` [PATCH 3/5] AF_UNIX: implement connect() on multicast Unix stream socket Alban Crequy
2010-09-24 17:25 ` [PATCH 4/5] AF_UNIX: find peers on multicast Unix stream sockets Alban Crequy
2010-09-24 18:00 ` Eric Dumazet
2010-09-30 19:24 ` Alban Crequy
2010-09-24 17:25 ` [PATCH 5/5] AF_UNIX: deliver the data to all the multicast peers Alban Crequy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox