[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <20080916165017.GJ8702@ghostprotocols.net>
Date: Tue, 16 Sep 2008 13:50:17 -0300
From: Arnaldo Carvalho de Melo <acme@...hat.com>
To: Rémi Denis-Courmont
<remi.denis-courmont@...ia.com>
Cc: netdev@...r.kernel.org
Subject: Re: [PATCH 07/14] Phonet: common socket glue
Em Tue, Sep 16, 2008 at 06:08:07PM +0300, Rémi Denis-Courmont escreveu:
> This provides the socket API for the Phonet protocols family.
>
> Signed-off-by: Remi Denis-Courmont <remi.denis-courmont@...ia.com>
> ---
> include/linux/phonet.h | 3 +
> include/net/phonet/phonet.h | 20 +++
> net/phonet/Makefile | 1 +
> net/phonet/socket.c | 312 +++++++++++++++++++++++++++++++++++++++++++
> 4 files changed, 336 insertions(+), 0 deletions(-)
> create mode 100644 net/phonet/socket.c
>
> diff --git a/include/linux/phonet.h b/include/linux/phonet.h
> index 000b6d7..4510458 100644
> --- a/include/linux/phonet.h
> +++ b/include/linux/phonet.h
> @@ -32,6 +32,9 @@
> #define PNADDR_ANY 0
> #define PNPORT_RESOURCE_ROUTING 0
>
> +/* ioctls */
> +#define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0)
> +
> /* Phonet protocol header */
> struct phonethdr {
> uint8_t rdev;
> diff --git a/include/net/phonet/phonet.h b/include/net/phonet/phonet.h
> index d62883c..e0fa080 100644
> --- a/include/net/phonet/phonet.h
> +++ b/include/net/phonet/phonet.h
> @@ -29,6 +29,25 @@
> */
> #define MAX_PHONET_HEADER 8
>
> +/*
> + * Every Phonet* socket has this structure first in its
> + * protocol-specific structure under name c.
> + */
> +struct pn_sock {
> + struct sock sk;
> + u16 sobject;
> + u8 resource;
> +};
> +
> +#define pn_sk(sk) ((struct pn_sock *)(sk))
Why do this as a define and not like, say, tcp_sk(), that way you at
least guarantee that you will not end up using some non-sock pointer
as your pn_sock :)
> +extern const struct proto_ops phonet_dgram_ops;
> +
> +struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *sa);
> +void pn_sock_hash(struct sock *sk);
> +void pn_sock_unhash(struct sock *sk);
> +int pn_sock_get_port(struct sock *sk, unsigned short sport);
> +
> #define pn_hdr(skb) ((struct phonethdr *)skb_network_header(skb))
At least here skb_network_header() will do the typechecking for you.
> /*
> @@ -68,5 +87,6 @@ struct phonet_protocol {
> int phonet_proto_register(int protocol, struct phonet_protocol *pp);
> void phonet_proto_unregister(int protocol, struct phonet_protocol *pp);
>
> +void phonet_socket_init(void);
> void phonet_netlink_register(void);
> #endif
> diff --git a/net/phonet/Makefile b/net/phonet/Makefile
> index 4143c3e..c1d671d 100644
> --- a/net/phonet/Makefile
> +++ b/net/phonet/Makefile
> @@ -3,4 +3,5 @@ obj-$(CONFIG_PHONET) += phonet.o
> phonet-objs := \
> pn_dev.o \
> pn_netlink.o \
> + socket.o \
> af_phonet.o
> diff --git a/net/phonet/socket.c b/net/phonet/socket.c
> new file mode 100644
> index 0000000..0e28327
> --- /dev/null
> +++ b/net/phonet/socket.c
> @@ -0,0 +1,312 @@
> +/*
> + * File: socket.c
> + *
> + * Phonet sockets
> + *
> + * Copyright (C) 2008 Nokia Corporation.
> + *
> + * Contact: Remi Denis-Courmont <remi.denis-courmont@...ia.com>
> + * Original author: Sakari Ailus <sakari.ailus@...ia.com>
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * version 2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful, but
> + * WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
> + * 02110-1301 USA
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/net.h>
> +#include <net/sock.h>
> +#include <net/tcp_states.h>
> +
> +#include <linux/phonet.h>
> +#include <net/phonet/phonet.h>
> +#include <net/phonet/pn_dev.h>
> +
> +static int pn_socket_release(struct socket *sock)
> +{
> + struct sock *sk = sock->sk;
> +
> + if (sk) {
> + sock->sk = NULL;
> + sk->sk_prot->close(sk, 0);
> + }
> + return 0;
> +}
> +
> +static struct {
> + struct hlist_head hlist;
> + spinlock_t lock;
> +} pnsocks;
See tcp_death_row on how to initialize it right here and not at some
init function.
> +/*
> + * Find address based on socket address, match only certain fields.
> + * Also grab sock if it was found. Remember to sock_put it later.
> + */
> +struct sock *pn_find_sock_by_sa(const struct sockaddr_pn *spn)
> +{
> + struct hlist_node *node;
> + struct sock *sknode;
> + struct sock *rval = NULL;
> + uint16_t obj = pn_sockaddr_get_object(spn);
> + uint8_t res = spn->spn_resource;
> +
> + spin_lock_bh(&pnsocks.lock);
> +
> + sk_for_each(sknode, node, &pnsocks.hlist) {
> + struct pn_sock *pn = pn_sk(sknode);
> + BUG_ON(!pn->sobject); /* unbound socket */
> +
> + if (pn_port(obj)) {
> + /* Look up socket by port */
> + if (pn_port(pn->sobject) != pn_port(obj))
> + continue;
> + } else {
> + /* If port is zero, look up by resource */
> + if (pn->resource != res)
> + continue;
> + }
> + if (pn_addr(pn->sobject)
> + && pn_addr(pn->sobject) != pn_addr(obj))
Please && at the end of previous line
> + continue;
> +
> + rval = sknode;
> + sock_hold(sknode);
> + break;
> + }
> +
> + spin_unlock_bh(&pnsocks.lock);
> +
> + return rval;
> +
> +}
> +
> +void pn_sock_hash(struct sock *sk)
> +{
> + spin_lock_bh(&pnsocks.lock);
> + sk_add_node(sk, &pnsocks.hlist);
> + spin_unlock_bh(&pnsocks.lock);
> +}
> +EXPORT_SYMBOL(pn_sock_hash);
> +
> +void pn_sock_unhash(struct sock *sk)
> +{
> + spin_lock_bh(&pnsocks.lock);
> + sk_del_node_init(sk);
> + spin_unlock_bh(&pnsocks.lock);
> +}
> +EXPORT_SYMBOL(pn_sock_unhash);
> +
> +static int pn_socket_bind(struct socket *sock, struct sockaddr *addr, int len)
> +{
> + struct sock *sk = sock->sk;
> + struct pn_sock *pn = pn_sk(sk);
> + struct sockaddr_pn *spn = (struct sockaddr_pn *)addr;
> + int err;
> + uint16_t handle;
> + uint8_t saddr;
> +
> + if (sk->sk_prot->bind)
> + return sk->sk_prot->bind(sk, addr, len);
> +
> + if (len < sizeof(struct sockaddr_pn))
> + return -EINVAL;
> + if (spn->spn_family != AF_PHONET)
> + return -EAFNOSUPPORT;
> +
> + handle = pn_sockaddr_get_object((struct sockaddr_pn *)addr);
> + saddr = pn_addr(handle);
> + if (saddr) {
> + struct phonet_address *pna;
> +
> + spin_lock_bh(&pndevs.lock);
> + pna = phonet_addr2addr(pn_addr(handle), PN_FIND_EXACT);
> + spin_unlock_bh(&pndevs.lock);
> + if (pna == NULL)
> + return -EADDRNOTAVAIL;
> + }
> +
> + lock_sock(sk);
> + if (sk->sk_state != TCP_CLOSE || pn_port(pn->sobject)) {
> + err = -EINVAL; /* attempt to rebind */
> + goto out;
> + }
> + err = sk->sk_prot->get_port(sk, pn_port(handle));
> + if (err)
> + goto out;
> +
> + /* get_port() sets the port, bind() sets the address if applicable */
> + pn->sobject = pn_object(saddr, pn_port(pn->sobject));
> + pn->resource = spn->spn_resource;
> +
> + /* Enable RX on the socket */
> + sk->sk_prot->hash(sk);
> +out:
> + release_sock(sk);
> + return err;
> +}
> +
> +static int pn_socket_autobind(struct socket *sock)
> +{
> + struct sockaddr_pn sa;
> + int err;
> +
> + memset(&sa, 0, sizeof(sa));
> + sa.spn_family = AF_PHONET;
> + err = pn_socket_bind(sock, (struct sockaddr *)&sa,
> + sizeof(struct sockaddr_pn));
> + if (err != -EINVAL)
> + return err;
> + BUG_ON(!pn_port(pn_sk(sock->sk)->sobject));
> + return 0; /* socket was already bound */
> +}
> +
> +static int pn_socket_getname(struct socket *sock, struct sockaddr *addr,
> + int *sockaddr_len, int peer)
> +{
> + struct sock *sk = sock->sk;
> + struct pn_sock *pn = pn_sk(sk);
> +
> + memset(addr, 0, sizeof(struct sockaddr_pn));
> + addr->sa_family = AF_PHONET;
> + if (!peer) /* Race with bind() here is userland's problem. */
> + pn_sockaddr_set_object((struct sockaddr_pn *)addr,
> + pn->sobject);
> +
> + *sockaddr_len = sizeof(struct sockaddr_pn);
> + return 0;
> +}
> +
> +static int pn_socket_ioctl(struct socket *sock, unsigned int cmd,
> + unsigned long arg)
> +{
> + struct sock *sk = sock->sk;
> + struct pn_sock *pn = pn_sk(sk);
> +
> + if (cmd == SIOCPNGETOBJECT) {
> + struct phonet_address *pna;
> + uint16_t handle;
> + uint8_t rdev;
> +
> + if (get_user(handle, (uint16_t __user *)arg))
> + return -EFAULT;
> +
> + rdev = pn_dev(handle);
> + spin_lock_bh(&pndevs.lock);
> + pna = phonet_addr2addr(rdev, 0);
> + if (pna)
> + handle = pn_object(pna->addr, pn_port(pn->sobject));
> + spin_unlock_bh(&pndevs.lock);
> +
> + if (!pna)
> + return -EHOSTUNREACH;
> + return put_user(handle, (uint16_t __user *)arg);
> + }
> +
> + return sk->sk_prot->ioctl(sk, cmd, arg);
I guess it is a requirement that each protocol will provide an ioctl
handler.
> +}
> +
> +static int pn_socket_sendmsg(struct kiocb *iocb, struct socket *sock,
> + struct msghdr *m, size_t total_len)
> +{
> + struct sock *sk = sock->sk;
> +
> + if (pn_socket_autobind(sock))
> + return -EAGAIN;
> +
> + return sk->sk_prot->sendmsg(iocb, sk, m, total_len);
> +}
> +
> +const struct proto_ops phonet_dgram_ops = {
> + .family = AF_PHONET,
> + .owner = THIS_MODULE,
> + .release = pn_socket_release,
> + .bind = pn_socket_bind,
> + .connect = sock_no_connect,
> + .socketpair = sock_no_socketpair,
> + .accept = sock_no_accept,
> + .getname = pn_socket_getname,
> + .poll = datagram_poll,
> + .ioctl = pn_socket_ioctl,
> + .listen = sock_no_listen,
> + .shutdown = sock_no_shutdown,
> + .setsockopt = sock_no_setsockopt,
> + .getsockopt = sock_no_getsockopt,
> +#ifdef CONFIG_COMPAT
> + .compat_setsockopt = sock_no_setsockopt,
> + .compat_getsockopt = sock_no_getsockopt,
> +#endif
> + .sendmsg = pn_socket_sendmsg,
> + .recvmsg = sock_common_recvmsg,
> + .mmap = sock_no_mmap,
> + .sendpage = sock_no_sendpage,
> +};
> +
> +static DEFINE_MUTEX(port_mutex);
> +
> +/* allocate port for a socket */
> +int pn_sock_get_port(struct sock *sk, unsigned short sport)
> +{
> + static int port_cur;
> + struct pn_sock *pn = pn_sk(sk);
> + struct sockaddr_pn try_sa;
> + struct sock *tmpsk;
> +
> + memset(&try_sa, 0, sizeof(struct sockaddr_pn));
> + try_sa.spn_family = AF_PHONET;
> +
> + mutex_lock(&port_mutex);
> +
> + if (!sport) {
> + /* search free port */
> + int port, pmin = 0x40, pmax = 0x7f;
> +
> + for (port = pmin; port <= pmax; port++) {
> + port_cur++;
> + if (port_cur < pmin || port_cur > pmax)
> + port_cur = pmin;
> +
> + pn_sockaddr_set_port(&try_sa, port_cur);
> + tmpsk = pn_find_sock_by_sa(&try_sa);
> + if (tmpsk == NULL) {
> + sport = port_cur;
> + goto found;
> + } else
> + sock_put(tmpsk);
> + }
> + } else {
> + /* try to find specific port */
> + pn_sockaddr_set_port(&try_sa, sport);
> + tmpsk = pn_find_sock_by_sa(&try_sa);
> + if (tmpsk == NULL)
> + /* No sock there! We can use that port... */
> + goto found;
> + else
> + sock_put(tmpsk);
> + }
> + mutex_unlock(&port_mutex);
> +
> + /* the port must be in use already */
> + return -EADDRINUSE;
> +
> +found:
> + mutex_unlock(&port_mutex);
> + pn->sobject = pn_object(pn_addr(pn->sobject), sport);
> + return 0;
> +}
> +EXPORT_SYMBOL(pn_sock_get_port);
> +
> +void __init phonet_socket_init(void)
> +{
> + INIT_HLIST_HEAD(&pnsocks.hlist);
> + spin_lock_init(&pnsocks.lock);
> +}
> --
> 1.5.4.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@...r.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists