diff options
Diffstat (limited to 'net/phonet')
| -rw-r--r-- | net/phonet/Kconfig | 12 | ||||
| -rw-r--r-- | net/phonet/af_phonet.c | 46 | ||||
| -rw-r--r-- | net/phonet/datagram.c | 20 | ||||
| -rw-r--r-- | net/phonet/pep-gprs.c | 6 | ||||
| -rw-r--r-- | net/phonet/pep.c | 933 | ||||
| -rw-r--r-- | net/phonet/pn_dev.c | 61 | ||||
| -rw-r--r-- | net/phonet/pn_netlink.c | 57 | ||||
| -rw-r--r-- | net/phonet/socket.c | 227 | ||||
| -rw-r--r-- | net/phonet/sysctl.c | 23 | 
9 files changed, 608 insertions, 777 deletions
diff --git a/net/phonet/Kconfig b/net/phonet/Kconfig index 0d9b8a220a7..6ec7d55b176 100644 --- a/net/phonet/Kconfig +++ b/net/phonet/Kconfig @@ -14,15 +14,3 @@ config PHONET  	  To compile this driver as a module, choose M here: the module  	  will be called phonet. If unsure, say N. - -config PHONET_PIPECTRLR -	bool "Phonet Pipe Controller (EXPERIMENTAL)" -	depends on PHONET && EXPERIMENTAL -	default N -	help -	  The Pipe Controller implementation in Phonet stack to support Pipe -	  data with Nokia Slim modems like WG2.5 used on ST-Ericsson U8500 -	  platform. - -	  This option is incompatible with older Nokia modems. -	  Say N here unless you really know what you are doing. diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c index fd95beb72f5..5a940dbd74a 100644 --- a/net/phonet/af_phonet.c +++ b/net/phonet/af_phonet.c @@ -5,8 +5,8 @@   *   * Copyright (C) 2008 Nokia Corporation.   * - * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> - * Original author: Sakari Ailus <sakari.ailus@nokia.com> + * Authors: Sakari Ailus <sakari.ailus@nokia.com> + *          Rémi Denis-Courmont   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -37,7 +37,7 @@  /* Transport protocol registration */  static struct phonet_protocol *proto_tab[PHONET_NPROTO] __read_mostly; -static struct phonet_protocol *phonet_proto_get(int protocol) +static struct phonet_protocol *phonet_proto_get(unsigned int protocol)  {  	struct phonet_protocol *pp; @@ -110,6 +110,7 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol,  	sk->sk_protocol = protocol;  	pn = pn_sk(sk);  	pn->sobject = 0; +	pn->dobject = 0;  	pn->resource = 0;  	sk->sk_prot->init(sk);  	err = 0; @@ -128,7 +129,7 @@ static const struct net_proto_family phonet_proto_family = {  /* Phonet device header operations */  static int pn_header_create(struct sk_buff *skb, struct net_device *dev,  				unsigned short type, const void *daddr, -				const void *saddr, unsigned len) +				const void *saddr, unsigned int len)  {  	u8 *media = skb_push(skb, 1); @@ -194,11 +195,7 @@ static int pn_send(struct sk_buff *skb, struct net_device *dev,  	if (skb->pkt_type == PACKET_LOOPBACK) {  		skb_reset_mac_header(skb);  		skb_orphan(skb); -		if (irq) -			netif_rx(skb); -		else -			netif_rx_ni(skb); -		err = 0; +		err = (irq ? netif_rx(skb) : netif_rx_ni(skb)) ? -ENOBUFS : 0;  	} else {  		err = dev_hard_header(skb, dev, ntohs(skb->protocol),  					NULL, NULL, skb->len); @@ -207,6 +204,8 @@ static int pn_send(struct sk_buff *skb, struct net_device *dev,  			goto drop;  		}  		err = dev_queue_xmit(skb); +		if (unlikely(err > 0)) +			err = net_xmit_errno(err);  	}  	return err; @@ -242,8 +241,18 @@ int pn_skb_send(struct sock *sk, struct sk_buff *skb,  	struct net_device *dev;  	struct pn_sock *pn = pn_sk(sk);  	int err; -	u16 src; -	u8 daddr = pn_sockaddr_get_addr(target), saddr = PN_NO_ADDR; +	u16 src, dst; +	u8 daddr, saddr, res; + +	src = pn->sobject; +	if (target != NULL) { +		dst = pn_sockaddr_get_object(target); +		res = pn_sockaddr_get_resource(target); +	} else { +		dst = pn->dobject; +		res = pn->resource; +	} +	daddr = pn_addr(dst);  	err = -EHOSTUNREACH;  	if (sk->sk_bound_dev_if) @@ -251,10 +260,9 @@ int pn_skb_send(struct sock *sk, struct sk_buff *skb,  	else if (phonet_address_lookup(net, daddr) == 0) {  		dev = phonet_device_get(net);  		skb->pkt_type = PACKET_LOOPBACK; -	} else if (pn_sockaddr_get_object(target) == 0) { +	} else if (dst == 0) {  		/* Resource routing (small race until phonet_rcv()) */ -		struct sock *sk = pn_find_sock_by_res(net, -							target->spn_resource); +		struct sock *sk = pn_find_sock_by_res(net, res);  		if (sk)	{  			sock_put(sk);  			dev = phonet_device_get(net); @@ -271,12 +279,10 @@ int pn_skb_send(struct sock *sk, struct sk_buff *skb,  	if (saddr == PN_NO_ADDR)  		goto drop; -	src = pn->sobject;  	if (!pn_addr(src))  		src = pn_object(saddr, pn_obj(src)); -	err = pn_send(skb, dev, pn_sockaddr_get_object(target), -			src, pn_sockaddr_get_resource(target), 0); +	err = pn_send(skb, dev, dst, src, res, 0);  	dev_put(dev);  	return err; @@ -458,7 +464,7 @@ static struct packet_type phonet_packet_type __read_mostly = {  static DEFINE_MUTEX(proto_tab_lock); -int __init_or_module phonet_proto_register(int protocol, +int __init_or_module phonet_proto_register(unsigned int protocol,  						struct phonet_protocol *pp)  {  	int err = 0; @@ -481,11 +487,11 @@ int __init_or_module phonet_proto_register(int protocol,  }  EXPORT_SYMBOL(phonet_proto_register); -void phonet_proto_unregister(int protocol, struct phonet_protocol *pp) +void phonet_proto_unregister(unsigned int protocol, struct phonet_protocol *pp)  {  	mutex_lock(&proto_tab_lock);  	BUG_ON(proto_tab[protocol] != pp); -	rcu_assign_pointer(proto_tab[protocol], NULL); +	RCU_INIT_POINTER(proto_tab[protocol], NULL);  	mutex_unlock(&proto_tab_lock);  	synchronize_rcu();  	proto_unregister(pp->prot); diff --git a/net/phonet/datagram.c b/net/phonet/datagram.c index 2f032381bd4..290352c0e6b 100644 --- a/net/phonet/datagram.c +++ b/net/phonet/datagram.c @@ -5,8 +5,8 @@   *   * Copyright (C) 2008 Nokia Corporation.   * - * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> - * Original author: Sakari Ailus <sakari.ailus@nokia.com> + * Authors: Sakari Ailus <sakari.ailus@nokia.com> + *          Rémi Denis-Courmont   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -30,6 +30,7 @@  #include <net/sock.h>  #include <linux/phonet.h> +#include <linux/export.h>  #include <net/phonet/phonet.h>  static int pn_backlog_rcv(struct sock *sk, struct sk_buff *skb); @@ -85,7 +86,7 @@ static int pn_init(struct sock *sk)  static int pn_sendmsg(struct kiocb *iocb, struct sock *sk,  			struct msghdr *msg, size_t len)  { -	struct sockaddr_pn *target; +	DECLARE_SOCKADDR(struct sockaddr_pn *, target, msg->msg_name);  	struct sk_buff *skb;  	int err; @@ -93,13 +94,12 @@ static int pn_sendmsg(struct kiocb *iocb, struct sock *sk,  				MSG_CMSG_COMPAT))  		return -EOPNOTSUPP; -	if (msg->msg_name == NULL) +	if (target == NULL)  		return -EDESTADDRREQ;  	if (msg->msg_namelen < sizeof(struct sockaddr_pn))  		return -EINVAL; -	target = (struct sockaddr_pn *)msg->msg_name;  	if (target->spn_family != AF_PHONET)  		return -EAFNOSUPPORT; @@ -138,9 +138,6 @@ static int pn_recvmsg(struct kiocb *iocb, struct sock *sk,  			MSG_CMSG_COMPAT))  		goto out_nofree; -	if (addr_len) -		*addr_len = sizeof(sa); -  	skb = skb_recv_datagram(sk, flags, noblock, &rval);  	if (skb == NULL)  		goto out_nofree; @@ -161,8 +158,11 @@ static int pn_recvmsg(struct kiocb *iocb, struct sock *sk,  	rval = (flags & MSG_TRUNC) ? skb->len : copylen; -	if (msg->msg_name != NULL) -		memcpy(msg->msg_name, &sa, sizeof(struct sockaddr_pn)); +	if (msg->msg_name != NULL) { +		__sockaddr_check_size(sizeof(sa)); +		memcpy(msg->msg_name, &sa, sizeof(sa)); +		*addr_len = sizeof(sa); +	}  out:  	skb_free_datagram(sk, skb); diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c index d01208968c8..66dc65e7c6a 100644 --- a/net/phonet/pep-gprs.c +++ b/net/phonet/pep-gprs.c @@ -5,7 +5,7 @@   *   * Copyright (C) 2008 Nokia Corporation.   * - * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> + * Author: Rémi Denis-Courmont   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -37,7 +37,7 @@  struct gprs_dev {  	struct sock		*sk;  	void			(*old_state_change)(struct sock *); -	void			(*old_data_ready)(struct sock *, int); +	void			(*old_data_ready)(struct sock *);  	void			(*old_write_space)(struct sock *);  	struct net_device	*dev; @@ -146,7 +146,7 @@ drop:  	return err;  } -static void gprs_data_ready(struct sock *sk, int len) +static void gprs_data_ready(struct sock *sk)  {  	struct gprs_dev *gp = sk->sk_user_data;  	struct sk_buff *skb; diff --git a/net/phonet/pep.c b/net/phonet/pep.c index 3e60f2e4e6c..70a547ea517 100644 --- a/net/phonet/pep.c +++ b/net/phonet/pep.c @@ -5,7 +5,7 @@   *   * Copyright (C) 2008 Nokia Corporation.   * - * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.com> + * Author: Rémi Denis-Courmont   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -30,6 +30,7 @@  #include <asm/ioctls.h>  #include <linux/phonet.h> +#include <linux/module.h>  #include <net/phonet/phonet.h>  #include <net/phonet/pep.h>  #include <net/phonet/gprs.h> @@ -42,7 +43,7 @@   * TCP_ESTABLISHED	connected pipe in enabled state   *   * pep_sock locking: - *  - sk_state, ackq, hlist: sock lock needed + *  - sk_state, hlist: sock lock needed   *  - listener: read only   *  - pipe_handle: read only   */ @@ -50,11 +51,6 @@  #define CREDITS_MAX	10  #define CREDITS_THR	7 -static const struct sockaddr_pn pipe_srv = { -	.spn_family = AF_PHONET, -	.spn_resource = 0xD9, /* pipe service */ -}; -  #define pep_sb_size(s) (((s) + 5) & ~3) /* 2-bytes head, 32-bits aligned */  /* Get the next TLV sub-block. */ @@ -82,236 +78,95 @@ static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,  	return data;  } -static int pep_reply(struct sock *sk, struct sk_buff *oskb, -			u8 code, const void *data, int len, gfp_t priority) +static struct sk_buff *pep_alloc_skb(struct sock *sk, const void *payload, +					int len, gfp_t priority)  { -	const struct pnpipehdr *oph = pnp_hdr(oskb); -	struct pnpipehdr *ph; -	struct sk_buff *skb; - -	skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); +	struct sk_buff *skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority);  	if (!skb) -		return -ENOMEM; +		return NULL;  	skb_set_owner_w(skb, sk);  	skb_reserve(skb, MAX_PNPIPE_HEADER);  	__skb_put(skb, len); -	skb_copy_to_linear_data(skb, data, len); -	__skb_push(skb, sizeof(*ph)); +	skb_copy_to_linear_data(skb, payload, len); +	__skb_push(skb, sizeof(struct pnpipehdr));  	skb_reset_transport_header(skb); -	ph = pnp_hdr(skb); -	ph->utid = oph->utid; -	ph->message_id = oph->message_id + 1; /* REQ -> RESP */ -	ph->pipe_handle = oph->pipe_handle; -	ph->error_code = code; - -	return pn_skb_send(sk, skb, &pipe_srv); -} - -#define PAD 0x00 - -#ifdef CONFIG_PHONET_PIPECTRLR -static u8 pipe_negotiate_fc(u8 *host_fc, u8 *remote_fc, int len) -{ -	int i, j; -	u8 base_fc, final_fc; - -	for (i = 0; i < len; i++) { -		base_fc = host_fc[i]; -		for (j = 0; j < len; j++) { -			if (remote_fc[j] == base_fc) { -				final_fc = base_fc; -				goto done; -			} -		} -	} -	return -EINVAL; - -done: -	return final_fc; - -} - -static int pipe_get_flow_info(struct sock *sk, struct sk_buff *skb, -		u8 *pref_rx_fc, u8 *req_tx_fc) -{ -	struct pnpipehdr *hdr; -	u8 n_sb; - -	if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) -		return -EINVAL; - -	hdr = pnp_hdr(skb); -	n_sb = hdr->data[4]; - -	__skb_pull(skb, sizeof(*hdr) + 4); -	while (n_sb > 0) { -		u8 type, buf[3], len = sizeof(buf); -		u8 *data = pep_get_sb(skb, &type, &len, buf); - -		if (data == NULL) -			return -EINVAL; - -		switch (type) { -		case PN_PIPE_SB_REQUIRED_FC_TX: -			if (len < 3 || (data[2] | data[3] | data[4]) > 3) -				break; -			req_tx_fc[0] = data[2]; -			req_tx_fc[1] = data[3]; -			req_tx_fc[2] = data[4]; -			break; - -		case PN_PIPE_SB_PREFERRED_FC_RX: -			if (len < 3 || (data[2] | data[3] | data[4]) > 3) -				break; -			pref_rx_fc[0] = data[2]; -			pref_rx_fc[1] = data[3]; -			pref_rx_fc[2] = data[4]; -			break; - -		} -		n_sb--; -	} -	return 0; +	return skb;  } -static int pipe_handler_send_req(struct sock *sk, u8 utid, -		u8 msg_id, gfp_t priority) +static int pep_reply(struct sock *sk, struct sk_buff *oskb, u8 code, +			const void *data, int len, gfp_t priority)  { -	int len; +	const struct pnpipehdr *oph = pnp_hdr(oskb);  	struct pnpipehdr *ph;  	struct sk_buff *skb; -	struct pep_sock *pn = pep_sk(sk); - -	static const u8 data[4] = { -		PAD, PAD, PAD, PAD, -	}; - -	switch (msg_id) { -	case PNS_PEP_CONNECT_REQ: -		len = sizeof(data); -		break; - -	case PNS_PEP_DISCONNECT_REQ: -	case PNS_PEP_ENABLE_REQ: -	case PNS_PEP_DISABLE_REQ: -		len = 0; -		break; +	struct sockaddr_pn peer; -	default: -		return -EINVAL; -	} - -	skb = alloc_skb(MAX_PNPIPE_HEADER + len, priority); +	skb = pep_alloc_skb(sk, data, len, priority);  	if (!skb)  		return -ENOMEM; -	skb_set_owner_w(skb, sk); -	skb_reserve(skb, MAX_PNPIPE_HEADER); -	if (len) { -		__skb_put(skb, len); -		skb_copy_to_linear_data(skb, data, len); -	} -	__skb_push(skb, sizeof(*ph)); -	skb_reset_transport_header(skb);  	ph = pnp_hdr(skb); -	ph->utid = utid; -	ph->message_id = msg_id; -	ph->pipe_handle = pn->pipe_handle; -	ph->error_code = PN_PIPE_NO_ERROR; +	ph->utid = oph->utid; +	ph->message_id = oph->message_id + 1; /* REQ -> RESP */ +	ph->pipe_handle = oph->pipe_handle; +	ph->error_code = code; -	return pn_skb_send(sk, skb, &pn->remote_pep); +	pn_skb_get_src_sockaddr(oskb, &peer); +	return pn_skb_send(sk, skb, &peer);  } -static int pipe_handler_send_created_ind(struct sock *sk, -		u8 utid, u8 msg_id) +static int pep_indicate(struct sock *sk, u8 id, u8 code, +			const void *data, int len, gfp_t priority)  { -	int err_code; +	struct pep_sock *pn = pep_sk(sk);  	struct pnpipehdr *ph;  	struct sk_buff *skb; -	struct pep_sock *pn = pep_sk(sk); -	static u8 data[4] = { -		0x03, 0x04, -	}; -	data[2] = pn->tx_fc; -	data[3] = pn->rx_fc; - -	/* -	 * actually, below is number of sub-blocks and not error code. -	 * Pipe_created_ind message format does not have any -	 * error code field. However, the Phonet stack will always send -	 * an error code as part of pnpipehdr. So, use that err_code to -	 * specify the number of sub-blocks. -	 */ -	err_code = 0x01; - -	skb = alloc_skb(MAX_PNPIPE_HEADER + sizeof(data), GFP_ATOMIC); +	skb = pep_alloc_skb(sk, data, len, priority);  	if (!skb)  		return -ENOMEM; -	skb_set_owner_w(skb, sk); -	skb_reserve(skb, MAX_PNPIPE_HEADER); -	__skb_put(skb, sizeof(data)); -	skb_copy_to_linear_data(skb, data, sizeof(data)); -	__skb_push(skb, sizeof(*ph)); -	skb_reset_transport_header(skb);  	ph = pnp_hdr(skb); -	ph->utid = utid; -	ph->message_id = msg_id; +	ph->utid = 0; +	ph->message_id = id;  	ph->pipe_handle = pn->pipe_handle; -	ph->error_code = err_code; - -	return pn_skb_send(sk, skb, &pn->remote_pep); +	ph->data[0] = code; +	return pn_skb_send(sk, skb, NULL);  } -static int pipe_handler_send_ind(struct sock *sk, u8 utid, u8 msg_id) +#define PAD 0x00 + +static int pipe_handler_request(struct sock *sk, u8 id, u8 code, +				const void *data, int len)  { -	int err_code; +	struct pep_sock *pn = pep_sk(sk);  	struct pnpipehdr *ph;  	struct sk_buff *skb; -	struct pep_sock *pn = pep_sk(sk); -	/* -	 * actually, below is a filler. -	 * Pipe_enabled/disabled_ind message format does not have any -	 * error code field. However, the Phonet stack will always send -	 * an error code as part of pnpipehdr. So, use that err_code to -	 * specify the filler value. -	 */ -	err_code = 0x0; - -	skb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC); +	skb = pep_alloc_skb(sk, data, len, GFP_KERNEL);  	if (!skb)  		return -ENOMEM; -	skb_set_owner_w(skb, sk); -	skb_reserve(skb, MAX_PNPIPE_HEADER); -	__skb_push(skb, sizeof(*ph)); -	skb_reset_transport_header(skb);  	ph = pnp_hdr(skb); -	ph->utid = utid; -	ph->message_id = msg_id; +	ph->utid = id; /* whatever */ +	ph->message_id = id;  	ph->pipe_handle = pn->pipe_handle; -	ph->error_code = err_code; - -	return pn_skb_send(sk, skb, &pn->remote_pep); +	ph->data[0] = code; +	return pn_skb_send(sk, skb, NULL);  } -static int pipe_handler_enable_pipe(struct sock *sk, int enable) +static int pipe_handler_send_created_ind(struct sock *sk)  { -	int utid, req; - -	if (enable) { -		utid = PNS_PIPE_ENABLE_UTID; -		req = PNS_PEP_ENABLE_REQ; -	} else { -		utid = PNS_PIPE_DISABLE_UTID; -		req = PNS_PEP_DISABLE_REQ; -	} -	return pipe_handler_send_req(sk, utid, req, GFP_ATOMIC); +	struct pep_sock *pn = pep_sk(sk); +	u8 data[4] = { +		PN_PIPE_SB_NEGOTIATED_FC, pep_sb_size(2), +		pn->tx_fc, pn->rx_fc, +	}; + +	return pep_indicate(sk, PNS_PIPE_CREATED_IND, 1 /* sub-blocks */, +				data, 4, GFP_ATOMIC);  } -#endif  static int pep_accept_conn(struct sock *sk, struct sk_buff *skb)  { @@ -334,11 +189,12 @@ static int pep_accept_conn(struct sock *sk, struct sk_buff *skb)  				GFP_KERNEL);  } -static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code) +static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code, +				gfp_t priority)  {  	static const u8 data[4] = { PAD, PAD, PAD, 0 /* sub-blocks */ };  	WARN_ON(code == PN_PIPE_NO_ERROR); -	return pep_reply(sk, skb, code, data, sizeof(data), GFP_ATOMIC); +	return pep_reply(sk, skb, code, data, sizeof(data), priority);  }  /* Control requests are not sent by the pipe service and have a specific @@ -350,23 +206,21 @@ static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code,  	struct sk_buff *skb;  	struct pnpipehdr *ph;  	struct sockaddr_pn dst; +	u8 data[4] = { +		oph->data[0], /* PEP type */ +		code, /* error code, at an unusual offset */ +		PAD, PAD, +	}; -	skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority); +	skb = pep_alloc_skb(sk, data, 4, priority);  	if (!skb)  		return -ENOMEM; -	skb_set_owner_w(skb, sk); - -	skb_reserve(skb, MAX_PHONET_HEADER); -	ph = (struct pnpipehdr *)skb_put(skb, sizeof(*ph) + 4); +	ph = pnp_hdr(skb);  	ph->utid = oph->utid;  	ph->message_id = PNS_PEP_CTRL_RESP;  	ph->pipe_handle = oph->pipe_handle;  	ph->data[0] = oph->data[1]; /* CTRL id */ -	ph->data[1] = oph->data[0]; /* PEP type */ -	ph->data[2] = code; /* error code, at an usual offset */ -	ph->data[3] = PAD; -	ph->data[4] = PAD;  	pn_skb_get_src_sockaddr(oskb, &dst);  	return pn_skb_send(sk, skb, &dst); @@ -374,38 +228,15 @@ static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code,  static int pipe_snd_status(struct sock *sk, u8 type, u8 status, gfp_t priority)  { -	struct pep_sock *pn = pep_sk(sk); -	struct pnpipehdr *ph; -	struct sk_buff *skb; +	u8 data[4] = { type, PAD, PAD, status }; -	skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority); -	if (!skb) -		return -ENOMEM; -	skb_set_owner_w(skb, sk); - -	skb_reserve(skb, MAX_PNPIPE_HEADER + 4); -	__skb_push(skb, sizeof(*ph) + 4); -	skb_reset_transport_header(skb); -	ph = pnp_hdr(skb); -	ph->utid = 0; -	ph->message_id = PNS_PEP_STATUS_IND; -	ph->pipe_handle = pn->pipe_handle; -	ph->pep_type = PN_PEP_TYPE_COMMON; -	ph->data[1] = type; -	ph->data[2] = PAD; -	ph->data[3] = PAD; -	ph->data[4] = status; - -#ifdef CONFIG_PHONET_PIPECTRLR -	return pn_skb_send(sk, skb, &pn->remote_pep); -#else -	return pn_skb_send(sk, skb, &pipe_srv); -#endif +	return pep_indicate(sk, PNS_PEP_STATUS_IND, PN_PEP_TYPE_COMMON, +				data, 4, priority);  }  /* Send our RX flow control information to the sender.   * Socket must be locked. */ -static void pipe_grant_credits(struct sock *sk) +static void pipe_grant_credits(struct sock *sk, gfp_t priority)  {  	struct pep_sock *pn = pep_sk(sk); @@ -415,16 +246,16 @@ static void pipe_grant_credits(struct sock *sk)  	case PN_LEGACY_FLOW_CONTROL: /* TODO */  		break;  	case PN_ONE_CREDIT_FLOW_CONTROL: -		pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL, -				PEP_IND_READY, GFP_ATOMIC); -		pn->rx_credits = 1; +		if (pipe_snd_status(sk, PN_PEP_IND_FLOW_CONTROL, +					PEP_IND_READY, priority) == 0) +			pn->rx_credits = 1;  		break;  	case PN_MULTI_CREDIT_FLOW_CONTROL:  		if ((pn->rx_credits + CREDITS_THR) > CREDITS_MAX)  			break;  		if (pipe_snd_status(sk, PN_PEP_IND_ID_MCFC_GRANT_CREDITS,  					CREDITS_MAX - pn->rx_credits, -					GFP_ATOMIC) == 0) +					priority) == 0)  			pn->rx_credits = CREDITS_MAX;  		break;  	} @@ -442,7 +273,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)  	hdr = pnp_hdr(skb);  	if (hdr->data[0] != PN_PEP_TYPE_COMMON) {  		LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP type: %u\n", -				(unsigned)hdr->data[0]); +				(unsigned int)hdr->data[0]);  		return -EOPNOTSUPP;  	} @@ -474,7 +305,7 @@ static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)  	default:  		LIMIT_NETDEBUG(KERN_DEBUG"Phonet unknown PEP indication: %u\n", -				(unsigned)hdr->data[1]); +				(unsigned int)hdr->data[1]);  		return -EOPNOTSUPP;  	}  	if (wake) @@ -522,7 +353,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)  	switch (hdr->message_id) {  	case PNS_PEP_CONNECT_REQ: -		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); +		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_ATOMIC);  		break;  	case PNS_PEP_DISCONNECT_REQ: @@ -532,35 +363,11 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)  			sk->sk_state_change(sk);  		break; -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNS_PEP_DISCONNECT_RESP: -		pn->pipe_state = PIPE_IDLE; -		sk->sk_state = TCP_CLOSE; -		break; -#endif -  	case PNS_PEP_ENABLE_REQ:  		/* Wait for PNS_PIPE_(ENABLED|REDIRECTED)_IND */  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);  		break; -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNS_PEP_ENABLE_RESP: -		pn->pipe_state = PIPE_ENABLED; -		pipe_handler_send_ind(sk, PNS_PIPE_ENABLED_IND_UTID, -				PNS_PIPE_ENABLED_IND); - -		if (!pn_flow_safe(pn->tx_fc)) { -			atomic_set(&pn->tx_credits, 1); -			sk->sk_write_space(sk); -		} -		if (sk->sk_state == TCP_ESTABLISHED) -			break; /* Nothing to do */ -		sk->sk_state = TCP_ESTABLISHED; -		pipe_grant_credits(sk); -		break; -#endif -  	case PNS_PEP_RESET_REQ:  		switch (hdr->state_after_reset) {  		case PN_PIPE_DISABLE: @@ -579,17 +386,6 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);  		break; -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNS_PEP_DISABLE_RESP: -		pn->pipe_state = PIPE_DISABLED; -		atomic_set(&pn->tx_credits, 0); -		pipe_handler_send_ind(sk, PNS_PIPE_DISABLED_IND_UTID, -				PNS_PIPE_DISABLED_IND); -		sk->sk_state = TCP_SYN_RECV; -		pn->rx_credits = 0; -		break; -#endif -  	case PNS_PEP_CTRL_REQ:  		if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX) {  			atomic_inc(&sk->sk_drops); @@ -607,7 +403,8 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)  		if (!pn_flow_safe(pn->rx_fc)) {  			err = sock_queue_rcv_skb(sk, skb);  			if (!err) -				return 0; +				return NET_RX_SUCCESS; +			err = -ENOBUFS;  			break;  		} @@ -645,7 +442,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)  		if (sk->sk_state == TCP_ESTABLISHED)  			break; /* Nothing to do */  		sk->sk_state = TCP_ESTABLISHED; -		pipe_grant_credits(sk); +		pipe_grant_credits(sk, GFP_ATOMIC);  		break;  	case PNS_PIPE_DISABLED_IND: @@ -660,16 +457,15 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)  	}  out:  	kfree_skb(skb); -	return err; +	return (err == -ENOBUFS) ? NET_RX_DROP : NET_RX_SUCCESS;  queue:  	skb->dev = NULL;  	skb_set_owner_r(skb, sk); -	err = skb->len;  	skb_queue_tail(queue, skb);  	if (!sock_flag(sk, SOCK_DEAD)) -		sk->sk_data_ready(sk, err); -	return 0; +		sk->sk_data_ready(sk); +	return NET_RX_SUCCESS;  }  /* Destroy connected sock. */ @@ -681,133 +477,160 @@ static void pipe_destruct(struct sock *sk)  	skb_queue_purge(&pn->ctrlreq_queue);  } -#ifdef CONFIG_PHONET_PIPECTRLR -static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb) +static u8 pipe_negotiate_fc(const u8 *fcs, unsigned int n)  { -	struct pep_sock *pn = pep_sk(sk); -	u8 host_pref_rx_fc[3] = {3, 2, 1}, host_req_tx_fc[3] = {3, 2, 1}; -	u8 remote_pref_rx_fc[3], remote_req_tx_fc[3]; -	u8 negotiated_rx_fc, negotiated_tx_fc; -	int ret; - -	pipe_get_flow_info(sk, skb, remote_pref_rx_fc, -			remote_req_tx_fc); -	negotiated_tx_fc = pipe_negotiate_fc(remote_req_tx_fc, -			host_pref_rx_fc, -			sizeof(host_pref_rx_fc)); -	negotiated_rx_fc = pipe_negotiate_fc(host_req_tx_fc, -			remote_pref_rx_fc, -			sizeof(host_pref_rx_fc)); - -	pn->pipe_state = PIPE_DISABLED; -	sk->sk_state = TCP_SYN_RECV; -	sk->sk_backlog_rcv = pipe_do_rcv; -	sk->sk_destruct = pipe_destruct; -	pn->rx_credits = 0; -	pn->rx_fc = negotiated_rx_fc; -	pn->tx_fc = negotiated_tx_fc; -	sk->sk_state_change(sk); +	unsigned int i; +	u8 final_fc = PN_NO_FLOW_CONTROL; -	ret = pipe_handler_send_created_ind(sk, -			PNS_PIPE_CREATED_IND_UTID, -			PNS_PIPE_CREATED_IND -			); +	for (i = 0; i < n; i++) { +		u8 fc = fcs[i]; -	return ret; +		if (fc > final_fc && fc < PN_MAX_FLOW_CONTROL) +			final_fc = fc; +	} +	return final_fc;  } -#endif -static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb) +static int pep_connresp_rcv(struct sock *sk, struct sk_buff *skb)  { -	struct sock *newsk; -	struct pep_sock *newpn, *pn = pep_sk(sk); +	struct pep_sock *pn = pep_sk(sk);  	struct pnpipehdr *hdr; -	struct sockaddr_pn dst; -	u16 peer_type; -	u8 pipe_handle, enabled, n_sb; -	u8 aligned = 0; +	u8 n_sb;  	if (!pskb_pull(skb, sizeof(*hdr) + 4))  		return -EINVAL;  	hdr = pnp_hdr(skb); -	pipe_handle = hdr->pipe_handle; -	switch (hdr->state_after_connect) { -	case PN_PIPE_DISABLE: -		enabled = 0; -		break; -	case PN_PIPE_ENABLE: -		enabled = 1; -		break; -	default: -		pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM); -		return -EINVAL; -	} -	peer_type = hdr->other_pep_type << 8; +	if (hdr->error_code != PN_PIPE_NO_ERROR) +		return -ECONNREFUSED; -	if (unlikely(sk->sk_state != TCP_LISTEN) || sk_acceptq_is_full(sk)) { -		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE); -		return -ENOBUFS; -	} - -	/* Parse sub-blocks (options) */ +	/* Parse sub-blocks */  	n_sb = hdr->data[4];  	while (n_sb > 0) { -		u8 type, buf[1], len = sizeof(buf); +		u8 type, buf[6], len = sizeof(buf);  		const u8 *data = pep_get_sb(skb, &type, &len, buf);  		if (data == NULL)  			return -EINVAL; +  		switch (type) { -		case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: -			if (len < 1) -				return -EINVAL; -			peer_type = (peer_type & 0xff00) | data[0]; +		case PN_PIPE_SB_REQUIRED_FC_TX: +			if (len < 2 || len < data[0]) +				break; +			pn->tx_fc = pipe_negotiate_fc(data + 2, len - 2);  			break; -		case PN_PIPE_SB_ALIGNED_DATA: -			aligned = data[0] != 0; + +		case PN_PIPE_SB_PREFERRED_FC_RX: +			if (len < 2 || len < data[0]) +				break; +			pn->rx_fc = pipe_negotiate_fc(data + 2, len - 2);  			break; +  		}  		n_sb--;  	} -	skb = skb_clone(skb, GFP_ATOMIC); -	if (!skb) -		return -ENOMEM; +	return pipe_handler_send_created_ind(sk); +} -	/* Create a new to-be-accepted sock */ -	newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_ATOMIC, sk->sk_prot); -	if (!newsk) { -		kfree_skb(skb); -		return -ENOMEM; +static int pep_enableresp_rcv(struct sock *sk, struct sk_buff *skb) +{ +	struct pnpipehdr *hdr = pnp_hdr(skb); + +	if (hdr->error_code != PN_PIPE_NO_ERROR) +		return -ECONNREFUSED; + +	return pep_indicate(sk, PNS_PIPE_ENABLED_IND, 0 /* sub-blocks */, +		NULL, 0, GFP_ATOMIC); + +} + +static void pipe_start_flow_control(struct sock *sk) +{ +	struct pep_sock *pn = pep_sk(sk); + +	if (!pn_flow_safe(pn->tx_fc)) { +		atomic_set(&pn->tx_credits, 1); +		sk->sk_write_space(sk);  	} -	sock_init_data(NULL, newsk); -	newsk->sk_state = TCP_SYN_RECV; -	newsk->sk_backlog_rcv = pipe_do_rcv; -	newsk->sk_protocol = sk->sk_protocol; -	newsk->sk_destruct = pipe_destruct; +	pipe_grant_credits(sk, GFP_ATOMIC); +} -	newpn = pep_sk(newsk); -	pn_skb_get_dst_sockaddr(skb, &dst); -	newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); -	newpn->pn_sk.resource = pn->pn_sk.resource; -	skb_queue_head_init(&newpn->ctrlreq_queue); -	newpn->pipe_handle = pipe_handle; -	atomic_set(&newpn->tx_credits, 0); -	newpn->peer_type = peer_type; -	newpn->rx_credits = 0; -	newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; -	newpn->init_enable = enabled; -	newpn->aligned = aligned; +/* Queue an skb to an actively connected sock. + * Socket lock must be held. */ +static int pipe_handler_do_rcv(struct sock *sk, struct sk_buff *skb) +{ +	struct pep_sock *pn = pep_sk(sk); +	struct pnpipehdr *hdr = pnp_hdr(skb); +	int err = NET_RX_SUCCESS; -	BUG_ON(!skb_queue_empty(&newsk->sk_receive_queue)); -	skb_queue_head(&newsk->sk_receive_queue, skb); -	if (!sock_flag(sk, SOCK_DEAD)) -		sk->sk_data_ready(sk, 0); +	switch (hdr->message_id) { +	case PNS_PIPE_ALIGNED_DATA: +		__skb_pull(skb, 1); +		/* fall through */ +	case PNS_PIPE_DATA: +		__skb_pull(skb, 3); /* Pipe data header */ +		if (!pn_flow_safe(pn->rx_fc)) { +			err = sock_queue_rcv_skb(sk, skb); +			if (!err) +				return NET_RX_SUCCESS; +			err = NET_RX_DROP; +			break; +		} -	sk_acceptq_added(sk); -	sk_add_node(newsk, &pn->ackq); -	return 0; +		if (pn->rx_credits == 0) { +			atomic_inc(&sk->sk_drops); +			err = NET_RX_DROP; +			break; +		} +		pn->rx_credits--; +		skb->dev = NULL; +		skb_set_owner_r(skb, sk); +		skb_queue_tail(&sk->sk_receive_queue, skb); +		if (!sock_flag(sk, SOCK_DEAD)) +			sk->sk_data_ready(sk); +		return NET_RX_SUCCESS; + +	case PNS_PEP_CONNECT_RESP: +		if (sk->sk_state != TCP_SYN_SENT) +			break; +		if (!sock_flag(sk, SOCK_DEAD)) +			sk->sk_state_change(sk); +		if (pep_connresp_rcv(sk, skb)) { +			sk->sk_state = TCP_CLOSE_WAIT; +			break; +		} +		if (pn->init_enable == PN_PIPE_DISABLE) +			sk->sk_state = TCP_SYN_RECV; +		else { +			sk->sk_state = TCP_ESTABLISHED; +			pipe_start_flow_control(sk); +		} +		break; + +	case PNS_PEP_ENABLE_RESP: +		if (sk->sk_state != TCP_SYN_SENT) +			break; + +		if (pep_enableresp_rcv(sk, skb)) { +			sk->sk_state = TCP_CLOSE_WAIT; +			break; +		} + +		sk->sk_state = TCP_ESTABLISHED; +		pipe_start_flow_control(sk); +		break; + +	case PNS_PEP_DISCONNECT_RESP: +		/* sock should already be dead, nothing to do */ +		break; + +	case PNS_PEP_STATUS_IND: +		pipe_rcv_status(sk, skb); +		break; +	} +	kfree_skb(skb); +	return err;  }  /* Listening sock must be locked */ @@ -815,11 +638,10 @@ static struct sock *pep_find_pipe(const struct hlist_head *hlist,  					const struct sockaddr_pn *dst,  					u8 pipe_handle)  { -	struct hlist_node *node;  	struct sock *sknode;  	u16 dobj = pn_sockaddr_get_object(dst); -	sk_for_each(sknode, node, hlist) { +	sk_for_each(sknode, hlist) {  		struct pep_sock *pnnode = pep_sk(sknode);  		/* Ports match, but addresses might not: */ @@ -847,7 +669,6 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)  	struct sock *sknode;  	struct pnpipehdr *hdr;  	struct sockaddr_pn dst; -	int err = NET_RX_SUCCESS;  	u8 pipe_handle;  	if (!pskb_may_pull(skb, sizeof(*hdr))) @@ -865,26 +686,18 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)  	if (sknode)  		return sk_receive_skb(sknode, skb, 1); -	/* Look for a pipe handle pending accept */ -	sknode = pep_find_pipe(&pn->ackq, &dst, pipe_handle); -	if (sknode) { -		sock_put(sknode); -		if (net_ratelimit()) -			printk(KERN_WARNING"Phonet unconnected PEP ignored"); -		err = NET_RX_DROP; -		goto drop; -	} -  	switch (hdr->message_id) {  	case PNS_PEP_CONNECT_REQ: -		err = pep_connreq_rcv(sk, skb); -		break; - -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNS_PEP_CONNECT_RESP: -		err = pep_connresp_rcv(sk, skb); -		break; -#endif +		if (sk->sk_state != TCP_LISTEN || sk_acceptq_is_full(sk)) { +			pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, +					GFP_ATOMIC); +			break; +		} +		skb_queue_head(&sk->sk_receive_queue, skb); +		sk_acceptq_added(sk); +		if (!sock_flag(sk, SOCK_DEAD)) +			sk->sk_data_ready(sk); +		return NET_RX_SUCCESS;  	case PNS_PEP_DISCONNECT_REQ:  		pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC); @@ -898,12 +711,17 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)  	case PNS_PEP_ENABLE_REQ:  	case PNS_PEP_DISABLE_REQ:  		/* invalid handle is not even allowed here! */ +		break; +  	default: -		err = NET_RX_DROP; +		if ((1 << sk->sk_state) +				& ~(TCPF_CLOSE|TCPF_LISTEN|TCPF_CLOSE_WAIT)) +			/* actively connected socket */ +			return pipe_handler_do_rcv(sk, skb);  	}  drop:  	kfree_skb(skb); -	return err; +	return NET_RX_SUCCESS;  }  static int pipe_do_remove(struct sock *sk) @@ -912,20 +730,16 @@ static int pipe_do_remove(struct sock *sk)  	struct pnpipehdr *ph;  	struct sk_buff *skb; -	skb = alloc_skb(MAX_PNPIPE_HEADER, GFP_KERNEL); +	skb = pep_alloc_skb(sk, NULL, 0, GFP_KERNEL);  	if (!skb)  		return -ENOMEM; -	skb_reserve(skb, MAX_PNPIPE_HEADER); -	__skb_push(skb, sizeof(*ph)); -	skb_reset_transport_header(skb);  	ph = pnp_hdr(skb);  	ph->utid = 0;  	ph->message_id = PNS_PIPE_REMOVE_REQ;  	ph->pipe_handle = pn->pipe_handle;  	ph->data[0] = PAD; - -	return pn_skb_send(sk, skb, &pipe_srv); +	return pn_skb_send(sk, skb, NULL);  }  /* associated socket ceases to exist */ @@ -938,29 +752,15 @@ static void pep_sock_close(struct sock *sk, long timeout)  	sk_common_release(sk);  	lock_sock(sk); -	if (sk->sk_state == TCP_LISTEN) { -		/* Destroy the listen queue */ -		struct sock *sknode; -		struct hlist_node *p, *n; - -		sk_for_each_safe(sknode, p, n, &pn->ackq) -			sk_del_node_init(sknode); -		sk->sk_state = TCP_CLOSE; -	} else if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) -		/* Forcefully remove dangling Phonet pipe */ -		pipe_do_remove(sk); - -#ifdef CONFIG_PHONET_PIPECTRLR -	if (pn->pipe_state != PIPE_IDLE) { -		/* send pep disconnect request */ -		pipe_handler_send_req(sk, -				PNS_PEP_DISCONNECT_UTID, PNS_PEP_DISCONNECT_REQ, -				GFP_KERNEL); - -		pn->pipe_state = PIPE_IDLE; -		sk->sk_state = TCP_CLOSE; +	if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) { +		if (sk->sk_backlog_rcv == pipe_do_rcv) +			/* Forcefully remove dangling Phonet pipe */ +			pipe_do_remove(sk); +		else +			pipe_handler_request(sk, PNS_PEP_DISCONNECT_REQ, PAD, +						NULL, 0);  	} -#endif +	sk->sk_state = TCP_CLOSE;  	ifindex = pn->ifindex;  	pn->ifindex = 0; @@ -971,96 +771,172 @@ static void pep_sock_close(struct sock *sk, long timeout)  	sock_put(sk);  } -static int pep_wait_connreq(struct sock *sk, int noblock) +static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp)  { -	struct task_struct *tsk = current; -	struct pep_sock *pn = pep_sk(sk); -	long timeo = sock_rcvtimeo(sk, noblock); - -	for (;;) { -		DEFINE_WAIT(wait); +	struct pep_sock *pn = pep_sk(sk), *newpn; +	struct sock *newsk = NULL; +	struct sk_buff *skb; +	struct pnpipehdr *hdr; +	struct sockaddr_pn dst, src; +	int err; +	u16 peer_type; +	u8 pipe_handle, enabled, n_sb; +	u8 aligned = 0; -		if (sk->sk_state != TCP_LISTEN) -			return -EINVAL; -		if (!hlist_empty(&pn->ackq)) -			break; -		if (!timeo) -			return -EWOULDBLOCK; -		if (signal_pending(tsk)) -			return sock_intr_errno(timeo); +	skb = skb_recv_datagram(sk, 0, flags & O_NONBLOCK, errp); +	if (!skb) +		return NULL; -		prepare_to_wait_exclusive(sk_sleep(sk), &wait, -						TASK_INTERRUPTIBLE); -		release_sock(sk); -		timeo = schedule_timeout(timeo); -		lock_sock(sk); -		finish_wait(sk_sleep(sk), &wait); +	lock_sock(sk); +	if (sk->sk_state != TCP_LISTEN) { +		err = -EINVAL; +		goto drop;  	} +	sk_acceptq_removed(sk); -	return 0; -} +	err = -EPROTO; +	if (!pskb_may_pull(skb, sizeof(*hdr) + 4)) +		goto drop; -static struct sock *pep_sock_accept(struct sock *sk, int flags, int *errp) -{ -	struct pep_sock *pn = pep_sk(sk); -	struct sock *newsk = NULL; -	struct sk_buff *oskb; -	int err; +	hdr = pnp_hdr(skb); +	pipe_handle = hdr->pipe_handle; +	switch (hdr->state_after_connect) { +	case PN_PIPE_DISABLE: +		enabled = 0; +		break; +	case PN_PIPE_ENABLE: +		enabled = 1; +		break; +	default: +		pep_reject_conn(sk, skb, PN_PIPE_ERR_INVALID_PARAM, +				GFP_KERNEL); +		goto drop; +	} +	peer_type = hdr->other_pep_type << 8; -	lock_sock(sk); -	err = pep_wait_connreq(sk, flags & O_NONBLOCK); -	if (err) -		goto out; +	/* Parse sub-blocks (options) */ +	n_sb = hdr->data[4]; +	while (n_sb > 0) { +		u8 type, buf[1], len = sizeof(buf); +		const u8 *data = pep_get_sb(skb, &type, &len, buf); -	newsk = __sk_head(&pn->ackq); +		if (data == NULL) +			goto drop; +		switch (type) { +		case PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE: +			if (len < 1) +				goto drop; +			peer_type = (peer_type & 0xff00) | data[0]; +			break; +		case PN_PIPE_SB_ALIGNED_DATA: +			aligned = data[0] != 0; +			break; +		} +		n_sb--; +	} -	oskb = skb_dequeue(&newsk->sk_receive_queue); -	err = pep_accept_conn(newsk, oskb); -	if (err) { -		skb_queue_head(&newsk->sk_receive_queue, oskb); +	/* Check for duplicate pipe handle */ +	newsk = pep_find_pipe(&pn->hlist, &dst, pipe_handle); +	if (unlikely(newsk)) { +		__sock_put(newsk);  		newsk = NULL; -		goto out; +		pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE, GFP_KERNEL); +		goto drop;  	} -	kfree_skb(oskb); +	/* Create a new to-be-accepted sock */ +	newsk = sk_alloc(sock_net(sk), PF_PHONET, GFP_KERNEL, sk->sk_prot); +	if (!newsk) { +		pep_reject_conn(sk, skb, PN_PIPE_ERR_OVERLOAD, GFP_KERNEL); +		err = -ENOBUFS; +		goto drop; +	} + +	sock_init_data(NULL, newsk); +	newsk->sk_state = TCP_SYN_RECV; +	newsk->sk_backlog_rcv = pipe_do_rcv; +	newsk->sk_protocol = sk->sk_protocol; +	newsk->sk_destruct = pipe_destruct; + +	newpn = pep_sk(newsk); +	pn_skb_get_dst_sockaddr(skb, &dst); +	pn_skb_get_src_sockaddr(skb, &src); +	newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst); +	newpn->pn_sk.dobject = pn_sockaddr_get_object(&src); +	newpn->pn_sk.resource = pn_sockaddr_get_resource(&dst);  	sock_hold(sk); -	pep_sk(newsk)->listener = sk; +	newpn->listener = sk; +	skb_queue_head_init(&newpn->ctrlreq_queue); +	newpn->pipe_handle = pipe_handle; +	atomic_set(&newpn->tx_credits, 0); +	newpn->ifindex = 0; +	newpn->peer_type = peer_type; +	newpn->rx_credits = 0; +	newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL; +	newpn->init_enable = enabled; +	newpn->aligned = aligned; -	sock_hold(newsk); -	sk_del_node_init(newsk); -	sk_acceptq_removed(sk); +	err = pep_accept_conn(newsk, skb); +	if (err) { +		sock_put(newsk); +		newsk = NULL; +		goto drop; +	}  	sk_add_node(newsk, &pn->hlist); -	__sock_put(newsk); - -out: +drop:  	release_sock(sk); +	kfree_skb(skb);  	*errp = err;  	return newsk;  } -#ifdef CONFIG_PHONET_PIPECTRLR  static int pep_sock_connect(struct sock *sk, struct sockaddr *addr, int len)  {  	struct pep_sock *pn = pep_sk(sk); -	struct sockaddr_pn *spn =  (struct sockaddr_pn *)addr; +	int err; +	u8 data[4] = { 0 /* sub-blocks */, PAD, PAD, PAD }; + +	if (pn->pipe_handle == PN_PIPE_INVALID_HANDLE) +		pn->pipe_handle = 1; /* anything but INVALID_HANDLE */ + +	err = pipe_handler_request(sk, PNS_PEP_CONNECT_REQ, +				pn->init_enable, data, 4); +	if (err) { +		pn->pipe_handle = PN_PIPE_INVALID_HANDLE; +		return err; +	} + +	sk->sk_state = TCP_SYN_SENT; + +	return 0; +} + +static int pep_sock_enable(struct sock *sk, struct sockaddr *addr, int len) +{ +	int err; + +	err = pipe_handler_request(sk, PNS_PEP_ENABLE_REQ, PAD, +				NULL, 0); +	if (err) +		return err; -	memcpy(&pn->remote_pep, spn, sizeof(struct sockaddr_pn)); +	sk->sk_state = TCP_SYN_SENT; -	return pipe_handler_send_req(sk, -			PNS_PEP_CONNECT_UTID, PNS_PEP_CONNECT_REQ, -			GFP_ATOMIC); +	return 0;  } -#endif  static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)  {  	struct pep_sock *pn = pep_sk(sk);  	int answ; +	int ret = -ENOIOCTLCMD;  	switch (cmd) {  	case SIOCINQ: -		if (sk->sk_state == TCP_LISTEN) -			return -EINVAL; +		if (sk->sk_state == TCP_LISTEN) { +			ret = -EINVAL; +			break; +		}  		lock_sock(sk);  		if (sock_flag(sk, SOCK_URGINLINE) && @@ -1071,20 +947,40 @@ static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)  		else  			answ = 0;  		release_sock(sk); -		return put_user(answ, (int __user *)arg); +		ret = put_user(answ, (int __user *)arg); +		break; + +	case SIOCPNENABLEPIPE: +		lock_sock(sk); +		if (sk->sk_state == TCP_SYN_SENT) +			ret =  -EBUSY; +		else if (sk->sk_state == TCP_ESTABLISHED) +			ret = -EISCONN; +		else +			ret = pep_sock_enable(sk, NULL, 0); +		release_sock(sk); +		break;  	} -	return -ENOIOCTLCMD; +	return ret;  }  static int pep_init(struct sock *sk)  {  	struct pep_sock *pn = pep_sk(sk); -	INIT_HLIST_HEAD(&pn->ackq); +	sk->sk_destruct = pipe_destruct;  	INIT_HLIST_HEAD(&pn->hlist); +	pn->listener = NULL;  	skb_queue_head_init(&pn->ctrlreq_queue); +	atomic_set(&pn->tx_credits, 0); +	pn->ifindex = 0; +	pn->peer_type = 0;  	pn->pipe_handle = PN_PIPE_INVALID_HANDLE; +	pn->rx_credits = 0; +	pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL; +	pn->init_enable = 1; +	pn->aligned = 0;  	return 0;  } @@ -1103,18 +999,6 @@ static int pep_setsockopt(struct sock *sk, int level, int optname,  	lock_sock(sk);  	switch (optname) { -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNPIPE_PIPE_HANDLE: -		if (val) { -			if (pn->pipe_state > PIPE_IDLE) { -				err = -EFAULT; -				break; -			} -			pn->pipe_handle = val; -			break; -		} -#endif -  	case PNPIPE_ENCAP:  		if (val && val != PNPIPE_ENCAP_IP) {  			err = -EINVAL; @@ -1141,15 +1025,17 @@ static int pep_setsockopt(struct sock *sk, int level, int optname,  		}  		goto out_norel; -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNPIPE_ENABLE: -		if (pn->pipe_state <= PIPE_IDLE) { -			err = -ENOTCONN; -			break; -		} -		err = pipe_handler_enable_pipe(sk, val); +	case PNPIPE_HANDLE: +		if ((sk->sk_state == TCP_CLOSE) && +			(val >= 0) && (val < PN_PIPE_INVALID_HANDLE)) +			pn->pipe_handle = val; +		else +			err = -EINVAL; +		break; + +	case PNPIPE_INITSTATE: +		pn->init_enable = !!val;  		break; -#endif  	default:  		err = -ENOPROTOOPT; @@ -1180,13 +1066,15 @@ static int pep_getsockopt(struct sock *sk, int level, int optname,  		val = pn->ifindex;  		break; -#ifdef CONFIG_PHONET_PIPECTRLR -	case PNPIPE_ENABLE: -		if (pn->pipe_state <= PIPE_IDLE) -			return -ENOTCONN; -		val = pn->pipe_state != PIPE_DISABLED; +	case PNPIPE_HANDLE: +		val = pn->pipe_handle; +		if (val == PN_PIPE_INVALID_HANDLE) +			return -EINVAL; +		break; + +	case PNPIPE_INITSTATE: +		val = pn->init_enable;  		break; -#endif  	default:  		return -ENOPROTOOPT; @@ -1222,11 +1110,7 @@ static int pipe_skb_send(struct sock *sk, struct sk_buff *skb)  	} else  		ph->message_id = PNS_PIPE_DATA;  	ph->pipe_handle = pn->pipe_handle; -#ifdef CONFIG_PHONET_PIPECTRLR -	err = pn_skb_send(sk, skb, &pn->remote_pep); -#else -	err = pn_skb_send(sk, skb, &pipe_srv); -#endif +	err = pn_skb_send(sk, skb, NULL);  	if (err && pn_flow_safe(pn->tx_fc))  		atomic_inc(&pn->tx_credits); @@ -1243,6 +1127,9 @@ static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,  	int flags = msg->msg_flags;  	int err, done; +	if (len > USHRT_MAX) +		return -EMSGSIZE; +  	if ((msg->msg_flags & ~(MSG_DONTWAIT|MSG_EOR|MSG_NOSIGNAL|  				MSG_CMSG_COMPAT)) ||  			!(msg->msg_flags & MSG_EOR)) @@ -1253,7 +1140,7 @@ static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,  	if (!skb)  		return err; -	skb_reserve(skb, MAX_PHONET_HEADER + 3); +	skb_reserve(skb, MAX_PHONET_HEADER + 3 + pn->aligned);  	err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);  	if (err < 0)  		goto outfree; @@ -1355,7 +1242,7 @@ struct sk_buff *pep_read(struct sock *sk)  	struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue);  	if (sk->sk_state == TCP_ESTABLISHED) -		pipe_grant_credits(sk); +		pipe_grant_credits(sk, GFP_ATOMIC);  	return skb;  } @@ -1400,7 +1287,7 @@ static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,  	}  	if (sk->sk_state == TCP_ESTABLISHED) -		pipe_grant_credits(sk); +		pipe_grant_credits(sk, GFP_KERNEL);  	release_sock(sk);  copy:  	msg->msg_flags |= MSG_EOR; @@ -1424,9 +1311,9 @@ static void pep_sock_unhash(struct sock *sk)  	lock_sock(sk); -#ifndef CONFIG_PHONET_PIPECTRLR -	if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) { +	if (pn->listener != NULL) {  		skparent = pn->listener; +		pn->listener = NULL;  		release_sock(sk);  		pn = pep_sk(skparent); @@ -1434,7 +1321,7 @@ static void pep_sock_unhash(struct sock *sk)  		sk_del_node_init(sk);  		sk = skparent;  	} -#endif +  	/* Unhash a listening sock only when it is closed  	 * and all of its active connected pipes are closed. */  	if (hlist_empty(&pn->hlist)) @@ -1448,9 +1335,7 @@ static void pep_sock_unhash(struct sock *sk)  static struct proto pep_proto = {  	.close		= pep_sock_close,  	.accept		= pep_sock_accept, -#ifdef CONFIG_PHONET_PIPECTRLR  	.connect	= pep_sock_connect, -#endif  	.ioctl		= pep_ioctl,  	.init		= pep_init,  	.setsockopt	= pep_setsockopt, diff --git a/net/phonet/pn_dev.c b/net/phonet/pn_dev.c index 947038ddd04..56a6146ac94 100644 --- a/net/phonet/pn_dev.c +++ b/net/phonet/pn_dev.c @@ -5,8 +5,8 @@   *   * Copyright (C) 2008 Nokia Corporation.   * - * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> - * Original author: Sakari Ailus <sakari.ailus@nokia.com> + * Authors: Sakari Ailus <sakari.ailus@nokia.com> + *          Rémi Denis-Courmont   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -44,7 +44,7 @@ struct phonet_net {  	struct phonet_routes routes;  }; -int phonet_net_id __read_mostly; +static int phonet_net_id __read_mostly;  static struct phonet_net *phonet_pernet(struct net *net)  { @@ -162,14 +162,6 @@ int phonet_address_add(struct net_device *dev, u8 addr)  	return err;  } -static void phonet_device_rcu_free(struct rcu_head *head) -{ -	struct phonet_device *pnd; - -	pnd = container_of(head, struct phonet_device, rcu); -	kfree(pnd); -} -  int phonet_address_del(struct net_device *dev, u8 addr)  {  	struct phonet_device_list *pndevs = phonet_device_list(dev_net(dev)); @@ -188,7 +180,7 @@ int phonet_address_del(struct net_device *dev, u8 addr)  	mutex_unlock(&pndevs->lock);  	if (pnd) -		call_rcu(&pnd->rcu, phonet_device_rcu_free); +		kfree_rcu(pnd, rcu);  	return err;  } @@ -276,7 +268,7 @@ static int phonet_device_autoconf(struct net_device *dev)  static void phonet_route_autodel(struct net_device *dev)  {  	struct phonet_net *pnn = phonet_pernet(dev_net(dev)); -	unsigned i; +	unsigned int i;  	DECLARE_BITMAP(deleted, 64);  	/* Remove left-over Phonet routes */ @@ -284,7 +276,7 @@ static void phonet_route_autodel(struct net_device *dev)  	mutex_lock(&pnn->routes.lock);  	for (i = 0; i < 64; i++)  		if (dev == pnn->routes.table[i]) { -			rcu_assign_pointer(pnn->routes.table[i], NULL); +			RCU_INIT_POINTER(pnn->routes.table[i], NULL);  			set_bit(i, deleted);  		}  	mutex_unlock(&pnn->routes.lock); @@ -300,9 +292,9 @@ static void phonet_route_autodel(struct net_device *dev)  /* notify Phonet of device events */  static int phonet_device_notify(struct notifier_block *me, unsigned long what, -				void *arg) +				void *ptr)  { -	struct net_device *dev = arg; +	struct net_device *dev = netdev_notifier_info_to_dev(ptr);  	switch (what) {  	case NETDEV_REGISTER: @@ -328,7 +320,7 @@ static int __net_init phonet_init_net(struct net *net)  {  	struct phonet_net *pnn = phonet_pernet(net); -	if (!proc_net_fops_create(net, "phonet", 0, &pn_sock_seq_fops)) +	if (!proc_create("phonet", 0, net->proc_net, &pn_sock_seq_fops))  		return -ENOMEM;  	INIT_LIST_HEAD(&pnn->pndevs.list); @@ -339,24 +331,7 @@ static int __net_init phonet_init_net(struct net *net)  static void __net_exit phonet_exit_net(struct net *net)  { -	struct phonet_net *pnn = phonet_pernet(net); -	struct net_device *dev; -	unsigned i; - -	rtnl_lock(); -	for_each_netdev(net, dev) -		phonet_device_destroy(dev); - -	for (i = 0; i < 64; i++) { -		dev = pnn->routes.table[i]; -		if (dev) { -			rtm_phonet_notify(RTM_DELROUTE, dev, i); -			dev_put(dev); -		} -	} -	rtnl_unlock(); - -	proc_net_remove(net, "phonet"); +	remove_proc_entry("phonet", net->proc_net);  }  static struct pernet_operations phonet_net_ops = { @@ -369,11 +344,11 @@ static struct pernet_operations phonet_net_ops = {  /* Initialize Phonet devices list */  int __init phonet_device_init(void)  { -	int err = register_pernet_device(&phonet_net_ops); +	int err = register_pernet_subsys(&phonet_net_ops);  	if (err)  		return err; -	proc_net_fops_create(&init_net, "pnresource", 0, &pn_res_seq_fops); +	proc_create("pnresource", 0, init_net.proc_net, &pn_res_seq_fops);  	register_netdevice_notifier(&phonet_device_notifier);  	err = phonet_netlink_register();  	if (err) @@ -385,8 +360,8 @@ void phonet_device_exit(void)  {  	rtnl_unregister_all(PF_PHONET);  	unregister_netdevice_notifier(&phonet_device_notifier); -	unregister_pernet_device(&phonet_net_ops); -	proc_net_remove(&init_net, "pnresource"); +	unregister_pernet_subsys(&phonet_net_ops); +	remove_proc_entry("pnresource", init_net.proc_net);  }  int phonet_route_add(struct net_device *dev, u8 daddr) @@ -414,7 +389,7 @@ int phonet_route_del(struct net_device *dev, u8 daddr)  	daddr = daddr >> 2;  	mutex_lock(&routes->lock);  	if (dev == routes->table[daddr]) -		rcu_assign_pointer(routes->table[daddr], NULL); +		RCU_INIT_POINTER(routes->table[daddr], NULL);  	else  		dev = NULL;  	mutex_unlock(&routes->lock); @@ -426,18 +401,14 @@ int phonet_route_del(struct net_device *dev, u8 daddr)  	return 0;  } -struct net_device *phonet_route_get(struct net *net, u8 daddr) +struct net_device *phonet_route_get_rcu(struct net *net, u8 daddr)  {  	struct phonet_net *pnn = phonet_pernet(net);  	struct phonet_routes *routes = &pnn->routes;  	struct net_device *dev; -	ASSERT_RTNL(); /* no need to hold the device */ -  	daddr >>= 2; -	rcu_read_lock();  	dev = rcu_dereference(routes->table[daddr]); -	rcu_read_unlock();  	return dev;  } diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 58b3b1f991e..b64151ade6b 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -5,8 +5,8 @@   *   * Copyright (C) 2008 Nokia Corporation.   * - * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> - * Original author: Sakari Ailus <sakari.ailus@nokia.com> + * Authors: Sakari Ailus <sakari.ailus@nokia.com> + *          Remi Denis-Courmont   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -33,7 +33,7 @@  /* Device address handling */  static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr, -		     u32 pid, u32 seq, int event); +		     u32 portid, u32 seq, int event);  void phonet_address_notify(int event, struct net_device *dev, u8 addr)  { @@ -61,7 +61,7 @@ static const struct nla_policy ifa_phonet_policy[IFA_MAX+1] = {  	[IFA_LOCAL] = { .type = NLA_U8 },  }; -static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr) +static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh)  {  	struct net *net = sock_net(skb->sk);  	struct nlattr *tb[IFA_MAX+1]; @@ -70,7 +70,10 @@ static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr)  	int err;  	u8 pnaddr; -	if (!capable(CAP_SYS_ADMIN)) +	if (!netlink_capable(skb, CAP_NET_ADMIN)) +		return -EPERM; + +	if (!netlink_capable(skb, CAP_SYS_ADMIN))  		return -EPERM;  	ASSERT_RTNL(); @@ -101,12 +104,12 @@ static int addr_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr)  }  static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr, -			u32 pid, u32 seq, int event) +			u32 portid, u32 seq, int event)  {  	struct ifaddrmsg *ifm;  	struct nlmsghdr *nlh; -	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*ifm), 0); +	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*ifm), 0);  	if (nlh == NULL)  		return -EMSGSIZE; @@ -116,7 +119,8 @@ static int fill_addr(struct sk_buff *skb, struct net_device *dev, u8 addr,  	ifm->ifa_flags = IFA_F_PERMANENT;  	ifm->ifa_scope = RT_SCOPE_LINK;  	ifm->ifa_index = dev->ifindex; -	NLA_PUT_U8(skb, IFA_LOCAL, addr); +	if (nla_put_u8(skb, IFA_LOCAL, addr)) +		goto nla_put_failure;  	return nlmsg_end(skb, nlh);  nla_put_failure: @@ -147,7 +151,7 @@ static int getaddr_dumpit(struct sk_buff *skb, struct netlink_callback *cb)  				continue;  			if (fill_addr(skb, pnd->netdev, addr << 2, -					 NETLINK_CB(cb->skb).pid, +					 NETLINK_CB(cb->skb).portid,  					cb->nlh->nlmsg_seq, RTM_NEWADDR) < 0)  				goto out;  		} @@ -164,12 +168,12 @@ out:  /* Routes handling */  static int fill_route(struct sk_buff *skb, struct net_device *dev, u8 dst, -			u32 pid, u32 seq, int event) +			u32 portid, u32 seq, int event)  {  	struct rtmsg *rtm;  	struct nlmsghdr *nlh; -	nlh = nlmsg_put(skb, pid, seq, event, sizeof(*rtm), 0); +	nlh = nlmsg_put(skb, portid, seq, event, sizeof(*rtm), 0);  	if (nlh == NULL)  		return -EMSGSIZE; @@ -183,8 +187,9 @@ static int fill_route(struct sk_buff *skb, struct net_device *dev, u8 dst,  	rtm->rtm_scope = RT_SCOPE_UNIVERSE;  	rtm->rtm_type = RTN_UNICAST;  	rtm->rtm_flags = 0; -	NLA_PUT_U8(skb, RTA_DST, dst); -	NLA_PUT_U32(skb, RTA_OIF, dev->ifindex); +	if (nla_put_u8(skb, RTA_DST, dst) || +	    nla_put_u32(skb, RTA_OIF, dev->ifindex)) +		goto nla_put_failure;  	return nlmsg_end(skb, nlh);  nla_put_failure: @@ -219,7 +224,7 @@ static const struct nla_policy rtm_phonet_policy[RTA_MAX+1] = {  	[RTA_OIF] = { .type = NLA_U32 },  }; -static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr) +static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh)  {  	struct net *net = sock_net(skb->sk);  	struct nlattr *tb[RTA_MAX+1]; @@ -228,7 +233,10 @@ static int route_doit(struct sk_buff *skb, struct nlmsghdr *nlh, void *attr)  	int err;  	u8 dst; -	if (!capable(CAP_SYS_ADMIN)) +	if (!netlink_capable(skb, CAP_NET_ADMIN)) +		return -EPERM; + +	if (!netlink_capable(skb, CAP_SYS_ADMIN))  		return -EPERM;  	ASSERT_RTNL(); @@ -264,21 +272,23 @@ static int route_dumpit(struct sk_buff *skb, struct netlink_callback *cb)  	struct net *net = sock_net(skb->sk);  	u8 addr, addr_idx = 0, addr_start_idx = cb->args[0]; +	rcu_read_lock();  	for (addr = 0; addr < 64; addr++) {  		struct net_device *dev; -		dev = phonet_route_get(net, addr << 2); +		dev = phonet_route_get_rcu(net, addr << 2);  		if (!dev)  			continue;  		if (addr_idx++ < addr_start_idx)  			continue; -		if (fill_route(skb, dev, addr << 2, NETLINK_CB(cb->skb).pid, +		if (fill_route(skb, dev, addr << 2, NETLINK_CB(cb->skb).portid,  				cb->nlh->nlmsg_seq, RTM_NEWROUTE))  			goto out;  	}  out: +	rcu_read_unlock();  	cb->args[0] = addr_idx;  	cb->args[1] = 0; @@ -287,15 +297,16 @@ out:  int __init phonet_netlink_register(void)  { -	int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, NULL); +	int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, +				  NULL, NULL);  	if (err)  		return err;  	/* Further __rtnl_register() cannot fail */ -	__rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL); -	__rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit); -	__rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL); -	__rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL); -	__rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit); +	__rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL, NULL); +	__rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit, NULL); +	__rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL, NULL); +	__rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL, NULL); +	__rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit, NULL);  	return 0;  } diff --git a/net/phonet/socket.c b/net/phonet/socket.c index 25f746d20c1..008214a3d5e 100644 --- a/net/phonet/socket.c +++ b/net/phonet/socket.c @@ -5,8 +5,8 @@   *   * Copyright (C) 2008 Nokia Corporation.   * - * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> - * Original author: Sakari Ailus <sakari.ailus@nokia.com> + * Authors: Sakari Ailus <sakari.ailus@nokia.com> + *          Rémi Denis-Courmont   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -31,6 +31,7 @@  #include <net/tcp_states.h>  #include <linux/phonet.h> +#include <linux/export.h>  #include <net/phonet/phonet.h>  #include <net/phonet/pep.h>  #include <net/phonet/pn_dev.h> @@ -52,16 +53,16 @@ static int pn_socket_release(struct socket *sock)  static struct  {  	struct hlist_head hlist[PN_HASHSIZE]; -	spinlock_t lock; +	struct mutex lock;  } pnsocks;  void __init pn_sock_init(void)  { -	unsigned i; +	unsigned int i;  	for (i = 0; i < PN_HASHSIZE; i++)  		INIT_HLIST_HEAD(pnsocks.hlist + i); -	spin_lock_init(&pnsocks.lock); +	mutex_init(&pnsocks.lock);  }  static struct hlist_head *pn_hash_list(u16 obj) @@ -75,16 +76,14 @@ static struct hlist_head *pn_hash_list(u16 obj)   */  struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *spn)  { -	struct hlist_node *node;  	struct sock *sknode;  	struct sock *rval = NULL;  	u16 obj = pn_sockaddr_get_object(spn);  	u8 res = spn->spn_resource;  	struct hlist_head *hlist = pn_hash_list(obj); -	spin_lock_bh(&pnsocks.lock); - -	sk_for_each(sknode, node, hlist) { +	rcu_read_lock(); +	sk_for_each_rcu(sknode, hlist) {  		struct pn_sock *pn = pn_sk(sknode);  		BUG_ON(!pn->sobject); /* unbound socket */ @@ -107,8 +106,7 @@ struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *spn)  		sock_hold(sknode);  		break;  	} - -	spin_unlock_bh(&pnsocks.lock); +	rcu_read_unlock();  	return rval;  } @@ -117,14 +115,13 @@ struct sock *pn_find_sock_by_sa(struct net *net, const struct sockaddr_pn *spn)  void pn_deliver_sock_broadcast(struct net *net, struct sk_buff *skb)  {  	struct hlist_head *hlist = pnsocks.hlist; -	unsigned h; +	unsigned int h; -	spin_lock(&pnsocks.lock); +	rcu_read_lock();  	for (h = 0; h < PN_HASHSIZE; h++) { -		struct hlist_node *node;  		struct sock *sknode; -		sk_for_each(sknode, node, hlist) { +		sk_for_each(sknode, hlist) {  			struct sk_buff *clone;  			if (!net_eq(sock_net(sknode), net)) @@ -140,25 +137,26 @@ void pn_deliver_sock_broadcast(struct net *net, struct sk_buff *skb)  		}  		hlist++;  	} -	spin_unlock(&pnsocks.lock); +	rcu_read_unlock();  }  void pn_sock_hash(struct sock *sk)  {  	struct hlist_head *hlist = pn_hash_list(pn_sk(sk)->sobject); -	spin_lock_bh(&pnsocks.lock); -	sk_add_node(sk, hlist); -	spin_unlock_bh(&pnsocks.lock); +	mutex_lock(&pnsocks.lock); +	sk_add_node_rcu(sk, hlist); +	mutex_unlock(&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); +	mutex_lock(&pnsocks.lock); +	sk_del_node_init_rcu(sk); +	mutex_unlock(&pnsocks.lock);  	pn_sock_unbind_all_res(sk); +	synchronize_rcu();  }  EXPORT_SYMBOL(pn_sock_unhash); @@ -225,15 +223,18 @@ static int pn_socket_autobind(struct socket *sock)  	return 0; /* socket was already bound */  } -#ifdef CONFIG_PHONET_PIPECTRLR  static int pn_socket_connect(struct socket *sock, struct sockaddr *addr,  		int len, int flags)  {  	struct sock *sk = sock->sk; +	struct pn_sock *pn = pn_sk(sk);  	struct sockaddr_pn *spn = (struct sockaddr_pn *)addr; -	long timeo; +	struct task_struct *tsk = current; +	long timeo = sock_rcvtimeo(sk, flags & O_NONBLOCK);  	int err; +	if (pn_socket_autobind(sock)) +		return -ENOBUFS;  	if (len < sizeof(struct sockaddr_pn))  		return -EINVAL;  	if (spn->spn_family != AF_PHONET) @@ -243,82 +244,61 @@ static int pn_socket_connect(struct socket *sock, struct sockaddr *addr,  	switch (sock->state) {  	case SS_UNCONNECTED: -		sk->sk_state = TCP_CLOSE; -		break; -	case SS_CONNECTING: -		switch (sk->sk_state) { -		case TCP_SYN_RECV: -			sock->state = SS_CONNECTED; -			err = -EISCONN; -			goto out; -		case TCP_CLOSE: -			err = -EALREADY; -			if (flags & O_NONBLOCK) -				goto out; -			goto wait_connect; -		} -		break; -	case SS_CONNECTED: -		switch (sk->sk_state) { -		case TCP_SYN_RECV: +		if (sk->sk_state != TCP_CLOSE) {  			err = -EISCONN;  			goto out; -		case TCP_CLOSE: -			sock->state = SS_UNCONNECTED; -			break;  		}  		break; -	case SS_DISCONNECTING: -	case SS_FREE: -		break; +	case SS_CONNECTING: +		err = -EALREADY; +		goto out; +	default: +		err = -EISCONN; +		goto out;  	} -	sk->sk_state = TCP_CLOSE; -	sk_stream_kill_queues(sk); +	pn->dobject = pn_sockaddr_get_object(spn); +	pn->resource = pn_sockaddr_get_resource(spn);  	sock->state = SS_CONNECTING; +  	err = sk->sk_prot->connect(sk, addr, len); -	if (err < 0) { +	if (err) {  		sock->state = SS_UNCONNECTED; -		sk->sk_state = TCP_CLOSE; +		pn->dobject = 0;  		goto out;  	} -	err = -EINPROGRESS; -wait_connect: -	if (sk->sk_state != TCP_SYN_RECV && (flags & O_NONBLOCK)) -		goto out; - -	timeo = sock_sndtimeo(sk, flags & O_NONBLOCK); -	release_sock(sk); - -	err = -ERESTARTSYS; -	timeo = wait_event_interruptible_timeout(*sk_sleep(sk), -			sk->sk_state != TCP_CLOSE, -			timeo); - -	lock_sock(sk); -	if (timeo < 0) -		goto out; /* -ERESTARTSYS */ +	while (sk->sk_state == TCP_SYN_SENT) { +		DEFINE_WAIT(wait); -	err = -ETIMEDOUT; -	if (timeo == 0 && sk->sk_state != TCP_SYN_RECV) -		goto out; +		if (!timeo) { +			err = -EINPROGRESS; +			goto out; +		} +		if (signal_pending(tsk)) { +			err = sock_intr_errno(timeo); +			goto out; +		} -	if (sk->sk_state != TCP_SYN_RECV) { -		sock->state = SS_UNCONNECTED; -		err = sock_error(sk); -		if (!err) -			err = -ECONNREFUSED; -		goto out; +		prepare_to_wait_exclusive(sk_sleep(sk), &wait, +						TASK_INTERRUPTIBLE); +		release_sock(sk); +		timeo = schedule_timeout(timeo); +		lock_sock(sk); +		finish_wait(sk_sleep(sk), &wait);  	} -	sock->state = SS_CONNECTED; -	err = 0; +	if ((1 << sk->sk_state) & (TCPF_SYN_RECV|TCPF_ESTABLISHED)) +		err = 0; +	else if (sk->sk_state == TCP_CLOSE_WAIT) +		err = -ECONNRESET; +	else +		err = -ECONNREFUSED; +	sock->state = err ? SS_UNCONNECTED : SS_CONNECTED;  out:  	release_sock(sk);  	return err;  } -#endif  static int pn_socket_accept(struct socket *sock, struct socket *newsock,  				int flags) @@ -327,6 +307,9 @@ static int pn_socket_accept(struct socket *sock, struct socket *newsock,  	struct sock *newsk;  	int err; +	if (unlikely(sk->sk_state != TCP_LISTEN)) +		return -EINVAL; +  	newsk = sk->sk_prot->accept(sk, flags, &err);  	if (!newsk)  		return err; @@ -363,13 +346,8 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock,  	poll_wait(file, sk_sleep(sk), wait); -	switch (sk->sk_state) { -	case TCP_LISTEN: -		return hlist_empty(&pn->ackq) ? 0 : POLLIN; -	case TCP_CLOSE: +	if (sk->sk_state == TCP_CLOSE)  		return POLLERR; -	} -  	if (!skb_queue_empty(&sk->sk_receive_queue))  		mask |= POLLIN | POLLRDNORM;  	if (!skb_queue_empty(&pn->ctrlreq_queue)) @@ -428,19 +406,19 @@ static int pn_socket_listen(struct socket *sock, int backlog)  	struct sock *sk = sock->sk;  	int err = 0; -	if (sock->state != SS_UNCONNECTED) -		return -EINVAL;  	if (pn_socket_autobind(sock))  		return -ENOBUFS;  	lock_sock(sk); -	if (sk->sk_state != TCP_CLOSE) { +	if (sock->state != SS_UNCONNECTED) {  		err = -EINVAL;  		goto out;  	} -	sk->sk_state = TCP_LISTEN; -	sk->sk_ack_backlog = 0; +	if (sk->sk_state != TCP_LISTEN) { +		sk->sk_state = TCP_LISTEN; +		sk->sk_ack_backlog = 0; +	}  	sk->sk_max_ack_backlog = backlog;  out:  	release_sock(sk); @@ -488,11 +466,7 @@ const struct proto_ops phonet_stream_ops = {  	.owner		= THIS_MODULE,  	.release	= pn_socket_release,  	.bind		= pn_socket_bind, -#ifdef CONFIG_PHONET_PIPECTRLR  	.connect	= pn_socket_connect, -#else -	.connect	= sock_no_connect, -#endif  	.socketpair	= sock_no_socketpair,  	.accept		= pn_socket_accept,  	.getname	= pn_socket_getname, @@ -567,12 +541,11 @@ static struct sock *pn_sock_get_idx(struct seq_file *seq, loff_t pos)  {  	struct net *net = seq_file_net(seq);  	struct hlist_head *hlist = pnsocks.hlist; -	struct hlist_node *node;  	struct sock *sknode; -	unsigned h; +	unsigned int h;  	for (h = 0; h < PN_HASHSIZE; h++) { -		sk_for_each(sknode, node, hlist) { +		sk_for_each_rcu(sknode, hlist) {  			if (!net_eq(net, sock_net(sknode)))  				continue;  			if (!pos) @@ -596,9 +569,9 @@ static struct sock *pn_sock_get_next(struct seq_file *seq, struct sock *sk)  }  static void *pn_sock_seq_start(struct seq_file *seq, loff_t *pos) -	__acquires(pnsocks.lock) +	__acquires(rcu)  { -	spin_lock_bh(&pnsocks.lock); +	rcu_read_lock();  	return *pos ? pn_sock_get_idx(seq, *pos - 1) : SEQ_START_TOKEN;  } @@ -615,32 +588,32 @@ static void *pn_sock_seq_next(struct seq_file *seq, void *v, loff_t *pos)  }  static void pn_sock_seq_stop(struct seq_file *seq, void *v) -	__releases(pnsocks.lock) +	__releases(rcu)  { -	spin_unlock_bh(&pnsocks.lock); +	rcu_read_unlock();  }  static int pn_sock_seq_show(struct seq_file *seq, void *v)  { -	int len; - +	seq_setwidth(seq, 127);  	if (v == SEQ_START_TOKEN) -		seq_printf(seq, "%s%n", "pt  loc  rem rs st tx_queue rx_queue " -			"  uid inode ref pointer drops", &len); +		seq_puts(seq, "pt  loc  rem rs st tx_queue rx_queue " +			"  uid inode ref pointer drops");  	else {  		struct sock *sk = v;  		struct pn_sock *pn = pn_sk(sk);  		seq_printf(seq, "%2d %04X:%04X:%02X %02X %08X:%08X %5d %lu " -			"%d %p %d%n", -			sk->sk_protocol, pn->sobject, 0, pn->resource, -			sk->sk_state, +			"%d %pK %d", +			sk->sk_protocol, pn->sobject, pn->dobject, +			pn->resource, sk->sk_state,  			sk_wmem_alloc_get(sk), sk_rmem_alloc_get(sk), -			sock_i_uid(sk), sock_i_ino(sk), +			from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)), +			sock_i_ino(sk),  			atomic_read(&sk->sk_refcnt), sk, -			atomic_read(&sk->sk_drops), &len); +			atomic_read(&sk->sk_drops));  	} -	seq_printf(seq, "%*s\n", 127 - len, ""); +	seq_pad(seq, '\n');  	return 0;  } @@ -720,7 +693,7 @@ int pn_sock_unbind_res(struct sock *sk, u8 res)  	mutex_lock(&resource_mutex);  	if (pnres.sk[res] == sk) { -		rcu_assign_pointer(pnres.sk[res], NULL); +		RCU_INIT_POINTER(pnres.sk[res], NULL);  		ret = 0;  	}  	mutex_unlock(&resource_mutex); @@ -734,31 +707,29 @@ int pn_sock_unbind_res(struct sock *sk, u8 res)  void pn_sock_unbind_all_res(struct sock *sk)  { -	unsigned res, match = 0; +	unsigned int res, match = 0;  	mutex_lock(&resource_mutex);  	for (res = 0; res < 256; res++) {  		if (pnres.sk[res] == sk) { -			rcu_assign_pointer(pnres.sk[res], NULL); +			RCU_INIT_POINTER(pnres.sk[res], NULL);  			match++;  		}  	}  	mutex_unlock(&resource_mutex); -	if (match == 0) -		return; -	synchronize_rcu();  	while (match > 0) { -		sock_put(sk); +		__sock_put(sk);  		match--;  	} +	/* Caller is responsible for RCU sync before final sock_put() */  }  #ifdef CONFIG_PROC_FS  static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos)  {  	struct net *net = seq_file_net(seq); -	unsigned i; +	unsigned int i;  	if (!net_eq(net, &init_net))  		return NULL; @@ -776,7 +747,7 @@ static struct sock **pn_res_get_idx(struct seq_file *seq, loff_t pos)  static struct sock **pn_res_get_next(struct seq_file *seq, struct sock **sk)  {  	struct net *net = seq_file_net(seq); -	unsigned i; +	unsigned int i;  	BUG_ON(!net_eq(net, &init_net)); @@ -813,19 +784,19 @@ static void pn_res_seq_stop(struct seq_file *seq, void *v)  static int pn_res_seq_show(struct seq_file *seq, void *v)  { -	int len; - +	seq_setwidth(seq, 63);  	if (v == SEQ_START_TOKEN) -		seq_printf(seq, "%s%n", "rs   uid inode", &len); +		seq_puts(seq, "rs   uid inode");  	else {  		struct sock **psk = v;  		struct sock *sk = *psk; -		seq_printf(seq, "%02X %5d %lu%n", -			   (int) (psk - pnres.sk), sock_i_uid(sk), -			   sock_i_ino(sk), &len); +		seq_printf(seq, "%02X %5u %lu", +			   (int) (psk - pnres.sk), +			   from_kuid_munged(seq_user_ns(seq), sock_i_uid(sk)), +			   sock_i_ino(sk));  	} -	seq_printf(seq, "%*s\n", 63 - len, ""); +	seq_pad(seq, '\n');  	return 0;  } diff --git a/net/phonet/sysctl.c b/net/phonet/sysctl.c index cea1c7dbdae..c02a8c4bc11 100644 --- a/net/phonet/sysctl.c +++ b/net/phonet/sysctl.c @@ -5,7 +5,7 @@   *   * Copyright (C) 2008 Nokia Corporation.   * - * Contact: Remi Denis-Courmont <remi.denis-courmont@nokia.com> + * Author: Rémi Denis-Courmont   *   * This program is free software; you can redistribute it and/or   * modify it under the terms of the GNU General Public License @@ -27,6 +27,10 @@  #include <linux/errno.h>  #include <linux/init.h> +#include <net/sock.h> +#include <linux/phonet.h> +#include <net/phonet/phonet.h> +  #define DYNAMIC_PORT_MIN	0x40  #define DYNAMIC_PORT_MAX	0x7f @@ -46,7 +50,8 @@ static void set_local_port_range(int range[2])  void phonet_get_local_port_range(int *min, int *max)  { -	unsigned seq; +	unsigned int seq; +  	do {  		seq = read_seqbegin(&local_port_range_lock);  		if (min) @@ -56,13 +61,13 @@ void phonet_get_local_port_range(int *min, int *max)  	} while (read_seqretry(&local_port_range_lock, seq));  } -static int proc_local_port_range(ctl_table *table, int write, +static int proc_local_port_range(struct ctl_table *table, int write,  				void __user *buffer,  				size_t *lenp, loff_t *ppos)  {  	int ret;  	int range[2] = {local_port_range[0], local_port_range[1]}; -	ctl_table tmp = { +	struct ctl_table tmp = {  		.data = &range,  		.maxlen = sizeof(range),  		.mode = table->mode, @@ -93,19 +98,13 @@ static struct ctl_table phonet_table[] = {  	{ }  }; -static struct ctl_path phonet_ctl_path[] = { -	{ .procname = "net", }, -	{ .procname = "phonet", }, -	{ }, -}; -  int __init phonet_sysctl_init(void)  { -	phonet_table_hrd = register_sysctl_paths(phonet_ctl_path, phonet_table); +	phonet_table_hrd = register_net_sysctl(&init_net, "net/phonet", phonet_table);  	return phonet_table_hrd == NULL ? -ENOMEM : 0;  }  void phonet_sysctl_exit(void)  { -	unregister_sysctl_table(phonet_table_hrd); +	unregister_net_sysctl_table(phonet_table_hrd);  }  | 
