* [PATCH 0/6] [RFC] Phonet pipes protocol (v2)
@ 2008-10-01 10:12 Rémi Denis-Courmont
2008-10-01 10:13 ` [PATCH 1/6] Phonet: transport protocol auto-loading Remi Denis-Courmont
` (5 more replies)
0 siblings, 6 replies; 22+ messages in thread
From: Rémi Denis-Courmont @ 2008-10-01 10:12 UTC (permalink / raw)
To: netdev
Hello again,
This patchset adds the Phonet pipe (end-point) protocol, without
missing patches nor dummy debug. This protocols provides some
connection handling and negotiated congestion control. It's used as a
generic protocol for bulk transfers with Nokia cellular modems.
The penultimate patch provides virtual IP interface support on top of a
connected Phonet pipe socket. This is used for IP over GPRS.
The implementation uses sock callbacks internally and socket options
toward userland. Not sure if this is the "right" way.
Documentation/networking/phonet.txt | 54 +
include/linux/phonet.h | 12
include/linux/socket.h | 1
include/net/phonet/gprs.h | 38 +
include/net/phonet/pep.h | 156 ++++
net/phonet/Makefile | 6
net/phonet/af_phonet.c | 8
net/phonet/pep-gprs.c | 341 ++++++++++
net/phonet/pep.c | 1126 +++++++++++++++++++++++++++++++++++-
net/phonet/socket.c | 109 +++
10 files changed, 1816 insertions(+), 35 deletions(-)
Sorry for the noise
--
Rémi Denis-Courmont
Maemo Software, Nokia Devices R&D
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 1/6] Phonet: transport protocol auto-loading
2008-10-01 10:12 [PATCH 0/6] [RFC] Phonet pipes protocol (v2) Rémi Denis-Courmont
@ 2008-10-01 10:13 ` Remi Denis-Courmont
2008-10-01 12:45 ` Arnaldo Carvalho de Melo
2008-10-01 10:13 ` [PATCH 2/6] Phonet: connected sockets glue Remi Denis-Courmont
` (4 subsequent siblings)
5 siblings, 1 reply; 22+ messages in thread
From: Remi Denis-Courmont @ 2008-10-01 10:13 UTC (permalink / raw)
To: netdev
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
---
net/phonet/af_phonet.c | 5 +++++
1 files changed, 5 insertions(+), 0 deletions(-)
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
index 1d8df6b..06627d3 100644
--- a/net/phonet/af_phonet.c
+++ b/net/phonet/af_phonet.c
@@ -64,6 +64,11 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol)
}
pnp = phonet_proto_get(protocol);
+#ifdef CONFIG_KMOD
+ if (pnp == NULL
+ && request_module("net-pf-%d-proto-%d", PF_PHONET, protocol) == 0)
+ pnp = phonet_proto_get(protocol);
+#endif
if (pnp == NULL)
return -EPROTONOSUPPORT;
if (sock->type != pnp->sock_type) {
--
1.5.4.3
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 2/6] Phonet: connected sockets glue
2008-10-01 10:12 [PATCH 0/6] [RFC] Phonet pipes protocol (v2) Rémi Denis-Courmont
2008-10-01 10:13 ` [PATCH 1/6] Phonet: transport protocol auto-loading Remi Denis-Courmont
@ 2008-10-01 10:13 ` Remi Denis-Courmont
2008-10-01 12:48 ` Arnaldo Carvalho de Melo
2008-10-01 10:13 ` [PATCH 3/6] Phonet: Pipe End Point for Phonet Pipes protocol Remi Denis-Courmont
` (3 subsequent siblings)
5 siblings, 1 reply; 22+ messages in thread
From: Remi Denis-Courmont @ 2008-10-01 10:13 UTC (permalink / raw)
To: netdev; +Cc: Rémi Denis-Courmont
From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
---
include/net/phonet/pep.h | 43 ++++++++++++++++++++
net/phonet/socket.c | 97 ++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 140 insertions(+), 0 deletions(-)
create mode 100644 include/net/phonet/pep.h
diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
new file mode 100644
index 0000000..b2f8c54
--- /dev/null
+++ b/include/net/phonet/pep.h
@@ -0,0 +1,43 @@
+/*
+ * File: pep.h
+ *
+ * Phonet Pipe End Point sockets definitions
+ *
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * 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
+ */
+
+#ifndef NET_PHONET_PEP_H
+#define NET_PHONET_PEP_H
+
+struct pep_sock {
+ struct pn_sock pn_sk;
+
+ /* Listening socket stuff: */
+ struct hlist_head ackq;
+
+ /* Connected socket stuff: */
+ u8 tx_credits;
+};
+
+static inline struct pep_sock *pep_sk(struct sock *sk)
+{
+ return (struct pep_sock *)sk;
+}
+
+extern const struct proto_ops phonet_stream_ops;
+
+#endif
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index dfd4061..cea1136 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -25,11 +25,13 @@
#include <linux/kernel.h>
#include <linux/net.h>
+#include <linux/poll.h>
#include <net/sock.h>
#include <net/tcp_states.h>
#include <linux/phonet.h>
#include <net/phonet/phonet.h>
+#include <net/phonet/pep.h>
#include <net/phonet/pn_dev.h>
static int pn_socket_release(struct socket *sock)
@@ -166,6 +168,24 @@ static int pn_socket_autobind(struct socket *sock)
return 0; /* socket was already bound */
}
+static int pn_socket_accept(struct socket *sock, struct socket *newsock,
+ int flags)
+{
+ struct sock *sk = sock->sk;
+ struct sock *newsk;
+ int err;
+
+ newsk = sk->sk_prot->accept(sk, flags, &err);
+ if (!newsk)
+ return err;
+
+ lock_sock(newsk);
+ sock_graft(newsk, newsock);
+ newsock->state = SS_CONNECTED;
+ release_sock(newsk);
+ return 0;
+}
+
static int pn_socket_getname(struct socket *sock, struct sockaddr *addr,
int *sockaddr_len, int peer)
{
@@ -182,6 +202,33 @@ static int pn_socket_getname(struct socket *sock, struct sockaddr *addr,
return 0;
}
+static unsigned int pn_socket_poll(struct file *file, struct socket *sock,
+ poll_table *wait)
+{
+ struct sock *sk = sock->sk;
+ struct pep_sock *pn = pep_sk(sk);
+ unsigned int mask = 0;
+
+ poll_wait(file, &sock->wait, wait);
+
+ switch (sk->sk_state) {
+ case TCP_LISTEN:
+ return hlist_empty(&pn->ackq) ? 0 : POLLIN;
+ case TCP_CLOSE:
+ return POLLERR;
+ }
+
+ if (!skb_queue_empty(&sk->sk_receive_queue))
+ mask |= POLLIN | POLLRDNORM;
+ else if (sk->sk_state == TCP_CLOSE_WAIT)
+ return POLLHUP;
+
+ if (sk->sk_state == TCP_ESTABLISHED && pn->tx_credits)
+ mask |= POLLOUT | POLLWRNORM | POLLWRBAND;
+
+ return mask;
+}
+
static int pn_socket_ioctl(struct socket *sock, unsigned int cmd,
unsigned long arg)
{
@@ -220,6 +267,30 @@ static int pn_socket_ioctl(struct socket *sock, unsigned int cmd,
return sk->sk_prot->ioctl(sk, cmd, arg);
}
+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) {
+ err = -EINVAL;
+ goto out;
+ }
+
+ sk->sk_state = TCP_LISTEN;
+ sk->sk_ack_backlog = 0;
+ sk->sk_max_ack_backlog = backlog;
+out:
+ release_sock(sk);
+ return err;
+}
+
static int pn_socket_sendmsg(struct kiocb *iocb, struct socket *sock,
struct msghdr *m, size_t total_len)
{
@@ -256,6 +327,32 @@ const struct proto_ops phonet_dgram_ops = {
.sendpage = sock_no_sendpage,
};
+const struct proto_ops phonet_stream_ops = {
+ .family = AF_PHONET,
+ .owner = THIS_MODULE,
+ .release = pn_socket_release,
+ .bind = pn_socket_bind,
+ .connect = sock_no_connect,
+ .socketpair = sock_no_socketpair,
+ .accept = pn_socket_accept,
+ .getname = pn_socket_getname,
+ .poll = pn_socket_poll,
+ .ioctl = pn_socket_ioctl,
+ .listen = pn_socket_listen,
+ .shutdown = sock_no_shutdown,
+ .setsockopt = sock_no_setsockopt,
+ .getsockopt = sock_no_getsockopt,
+#ifdef CONFIG_COMPAT
+ .compat_setsockopt = sock_no_setsockopt,
+ .compat_getsockopt = compat_sock_no_getsockopt,
+#endif
+ .sendmsg = pn_socket_sendmsg,
+ .recvmsg = sock_common_recvmsg,
+ .mmap = sock_no_mmap,
+ .sendpage = sock_no_sendpage,
+};
+EXPORT_SYMBOL(phonet_stream_ops);
+
static DEFINE_MUTEX(port_mutex);
/* allocate port for a socket */
--
1.5.4.3
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 3/6] Phonet: Pipe End Point for Phonet Pipes protocol
2008-10-01 10:12 [PATCH 0/6] [RFC] Phonet pipes protocol (v2) Rémi Denis-Courmont
2008-10-01 10:13 ` [PATCH 1/6] Phonet: transport protocol auto-loading Remi Denis-Courmont
2008-10-01 10:13 ` [PATCH 2/6] Phonet: connected sockets glue Remi Denis-Courmont
@ 2008-10-01 10:13 ` Remi Denis-Courmont
2008-10-01 13:18 ` Arnaldo Carvalho de Melo
2008-10-01 10:13 ` [PATCH 4/6] Phonet: receive pipe control requests as out-of-band data Remi Denis-Courmont
` (2 subsequent siblings)
5 siblings, 1 reply; 22+ messages in thread
From: Remi Denis-Courmont @ 2008-10-01 10:13 UTC (permalink / raw)
To: netdev; +Cc: Rémi Denis-Courmont
From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
This protocol provides some connection handling and negotiated
congestion control. Nokia cellular modems use it for bulk transfers.
It provides packet boundaries (hence SOCK_SEQPACKET). Congestion
control is per packet rather per byte, so we do not re-use the
generic socket memory accounting.
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
---
include/linux/phonet.h | 4 +-
include/net/phonet/pep.h | 110 ++++++
net/phonet/Makefile | 4 +-
net/phonet/af_phonet.c | 3 +
net/phonet/pep.c | 904 ++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1023 insertions(+), 2 deletions(-)
create mode 100644 net/phonet/pep.c
diff --git a/include/linux/phonet.h b/include/linux/phonet.h
index 3a027f5..f921852 100644
--- a/include/linux/phonet.h
+++ b/include/linux/phonet.h
@@ -27,7 +27,9 @@
#define PN_PROTO_TRANSPORT 0
/* Phonet datagram socket */
#define PN_PROTO_PHONET 1
-#define PHONET_NPROTO 2
+/* Phonet pipe */
+#define PN_PROTO_PIPE 2
+#define PHONET_NPROTO 3
#define PNADDR_ANY 0
#define PNPORT_RESOURCE_ROUTING 0
diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
index b2f8c54..6b89d12 100644
--- a/include/net/phonet/pep.h
+++ b/include/net/phonet/pep.h
@@ -26,11 +26,21 @@
struct pep_sock {
struct pn_sock pn_sk;
+ /* XXX: union-ify listening/connect stuff ? */
/* Listening socket stuff: */
struct hlist_head ackq;
+ struct hlist_head hlist;
/* Connected socket stuff: */
+ struct sock *listener;
+ u16 peer_type; /* peer type/subtype */
+ u8 pipe_handle;
+
+ u8 rx_credits;
u8 tx_credits;
+ unsigned rx_fc:2; /* RX flow control */
+ unsigned tx_fc:2; /* TX flow control */
+ unsigned init_enable:1; /* auto-enable at creation */
};
static inline struct pep_sock *pep_sk(struct sock *sk)
@@ -40,4 +50,104 @@ static inline struct pep_sock *pep_sk(struct sock *sk)
extern const struct proto_ops phonet_stream_ops;
+/* Pipe protocol definitions */
+struct pnpipehdr {
+ u8 utid; /* transaction ID */
+ u8 message_id;
+ u8 pipe_handle;
+ u8 data[1];
+};
+#define state_after_connect data[0]
+#define other_pep_type data[1]
+#define state_after_reset data[0]
+
+static inline struct pnpipehdr *pnp_hdr(struct sk_buff *skb)
+{
+ return (struct pnpipehdr *)skb_transport_header(skb);
+}
+
+#define MAX_PNPIPE_HEADER (MAX_PHONET_HEADER + 4)
+
+enum {
+ PNS_PIPE_DATA = 0x20,
+
+ PNS_PEP_CONNECT_REQ = 0x40,
+ PNS_PEP_CONNECT_RESP,
+ PNS_PEP_DISCONNECT_REQ,
+ PNS_PEP_DISCONNECT_RESP,
+ PNS_PEP_RESET_REQ,
+ PNS_PEP_RESET_RESP,
+ PNS_PEP_ENABLE_REQ,
+ PNS_PEP_ENABLE_RESP,
+ PNS_PEP_CTRL_REQ,
+ PNS_PEP_CTRL_RESP,
+ PNS_PEP_DISABLE_REQ = 0x4C,
+ PNS_PEP_DISABLE_RESP,
+
+ PNS_PEP_STATUS_IND = 0x60,
+ PNS_PIPE_CREATED_IND,
+ PNS_PIPE_RESET_IND = 0x63,
+ PNS_PIPE_ENABLED_IND,
+ PNS_PIPE_REDIRECTED_IND,
+ PNS_PIPE_DISABLED_IND = 0x66,
+};
+
+#define PN_PIPE_INVALID_HANDLE 0xff
+#define PN_PEP_TYPE_COMMON 0x00
+
+/* Phonet pipe status indication */
+enum {
+ PN_PEP_IND_FLOW_CONTROL,
+ PN_PEP_IND_ID_MCFC_GRANT_CREDITS,
+};
+
+/* Phonet pipe error codes */
+enum {
+ PN_PIPE_NO_ERROR,
+ PN_PIPE_ERR_INVALID_PARAM,
+ PN_PIPE_ERR_INVALID_HANDLE,
+ PN_PIPE_ERR_INVALID_CTRL_ID,
+ PN_PIPE_ERR_NOT_ALLOWED,
+ PN_PIPE_ERR_PEP_IN_USE,
+ PN_PIPE_ERR_OVERLOAD,
+ PN_PIPE_ERR_DEV_DISCONNECTED,
+ PN_PIPE_ERR_TIMEOUT,
+ PN_PIPE_ERR_ALL_PIPES_IN_USE,
+ PN_PIPE_ERR_GENERAL,
+ PN_PIPE_ERR_NOT_SUPPORTED,
+};
+
+/* Phonet pipe states */
+enum {
+ PN_PIPE_DISABLE,
+ PN_PIPE_ENABLE,
+};
+
+/* Phonet pipe sub-block types */
+enum {
+ PN_PIPE_SB_CREATE_REQ_PEP_SUB_TYPE,
+ PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE,
+ PN_PIPE_SB_REDIRECT_REQ_PEP_SUB_TYPE,
+ PN_PIPE_SB_NEGOTIATED_FC,
+ PN_PIPE_SB_REQUIRED_FC_TX,
+ PN_PIPE_SB_PREFERRED_FC_RX,
+};
+
+/* Phonet pipe flow control models */
+enum {
+ PN_NO_FLOW_CONTROL,
+ PN_LEGACY_FLOW_CONTROL,
+ PN_ONE_CREDIT_FLOW_CONTROL,
+ PN_MULTI_CREDIT_FLOW_CONTROL,
+};
+
+#define pn_flow_safe(fc) ((fc) >> 1)
+
+/* Phonet pipe flow control states */
+enum {
+ PEP_IND_EMPTY,
+ PEP_IND_BUSY,
+ PEP_IND_READY,
+};
+
#endif
diff --git a/net/phonet/Makefile b/net/phonet/Makefile
index ae9c3ed..505df2a 100644
--- a/net/phonet/Makefile
+++ b/net/phonet/Makefile
@@ -1,4 +1,4 @@
-obj-$(CONFIG_PHONET) += phonet.o
+obj-$(CONFIG_PHONET) += phonet.o pn_pep.o
phonet-objs := \
pn_dev.o \
@@ -7,3 +7,5 @@ phonet-objs := \
datagram.o \
sysctl.o \
af_phonet.o
+
+pn_pep-objs := pep.o
diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
index 06627d3..126dd73 100644
--- a/net/phonet/af_phonet.c
+++ b/net/phonet/af_phonet.c
@@ -58,6 +58,9 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol)
case SOCK_DGRAM:
protocol = PN_PROTO_PHONET;
break;
+ case SOCK_SEQPACKET:
+ protocol = PN_PROTO_PIPE;
+ break;
default:
return -EPROTONOSUPPORT;
}
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
new file mode 100644
index 0000000..0c1f19f
--- /dev/null
+++ b/net/phonet/pep.c
@@ -0,0 +1,904 @@
+/*
+ * File: pep.c
+ *
+ * Phonet pipe protocol end point socket
+ *
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.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/socket.h>
+#include <net/sock.h>
+#include <net/tcp_states.h>
+#include <asm/ioctls.h>
+
+#include <linux/phonet.h>
+#include <net/phonet/phonet.h>
+#include <net/phonet/pep.h>
+
+/* sk_state values:
+ * TCP_CLOSE sock not in use yet
+ * TCP_CLOSE_WAIT disconnected pipe
+ * TCP_LISTEN listening pipe endpoint
+ * TCP_SYN_RECV connected pipe in disabled state
+ * TCP_ESTABLISHED connected pipe in enabled state
+ *
+ * pep_sock locking:
+ * - sk_state, ackq, hlist: sock lock needed
+ * - listener: read only
+ * - pipe_handle: read only
+ */
+
+#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. */
+static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,
+ void *buf)
+{
+ void *data = NULL;
+ struct {
+ u8 sb_type;
+ u8 sb_len;
+ } *ph, h;
+ int buflen = *plen;
+
+ ph = skb_header_pointer(skb, 0, 2, &h);
+ if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len))
+ return NULL;
+ ph->sb_len -= 2;
+ *ptype = ph->sb_type;
+ *plen = ph->sb_len;
+
+ if (buflen > ph->sb_len)
+ buflen = ph->sb_len;
+ data = skb_header_pointer(skb, 2, buflen, buf);
+ __skb_pull(skb, 2 + ph->sb_len);
+ return data;
+}
+
+static int pep_reply(struct sock *sk, struct sk_buff *oskb,
+ u8 code, const void *data, 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);
+ if (!skb)
+ return -ENOMEM;
+ skb_set_owner_w(skb, sk);
+
+ skb_reserve(skb, MAX_PNPIPE_HEADER);
+ __skb_put(skb, len);
+ skb_copy_to_linear_data(skb, data, len);
+ ph = (void *)__skb_push(skb, sizeof(*ph));
+ ph->utid = oph->utid;
+ ph->message_id = oph->message_id + 1; /* REQ -> RESP */
+ ph->pipe_handle = oph->pipe_handle;
+ ph->data[0] = code; /* error code */
+
+ return pn_skb_send(sk, skb, &pipe_srv);
+}
+
+#define PAD 0x00
+static int pep_accept_conn(struct sock *sk, struct sk_buff *skb)
+{
+ static const u8 data[20] = {
+ PAD, PAD, PAD, 2 /* sub-blocks */,
+ PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD,
+ PN_MULTI_CREDIT_FLOW_CONTROL,
+ PN_ONE_CREDIT_FLOW_CONTROL,
+ PN_LEGACY_FLOW_CONTROL,
+ PAD,
+ PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD,
+ PN_MULTI_CREDIT_FLOW_CONTROL,
+ PN_ONE_CREDIT_FLOW_CONTROL,
+ PN_LEGACY_FLOW_CONTROL,
+ PAD,
+ };
+
+ might_sleep();
+ return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data),
+ GFP_KERNEL);
+}
+
+static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code)
+{
+ 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);
+}
+
+/* Control requests are not sent by the pipe service and have a specific
+ * message format. */
+static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code)
+{
+ const struct pnpipehdr *oph = pnp_hdr(oskb);
+ struct sk_buff *skb;
+ struct pnpipehdr *ph;
+ struct sockaddr_pn dst;
+
+ skb = alloc_skb(MAX_PNPIPE_HEADER + 4, GFP_ATOMIC);
+ 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->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);
+}
+
+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;
+
+ 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);
+ ph = (void *)__skb_push(skb, sizeof(*ph) + 4);
+ ph->utid = 0;
+ ph->message_id = PNS_PEP_STATUS_IND;
+ ph->pipe_handle = pn->pipe_handle;
+ ph->data[0] = PN_PEP_TYPE_COMMON;
+ ph->data[1] = type;
+ ph->data[2] = PAD;
+ ph->data[3] = PAD;
+ ph->data[4] = status;
+
+ return pn_skb_send(sk, skb, &pipe_srv);
+}
+
+/* Send our RX flow control information to the sender.
+ * Socket must be locked. */
+static void pipe_grant_credits(struct sock *sk)
+{
+ struct pep_sock *pn = pep_sk(sk);
+
+ BUG_ON(sk->sk_state != TCP_ESTABLISHED);
+
+ switch (pn->rx_fc) {
+ 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;
+ 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)
+ pn->rx_credits = CREDITS_MAX;
+ break;
+ }
+}
+
+static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
+{
+ struct pep_sock *pn = pep_sk(sk);
+ struct pnpipehdr *hdr = pnp_hdr(skb);
+
+ if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
+ return -EINVAL;
+
+ if (hdr->data[0] != PN_PEP_TYPE_COMMON) {
+ if (net_ratelimit())
+ printk(KERN_DEBUG"Phonet unknown PEP type: %u\n",
+ (unsigned)hdr->data[0]);
+ return -EOPNOTSUPP;
+ }
+
+ switch (hdr->data[1]) {
+ case PN_PEP_IND_FLOW_CONTROL:
+ switch (pn->tx_fc) {
+ case PN_LEGACY_FLOW_CONTROL:
+ switch (hdr->data[4]) {
+ case PEP_IND_BUSY:
+ pn->tx_credits = 0;
+ break;
+ case PEP_IND_READY:
+ pn->tx_credits = 1;
+ break;
+ }
+ break;
+ case PN_ONE_CREDIT_FLOW_CONTROL:
+ if (hdr->data[4] == PEP_IND_READY)
+ pn->tx_credits = 1;
+ break;
+ }
+ break;
+
+ case PN_PEP_IND_ID_MCFC_GRANT_CREDITS:
+ if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL)
+ break;
+ if (pn->tx_credits + hdr->data[4] > 0xff)
+ pn->tx_credits = 0xff;
+ else
+ pn->tx_credits += hdr->data[4];
+ break;
+
+ default:
+ if (net_ratelimit())
+ printk(KERN_DEBUG"Phonet unknown PEP indication: %u\n",
+ (unsigned)hdr->data[1]);
+ return -EOPNOTSUPP;
+ }
+ if (pn->tx_credits)
+ sk->sk_write_space(sk);
+ return 0;
+}
+
+static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb)
+{
+ struct pep_sock *pn = pep_sk(sk);
+ struct pnpipehdr *hdr = pnp_hdr(skb);
+ u8 n_sb = hdr->data[0];
+
+ pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
+ __skb_pull(skb, sizeof(*hdr));
+ while (n_sb > 0) {
+ u8 type, buf[2], len = sizeof(buf);
+ u8 *data = pep_get_sb(skb, &type, &len, buf);
+
+ if (data == NULL)
+ return -EINVAL;
+ switch (type) {
+ case PN_PIPE_SB_NEGOTIATED_FC:
+ if (len < 2 || (data[0] | data[1]) > 3)
+ break;
+ pn->tx_fc = data[0] & 3;
+ pn->rx_fc = data[1] & 3;
+ break;
+ }
+ n_sb--;
+ }
+ return 0;
+}
+
+/* Queue an skb to a connected sock.
+ * Socket lock must be held. */
+static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ struct pep_sock *pn = pep_sk(sk);
+ struct pnpipehdr *hdr = pnp_hdr(skb);
+ int err = 0;
+
+ BUG_ON(sk->sk_state == TCP_CLOSE_WAIT);
+
+ switch (hdr->message_id) {
+ case PNS_PEP_CONNECT_REQ:
+ pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE);
+ break;
+
+ case PNS_PEP_DISCONNECT_REQ:
+ pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
+ sk->sk_state = TCP_CLOSE_WAIT;
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_state_change(sk);
+ break;
+
+ 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;
+
+ case PNS_PEP_RESET_REQ:
+ switch (hdr->state_after_reset) {
+ case PN_PIPE_DISABLE:
+ pn->init_enable = 0;
+ break;
+ case PN_PIPE_ENABLE:
+ pn->init_enable = 1;
+ break;
+ default: /* not allowed to send an error here!? */
+ err = -EINVAL;
+ goto out;
+ }
+ /* fall through */
+ case PNS_PEP_DISABLE_REQ:
+ pn->tx_credits = 0;
+ pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
+ break;
+
+ case PNS_PEP_CTRL_REQ:
+ /* TODO */
+ pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR);
+ break;
+
+ 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 0;
+ break;
+ }
+
+ if (pn->rx_credits == 0) {
+ err = -ENOBUFS;
+ break;
+ }
+ pn->rx_credits--;
+ skb->dev = NULL;
+ skb_set_owner_r(skb, sk);
+ err = skb->len;
+ skb_queue_tail(&sk->sk_receive_queue, skb);
+ if (!sock_flag(sk, SOCK_DEAD))
+ sk->sk_data_ready(sk, err);
+ return 0;
+
+ case PNS_PEP_STATUS_IND:
+ pipe_rcv_status(sk, skb);
+ break;
+
+ case PNS_PIPE_REDIRECTED_IND:
+ err = pipe_rcv_created(sk, skb);
+ break;
+
+ case PNS_PIPE_CREATED_IND:
+ err = pipe_rcv_created(sk, skb);
+ if (err)
+ break;
+ /* fall through */
+ case PNS_PIPE_RESET_IND:
+ if (!pn->init_enable)
+ break;
+ /* fall through */
+ case PNS_PIPE_ENABLED_IND:
+ if (!pn_flow_safe(pn->tx_fc)) {
+ 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;
+
+ case PNS_PIPE_DISABLED_IND:
+ sk->sk_state = TCP_SYN_RECV;
+ pn->rx_credits = 0;
+ break;
+
+ default:
+ if (net_ratelimit())
+ printk(KERN_DEBUG"Phonet unknown PEP message: %u\n",
+ hdr->message_id);
+ err = -EINVAL;
+ }
+out:
+ kfree_skb(skb);
+ return err;
+}
+
+/* Destroy connected sock. */
+static void pipe_destruct(struct sock *sk)
+{
+ skb_queue_purge(&sk->sk_receive_queue);
+}
+
+static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ struct sock *newsk;
+ struct pep_sock *newpn, *pn = pep_sk(sk);
+ struct pnpipehdr *hdr;
+ struct sockaddr_pn dst;
+ u16 peer_type;
+ u8 pipe_handle, enabled, 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 (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) */
+ 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);
+
+ 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];
+ break;
+ }
+ n_sb--;
+ }
+
+ skb = skb_clone(skb, GFP_ATOMIC);
+ if (!skb)
+ return -ENOMEM;
+
+ /* 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;
+ }
+ 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);
+ newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
+ newpn->pn_sk.resource = pn->pn_sk.resource;
+ newpn->pipe_handle = pipe_handle;
+ newpn->peer_type = peer_type;
+ newpn->rx_credits = newpn->tx_credits = 0;
+ newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
+ newpn->init_enable = enabled;
+
+ 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);
+
+ sk_acceptq_added(sk);
+ sk_add_node(newsk, &pn->ackq);
+ return 0;
+}
+
+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) {
+ struct pep_sock *pnnode = pep_sk(sknode);
+
+ /* Ports match, but addresses might not: */
+ if (pnnode->pn_sk.sobject != dobj)
+ continue;
+ if (pnnode->pipe_handle != pipe_handle)
+ continue;
+ if (sknode->sk_state == TCP_CLOSE_WAIT)
+ continue;
+
+ sock_hold(sknode);
+ return sknode;
+ }
+ return NULL;
+}
+
+/*
+ * Deliver an skb to a listening sock.
+ * Socket lock must be held.
+ * We then queue the skb to the right connected sock (if any).
+ */
+static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)
+{
+ struct pep_sock *pn = pep_sk(sk);
+ struct sock *sknode;
+ struct pnpipehdr *hdr = pnp_hdr(skb);
+ struct sockaddr_pn dst;
+ int err = NET_RX_SUCCESS;
+ u8 pipe_handle;
+
+ if (!pskb_may_pull(skb, sizeof(*hdr)))
+ goto drop;
+
+ hdr = pnp_hdr(skb);
+ pipe_handle = hdr->pipe_handle;
+ if (pipe_handle == PN_PIPE_INVALID_HANDLE)
+ goto drop;
+
+ pn_skb_get_dst_sockaddr(skb, &dst);
+
+ /* Look for an existing pipe handle */
+ sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
+ 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;
+
+ case PNS_PEP_DISCONNECT_REQ:
+ pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
+ break;
+
+ case PNS_PEP_CTRL_REQ:
+ pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE);
+ break;
+
+ case PNS_PEP_RESET_REQ:
+ case PNS_PEP_ENABLE_REQ:
+ case PNS_PEP_DISABLE_REQ:
+ /* invalid handle is not even allowed here! */
+ default:
+ err = NET_RX_DROP;
+ }
+drop:
+ kfree_skb(skb);
+ return err;
+}
+
+/* associated socket ceases to exist */
+static void pep_sock_close(struct sock *sk, long timeout)
+{
+ struct pep_sock *pn = pep_sk(sk);
+
+ 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;
+ }
+ release_sock(sk);
+}
+
+static int pep_wait_connreq(struct sock *sk, int noblock)
+{
+ struct task_struct *tsk = current;
+ struct pep_sock *pn = pep_sk(sk);
+ long timeo = sock_rcvtimeo(sk, noblock);
+
+ for (;;) {
+ DEFINE_WAIT(wait);
+
+ 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);
+
+ prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait,
+ TASK_INTERRUPTIBLE);
+ release_sock(sk);
+ timeo = schedule_timeout(timeo);
+ lock_sock(sk);
+ finish_wait(&sk->sk_socket->wait, &wait);
+ }
+
+ return 0;
+}
+
+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;
+
+ lock_sock(sk);
+ err = pep_wait_connreq(sk, flags & O_NONBLOCK);
+ if (err)
+ goto out;
+
+ newsk = __sk_head(&pn->ackq);
+
+ oskb = skb_dequeue(&newsk->sk_receive_queue);
+ err = pep_accept_conn(newsk, oskb);
+ if (err) {
+ skb_queue_head(&newsk->sk_receive_queue, oskb);
+ newsk = NULL;
+ goto out;
+ }
+
+ sock_hold(sk);
+ pep_sk(newsk)->listener = sk;
+
+ sock_hold(newsk);
+ sk_del_node_init(newsk);
+ sk_acceptq_removed(sk);
+ sk_add_node(newsk, &pn->hlist);
+ __sock_put(newsk);
+
+out:
+ release_sock(sk);
+ *errp = err;
+ return newsk;
+}
+
+static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
+{
+ int answ;
+
+ switch (cmd) {
+ case SIOCINQ:
+ if (sk->sk_state == TCP_LISTEN)
+ return -EINVAL;
+
+ lock_sock(sk);
+ if (!skb_queue_empty(&sk->sk_receive_queue))
+ answ = skb_peek(&sk->sk_receive_queue)->len;
+ else
+ answ = 0;
+ release_sock(sk);
+ return put_user(answ, (int __user *)arg);
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static int pep_init(struct sock *sk)
+{
+ struct pep_sock *pn = pep_sk(sk);
+
+ INIT_HLIST_HEAD(&pn->ackq);
+ INIT_HLIST_HEAD(&pn->hlist);
+ pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
+ return 0;
+}
+
+static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
+ struct msghdr *msg, size_t len)
+{
+ struct pep_sock *pn = pep_sk(sk);
+ struct sk_buff *skb = NULL;
+ struct pnpipehdr *ph;
+ long timeo;
+ int flags = msg->msg_flags;
+ int err, done;
+
+ if (msg->msg_flags & MSG_OOB || !(msg->msg_flags & MSG_EOR))
+ return -EOPNOTSUPP;
+
+ lock_sock(sk);
+ timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
+ if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) {
+ err = -ENOTCONN;
+ goto out;
+ }
+ if (sk->sk_state != TCP_ESTABLISHED) {
+ /* Wait until the pipe gets to enabled state */
+disabled:
+ err = sk_stream_wait_connect(sk, &timeo);
+ if (err)
+ goto out;
+
+ if (sk->sk_state == TCP_CLOSE_WAIT) {
+ err = -ECONNRESET;
+ goto out;
+ }
+ }
+ BUG_ON(sk->sk_state != TCP_ESTABLISHED);
+
+ /* Wait until flow control allows TX */
+ done = pn->tx_credits > 0;
+ while (!done) {
+ DEFINE_WAIT(wait);
+
+ if (!timeo) {
+ err = -EAGAIN;
+ goto out;
+ }
+ if (signal_pending(current)) {
+ err = sock_intr_errno(timeo);
+ goto out;
+ }
+
+ prepare_to_wait(&sk->sk_socket->wait, &wait,
+ TASK_INTERRUPTIBLE);
+ done = sk_wait_event(sk, &timeo, pn->tx_credits > 0);
+ finish_wait(&sk->sk_socket->wait, &wait);
+
+ if (sk->sk_state != TCP_ESTABLISHED)
+ goto disabled;
+ }
+
+ if (!skb) {
+ skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len,
+ flags & MSG_DONTWAIT, &err);
+ if (skb == NULL)
+ goto out;
+ skb_reserve(skb, MAX_PHONET_HEADER + 3);
+
+ if (sk->sk_state != TCP_ESTABLISHED || !pn->tx_credits)
+ goto disabled; /* sock_alloc_send_skb might sleep */
+ }
+
+ err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
+ if (err < 0)
+ goto out;
+
+ ph = (struct pnpipehdr *)skb_push(skb, 3);
+ ph->utid = 0;
+ ph->message_id = PNS_PIPE_DATA;
+ ph->pipe_handle = pn->pipe_handle;
+ if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */
+ pn->tx_credits--;
+
+ err = pn_skb_send(sk, skb, &pipe_srv);
+ if (err >= 0)
+ err = len; /* success! */
+ skb = NULL;
+out:
+ release_sock(sk);
+ if (skb)
+ kfree_skb(skb);
+ return err;
+}
+
+static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
+ struct msghdr *msg, size_t len, int noblock,
+ int flags, int *addr_len)
+{
+ struct sk_buff *skb;
+ int err;
+
+ if (unlikely(flags & MSG_OOB))
+ return -EOPNOTSUPP;
+ if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE)))
+ return -ENOTCONN;
+
+ skb = skb_recv_datagram(sk, flags, noblock, &err);
+ lock_sock(sk);
+ if (skb == NULL) {
+ if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT)
+ err = -ECONNRESET;
+ release_sock(sk);
+ return err;
+ }
+
+ if (sk->sk_state == TCP_ESTABLISHED)
+ pipe_grant_credits(sk);
+ release_sock(sk);
+
+ msg->msg_flags |= MSG_EOR;
+
+ if (skb->len > len)
+ msg->msg_flags |= MSG_TRUNC;
+ else
+ len = skb->len;
+
+ err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
+ if (!err)
+ err = (flags & MSG_TRUNC) ? skb->len : len;
+
+ skb_free_datagram(sk, skb);
+ return err;
+}
+
+static void pep_sock_unhash(struct sock *sk)
+{
+ struct pep_sock *pn = pep_sk(sk);
+ struct sock *skparent = NULL;
+
+ lock_sock(sk);
+ if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) {
+ skparent = pn->listener;
+ sk_del_node_init(sk);
+ release_sock(sk);
+
+ sk = skparent;
+ pn = pep_sk(skparent);
+ lock_sock(sk);
+ }
+ /* Unhash a listening sock only when it is closed
+ * and all of its active connected pipes are closed. */
+ if (hlist_empty(&pn->hlist))
+ pn_sock_unhash(&pn->pn_sk.sk);
+ release_sock(sk);
+
+ if (skparent)
+ sock_put(skparent);
+}
+
+static struct proto pep_proto = {
+ .close = pep_sock_close,
+ .accept = pep_sock_accept,
+ .ioctl = pep_ioctl,
+ .init = pep_init,
+ .sendmsg = pep_sendmsg,
+ .recvmsg = pep_recvmsg,
+ .backlog_rcv = pep_do_rcv,
+ .hash = pn_sock_hash,
+ .unhash = pep_sock_unhash,
+ .get_port = pn_sock_get_port,
+ .obj_size = sizeof(struct pep_sock),
+ .owner = THIS_MODULE,
+ .name = "PNPIPE",
+};
+
+static struct phonet_protocol pep_pn_proto = {
+ .ops = &phonet_stream_ops,
+ .prot = &pep_proto,
+ .sock_type = SOCK_SEQPACKET,
+};
+
+static int __init pep_register(void)
+{
+ return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto);
+}
+
+static void __exit pep_unregister(void)
+{
+ phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto);
+}
+
+module_init(pep_register);
+module_exit(pep_unregister);
+MODULE_AUTHOR("Remi Denis-Courmont, Nokia");
+MODULE_DESCRIPTION("Phonet pipe protocol");
+MODULE_LICENSE("GPL");
--
1.5.4.3
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 4/6] Phonet: receive pipe control requests as out-of-band data
2008-10-01 10:12 [PATCH 0/6] [RFC] Phonet pipes protocol (v2) Rémi Denis-Courmont
` (2 preceding siblings ...)
2008-10-01 10:13 ` [PATCH 3/6] Phonet: Pipe End Point for Phonet Pipes protocol Remi Denis-Courmont
@ 2008-10-01 10:13 ` Remi Denis-Courmont
2008-10-01 10:13 ` [PATCH 5/6] Phonet: implement GPRS virtual interface over PEP socket Remi Denis-Courmont
2008-10-01 10:13 ` [PATCH 6/6] Phonet: pipe end-point protocol documentation Remi Denis-Courmont
5 siblings, 0 replies; 22+ messages in thread
From: Remi Denis-Courmont @ 2008-10-01 10:13 UTC (permalink / raw)
To: netdev
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
---
include/net/phonet/pep.h | 2 +
net/phonet/pep.c | 65 +++++++++++++++++++++++++++++++++------------
net/phonet/socket.c | 4 ++-
3 files changed, 52 insertions(+), 19 deletions(-)
diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
index 6b89d12..b06852b 100644
--- a/include/net/phonet/pep.h
+++ b/include/net/phonet/pep.h
@@ -33,6 +33,8 @@ struct pep_sock {
/* Connected socket stuff: */
struct sock *listener;
+ struct sk_buff_head ctrlreq_queue;
+#define PNPIPE_CTRLREQ_MAX 10
u16 peer_type; /* peer type/subtype */
u8 pipe_handle;
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 0c1f19f..9a2ed45 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -135,14 +135,15 @@ static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code)
/* Control requests are not sent by the pipe service and have a specific
* message format. */
-static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code)
+static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code,
+ gfp_t priority)
{
const struct pnpipehdr *oph = pnp_hdr(oskb);
struct sk_buff *skb;
struct pnpipehdr *ph;
struct sockaddr_pn dst;
- skb = alloc_skb(MAX_PNPIPE_HEADER + 4, GFP_ATOMIC);
+ skb = alloc_skb(MAX_PNPIPE_HEADER + 4, priority);
if (!skb)
return -ENOMEM;
skb_set_owner_w(skb, sk);
@@ -303,6 +304,7 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
{
struct pep_sock *pn = pep_sk(sk);
struct pnpipehdr *hdr = pnp_hdr(skb);
+ struct sk_buff_head *queue;
int err = 0;
BUG_ON(sk->sk_state == TCP_CLOSE_WAIT);
@@ -343,9 +345,11 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
break;
case PNS_PEP_CTRL_REQ:
- /* TODO */
- pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR);
- break;
+ if (skb_queue_len(&pn->ctrlreq_queue) >= PNPIPE_CTRLREQ_MAX)
+ break;
+ __skb_pull(skb, 4);
+ queue = &pn->ctrlreq_queue;
+ goto queue;
case PNS_PIPE_DATA:
__skb_pull(skb, 3); /* Pipe data header */
@@ -361,13 +365,8 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
break;
}
pn->rx_credits--;
- skb->dev = NULL;
- skb_set_owner_r(skb, sk);
- err = skb->len;
- skb_queue_tail(&sk->sk_receive_queue, skb);
- if (!sock_flag(sk, SOCK_DEAD))
- sk->sk_data_ready(sk, err);
- return 0;
+ queue = &sk->sk_receive_queue;
+ goto queue;
case PNS_PEP_STATUS_IND:
pipe_rcv_status(sk, skb);
@@ -411,12 +410,24 @@ static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
out:
kfree_skb(skb);
return err;
+
+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;
}
/* Destroy connected sock. */
static void pipe_destruct(struct sock *sk)
{
+ struct pep_sock *pn = pep_sk(sk);
+
skb_queue_purge(&sk->sk_receive_queue);
+ skb_queue_purge(&pn->ctrlreq_queue);
}
static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb)
@@ -489,6 +500,7 @@ static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb)
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;
newpn->peer_type = peer_type;
newpn->rx_credits = newpn->tx_credits = 0;
@@ -579,7 +591,7 @@ static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)
break;
case PNS_PEP_CTRL_REQ:
- pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE);
+ pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE, GFP_ATOMIC);
break;
case PNS_PEP_RESET_REQ:
@@ -682,6 +694,7 @@ out:
static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
{
+ struct pep_sock *pn = pep_sk(sk);
int answ;
switch (cmd) {
@@ -690,7 +703,10 @@ static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
return -EINVAL;
lock_sock(sk);
- if (!skb_queue_empty(&sk->sk_receive_queue))
+ if (sock_flag(sk, SOCK_URGINLINE)
+ && !skb_queue_empty(&pn->ctrlreq_queue))
+ answ = skb_peek(&pn->ctrlreq_queue)->len;
+ else if (!skb_queue_empty(&sk->sk_receive_queue))
answ = skb_peek(&sk->sk_receive_queue)->len;
else
answ = 0;
@@ -707,6 +723,7 @@ static int pep_init(struct sock *sk)
INIT_HLIST_HEAD(&pn->ackq);
INIT_HLIST_HEAD(&pn->hlist);
+ skb_queue_head_init(&pn->ctrlreq_queue);
pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
return 0;
}
@@ -807,11 +824,24 @@ static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
struct sk_buff *skb;
int err;
- if (unlikely(flags & MSG_OOB))
- return -EOPNOTSUPP;
if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE)))
return -ENOTCONN;
+ if ((flags & MSG_OOB) || sock_flag(sk, SOCK_URGINLINE)) {
+ /* Dequeue and acknowledge control request */
+ struct pep_sock *pn = pep_sk(sk);
+
+ skb = skb_dequeue(&pn->ctrlreq_queue);
+ if (skb) {
+ pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR,
+ GFP_KERNEL);
+ msg->msg_flags |= MSG_OOB;
+ goto copy;
+ }
+ if (flags & MSG_OOB)
+ return -EINVAL;
+ }
+
skb = skb_recv_datagram(sk, flags, noblock, &err);
lock_sock(sk);
if (skb == NULL) {
@@ -824,9 +854,8 @@ static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
if (sk->sk_state == TCP_ESTABLISHED)
pipe_grant_credits(sk);
release_sock(sk);
-
+copy:
msg->msg_flags |= MSG_EOR;
-
if (skb->len > len)
msg->msg_flags |= MSG_TRUNC;
else
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index cea1136..a9c3d1f 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -220,7 +220,9 @@ static unsigned int pn_socket_poll(struct file *file, struct socket *sock,
if (!skb_queue_empty(&sk->sk_receive_queue))
mask |= POLLIN | POLLRDNORM;
- else if (sk->sk_state == TCP_CLOSE_WAIT)
+ if (!skb_queue_empty(&pn->ctrlreq_queue))
+ mask |= POLLPRI;
+ if (!mask && sk->sk_state == TCP_CLOSE_WAIT)
return POLLHUP;
if (sk->sk_state == TCP_ESTABLISHED && pn->tx_credits)
--
1.5.4.3
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 5/6] Phonet: implement GPRS virtual interface over PEP socket
2008-10-01 10:12 [PATCH 0/6] [RFC] Phonet pipes protocol (v2) Rémi Denis-Courmont
` (3 preceding siblings ...)
2008-10-01 10:13 ` [PATCH 4/6] Phonet: receive pipe control requests as out-of-band data Remi Denis-Courmont
@ 2008-10-01 10:13 ` Remi Denis-Courmont
2008-10-01 13:32 ` Arnaldo Carvalho de Melo
2008-10-01 10:13 ` [PATCH 6/6] Phonet: pipe end-point protocol documentation Remi Denis-Courmont
5 siblings, 1 reply; 22+ messages in thread
From: Remi Denis-Courmont @ 2008-10-01 10:13 UTC (permalink / raw)
To: netdev; +Cc: Rémi Denis-Courmont
From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
---
include/linux/phonet.h | 8 +
include/linux/socket.h | 1 +
include/net/phonet/gprs.h | 38 +++++
include/net/phonet/pep.h | 1 +
net/phonet/Makefile | 2 +-
net/phonet/pep-gprs.c | 341 +++++++++++++++++++++++++++++++++++++++++++++
net/phonet/pep.c | 157 ++++++++++++++++++++--
net/phonet/socket.c | 8 +-
8 files changed, 542 insertions(+), 14 deletions(-)
create mode 100644 include/net/phonet/gprs.h
create mode 100644 net/phonet/pep-gprs.c
diff --git a/include/linux/phonet.h b/include/linux/phonet.h
index f921852..c9609f9 100644
--- a/include/linux/phonet.h
+++ b/include/linux/phonet.h
@@ -31,9 +31,17 @@
#define PN_PROTO_PIPE 2
#define PHONET_NPROTO 3
+/* Socket options for SOL_PNPIPE level */
+#define PNPIPE_ENCAP 1
+#define PNPIPE_IFINDEX 2
+
#define PNADDR_ANY 0
#define PNPORT_RESOURCE_ROUTING 0
+/* Values for PNPIPE_ENCAP option */
+#define PNPIPE_ENCAP_NONE 0
+#define PNPIPE_ENCAP_IP 1
+
/* ioctls */
#define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0)
diff --git a/include/linux/socket.h b/include/linux/socket.h
index 818ca33..20fc4bb 100644
--- a/include/linux/socket.h
+++ b/include/linux/socket.h
@@ -297,6 +297,7 @@ struct ucred {
#define SOL_RXRPC 272
#define SOL_PPPOL2TP 273
#define SOL_BLUETOOTH 274
+#define SOL_PNPIPE 275
/* IPX options */
#define IPX_TYPE 1
diff --git a/include/net/phonet/gprs.h b/include/net/phonet/gprs.h
new file mode 100644
index 0000000..928daf5
--- /dev/null
+++ b/include/net/phonet/gprs.h
@@ -0,0 +1,38 @@
+/*
+ * File: pep_gprs.h
+ *
+ * GPRS over Phonet pipe end point socket
+ *
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.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
+ */
+
+#ifndef NET_PHONET_GPRS_H
+#define NET_PHONET_GPRS_H
+
+struct sock;
+struct sk_buff;
+
+int pep_writeable(struct sock *sk);
+int pep_write(struct sock *sk, struct sk_buff *skb);
+struct sk_buff *pep_read(struct sock *sk);
+
+int gprs_attach(struct sock *sk);
+void gprs_detach(struct sock *sk);
+
+#endif
diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
index b06852b..a202adc 100644
--- a/include/net/phonet/pep.h
+++ b/include/net/phonet/pep.h
@@ -35,6 +35,7 @@ struct pep_sock {
struct sock *listener;
struct sk_buff_head ctrlreq_queue;
#define PNPIPE_CTRLREQ_MAX 10
+ int ifindex;
u16 peer_type; /* peer type/subtype */
u8 pipe_handle;
diff --git a/net/phonet/Makefile b/net/phonet/Makefile
index 505df2a..d62bbba 100644
--- a/net/phonet/Makefile
+++ b/net/phonet/Makefile
@@ -8,4 +8,4 @@ phonet-objs := \
sysctl.o \
af_phonet.o
-pn_pep-objs := pep.o
+pn_pep-objs := pep.o pep-gprs.o
diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c
new file mode 100644
index 0000000..3341346
--- /dev/null
+++ b/net/phonet/pep-gprs.c
@@ -0,0 +1,341 @@
+/*
+ * File: pep-gprs.c
+ *
+ * GPRS over Phonet pipe end point socket
+ *
+ * Copyright (C) 2008 Nokia Corporation.
+ *
+ * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.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/netdevice.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <net/sock.h>
+
+#include <linux/if_phonet.h>
+#include <net/tcp_states.h>
+#include <net/phonet/gprs.h>
+
+#define GPRS_DEFAULT_MTU 1400
+
+struct gprs_dev {
+ struct sock *sk;
+ void (*old_state_change)(struct sock *);
+ void (*old_data_ready)(struct sock *, int);
+ void (*old_write_space)(struct sock *);
+
+ struct net_device *net;
+ struct net_device_stats stats;
+
+ struct sk_buff_head tx_queue;
+ struct work_struct tx_work;
+ spinlock_t tx_lock;
+ unsigned tx_max;
+};
+
+/*
+ * Socket callbacks
+ */
+
+static void gprs_state_change(struct sock *sk)
+{
+ struct gprs_dev *dev = sk->sk_user_data;
+
+ if (sk->sk_state == TCP_CLOSE_WAIT) {
+ netif_stop_queue(dev->net);
+ netif_carrier_off(dev->net);
+ }
+}
+
+static int gprs_recv(struct gprs_dev *dev, struct sk_buff *skb)
+{
+ int err = 0;
+ u16 protocol;
+
+ if (!pskb_may_pull(skb, 1))
+ goto drop;
+
+ /* Look at IP version field */
+ switch (skb->data[0] >> 4) {
+ case 4:
+ protocol = htons(ETH_P_IP);
+ break;
+ case 6:
+ protocol = htons(ETH_P_IPV6);
+ break;
+ default:
+ err = -EINVAL;
+ goto drop;
+ }
+
+ if (likely(skb_headroom(skb) & 3)) {
+ struct sk_buff *rskb, *fs;
+ int flen = 0;
+
+ /* Phonet Pipe data header is misaligned (3 bytes),
+ * so wrap the IP packet as a single fragment of an head-less
+ * socket buffer. The network stack will pull what it needs,
+ * but at least, the whole IP payload is not memcpy'd. */
+ rskb = netdev_alloc_skb(dev->net, 0);
+ if (!rskb) {
+ err = -ENOBUFS;
+ goto drop;
+ }
+ skb_shinfo(rskb)->frag_list = skb;
+ rskb->len += skb->len;
+ rskb->data_len += rskb->len;
+ rskb->truesize += rskb->len;
+
+ /* Avoid nested fragments */
+ for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next)
+ flen += fs->len;
+ skb->next = skb_shinfo(skb)->frag_list;
+ skb_shinfo(skb)->frag_list = NULL;
+ skb->len -= flen;
+ skb->data_len -= flen;
+ skb->truesize -= flen;
+
+ skb = rskb;
+ }
+
+ skb->protocol = protocol;
+ skb_reset_mac_header(skb);
+ skb->dev = dev->net;
+
+ if (likely(dev->net->flags & IFF_UP)) {
+ dev->stats.rx_packets++;
+ dev->stats.rx_bytes += skb->len;
+ netif_rx(skb);
+ skb = NULL;
+ } else
+ err = -ENODEV;
+
+drop:
+ if (skb) {
+ dev_kfree_skb(skb);
+ dev->stats.rx_dropped++;
+ }
+ return err;
+}
+
+static void gprs_data_ready(struct sock *sk, int len)
+{
+ struct gprs_dev *dev = sk->sk_user_data;
+ struct sk_buff *skb;
+
+ while ((skb = pep_read(sk)) != NULL) {
+ skb_orphan(skb);
+ gprs_recv(dev, skb);
+ }
+}
+
+static void gprs_write_space(struct sock *sk)
+{
+ struct gprs_dev *dev = sk->sk_user_data;
+ unsigned credits = pep_writeable(sk);
+
+ spin_lock_bh(&dev->tx_lock);
+ dev->tx_max = credits;
+ if (credits > skb_queue_len(&dev->tx_queue))
+ netif_wake_queue(dev->net);
+ spin_unlock_bh(&dev->tx_lock);
+}
+
+/*
+ * Network device callbacks
+ */
+
+static int gprs_xmit(struct sk_buff *skb, struct net_device *net)
+{
+ struct gprs_dev *dev = netdev_priv(net);
+
+ switch (skb->protocol) {
+ case htons(ETH_P_IP):
+ case htons(ETH_P_IPV6):
+ break;
+ default:
+ dev_kfree_skb(skb);
+ return 0;
+ }
+
+ spin_lock(&dev->tx_lock);
+ if (likely(skb_queue_len(&dev->tx_queue) < dev->tx_max)) {
+ skb_queue_tail(&dev->tx_queue, skb);
+ skb = NULL;
+ }
+ if (skb_queue_len(&dev->tx_queue) >= dev->tx_max)
+ netif_stop_queue(net);
+ spin_unlock(&dev->tx_lock);
+
+ schedule_work(&dev->tx_work);
+ if (unlikely(skb))
+ dev_kfree_skb(skb);
+ return 0;
+}
+
+static void gprs_tx(struct work_struct *work)
+{
+ struct gprs_dev *dev = container_of(work, struct gprs_dev, tx_work);
+ struct sock *sk = dev->sk;
+ struct sk_buff *skb;
+
+ while ((skb = skb_dequeue(&dev->tx_queue)) != NULL) {
+ int err;
+
+ dev->stats.tx_bytes += skb->len;
+ dev->stats.tx_packets++;
+
+ skb_orphan(skb);
+ skb_set_owner_w(skb, sk);
+
+ lock_sock(sk);
+ err = pep_write(sk, skb);
+ if (err) {
+ if (net_ratelimit())
+ printk(KERN_WARNING"%s: TX error (%d)\n",
+ dev->net->name, err);
+ dev->stats.tx_aborted_errors++;
+ dev->stats.tx_errors++;
+ }
+ release_sock(sk);
+ }
+
+ lock_sock(sk);
+ gprs_write_space(sk);
+ release_sock(sk);
+}
+
+static int gprs_set_mtu(struct net_device *net, int new_mtu)
+{
+ if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11)))
+ return -EINVAL;
+
+ net->mtu = new_mtu;
+ return 0;
+}
+
+static struct net_device_stats *gprs_get_stats(struct net_device *net)
+{
+ struct gprs_dev *dev = netdev_priv(net);
+
+ return &dev->stats;
+}
+
+static void gprs_setup(struct net_device *net)
+{
+ net->features = NETIF_F_FRAGLIST;
+ net->type = ARPHRD_NONE;
+ net->flags = IFF_POINTOPOINT | IFF_NOARP;
+ net->mtu = GPRS_DEFAULT_MTU;
+ net->hard_header_len = 0;
+ net->addr_len = 0;
+ net->tx_queue_len = 10;
+
+ net->destructor = free_netdev;
+ net->hard_start_xmit = gprs_xmit; /* mandatory */
+ net->change_mtu = gprs_set_mtu;
+ net->get_stats = gprs_get_stats;
+}
+
+/*
+ * External interface
+ */
+
+/*
+ * Attach a GPRS interface to a datagram socket.
+ * Returns the interface index on success, negative error code on error.
+ */
+int gprs_attach(struct sock *sk)
+{
+ static const char ifname[] = "gprs%d";
+ struct gprs_dev *dev;
+ struct net_device *net;
+ int err;
+
+ if (unlikely(sk->sk_type == SOCK_STREAM))
+ return -EINVAL; /* need packet boundaries */
+
+ /* Create net device */
+ net = alloc_netdev(sizeof(*dev), ifname, gprs_setup);
+ if (!net)
+ return -ENOMEM;
+ dev = netdev_priv(net);
+ dev->net = net;
+ dev->tx_max = 0;
+ spin_lock_init(&dev->tx_lock);
+ skb_queue_head_init(&dev->tx_queue);
+ INIT_WORK(&dev->tx_work, gprs_tx);
+
+ netif_stop_queue(net);
+ err = register_netdev(net);
+ if (err) {
+ free_netdev(net);
+ return err;
+ }
+
+ lock_sock(sk);
+ if (unlikely(sk->sk_user_data)) {
+ err = -EBUSY;
+ goto out_rel;
+ }
+ if (unlikely((1 << sk->sk_state & (TCPF_CLOSE|TCPF_LISTEN)) ||
+ sock_flag(sk, SOCK_DEAD))) {
+ err = -EINVAL;
+ goto out_rel;
+ }
+ sk->sk_user_data = dev;
+ dev->old_state_change = sk->sk_state_change;
+ dev->old_data_ready = sk->sk_data_ready;
+ dev->old_write_space = sk->sk_write_space;
+ sk->sk_state_change = gprs_state_change;
+ sk->sk_data_ready = gprs_data_ready;
+ sk->sk_write_space = gprs_write_space;
+ release_sock(sk);
+
+ sock_hold(sk);
+ dev->sk = sk;
+
+ printk(KERN_DEBUG"%s: attached\n", net->name);
+ gprs_write_space(sk); /* kick off TX */
+ return net->ifindex;
+
+out_rel:
+ release_sock(sk);
+ unregister_netdev(net);
+ return err;
+}
+
+void gprs_detach(struct sock *sk)
+{
+ struct gprs_dev *dev = sk->sk_user_data;
+ struct net_device *net = dev->net;
+
+ lock_sock(sk);
+ sk->sk_user_data = NULL;
+ sk->sk_state_change = dev->old_state_change;
+ sk->sk_data_ready = dev->old_data_ready;
+ sk->sk_write_space = dev->old_write_space;
+ release_sock(sk);
+
+ printk(KERN_DEBUG"%s: detached\n", net->name);
+ unregister_netdev(net);
+ flush_scheduled_work();
+ sock_put(sk);
+ skb_queue_purge(&dev->tx_queue);
+}
diff --git a/net/phonet/pep.c b/net/phonet/pep.c
index 9a2ed45..f0c212f 100644
--- a/net/phonet/pep.c
+++ b/net/phonet/pep.c
@@ -31,6 +31,7 @@
#include <linux/phonet.h>
#include <net/phonet/phonet.h>
#include <net/phonet/pep.h>
+#include <net/phonet/gprs.h>
/* sk_state values:
* TCP_CLOSE sock not in use yet
@@ -610,6 +611,7 @@ drop:
static void pep_sock_close(struct sock *sk, long timeout)
{
struct pep_sock *pn = pep_sk(sk);
+ int ifindex = 0;
sk_common_release(sk);
@@ -623,7 +625,12 @@ static void pep_sock_close(struct sock *sk, long timeout)
sk_del_node_init(sknode);
sk->sk_state = TCP_CLOSE;
}
+ ifindex = pn->ifindex;
+ pn->ifindex = 0;
release_sock(sk);
+
+ if (ifindex)
+ gprs_detach(sk);
}
static int pep_wait_connreq(struct sock *sk, int noblock)
@@ -728,12 +735,105 @@ static int pep_init(struct sock *sk)
return 0;
}
+static int pep_setsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int optlen)
+{
+ struct pep_sock *pn = pep_sk(sk);
+ int val = 0, err = 0;
+
+ if (level != SOL_PNPIPE)
+ return -ENOPROTOOPT;
+ if (optlen >= sizeof(int)) {
+ if (get_user(val, (int __user *) optval))
+ return -EFAULT;
+ }
+
+ lock_sock(sk);
+ switch (optname) {
+ case PNPIPE_ENCAP:
+ if (val && val != PNPIPE_ENCAP_IP) {
+ err = -EINVAL;
+ break;
+ }
+ if (!pn->ifindex == !val)
+ break; /* Nothing to do! */
+ if (!capable(CAP_NET_ADMIN)) {
+ err = -EPERM;
+ break;
+ }
+ if (val) {
+ release_sock(sk);
+ err = gprs_attach(sk);
+ if (err > 0) {
+ pn->ifindex = err;
+ err = 0;
+ }
+ } else {
+ pn->ifindex = 0;
+ release_sock(sk);
+ gprs_detach(sk);
+ err = 0;
+ }
+ goto out_norel;
+ default:
+ err = -ENOPROTOOPT;
+ }
+ release_sock(sk);
+
+out_norel:
+ return err;
+}
+
+static int pep_getsockopt(struct sock *sk, int level, int optname,
+ char __user *optval, int __user *optlen)
+{
+ struct pep_sock *pn = pep_sk(sk);
+ int len, val;
+
+ if (level != SOL_PNPIPE)
+ return -ENOPROTOOPT;
+ if (get_user(len, optlen))
+ return -EFAULT;
+
+ switch (optname) {
+ case PNPIPE_ENCAP:
+ val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE;
+ break;
+ case PNPIPE_IFINDEX:
+ val = pn->ifindex;
+ break;
+ default:
+ return -ENOPROTOOPT;
+ }
+
+ len = min_t(unsigned int, sizeof(int), len);
+ if (put_user(len, optlen))
+ return -EFAULT;
+ if (put_user(val, (int __user *) optval))
+ return -EFAULT;
+ return 0;
+}
+
+static int pipe_skb_send(struct sock *sk, struct sk_buff *skb)
+{
+ struct pep_sock *pn = pep_sk(sk);
+ struct pnpipehdr *ph;
+
+ ph = (struct pnpipehdr *)skb_push(skb, 3);
+ ph->utid = 0;
+ ph->message_id = PNS_PIPE_DATA;
+ ph->pipe_handle = pn->pipe_handle;
+ if (pn_flow_safe(pn->tx_fc) && pn->tx_credits)
+ pn->tx_credits--;
+
+ return pn_skb_send(sk, skb, &pipe_srv);
+}
+
static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len)
{
struct pep_sock *pn = pep_sk(sk);
struct sk_buff *skb = NULL;
- struct pnpipehdr *ph;
long timeo;
int flags = msg->msg_flags;
int err, done;
@@ -799,14 +899,7 @@ disabled:
if (err < 0)
goto out;
- ph = (struct pnpipehdr *)skb_push(skb, 3);
- ph->utid = 0;
- ph->message_id = PNS_PIPE_DATA;
- ph->pipe_handle = pn->pipe_handle;
- if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */
- pn->tx_credits--;
-
- err = pn_skb_send(sk, skb, &pipe_srv);
+ err = pipe_skb_send(sk, skb);
if (err >= 0)
err = len; /* success! */
skb = NULL;
@@ -817,6 +910,50 @@ out:
return err;
}
+int pep_writeable(struct sock *sk)
+{
+ struct pep_sock *pn = pep_sk(sk);
+
+ return (sk->sk_state == TCP_ESTABLISHED) ? pn->tx_credits : 0;
+}
+
+int pep_write(struct sock *sk, struct sk_buff *skb)
+{
+ struct sk_buff *rskb, *fs;
+ int flen = 0;
+
+ rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC);
+ if (!rskb) {
+ kfree_skb(skb);
+ return -ENOMEM;
+ }
+ skb_shinfo(rskb)->frag_list = skb;
+ rskb->len += skb->len;
+ rskb->data_len += rskb->len;
+ rskb->truesize += rskb->len;
+
+ /* Avoid nested fragments */
+ for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next)
+ flen += fs->len;
+ skb->next = skb_shinfo(skb)->frag_list;
+ skb_shinfo(skb)->frag_list = NULL;
+ skb->len -= flen;
+ skb->data_len -= flen;
+ skb->truesize -= flen;
+
+ skb_reserve(rskb, MAX_PHONET_HEADER + 3);
+ return pipe_skb_send(sk, rskb);
+}
+
+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);
+ return skb;
+}
+
static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
struct msghdr *msg, size_t len, int noblock,
int flags, int *addr_len)
@@ -899,6 +1036,8 @@ static struct proto pep_proto = {
.accept = pep_sock_accept,
.ioctl = pep_ioctl,
.init = pep_init,
+ .setsockopt = pep_setsockopt,
+ .getsockopt = pep_getsockopt,
.sendmsg = pep_sendmsg,
.recvmsg = pep_recvmsg,
.backlog_rcv = pep_do_rcv,
diff --git a/net/phonet/socket.c b/net/phonet/socket.c
index a9c3d1f..d817401 100644
--- a/net/phonet/socket.c
+++ b/net/phonet/socket.c
@@ -342,11 +342,11 @@ const struct proto_ops phonet_stream_ops = {
.ioctl = pn_socket_ioctl,
.listen = pn_socket_listen,
.shutdown = sock_no_shutdown,
- .setsockopt = sock_no_setsockopt,
- .getsockopt = sock_no_getsockopt,
+ .setsockopt = sock_common_setsockopt,
+ .getsockopt = sock_common_getsockopt,
#ifdef CONFIG_COMPAT
- .compat_setsockopt = sock_no_setsockopt,
- .compat_getsockopt = compat_sock_no_getsockopt,
+ .compat_setsockopt = compat_sock_common_setsockopt,
+ .compat_getsockopt = compat_sock_common_getsockopt,
#endif
.sendmsg = pn_socket_sendmsg,
.recvmsg = sock_common_recvmsg,
--
1.5.4.3
^ permalink raw reply related [flat|nested] 22+ messages in thread
* [PATCH 6/6] Phonet: pipe end-point protocol documentation
2008-10-01 10:12 [PATCH 0/6] [RFC] Phonet pipes protocol (v2) Rémi Denis-Courmont
` (4 preceding siblings ...)
2008-10-01 10:13 ` [PATCH 5/6] Phonet: implement GPRS virtual interface over PEP socket Remi Denis-Courmont
@ 2008-10-01 10:13 ` Remi Denis-Courmont
2008-10-01 15:19 ` Randy Macleod
2008-10-10 18:24 ` Randy Macleod
5 siblings, 2 replies; 22+ messages in thread
From: Remi Denis-Courmont @ 2008-10-01 10:13 UTC (permalink / raw)
To: netdev; +Cc: Rémi Denis-Courmont
From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
---
Documentation/networking/phonet.txt | 54 +++++++++++++++++++++++++++++++++++
1 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt
index 57d3e59..9c38cdf 100644
--- a/Documentation/networking/phonet.txt
+++ b/Documentation/networking/phonet.txt
@@ -112,6 +112,60 @@ However, connect() and getpeername() are not supported, as they did
not seem useful with Phonet usages (could be added easily).
+Phonet Pipe protocol
+--------------------
+
+The Phonet Pipe protocol is a simple sequenced packets protocol
+with end-to-end congestion control. It uses the passive listening
+socket paradigm. The listening socket is bound to an unique free object
+ID. Each listening socket can handle up to 255 simultaneous
+connections, one per accept()'d socket.
+
+ int lfd, cfd;
+
+ lfd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
+ listen (lfd, INT_MAX);
+
+ /* ... */
+ cfd = accept(lfd, NULL, NULL);
+ for (;;)
+ {
+ char buf[...];
+ ssize_t len = read(cfd, buf, sizeof(buf));
+
+ /* ... */
+
+ write(cfd, msg, msglen);
+ }
+
+Connections are established between two endpoints by a "third party"
+application. This means that both endpoints are passive; so connect()
+is not possible.
+
+WARNING:
+When polling a connected pipe socket for writability, there is an
+intrinsic race condition whereby writability might be lost between the
+polling and the writing system calls. In this case, the socket will
+block until write because possible again, unless non-blocking mode is
+enabled.
+
+
+The pipe protocol provides two socket options at the SOL_PNPIPE level:
+
+ PNPIPE_ENCAP accepts one integer value (int) of:
+
+ PNPIPE_ENCAP_NONE: The socket operates normally (default).
+
+ PNPIPE_ENCAP_IP: The socket is used as a backend for a virtual IP
+ interface. This requires CAP_NET_ADMIN capability. GPRS data
+ support on Nokia modems can use this. Note that the socket cannot
+ be reliably poll()'d or read() from while in this mode.
+
+ PNPIPE_IFINDEX is a read-only integer value. It contains the
+ interface index of the network interface created by PNPIPE_ENCAP,
+ or zero if encapsulation is off.
+
+
Authors
-------
--
1.5.4.3
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] Phonet: transport protocol auto-loading
2008-10-01 10:13 ` [PATCH 1/6] Phonet: transport protocol auto-loading Remi Denis-Courmont
@ 2008-10-01 12:45 ` Arnaldo Carvalho de Melo
2008-10-01 15:01 ` Marcel Holtmann
2008-10-03 13:49 ` Rémi Denis-Courmont
0 siblings, 2 replies; 22+ messages in thread
From: Arnaldo Carvalho de Melo @ 2008-10-01 12:45 UTC (permalink / raw)
To: Remi Denis-Courmont; +Cc: netdev
Em Wed, Oct 01, 2008 at 01:13:00PM +0300, Remi Denis-Courmont escreveu:
> Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
> ---
> net/phonet/af_phonet.c | 5 +++++
> 1 files changed, 5 insertions(+), 0 deletions(-)
>
> diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
> index 1d8df6b..06627d3 100644
> --- a/net/phonet/af_phonet.c
> +++ b/net/phonet/af_phonet.c
> @@ -64,6 +64,11 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol)
> }
>
> pnp = phonet_proto_get(protocol);
> +#ifdef CONFIG_KMOD
> + if (pnp == NULL
> + && request_module("net-pf-%d-proto-%d", PF_PHONET, protocol) == 0)
&& at the end of previous line, please.
I wonder if we shouldn't have:
net_pf_request_module(PF_PHONET, protocol)
[acme@doppio linux-2.6]$ grep "net-pf-%d-proto-%d" net/*/*.c
net/ipv4/af_inet.c: request_module("net-pf-%d-proto-%d-type-%d",
net/ipv4/af_inet.c: request_module("net-pf-%d-proto-%d",
net/ipv4/inet_diag.c: request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
net/ipv6/af_inet6.c: request_module("net-pf-%d-proto-%d-type-%d",
net/ipv6/af_inet6.c: request_module("net-pf-%d-proto-%d",
net/netlink/af_netlink.c: request_module("net-pf-%d-proto-%d", PF_NETLINK, protocol);
[acme@doppio linux-2.6]$
Nah, perhaps not...
> + pnp = phonet_proto_get(protocol);
> +#endif
> if (pnp == NULL)
> return -EPROTONOSUPPORT;
> if (sock->type != pnp->sock_type) {
> --
> 1.5.4.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 2/6] Phonet: connected sockets glue
2008-10-01 10:13 ` [PATCH 2/6] Phonet: connected sockets glue Remi Denis-Courmont
@ 2008-10-01 12:48 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 22+ messages in thread
From: Arnaldo Carvalho de Melo @ 2008-10-01 12:48 UTC (permalink / raw)
To: Remi Denis-Courmont; +Cc: netdev
Em Wed, Oct 01, 2008 at 01:13:01PM +0300, Remi Denis-Courmont escreveu:
> From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
>
> Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Looks good,
Acked-by: Arnaldo Carvalho de Melo <acme@redhat.com>
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 3/6] Phonet: Pipe End Point for Phonet Pipes protocol
2008-10-01 10:13 ` [PATCH 3/6] Phonet: Pipe End Point for Phonet Pipes protocol Remi Denis-Courmont
@ 2008-10-01 13:18 ` Arnaldo Carvalho de Melo
2008-10-02 10:50 ` Rémi Denis-Courmont
0 siblings, 1 reply; 22+ messages in thread
From: Arnaldo Carvalho de Melo @ 2008-10-01 13:18 UTC (permalink / raw)
To: Remi Denis-Courmont; +Cc: netdev
Em Wed, Oct 01, 2008 at 01:13:02PM +0300, Remi Denis-Courmont escreveu:
> From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
>
> This protocol provides some connection handling and negotiated
> congestion control. Nokia cellular modems use it for bulk transfers.
> It provides packet boundaries (hence SOCK_SEQPACKET). Congestion
> control is per packet rather per byte, so we do not re-use the
> generic socket memory accounting.
>
> Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
> ---
> include/linux/phonet.h | 4 +-
> include/net/phonet/pep.h | 110 ++++++
> net/phonet/Makefile | 4 +-
> net/phonet/af_phonet.c | 3 +
> net/phonet/pep.c | 904 ++++++++++++++++++++++++++++++++++++++++++++++
> 5 files changed, 1023 insertions(+), 2 deletions(-)
> create mode 100644 net/phonet/pep.c
>
> diff --git a/include/linux/phonet.h b/include/linux/phonet.h
> index 3a027f5..f921852 100644
> --- a/include/linux/phonet.h
> +++ b/include/linux/phonet.h
> @@ -27,7 +27,9 @@
> #define PN_PROTO_TRANSPORT 0
> /* Phonet datagram socket */
> #define PN_PROTO_PHONET 1
> -#define PHONET_NPROTO 2
> +/* Phonet pipe */
> +#define PN_PROTO_PIPE 2
> +#define PHONET_NPROTO 3
>
> #define PNADDR_ANY 0
> #define PNPORT_RESOURCE_ROUTING 0
> diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
> index b2f8c54..6b89d12 100644
> --- a/include/net/phonet/pep.h
> +++ b/include/net/phonet/pep.h
> @@ -26,11 +26,21 @@
> struct pep_sock {
> struct pn_sock pn_sk;
>
> + /* XXX: union-ify listening/connect stuff ? */
> /* Listening socket stuff: */
> struct hlist_head ackq;
> + struct hlist_head hlist;
>
> /* Connected socket stuff: */
> + struct sock *listener;
> + u16 peer_type; /* peer type/subtype */
> + u8 pipe_handle;
> +
> + u8 rx_credits;
> u8 tx_credits;
> + unsigned rx_fc:2; /* RX flow control */
> + unsigned tx_fc:2; /* TX flow control */
> + unsigned init_enable:1; /* auto-enable at creation */
On 64 bits you will be left with 24 bytes of padding anyway, why not use
u8 for rx_fc, tx_fc and init_enable? Use bitfields when you can save
space, otherwise you will probably just use _more_ space in .text, while
using the same amount of bytes for your data 8-)
> };
>
> static inline struct pep_sock *pep_sk(struct sock *sk)
> @@ -40,4 +50,104 @@ static inline struct pep_sock *pep_sk(struct sock *sk)
>
> extern const struct proto_ops phonet_stream_ops;
>
> +/* Pipe protocol definitions */
> +struct pnpipehdr {
> + u8 utid; /* transaction ID */
> + u8 message_id;
> + u8 pipe_handle;
> + u8 data[1];
> +};
> +#define state_after_connect data[0]
> +#define other_pep_type data[1]
> +#define state_after_reset data[0]
why not:
struct pnpipehdr {
u8 utid;
u8 message_id;
u8 pipe_handle;
union {
u8 data;
u8 state_after_connect;
u8 state_after_reset;
};
};
?
> +
> +static inline struct pnpipehdr *pnp_hdr(struct sk_buff *skb)
> +{
> + return (struct pnpipehdr *)skb_transport_header(skb);
> +}
> +
> +#define MAX_PNPIPE_HEADER (MAX_PHONET_HEADER + 4)
> +
> +enum {
> + PNS_PIPE_DATA = 0x20,
> +
> + PNS_PEP_CONNECT_REQ = 0x40,
> + PNS_PEP_CONNECT_RESP,
> + PNS_PEP_DISCONNECT_REQ,
> + PNS_PEP_DISCONNECT_RESP,
> + PNS_PEP_RESET_REQ,
> + PNS_PEP_RESET_RESP,
> + PNS_PEP_ENABLE_REQ,
> + PNS_PEP_ENABLE_RESP,
> + PNS_PEP_CTRL_REQ,
> + PNS_PEP_CTRL_RESP,
> + PNS_PEP_DISABLE_REQ = 0x4C,
> + PNS_PEP_DISABLE_RESP,
> +
> + PNS_PEP_STATUS_IND = 0x60,
> + PNS_PIPE_CREATED_IND,
> + PNS_PIPE_RESET_IND = 0x63,
> + PNS_PIPE_ENABLED_IND,
> + PNS_PIPE_REDIRECTED_IND,
> + PNS_PIPE_DISABLED_IND = 0x66,
> +};
> +
> +#define PN_PIPE_INVALID_HANDLE 0xff
> +#define PN_PEP_TYPE_COMMON 0x00
> +
> +/* Phonet pipe status indication */
> +enum {
> + PN_PEP_IND_FLOW_CONTROL,
> + PN_PEP_IND_ID_MCFC_GRANT_CREDITS,
> +};
> +
> +/* Phonet pipe error codes */
> +enum {
> + PN_PIPE_NO_ERROR,
> + PN_PIPE_ERR_INVALID_PARAM,
> + PN_PIPE_ERR_INVALID_HANDLE,
> + PN_PIPE_ERR_INVALID_CTRL_ID,
> + PN_PIPE_ERR_NOT_ALLOWED,
> + PN_PIPE_ERR_PEP_IN_USE,
> + PN_PIPE_ERR_OVERLOAD,
> + PN_PIPE_ERR_DEV_DISCONNECTED,
> + PN_PIPE_ERR_TIMEOUT,
> + PN_PIPE_ERR_ALL_PIPES_IN_USE,
> + PN_PIPE_ERR_GENERAL,
> + PN_PIPE_ERR_NOT_SUPPORTED,
> +};
> +
> +/* Phonet pipe states */
> +enum {
> + PN_PIPE_DISABLE,
> + PN_PIPE_ENABLE,
> +};
> +
> +/* Phonet pipe sub-block types */
> +enum {
> + PN_PIPE_SB_CREATE_REQ_PEP_SUB_TYPE,
> + PN_PIPE_SB_CONNECT_REQ_PEP_SUB_TYPE,
> + PN_PIPE_SB_REDIRECT_REQ_PEP_SUB_TYPE,
> + PN_PIPE_SB_NEGOTIATED_FC,
> + PN_PIPE_SB_REQUIRED_FC_TX,
> + PN_PIPE_SB_PREFERRED_FC_RX,
> +};
> +
> +/* Phonet pipe flow control models */
> +enum {
> + PN_NO_FLOW_CONTROL,
> + PN_LEGACY_FLOW_CONTROL,
> + PN_ONE_CREDIT_FLOW_CONTROL,
> + PN_MULTI_CREDIT_FLOW_CONTROL,
> +};
> +
> +#define pn_flow_safe(fc) ((fc) >> 1)
> +
> +/* Phonet pipe flow control states */
> +enum {
> + PEP_IND_EMPTY,
> + PEP_IND_BUSY,
> + PEP_IND_READY,
> +};
> +
> #endif
> diff --git a/net/phonet/Makefile b/net/phonet/Makefile
> index ae9c3ed..505df2a 100644
> --- a/net/phonet/Makefile
> +++ b/net/phonet/Makefile
> @@ -1,4 +1,4 @@
> -obj-$(CONFIG_PHONET) += phonet.o
> +obj-$(CONFIG_PHONET) += phonet.o pn_pep.o
>
> phonet-objs := \
> pn_dev.o \
> @@ -7,3 +7,5 @@ phonet-objs := \
> datagram.o \
> sysctl.o \
> af_phonet.o
> +
> +pn_pep-objs := pep.o
> diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
> index 06627d3..126dd73 100644
> --- a/net/phonet/af_phonet.c
> +++ b/net/phonet/af_phonet.c
> @@ -58,6 +58,9 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol)
> case SOCK_DGRAM:
> protocol = PN_PROTO_PHONET;
> break;
> + case SOCK_SEQPACKET:
> + protocol = PN_PROTO_PIPE;
> + break;
I keep thinking you could use SOCK_SEQPACKET everywhere instead of
PN_PROTO_PIPE, but bear with me, its just a hunch :-)
> default:
> return -EPROTONOSUPPORT;
> }
> diff --git a/net/phonet/pep.c b/net/phonet/pep.c
> new file mode 100644
> index 0000000..0c1f19f
> --- /dev/null
> +++ b/net/phonet/pep.c
> @@ -0,0 +1,904 @@
> +/*
> + * File: pep.c
> + *
> + * Phonet pipe protocol end point socket
> + *
> + * Copyright (C) 2008 Nokia Corporation.
> + *
> + * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.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/socket.h>
> +#include <net/sock.h>
> +#include <net/tcp_states.h>
> +#include <asm/ioctls.h>
> +
> +#include <linux/phonet.h>
> +#include <net/phonet/phonet.h>
> +#include <net/phonet/pep.h>
> +
> +/* sk_state values:
> + * TCP_CLOSE sock not in use yet
> + * TCP_CLOSE_WAIT disconnected pipe
> + * TCP_LISTEN listening pipe endpoint
> + * TCP_SYN_RECV connected pipe in disabled state
> + * TCP_ESTABLISHED connected pipe in enabled state
> + *
> + * pep_sock locking:
> + * - sk_state, ackq, hlist: sock lock needed
> + * - listener: read only
> + * - pipe_handle: read only
> + */
> +
> +#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. */
> +static unsigned char *pep_get_sb(struct sk_buff *skb, u8 *ptype, u8 *plen,
> + void *buf)
> +{
> + void *data = NULL;
> + struct {
> + u8 sb_type;
> + u8 sb_len;
> + } *ph, h;
> + int buflen = *plen;
> +
> + ph = skb_header_pointer(skb, 0, 2, &h);
> + if (ph == NULL || ph->sb_len < 2 || !pskb_may_pull(skb, ph->sb_len))
> + return NULL;
> + ph->sb_len -= 2;
> + *ptype = ph->sb_type;
> + *plen = ph->sb_len;
> +
> + if (buflen > ph->sb_len)
> + buflen = ph->sb_len;
> + data = skb_header_pointer(skb, 2, buflen, buf);
> + __skb_pull(skb, 2 + ph->sb_len);
> + return data;
> +}
> +
> +static int pep_reply(struct sock *sk, struct sk_buff *oskb,
> + u8 code, const void *data, 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);
> + if (!skb)
> + return -ENOMEM;
> + skb_set_owner_w(skb, sk);
> +
> + skb_reserve(skb, MAX_PNPIPE_HEADER);
> + __skb_put(skb, len);
> + skb_copy_to_linear_data(skb, data, len);
Don't you have to use skb_reset_transport_header(skb) and then...
> + ph = (void *)__skb_push(skb, sizeof(*ph));
... use ph = pnp_hdr(skb); ?
Haven't checked if it is needed, perhaps its a nice optimization
not to use it, but looked inconsistent/uncommon at first sight.
> + ph->utid = oph->utid;
> + ph->message_id = oph->message_id + 1; /* REQ -> RESP */
> + ph->pipe_handle = oph->pipe_handle;
> + ph->data[0] = code; /* error code */
perhaps that unnamed union could have a error_code so that you
could do without a comment? 8)
ph->error_code = code;
> +
> + return pn_skb_send(sk, skb, &pipe_srv);
> +}
> +
> +#define PAD 0x00
> +static int pep_accept_conn(struct sock *sk, struct sk_buff *skb)
> +{
> + static const u8 data[20] = {
> + PAD, PAD, PAD, 2 /* sub-blocks */,
> + PN_PIPE_SB_REQUIRED_FC_TX, pep_sb_size(5), 3, PAD,
> + PN_MULTI_CREDIT_FLOW_CONTROL,
> + PN_ONE_CREDIT_FLOW_CONTROL,
> + PN_LEGACY_FLOW_CONTROL,
> + PAD,
> + PN_PIPE_SB_PREFERRED_FC_RX, pep_sb_size(5), 3, PAD,
> + PN_MULTI_CREDIT_FLOW_CONTROL,
> + PN_ONE_CREDIT_FLOW_CONTROL,
> + PN_LEGACY_FLOW_CONTROL,
> + PAD,
> + };
> +
> + might_sleep();
> + return pep_reply(sk, skb, PN_PIPE_NO_ERROR, data, sizeof(data),
> + GFP_KERNEL);
> +}
> +
> +static int pep_reject_conn(struct sock *sk, struct sk_buff *skb, u8 code)
> +{
> + 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);
> +}
> +
> +/* Control requests are not sent by the pipe service and have a specific
> + * message format. */
> +static int pep_ctrlreq_error(struct sock *sk, struct sk_buff *oskb, u8 code)
> +{
> + const struct pnpipehdr *oph = pnp_hdr(oskb);
> + struct sk_buff *skb;
> + struct pnpipehdr *ph;
> + struct sockaddr_pn dst;
> +
> + skb = alloc_skb(MAX_PNPIPE_HEADER + 4, GFP_ATOMIC);
> + 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->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;
ditto
> +
> + pn_skb_get_src_sockaddr(oskb, &dst);
> + return pn_skb_send(sk, skb, &dst);
> +}
> +
> +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;
> +
> + 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);
> + ph = (void *)__skb_push(skb, sizeof(*ph) + 4);
> + ph->utid = 0;
> + ph->message_id = PNS_PEP_STATUS_IND;
> + ph->pipe_handle = pn->pipe_handle;
> + ph->data[0] = PN_PEP_TYPE_COMMON;
> + ph->data[1] = type;
> + ph->data[2] = PAD;
> + ph->data[3] = PAD;
> + ph->data[4] = status;
ditto
> +
> + return pn_skb_send(sk, skb, &pipe_srv);
> +}
> +
> +/* Send our RX flow control information to the sender.
> + * Socket must be locked. */
> +static void pipe_grant_credits(struct sock *sk)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> +
> + BUG_ON(sk->sk_state != TCP_ESTABLISHED);
> +
> + switch (pn->rx_fc) {
> + 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;
> + 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)
> + pn->rx_credits = CREDITS_MAX;
> + break;
> + }
> +}
> +
> +static int pipe_rcv_status(struct sock *sk, struct sk_buff *skb)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> + struct pnpipehdr *hdr = pnp_hdr(skb);
> +
> + if (!pskb_may_pull(skb, sizeof(*hdr) + 4))
> + return -EINVAL;
> +
> + if (hdr->data[0] != PN_PEP_TYPE_COMMON) {
> + if (net_ratelimit())
> + printk(KERN_DEBUG"Phonet unknown PEP type: %u\n",
> + (unsigned)hdr->data[0]);
> + return -EOPNOTSUPP;
> + }
> +
> + switch (hdr->data[1]) {
> + case PN_PEP_IND_FLOW_CONTROL:
> + switch (pn->tx_fc) {
> + case PN_LEGACY_FLOW_CONTROL:
> + switch (hdr->data[4]) {
> + case PEP_IND_BUSY:
> + pn->tx_credits = 0;
> + break;
> + case PEP_IND_READY:
> + pn->tx_credits = 1;
> + break;
> + }
> + break;
> + case PN_ONE_CREDIT_FLOW_CONTROL:
> + if (hdr->data[4] == PEP_IND_READY)
> + pn->tx_credits = 1;
> + break;
> + }
> + break;
> +
> + case PN_PEP_IND_ID_MCFC_GRANT_CREDITS:
> + if (pn->tx_fc != PN_MULTI_CREDIT_FLOW_CONTROL)
> + break;
> + if (pn->tx_credits + hdr->data[4] > 0xff)
> + pn->tx_credits = 0xff;
> + else
> + pn->tx_credits += hdr->data[4];
> + break;
> +
> + default:
> + if (net_ratelimit())
> + printk(KERN_DEBUG"Phonet unknown PEP indication: %u\n",
> + (unsigned)hdr->data[1]);
Consider using LIMIT_NETDEBUG() or printk_ratelimit()
> + return -EOPNOTSUPP;
> + }
> + if (pn->tx_credits)
> + sk->sk_write_space(sk);
> + return 0;
> +}
> +
> +static int pipe_rcv_created(struct sock *sk, struct sk_buff *skb)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> + struct pnpipehdr *hdr = pnp_hdr(skb);
> + u8 n_sb = hdr->data[0];
> +
> + pn->rx_fc = pn->tx_fc = PN_LEGACY_FLOW_CONTROL;
> + __skb_pull(skb, sizeof(*hdr));
> + while (n_sb > 0) {
> + u8 type, buf[2], len = sizeof(buf);
> + u8 *data = pep_get_sb(skb, &type, &len, buf);
> +
> + if (data == NULL)
> + return -EINVAL;
> + switch (type) {
> + case PN_PIPE_SB_NEGOTIATED_FC:
> + if (len < 2 || (data[0] | data[1]) > 3)
> + break;
> + pn->tx_fc = data[0] & 3;
> + pn->rx_fc = data[1] & 3;
> + break;
> + }
> + n_sb--;
> + }
> + return 0;
> +}
> +
> +/* Queue an skb to a connected sock.
> + * Socket lock must be held. */
> +static int pipe_do_rcv(struct sock *sk, struct sk_buff *skb)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> + struct pnpipehdr *hdr = pnp_hdr(skb);
> + int err = 0;
> +
> + BUG_ON(sk->sk_state == TCP_CLOSE_WAIT);
> +
> + switch (hdr->message_id) {
> + case PNS_PEP_CONNECT_REQ:
> + pep_reject_conn(sk, skb, PN_PIPE_ERR_PEP_IN_USE);
> + break;
> +
> + case PNS_PEP_DISCONNECT_REQ:
> + pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
> + sk->sk_state = TCP_CLOSE_WAIT;
> + if (!sock_flag(sk, SOCK_DEAD))
> + sk->sk_state_change(sk);
> + break;
> +
> + 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;
> +
> + case PNS_PEP_RESET_REQ:
> + switch (hdr->state_after_reset) {
> + case PN_PIPE_DISABLE:
> + pn->init_enable = 0;
> + break;
> + case PN_PIPE_ENABLE:
> + pn->init_enable = 1;
> + break;
> + default: /* not allowed to send an error here!? */
> + err = -EINVAL;
> + goto out;
> + }
> + /* fall through */
> + case PNS_PEP_DISABLE_REQ:
> + pn->tx_credits = 0;
> + pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
> + break;
> +
> + case PNS_PEP_CTRL_REQ:
> + /* TODO */
> + pep_ctrlreq_error(sk, skb, PN_PIPE_NO_ERROR);
> + break;
> +
> + 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 0;
> + break;
> + }
> +
> + if (pn->rx_credits == 0) {
> + err = -ENOBUFS;
> + break;
> + }
> + pn->rx_credits--;
> + skb->dev = NULL;
> + skb_set_owner_r(skb, sk);
> + err = skb->len;
> + skb_queue_tail(&sk->sk_receive_queue, skb);
> + if (!sock_flag(sk, SOCK_DEAD))
> + sk->sk_data_ready(sk, err);
> + return 0;
> +
> + case PNS_PEP_STATUS_IND:
> + pipe_rcv_status(sk, skb);
> + break;
> +
> + case PNS_PIPE_REDIRECTED_IND:
> + err = pipe_rcv_created(sk, skb);
> + break;
> +
> + case PNS_PIPE_CREATED_IND:
> + err = pipe_rcv_created(sk, skb);
> + if (err)
> + break;
> + /* fall through */
> + case PNS_PIPE_RESET_IND:
> + if (!pn->init_enable)
> + break;
> + /* fall through */
> + case PNS_PIPE_ENABLED_IND:
> + if (!pn_flow_safe(pn->tx_fc)) {
> + 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;
> +
> + case PNS_PIPE_DISABLED_IND:
> + sk->sk_state = TCP_SYN_RECV;
> + pn->rx_credits = 0;
> + break;
> +
> + default:
> + if (net_ratelimit())
> + printk(KERN_DEBUG"Phonet unknown PEP message: %u\n",
> + hdr->message_id);
ditto
> + err = -EINVAL;
> + }
> +out:
> + kfree_skb(skb);
> + return err;
> +}
> +
> +/* Destroy connected sock. */
> +static void pipe_destruct(struct sock *sk)
> +{
> + skb_queue_purge(&sk->sk_receive_queue);
> +}
> +
> +static int pep_connreq_rcv(struct sock *sk, struct sk_buff *skb)
> +{
> + struct sock *newsk;
> + struct pep_sock *newpn, *pn = pep_sk(sk);
> + struct pnpipehdr *hdr;
> + struct sockaddr_pn dst;
> + u16 peer_type;
> + u8 pipe_handle, enabled, 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 (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) */
> + 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);
> +
> + 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];
> + break;
> + }
> + n_sb--;
> + }
> +
> + skb = skb_clone(skb, GFP_ATOMIC);
> + if (!skb)
> + return -ENOMEM;
> +
> + /* 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;
> + }
> + 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);
> + newpn->pn_sk.sobject = pn_sockaddr_get_object(&dst);
> + newpn->pn_sk.resource = pn->pn_sk.resource;
> + newpn->pipe_handle = pipe_handle;
> + newpn->peer_type = peer_type;
> + newpn->rx_credits = newpn->tx_credits = 0;
> + newpn->rx_fc = newpn->tx_fc = PN_LEGACY_FLOW_CONTROL;
> + newpn->init_enable = enabled;
> +
> + 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);
> +
> + sk_acceptq_added(sk);
> + sk_add_node(newsk, &pn->ackq);
> + return 0;
> +}
> +
> +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);
What is the lock that protects this list traversal?
> +
> + sk_for_each(sknode, node, hlist) {
> + struct pep_sock *pnnode = pep_sk(sknode);
> +
> + /* Ports match, but addresses might not: */
> + if (pnnode->pn_sk.sobject != dobj)
> + continue;
> + if (pnnode->pipe_handle != pipe_handle)
> + continue;
> + if (sknode->sk_state == TCP_CLOSE_WAIT)
> + continue;
> +
> + sock_hold(sknode);
> + return sknode;
> + }
> + return NULL;
> +}
> +
> +/*
> + * Deliver an skb to a listening sock.
> + * Socket lock must be held.
> + * We then queue the skb to the right connected sock (if any).
> + */
> +static int pep_do_rcv(struct sock *sk, struct sk_buff *skb)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> + struct sock *sknode;
> + struct pnpipehdr *hdr = pnp_hdr(skb);
> + struct sockaddr_pn dst;
> + int err = NET_RX_SUCCESS;
> + u8 pipe_handle;
> +
> + if (!pskb_may_pull(skb, sizeof(*hdr)))
> + goto drop;
> +
> + hdr = pnp_hdr(skb);
> + pipe_handle = hdr->pipe_handle;
> + if (pipe_handle == PN_PIPE_INVALID_HANDLE)
> + goto drop;
> +
> + pn_skb_get_dst_sockaddr(skb, &dst);
> +
> + /* Look for an existing pipe handle */
> + sknode = pep_find_pipe(&pn->hlist, &dst, pipe_handle);
> + 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");
LIMIT_NETDEBUG()
> + err = NET_RX_DROP;
> + goto drop;
> + }
> +
> + switch (hdr->message_id) {
> + case PNS_PEP_CONNECT_REQ:
> + err = pep_connreq_rcv(sk, skb);
> + break;
> +
> + case PNS_PEP_DISCONNECT_REQ:
> + pep_reply(sk, skb, PN_PIPE_NO_ERROR, NULL, 0, GFP_ATOMIC);
> + break;
> +
> + case PNS_PEP_CTRL_REQ:
> + pep_ctrlreq_error(sk, skb, PN_PIPE_INVALID_HANDLE);
> + break;
> +
> + case PNS_PEP_RESET_REQ:
> + case PNS_PEP_ENABLE_REQ:
> + case PNS_PEP_DISABLE_REQ:
> + /* invalid handle is not even allowed here! */
> + default:
> + err = NET_RX_DROP;
> + }
> +drop:
> + kfree_skb(skb);
> + return err;
> +}
> +
> +/* associated socket ceases to exist */
> +static void pep_sock_close(struct sock *sk, long timeout)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> +
> + 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;
> + }
> + release_sock(sk);
> +}
> +
> +static int pep_wait_connreq(struct sock *sk, int noblock)
This function looks familiar... inet_csk_accept,
inet_csk_wait_for_connect... perhaps we need a connection_sock father
for inet_connection_sock? :-)
> +{
> + struct task_struct *tsk = current;
> + struct pep_sock *pn = pep_sk(sk);
> + long timeo = sock_rcvtimeo(sk, noblock);
> +
> + for (;;) {
> + DEFINE_WAIT(wait);
> +
> + 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);
> +
> + prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait,
> + TASK_INTERRUPTIBLE);
> + release_sock(sk);
> + timeo = schedule_timeout(timeo);
> + lock_sock(sk);
> + finish_wait(&sk->sk_socket->wait, &wait);
> + }
> +
> + return 0;
> +}
> +
> +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;
> +
> + lock_sock(sk);
> + err = pep_wait_connreq(sk, flags & O_NONBLOCK);
> + if (err)
> + goto out;
> +
> + newsk = __sk_head(&pn->ackq);
> +
> + oskb = skb_dequeue(&newsk->sk_receive_queue);
> + err = pep_accept_conn(newsk, oskb);
> + if (err) {
> + skb_queue_head(&newsk->sk_receive_queue, oskb);
> + newsk = NULL;
> + goto out;
> + }
> +
> + sock_hold(sk);
> + pep_sk(newsk)->listener = sk;
> +
> + sock_hold(newsk);
> + sk_del_node_init(newsk);
> + sk_acceptq_removed(sk);
> + sk_add_node(newsk, &pn->hlist);
> + __sock_put(newsk);
> +
> +out:
> + release_sock(sk);
> + *errp = err;
> + return newsk;
> +}
> +
> +static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
> +{
> + int answ;
> +
> + switch (cmd) {
> + case SIOCINQ:
> + if (sk->sk_state == TCP_LISTEN)
> + return -EINVAL;
> +
> + lock_sock(sk);
> + if (!skb_queue_empty(&sk->sk_receive_queue))
> + answ = skb_peek(&sk->sk_receive_queue)->len;
> + else
> + answ = 0;
> + release_sock(sk);
> + return put_user(answ, (int __user *)arg);
this is so common I wonder we if a helper wouldn't help 8) Look at
dccp_ioctl before Ilpo does 8)
> + }
> +
> + return -ENOIOCTLCMD;
> +}
> +
> +static int pep_init(struct sock *sk)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> +
> + INIT_HLIST_HEAD(&pn->ackq);
> + INIT_HLIST_HEAD(&pn->hlist);
> + pn->pipe_handle = PN_PIPE_INVALID_HANDLE;
> + return 0;
> +}
> +
> +static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
> + struct msghdr *msg, size_t len)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> + struct sk_buff *skb = NULL;
> + struct pnpipehdr *ph;
> + long timeo;
> + int flags = msg->msg_flags;
> + int err, done;
> +
> + if (msg->msg_flags & MSG_OOB || !(msg->msg_flags & MSG_EOR))
> + return -EOPNOTSUPP;
> +
> + lock_sock(sk);
> + timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);
> + if ((1 << sk->sk_state) & (TCPF_LISTEN|TCPF_CLOSE)) {
> + err = -ENOTCONN;
> + goto out;
> + }
> + if (sk->sk_state != TCP_ESTABLISHED) {
> + /* Wait until the pipe gets to enabled state */
> +disabled:
> + err = sk_stream_wait_connect(sk, &timeo);
> + if (err)
> + goto out;
> +
> + if (sk->sk_state == TCP_CLOSE_WAIT) {
> + err = -ECONNRESET;
> + goto out;
> + }
> + }
> + BUG_ON(sk->sk_state != TCP_ESTABLISHED);
> +
> + /* Wait until flow control allows TX */
> + done = pn->tx_credits > 0;
> + while (!done) {
> + DEFINE_WAIT(wait);
> +
> + if (!timeo) {
> + err = -EAGAIN;
> + goto out;
> + }
> + if (signal_pending(current)) {
> + err = sock_intr_errno(timeo);
> + goto out;
> + }
> +
> + prepare_to_wait(&sk->sk_socket->wait, &wait,
> + TASK_INTERRUPTIBLE);
> + done = sk_wait_event(sk, &timeo, pn->tx_credits > 0);
> + finish_wait(&sk->sk_socket->wait, &wait);
> +
> + if (sk->sk_state != TCP_ESTABLISHED)
> + goto disabled;
> + }
> +
> + if (!skb) {
> + skb = sock_alloc_send_skb(sk, MAX_PNPIPE_HEADER + len,
> + flags & MSG_DONTWAIT, &err);
> + if (skb == NULL)
> + goto out;
> + skb_reserve(skb, MAX_PHONET_HEADER + 3);
> +
> + if (sk->sk_state != TCP_ESTABLISHED || !pn->tx_credits)
> + goto disabled; /* sock_alloc_send_skb might sleep */
> + }
> +
> + err = memcpy_fromiovec(skb_put(skb, len), msg->msg_iov, len);
> + if (err < 0)
> + goto out;
> +
> + ph = (struct pnpipehdr *)skb_push(skb, 3);
skb_reset_transport_header?
> + ph->utid = 0;
> + ph->message_id = PNS_PIPE_DATA;
> + ph->pipe_handle = pn->pipe_handle;
> + if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */
> + pn->tx_credits--;
> +
> + err = pn_skb_send(sk, skb, &pipe_srv);
> + if (err >= 0)
> + err = len; /* success! */
> + skb = NULL;
> +out:
> + release_sock(sk);
> + if (skb)
> + kfree_skb(skb);
Drop the if, kfree_skb eats NULL just fine
> + return err;
> +}
> +
> +static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
> + struct msghdr *msg, size_t len, int noblock,
> + int flags, int *addr_len)
> +{
> + struct sk_buff *skb;
> + int err;
> +
> + if (unlikely(flags & MSG_OOB))
> + return -EOPNOTSUPP;
> + if (unlikely(1 << sk->sk_state & (TCPF_LISTEN | TCPF_CLOSE)))
> + return -ENOTCONN;
> +
> + skb = skb_recv_datagram(sk, flags, noblock, &err);
> + lock_sock(sk);
> + if (skb == NULL) {
> + if (err == -ENOTCONN && sk->sk_state == TCP_CLOSE_WAIT)
> + err = -ECONNRESET;
> + release_sock(sk);
> + return err;
> + }
> +
> + if (sk->sk_state == TCP_ESTABLISHED)
> + pipe_grant_credits(sk);
> + release_sock(sk);
> +
> + msg->msg_flags |= MSG_EOR;
> +
> + if (skb->len > len)
> + msg->msg_flags |= MSG_TRUNC;
> + else
> + len = skb->len;
> +
> + err = skb_copy_datagram_iovec(skb, 0, msg->msg_iov, len);
> + if (!err)
> + err = (flags & MSG_TRUNC) ? skb->len : len;
> +
> + skb_free_datagram(sk, skb);
> + return err;
> +}
> +
> +static void pep_sock_unhash(struct sock *sk)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> + struct sock *skparent = NULL;
> +
> + lock_sock(sk);
> + if ((1 << sk->sk_state) & ~(TCPF_CLOSE|TCPF_LISTEN)) {
> + skparent = pn->listener;
> + sk_del_node_init(sk);
> + release_sock(sk);
> +
> + sk = skparent;
> + pn = pep_sk(skparent);
> + lock_sock(sk);
> + }
> + /* Unhash a listening sock only when it is closed
> + * and all of its active connected pipes are closed. */
> + if (hlist_empty(&pn->hlist))
> + pn_sock_unhash(&pn->pn_sk.sk);
> + release_sock(sk);
> +
> + if (skparent)
> + sock_put(skparent);
> +}
> +
> +static struct proto pep_proto = {
> + .close = pep_sock_close,
> + .accept = pep_sock_accept,
> + .ioctl = pep_ioctl,
> + .init = pep_init,
> + .sendmsg = pep_sendmsg,
> + .recvmsg = pep_recvmsg,
> + .backlog_rcv = pep_do_rcv,
> + .hash = pn_sock_hash,
> + .unhash = pep_sock_unhash,
> + .get_port = pn_sock_get_port,
> + .obj_size = sizeof(struct pep_sock),
> + .owner = THIS_MODULE,
> + .name = "PNPIPE",
> +};
> +
> +static struct phonet_protocol pep_pn_proto = {
> + .ops = &phonet_stream_ops,
> + .prot = &pep_proto,
> + .sock_type = SOCK_SEQPACKET,
> +};
> +
> +static int __init pep_register(void)
> +{
> + return phonet_proto_register(PN_PROTO_PIPE, &pep_pn_proto);
> +}
> +
> +static void __exit pep_unregister(void)
> +{
> + phonet_proto_unregister(PN_PROTO_PIPE, &pep_pn_proto);
> +}
> +
> +module_init(pep_register);
> +module_exit(pep_unregister);
> +MODULE_AUTHOR("Remi Denis-Courmont, Nokia");
> +MODULE_DESCRIPTION("Phonet pipe protocol");
> +MODULE_LICENSE("GPL");
> --
> 1.5.4.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 5/6] Phonet: implement GPRS virtual interface over PEP socket
2008-10-01 10:13 ` [PATCH 5/6] Phonet: implement GPRS virtual interface over PEP socket Remi Denis-Courmont
@ 2008-10-01 13:32 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 22+ messages in thread
From: Arnaldo Carvalho de Melo @ 2008-10-01 13:32 UTC (permalink / raw)
To: Remi Denis-Courmont; +Cc: netdev
Em Wed, Oct 01, 2008 at 01:13:04PM +0300, Remi Denis-Courmont escreveu:
> From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
>
> Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
> ---
> include/linux/phonet.h | 8 +
> include/linux/socket.h | 1 +
> include/net/phonet/gprs.h | 38 +++++
> include/net/phonet/pep.h | 1 +
> net/phonet/Makefile | 2 +-
> net/phonet/pep-gprs.c | 341 +++++++++++++++++++++++++++++++++++++++++++++
> net/phonet/pep.c | 157 ++++++++++++++++++++--
> net/phonet/socket.c | 8 +-
> 8 files changed, 542 insertions(+), 14 deletions(-)
> create mode 100644 include/net/phonet/gprs.h
> create mode 100644 net/phonet/pep-gprs.c
>
> diff --git a/include/linux/phonet.h b/include/linux/phonet.h
> index f921852..c9609f9 100644
> --- a/include/linux/phonet.h
> +++ b/include/linux/phonet.h
> @@ -31,9 +31,17 @@
> #define PN_PROTO_PIPE 2
> #define PHONET_NPROTO 3
>
> +/* Socket options for SOL_PNPIPE level */
> +#define PNPIPE_ENCAP 1
> +#define PNPIPE_IFINDEX 2
> +
> #define PNADDR_ANY 0
> #define PNPORT_RESOURCE_ROUTING 0
>
> +/* Values for PNPIPE_ENCAP option */
> +#define PNPIPE_ENCAP_NONE 0
> +#define PNPIPE_ENCAP_IP 1
> +
> /* ioctls */
> #define SIOCPNGETOBJECT (SIOCPROTOPRIVATE + 0)
>
> diff --git a/include/linux/socket.h b/include/linux/socket.h
> index 818ca33..20fc4bb 100644
> --- a/include/linux/socket.h
> +++ b/include/linux/socket.h
> @@ -297,6 +297,7 @@ struct ucred {
> #define SOL_RXRPC 272
> #define SOL_PPPOL2TP 273
> #define SOL_BLUETOOTH 274
> +#define SOL_PNPIPE 275
>
> /* IPX options */
> #define IPX_TYPE 1
> diff --git a/include/net/phonet/gprs.h b/include/net/phonet/gprs.h
> new file mode 100644
> index 0000000..928daf5
> --- /dev/null
> +++ b/include/net/phonet/gprs.h
> @@ -0,0 +1,38 @@
> +/*
> + * File: pep_gprs.h
> + *
> + * GPRS over Phonet pipe end point socket
> + *
> + * Copyright (C) 2008 Nokia Corporation.
> + *
> + * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.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
> + */
> +
> +#ifndef NET_PHONET_GPRS_H
> +#define NET_PHONET_GPRS_H
> +
> +struct sock;
> +struct sk_buff;
> +
> +int pep_writeable(struct sock *sk);
> +int pep_write(struct sock *sk, struct sk_buff *skb);
> +struct sk_buff *pep_read(struct sock *sk);
> +
> +int gprs_attach(struct sock *sk);
> +void gprs_detach(struct sock *sk);
> +
> +#endif
> diff --git a/include/net/phonet/pep.h b/include/net/phonet/pep.h
> index b06852b..a202adc 100644
> --- a/include/net/phonet/pep.h
> +++ b/include/net/phonet/pep.h
> @@ -35,6 +35,7 @@ struct pep_sock {
> struct sock *listener;
> struct sk_buff_head ctrlreq_queue;
> #define PNPIPE_CTRLREQ_MAX 10
> + int ifindex;
> u16 peer_type; /* peer type/subtype */
> u8 pipe_handle;
>
> diff --git a/net/phonet/Makefile b/net/phonet/Makefile
> index 505df2a..d62bbba 100644
> --- a/net/phonet/Makefile
> +++ b/net/phonet/Makefile
> @@ -8,4 +8,4 @@ phonet-objs := \
> sysctl.o \
> af_phonet.o
>
> -pn_pep-objs := pep.o
> +pn_pep-objs := pep.o pep-gprs.o
> diff --git a/net/phonet/pep-gprs.c b/net/phonet/pep-gprs.c
> new file mode 100644
> index 0000000..3341346
> --- /dev/null
> +++ b/net/phonet/pep-gprs.c
> @@ -0,0 +1,341 @@
> +/*
> + * File: pep-gprs.c
> + *
> + * GPRS over Phonet pipe end point socket
> + *
> + * Copyright (C) 2008 Nokia Corporation.
> + *
> + * Author: Rémi Denis-Courmont <remi.denis-courmont@nokia.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/netdevice.h>
> +#include <linux/if_ether.h>
> +#include <linux/if_arp.h>
> +#include <net/sock.h>
> +
> +#include <linux/if_phonet.h>
> +#include <net/tcp_states.h>
> +#include <net/phonet/gprs.h>
> +
> +#define GPRS_DEFAULT_MTU 1400
> +
> +struct gprs_dev {
> + struct sock *sk;
> + void (*old_state_change)(struct sock *);
> + void (*old_data_ready)(struct sock *, int);
> + void (*old_write_space)(struct sock *);
> +
> + struct net_device *net;
> + struct net_device_stats stats;
> +
> + struct sk_buff_head tx_queue;
> + struct work_struct tx_work;
> + spinlock_t tx_lock;
> + unsigned tx_max;
> +};
> +
> +/*
> + * Socket callbacks
> + */
> +
> +static void gprs_state_change(struct sock *sk)
> +{
> + struct gprs_dev *dev = sk->sk_user_data;
> +
> + if (sk->sk_state == TCP_CLOSE_WAIT) {
> + netif_stop_queue(dev->net);
> + netif_carrier_off(dev->net);
> + }
> +}
> +
> +static int gprs_recv(struct gprs_dev *dev, struct sk_buff *skb)
> +{
> + int err = 0;
> + u16 protocol;
> +
> + if (!pskb_may_pull(skb, 1))
> + goto drop;
> +
> + /* Look at IP version field */
> + switch (skb->data[0] >> 4) {
> + case 4:
> + protocol = htons(ETH_P_IP);
> + break;
> + case 6:
> + protocol = htons(ETH_P_IPV6);
> + break;
> + default:
> + err = -EINVAL;
> + goto drop;
> + }
> +
> + if (likely(skb_headroom(skb) & 3)) {
> + struct sk_buff *rskb, *fs;
> + int flen = 0;
> +
> + /* Phonet Pipe data header is misaligned (3 bytes),
> + * so wrap the IP packet as a single fragment of an head-less
> + * socket buffer. The network stack will pull what it needs,
> + * but at least, the whole IP payload is not memcpy'd. */
> + rskb = netdev_alloc_skb(dev->net, 0);
> + if (!rskb) {
> + err = -ENOBUFS;
> + goto drop;
> + }
> + skb_shinfo(rskb)->frag_list = skb;
> + rskb->len += skb->len;
> + rskb->data_len += rskb->len;
> + rskb->truesize += rskb->len;
> +
> + /* Avoid nested fragments */
> + for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next)
> + flen += fs->len;
> + skb->next = skb_shinfo(skb)->frag_list;
> + skb_shinfo(skb)->frag_list = NULL;
> + skb->len -= flen;
> + skb->data_len -= flen;
> + skb->truesize -= flen;
> +
> + skb = rskb;
> + }
> +
> + skb->protocol = protocol;
> + skb_reset_mac_header(skb);
> + skb->dev = dev->net;
Perhaps introduce gprs_type_trans() and do:
skb->protocol = gprs_type_trans() ?
Look at:
[acme@doppio linux-2.6]$ grep _type_trans tags | cut -f 1 | sort -u
ax25_type_trans
cisco_type_trans
dvb_net_eth_type_trans
ether1394_type_trans
eth_type_trans
farsync_type_trans
fddi_type_trans
hdlc_type_trans
hippi_type_trans
isdn_net_type_trans
lan_type_trans
mpt_lan_type_trans
myri_type_trans
plip_type_trans
ppp_type_trans
raw_type_trans
tr_type_trans
x25_type_trans
[acme@doppio linux-2.6]$
> +
> + if (likely(dev->net->flags & IFF_UP)) {
> + dev->stats.rx_packets++;
> + dev->stats.rx_bytes += skb->len;
> + netif_rx(skb);
> + skb = NULL;
> + } else
> + err = -ENODEV;
> +
> +drop:
> + if (skb) {
> + dev_kfree_skb(skb);
> + dev->stats.rx_dropped++;
> + }
> + return err;
> +}
> +
> +static void gprs_data_ready(struct sock *sk, int len)
> +{
> + struct gprs_dev *dev = sk->sk_user_data;
> + struct sk_buff *skb;
> +
> + while ((skb = pep_read(sk)) != NULL) {
> + skb_orphan(skb);
> + gprs_recv(dev, skb);
> + }
> +}
> +
> +static void gprs_write_space(struct sock *sk)
> +{
> + struct gprs_dev *dev = sk->sk_user_data;
> + unsigned credits = pep_writeable(sk);
> +
> + spin_lock_bh(&dev->tx_lock);
> + dev->tx_max = credits;
> + if (credits > skb_queue_len(&dev->tx_queue))
> + netif_wake_queue(dev->net);
> + spin_unlock_bh(&dev->tx_lock);
> +}
> +
> +/*
> + * Network device callbacks
> + */
> +
> +static int gprs_xmit(struct sk_buff *skb, struct net_device *net)
> +{
> + struct gprs_dev *dev = netdev_priv(net);
> +
> + switch (skb->protocol) {
> + case htons(ETH_P_IP):
> + case htons(ETH_P_IPV6):
> + break;
> + default:
> + dev_kfree_skb(skb);
> + return 0;
> + }
> +
> + spin_lock(&dev->tx_lock);
> + if (likely(skb_queue_len(&dev->tx_queue) < dev->tx_max)) {
> + skb_queue_tail(&dev->tx_queue, skb);
> + skb = NULL;
> + }
> + if (skb_queue_len(&dev->tx_queue) >= dev->tx_max)
> + netif_stop_queue(net);
> + spin_unlock(&dev->tx_lock);
> +
> + schedule_work(&dev->tx_work);
> + if (unlikely(skb))
> + dev_kfree_skb(skb);
> + return 0;
> +}
> +
> +static void gprs_tx(struct work_struct *work)
> +{
> + struct gprs_dev *dev = container_of(work, struct gprs_dev, tx_work);
> + struct sock *sk = dev->sk;
> + struct sk_buff *skb;
> +
> + while ((skb = skb_dequeue(&dev->tx_queue)) != NULL) {
> + int err;
> +
> + dev->stats.tx_bytes += skb->len;
> + dev->stats.tx_packets++;
> +
> + skb_orphan(skb);
> + skb_set_owner_w(skb, sk);
> +
> + lock_sock(sk);
> + err = pep_write(sk, skb);
> + if (err) {
> + if (net_ratelimit())
> + printk(KERN_WARNING"%s: TX error (%d)\n",
> + dev->net->name, err);
LIMIT_NETDEBUG()
> + dev->stats.tx_aborted_errors++;
> + dev->stats.tx_errors++;
> + }
> + release_sock(sk);
> + }
> +
> + lock_sock(sk);
> + gprs_write_space(sk);
> + release_sock(sk);
> +}
> +
> +static int gprs_set_mtu(struct net_device *net, int new_mtu)
> +{
> + if ((new_mtu < 576) || (new_mtu > (PHONET_MAX_MTU - 11)))
> + return -EINVAL;
> +
> + net->mtu = new_mtu;
> + return 0;
> +}
> +
> +static struct net_device_stats *gprs_get_stats(struct net_device *net)
> +{
> + struct gprs_dev *dev = netdev_priv(net);
> +
> + return &dev->stats;
> +}
> +
> +static void gprs_setup(struct net_device *net)
> +{
> + net->features = NETIF_F_FRAGLIST;
> + net->type = ARPHRD_NONE;
> + net->flags = IFF_POINTOPOINT | IFF_NOARP;
> + net->mtu = GPRS_DEFAULT_MTU;
> + net->hard_header_len = 0;
> + net->addr_len = 0;
> + net->tx_queue_len = 10;
> +
> + net->destructor = free_netdev;
> + net->hard_start_xmit = gprs_xmit; /* mandatory */
> + net->change_mtu = gprs_set_mtu;
> + net->get_stats = gprs_get_stats;
> +}
> +
> +/*
> + * External interface
> + */
> +
> +/*
> + * Attach a GPRS interface to a datagram socket.
> + * Returns the interface index on success, negative error code on error.
> + */
> +int gprs_attach(struct sock *sk)
> +{
> + static const char ifname[] = "gprs%d";
> + struct gprs_dev *dev;
> + struct net_device *net;
> + int err;
> +
> + if (unlikely(sk->sk_type == SOCK_STREAM))
> + return -EINVAL; /* need packet boundaries */
> +
> + /* Create net device */
> + net = alloc_netdev(sizeof(*dev), ifname, gprs_setup);
> + if (!net)
> + return -ENOMEM;
> + dev = netdev_priv(net);
> + dev->net = net;
> + dev->tx_max = 0;
> + spin_lock_init(&dev->tx_lock);
> + skb_queue_head_init(&dev->tx_queue);
> + INIT_WORK(&dev->tx_work, gprs_tx);
> +
> + netif_stop_queue(net);
> + err = register_netdev(net);
> + if (err) {
> + free_netdev(net);
> + return err;
> + }
> +
> + lock_sock(sk);
> + if (unlikely(sk->sk_user_data)) {
> + err = -EBUSY;
> + goto out_rel;
> + }
> + if (unlikely((1 << sk->sk_state & (TCPF_CLOSE|TCPF_LISTEN)) ||
> + sock_flag(sk, SOCK_DEAD))) {
> + err = -EINVAL;
> + goto out_rel;
> + }
> + sk->sk_user_data = dev;
> + dev->old_state_change = sk->sk_state_change;
> + dev->old_data_ready = sk->sk_data_ready;
> + dev->old_write_space = sk->sk_write_space;
> + sk->sk_state_change = gprs_state_change;
> + sk->sk_data_ready = gprs_data_ready;
> + sk->sk_write_space = gprs_write_space;
> + release_sock(sk);
> +
> + sock_hold(sk);
> + dev->sk = sk;
> +
> + printk(KERN_DEBUG"%s: attached\n", net->name);
> + gprs_write_space(sk); /* kick off TX */
> + return net->ifindex;
> +
> +out_rel:
> + release_sock(sk);
> + unregister_netdev(net);
> + return err;
> +}
> +
> +void gprs_detach(struct sock *sk)
> +{
> + struct gprs_dev *dev = sk->sk_user_data;
> + struct net_device *net = dev->net;
> +
> + lock_sock(sk);
> + sk->sk_user_data = NULL;
> + sk->sk_state_change = dev->old_state_change;
> + sk->sk_data_ready = dev->old_data_ready;
> + sk->sk_write_space = dev->old_write_space;
> + release_sock(sk);
> +
> + printk(KERN_DEBUG"%s: detached\n", net->name);
> + unregister_netdev(net);
> + flush_scheduled_work();
> + sock_put(sk);
> + skb_queue_purge(&dev->tx_queue);
> +}
> diff --git a/net/phonet/pep.c b/net/phonet/pep.c
> index 9a2ed45..f0c212f 100644
> --- a/net/phonet/pep.c
> +++ b/net/phonet/pep.c
> @@ -31,6 +31,7 @@
> #include <linux/phonet.h>
> #include <net/phonet/phonet.h>
> #include <net/phonet/pep.h>
> +#include <net/phonet/gprs.h>
>
> /* sk_state values:
> * TCP_CLOSE sock not in use yet
> @@ -610,6 +611,7 @@ drop:
> static void pep_sock_close(struct sock *sk, long timeout)
> {
> struct pep_sock *pn = pep_sk(sk);
> + int ifindex = 0;
>
> sk_common_release(sk);
>
> @@ -623,7 +625,12 @@ static void pep_sock_close(struct sock *sk, long timeout)
> sk_del_node_init(sknode);
> sk->sk_state = TCP_CLOSE;
> }
> + ifindex = pn->ifindex;
> + pn->ifindex = 0;
> release_sock(sk);
> +
> + if (ifindex)
> + gprs_detach(sk);
> }
>
> static int pep_wait_connreq(struct sock *sk, int noblock)
> @@ -728,12 +735,105 @@ static int pep_init(struct sock *sk)
> return 0;
> }
>
> +static int pep_setsockopt(struct sock *sk, int level, int optname,
> + char __user *optval, int optlen)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> + int val = 0, err = 0;
> +
> + if (level != SOL_PNPIPE)
> + return -ENOPROTOOPT;
> + if (optlen >= sizeof(int)) {
> + if (get_user(val, (int __user *) optval))
> + return -EFAULT;
> + }
> +
> + lock_sock(sk);
> + switch (optname) {
> + case PNPIPE_ENCAP:
> + if (val && val != PNPIPE_ENCAP_IP) {
> + err = -EINVAL;
> + break;
> + }
> + if (!pn->ifindex == !val)
> + break; /* Nothing to do! */
> + if (!capable(CAP_NET_ADMIN)) {
> + err = -EPERM;
> + break;
> + }
> + if (val) {
> + release_sock(sk);
> + err = gprs_attach(sk);
> + if (err > 0) {
> + pn->ifindex = err;
> + err = 0;
> + }
> + } else {
> + pn->ifindex = 0;
> + release_sock(sk);
> + gprs_detach(sk);
> + err = 0;
> + }
> + goto out_norel;
> + default:
> + err = -ENOPROTOOPT;
> + }
> + release_sock(sk);
> +
> +out_norel:
> + return err;
> +}
> +
> +static int pep_getsockopt(struct sock *sk, int level, int optname,
> + char __user *optval, int __user *optlen)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> + int len, val;
> +
> + if (level != SOL_PNPIPE)
> + return -ENOPROTOOPT;
> + if (get_user(len, optlen))
> + return -EFAULT;
> +
> + switch (optname) {
> + case PNPIPE_ENCAP:
> + val = pn->ifindex ? PNPIPE_ENCAP_IP : PNPIPE_ENCAP_NONE;
> + break;
> + case PNPIPE_IFINDEX:
> + val = pn->ifindex;
> + break;
> + default:
> + return -ENOPROTOOPT;
> + }
> +
> + len = min_t(unsigned int, sizeof(int), len);
> + if (put_user(len, optlen))
> + return -EFAULT;
> + if (put_user(val, (int __user *) optval))
> + return -EFAULT;
> + return 0;
> +}
> +
> +static int pipe_skb_send(struct sock *sk, struct sk_buff *skb)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> + struct pnpipehdr *ph;
> +
> + ph = (struct pnpipehdr *)skb_push(skb, 3);
skb_reset_transport_header + pnp_hdr()?
> + ph->utid = 0;
> + ph->message_id = PNS_PIPE_DATA;
> + ph->pipe_handle = pn->pipe_handle;
> + if (pn_flow_safe(pn->tx_fc) && pn->tx_credits)
> + pn->tx_credits--;
> +
> + return pn_skb_send(sk, skb, &pipe_srv);
> +}
> +
> static int pep_sendmsg(struct kiocb *iocb, struct sock *sk,
> struct msghdr *msg, size_t len)
> {
> struct pep_sock *pn = pep_sk(sk);
> struct sk_buff *skb = NULL;
> - struct pnpipehdr *ph;
> long timeo;
> int flags = msg->msg_flags;
> int err, done;
> @@ -799,14 +899,7 @@ disabled:
> if (err < 0)
> goto out;
>
> - ph = (struct pnpipehdr *)skb_push(skb, 3);
> - ph->utid = 0;
> - ph->message_id = PNS_PIPE_DATA;
> - ph->pipe_handle = pn->pipe_handle;
> - if (pn_flow_safe(pn->tx_fc)) /* credit-based flow control */
> - pn->tx_credits--;
> -
> - err = pn_skb_send(sk, skb, &pipe_srv);
> + err = pipe_skb_send(sk, skb);
> if (err >= 0)
> err = len; /* success! */
> skb = NULL;
> @@ -817,6 +910,50 @@ out:
> return err;
> }
>
> +int pep_writeable(struct sock *sk)
> +{
> + struct pep_sock *pn = pep_sk(sk);
> +
> + return (sk->sk_state == TCP_ESTABLISHED) ? pn->tx_credits : 0;
> +}
> +
> +int pep_write(struct sock *sk, struct sk_buff *skb)
> +{
> + struct sk_buff *rskb, *fs;
> + int flen = 0;
> +
> + rskb = alloc_skb(MAX_PNPIPE_HEADER, GFP_ATOMIC);
> + if (!rskb) {
> + kfree_skb(skb);
> + return -ENOMEM;
> + }
> + skb_shinfo(rskb)->frag_list = skb;
> + rskb->len += skb->len;
> + rskb->data_len += rskb->len;
> + rskb->truesize += rskb->len;
> +
> + /* Avoid nested fragments */
> + for (fs = skb_shinfo(skb)->frag_list; fs; fs = fs->next)
> + flen += fs->len;
> + skb->next = skb_shinfo(skb)->frag_list;
> + skb_shinfo(skb)->frag_list = NULL;
> + skb->len -= flen;
> + skb->data_len -= flen;
> + skb->truesize -= flen;
> +
> + skb_reserve(rskb, MAX_PHONET_HEADER + 3);
> + return pipe_skb_send(sk, rskb);
> +}
> +
> +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);
> + return skb;
> +}
> +
> static int pep_recvmsg(struct kiocb *iocb, struct sock *sk,
> struct msghdr *msg, size_t len, int noblock,
> int flags, int *addr_len)
> @@ -899,6 +1036,8 @@ static struct proto pep_proto = {
> .accept = pep_sock_accept,
> .ioctl = pep_ioctl,
> .init = pep_init,
> + .setsockopt = pep_setsockopt,
> + .getsockopt = pep_getsockopt,
> .sendmsg = pep_sendmsg,
> .recvmsg = pep_recvmsg,
> .backlog_rcv = pep_do_rcv,
> diff --git a/net/phonet/socket.c b/net/phonet/socket.c
> index a9c3d1f..d817401 100644
> --- a/net/phonet/socket.c
> +++ b/net/phonet/socket.c
> @@ -342,11 +342,11 @@ const struct proto_ops phonet_stream_ops = {
> .ioctl = pn_socket_ioctl,
> .listen = pn_socket_listen,
> .shutdown = sock_no_shutdown,
> - .setsockopt = sock_no_setsockopt,
> - .getsockopt = sock_no_getsockopt,
> + .setsockopt = sock_common_setsockopt,
> + .getsockopt = sock_common_getsockopt,
> #ifdef CONFIG_COMPAT
> - .compat_setsockopt = sock_no_setsockopt,
> - .compat_getsockopt = compat_sock_no_getsockopt,
> + .compat_setsockopt = compat_sock_common_setsockopt,
> + .compat_getsockopt = compat_sock_common_getsockopt,
> #endif
> .sendmsg = pn_socket_sendmsg,
> .recvmsg = sock_common_recvmsg,
> --
> 1.5.4.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe netdev" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] Phonet: transport protocol auto-loading
2008-10-01 12:45 ` Arnaldo Carvalho de Melo
@ 2008-10-01 15:01 ` Marcel Holtmann
2008-10-03 13:49 ` Rémi Denis-Courmont
1 sibling, 0 replies; 22+ messages in thread
From: Marcel Holtmann @ 2008-10-01 15:01 UTC (permalink / raw)
To: Arnaldo Carvalho de Melo; +Cc: Remi Denis-Courmont, netdev
Hi,
> > Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
> > ---
> > net/phonet/af_phonet.c | 5 +++++
> > 1 files changed, 5 insertions(+), 0 deletions(-)
> >
> > diff --git a/net/phonet/af_phonet.c b/net/phonet/af_phonet.c
> > index 1d8df6b..06627d3 100644
> > --- a/net/phonet/af_phonet.c
> > +++ b/net/phonet/af_phonet.c
> > @@ -64,6 +64,11 @@ static int pn_socket_create(struct net *net, struct socket *sock, int protocol)
> > }
> >
> > pnp = phonet_proto_get(protocol);
> > +#ifdef CONFIG_KMOD
> > + if (pnp == NULL
> > + && request_module("net-pf-%d-proto-%d", PF_PHONET, protocol) == 0)
>
> && at the end of previous line, please.
>
> I wonder if we shouldn't have:
>
> net_pf_request_module(PF_PHONET, protocol)
>
> [acme@doppio linux-2.6]$ grep "net-pf-%d-proto-%d" net/*/*.c
> net/ipv4/af_inet.c: request_module("net-pf-%d-proto-%d-type-%d",
> net/ipv4/af_inet.c: request_module("net-pf-%d-proto-%d",
> net/ipv4/inet_diag.c: request_module("net-pf-%d-proto-%d-type-%d", PF_NETLINK,
> net/ipv6/af_inet6.c: request_module("net-pf-%d-proto-%d-type-%d",
> net/ipv6/af_inet6.c: request_module("net-pf-%d-proto-%d",
> net/netlink/af_netlink.c: request_module("net-pf-%d-proto-%d", PF_NETLINK, protocol);
> [acme@doppio linux-2.6]$
>
> Nah, perhaps not...
maybe it is not such a bad idea. Every protocol seems to have its own
code in it. I am using this:
#if defined(CONFIG_KMOD)
if (!bt_proto[proto]) {
request_module("bt-proto-%d", proto);
}
#endif
Regards
Marcel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 6/6] Phonet: pipe end-point protocol documentation
2008-10-01 10:13 ` [PATCH 6/6] Phonet: pipe end-point protocol documentation Remi Denis-Courmont
@ 2008-10-01 15:19 ` Randy Macleod
2008-10-10 18:24 ` Randy Macleod
1 sibling, 0 replies; 22+ messages in thread
From: Randy Macleod @ 2008-10-01 15:19 UTC (permalink / raw)
To: Remi Denis-Courmont; +Cc: netdev
Remi Denis-Courmont wrote:
> From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
...
> +WARNING:
> +When polling a connected pipe socket for writability, there is an
> +intrinsic race condition whereby writability might be lost between the
> +polling and the writing system calls. In this case, the socket will
> +block until write because possible again, unless non-blocking mode is
---------------------becomes
> +enabled.
// Randy
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 3/6] Phonet: Pipe End Point for Phonet Pipes protocol
2008-10-01 13:18 ` Arnaldo Carvalho de Melo
@ 2008-10-02 10:50 ` Rémi Denis-Courmont
2008-10-02 13:41 ` Arnaldo Carvalho de Melo
0 siblings, 1 reply; 22+ messages in thread
From: Rémi Denis-Courmont @ 2008-10-02 10:50 UTC (permalink / raw)
To: ext Arnaldo Carvalho de Melo; +Cc: netdev
On Wednesday 01 October 2008 16:18:56 ext Arnaldo Carvalho de Melo, you wrote:
> > +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);
>
> What is the lock that protects this list traversal?
Either (accepted or unaccepted) sock lists should only be used with the sock
lock of the listening sock. Is that insufficient?
> > +static int pep_wait_connreq(struct sock *sk, int noblock)
>
> This function looks familiar... inet_csk_accept,
> inet_csk_wait_for_connect...
I don't recall why I gave up on using request_sock and listen_sock there.
> perhaps we need a connection_sock father
> for inet_connection_sock? :-)
But you cannot have double inheritance, right? inet_sock and
connection_sock... I guess that's why listen_sock is _not_ a sock.
> > +{
> > + struct task_struct *tsk = current;
> > + struct pep_sock *pn = pep_sk(sk);
> > + long timeo = sock_rcvtimeo(sk, noblock);
> > +
> > + for (;;) {
> > + DEFINE_WAIT(wait);
> > +
> > + 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);
> > +
> > + prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait,
> > + TASK_INTERRUPTIBLE);
> > + release_sock(sk);
> > + timeo = schedule_timeout(timeo);
> > + lock_sock(sk);
> > + finish_wait(&sk->sk_socket->wait, &wait);
> > + }
> > +
> > + return 0;
> > +}
> > +
> > +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;
> > +
> > + lock_sock(sk);
> > + err = pep_wait_connreq(sk, flags & O_NONBLOCK);
> > + if (err)
> > + goto out;
> > +
> > + newsk = __sk_head(&pn->ackq);
> > +
> > + oskb = skb_dequeue(&newsk->sk_receive_queue);
> > + err = pep_accept_conn(newsk, oskb);
> > + if (err) {
> > + skb_queue_head(&newsk->sk_receive_queue, oskb);
> > + newsk = NULL;
> > + goto out;
> > + }
> > +
> > + sock_hold(sk);
> > + pep_sk(newsk)->listener = sk;
> > +
> > + sock_hold(newsk);
> > + sk_del_node_init(newsk);
> > + sk_acceptq_removed(sk);
> > + sk_add_node(newsk, &pn->hlist);
> > + __sock_put(newsk);
> > +
> > +out:
> > + release_sock(sk);
> > + *errp = err;
> > + return newsk;
> > +}
> > +
> > +static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
> > +{
> > + int answ;
> > +
> > + switch (cmd) {
> > + case SIOCINQ:
> > + if (sk->sk_state == TCP_LISTEN)
> > + return -EINVAL;
> > +
> > + lock_sock(sk);
> > + if (!skb_queue_empty(&sk->sk_receive_queue))
> > + answ = skb_peek(&sk->sk_receive_queue)->len;
> > + else
> > + answ = 0;
> > + release_sock(sk);
> > + return put_user(answ, (int __user *)arg);
>
> this is so common I wonder we if a helper wouldn't help 8) Look at
> dccp_ioctl before Ilpo does 8)
It is not so common with the next patch which checks for urgent inline... As
far as I know, there is no common queue for out-of-band data.
--
Rémi Denis-Courmont
Maemo Software, Nokia Devices R&D
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 3/6] Phonet: Pipe End Point for Phonet Pipes protocol
2008-10-02 10:50 ` Rémi Denis-Courmont
@ 2008-10-02 13:41 ` Arnaldo Carvalho de Melo
0 siblings, 0 replies; 22+ messages in thread
From: Arnaldo Carvalho de Melo @ 2008-10-02 13:41 UTC (permalink / raw)
To: Rémi Denis-Courmont; +Cc: netdev
Em Thu, Oct 02, 2008 at 01:50:52PM +0300, Rémi Denis-Courmont escreveu:
> On Wednesday 01 October 2008 16:18:56 ext Arnaldo Carvalho de Melo, you wrote:
> > > +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);
> >
> > What is the lock that protects this list traversal?
>
> Either (accepted or unaccepted) sock lists should only be used with the sock
> lock of the listening sock. Is that insufficient?
>
> > > +static int pep_wait_connreq(struct sock *sk, int noblock)
> >
> > This function looks familiar... inet_csk_accept,
> > inet_csk_wait_for_connect...
>
> I don't recall why I gave up on using request_sock and listen_sock there.
>
> > perhaps we need a connection_sock father
> > for inet_connection_sock? :-)
>
> But you cannot have double inheritance, right? inet_sock and
> connection_sock... I guess that's why listen_sock is _not_ a sock.
I'm not saying that you could use in its current form, only that it
looks as something to think about after you get phonet merged and
polished.
> > > +{
> > > + struct task_struct *tsk = current;
> > > + struct pep_sock *pn = pep_sk(sk);
> > > + long timeo = sock_rcvtimeo(sk, noblock);
> > > +
> > > + for (;;) {
> > > + DEFINE_WAIT(wait);
> > > +
> > > + 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);
> > > +
> > > + prepare_to_wait_exclusive(&sk->sk_socket->wait, &wait,
> > > + TASK_INTERRUPTIBLE);
> > > + release_sock(sk);
> > > + timeo = schedule_timeout(timeo);
> > > + lock_sock(sk);
> > > + finish_wait(&sk->sk_socket->wait, &wait);
> > > + }
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +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;
> > > +
> > > + lock_sock(sk);
> > > + err = pep_wait_connreq(sk, flags & O_NONBLOCK);
> > > + if (err)
> > > + goto out;
> > > +
> > > + newsk = __sk_head(&pn->ackq);
> > > +
> > > + oskb = skb_dequeue(&newsk->sk_receive_queue);
> > > + err = pep_accept_conn(newsk, oskb);
> > > + if (err) {
> > > + skb_queue_head(&newsk->sk_receive_queue, oskb);
> > > + newsk = NULL;
> > > + goto out;
> > > + }
> > > +
> > > + sock_hold(sk);
> > > + pep_sk(newsk)->listener = sk;
> > > +
> > > + sock_hold(newsk);
> > > + sk_del_node_init(newsk);
> > > + sk_acceptq_removed(sk);
> > > + sk_add_node(newsk, &pn->hlist);
> > > + __sock_put(newsk);
> > > +
> > > +out:
> > > + release_sock(sk);
> > > + *errp = err;
> > > + return newsk;
> > > +}
> > > +
> > > +static int pep_ioctl(struct sock *sk, int cmd, unsigned long arg)
> > > +{
> > > + int answ;
> > > +
> > > + switch (cmd) {
> > > + case SIOCINQ:
> > > + if (sk->sk_state == TCP_LISTEN)
> > > + return -EINVAL;
> > > +
> > > + lock_sock(sk);
> > > + if (!skb_queue_empty(&sk->sk_receive_queue))
> > > + answ = skb_peek(&sk->sk_receive_queue)->len;
> > > + else
> > > + answ = 0;
> > > + release_sock(sk);
> > > + return put_user(answ, (int __user *)arg);
> >
> > this is so common I wonder we if a helper wouldn't help 8) Look at
> > dccp_ioctl before Ilpo does 8)
>
> It is not so common with the next patch which checks for urgent inline... As
> far as I know, there is no common queue for out-of-band data.
I saw that, but out-of-band data is also something up for study wheter
introductin common infrastructure is feasible.
- Arnaldo
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 1/6] Phonet: transport protocol auto-loading
2008-10-01 12:45 ` Arnaldo Carvalho de Melo
2008-10-01 15:01 ` Marcel Holtmann
@ 2008-10-03 13:49 ` Rémi Denis-Courmont
1 sibling, 0 replies; 22+ messages in thread
From: Rémi Denis-Courmont @ 2008-10-03 13:49 UTC (permalink / raw)
To: ext Arnaldo Carvalho de Melo; +Cc: netdev
On Wednesday 01 October 2008 15:45:43 ext Arnaldo Carvalho de Melo, you wrote:
> I wonder if we shouldn't have:
>
> net_pf_request_module(PF_PHONET, protocol)
>
> [acme@doppio linux-2.6]$ grep "net-pf-%d-proto-%d" net/*/*.c
> net/ipv4/af_inet.c: request_module("net-pf-%d-proto-%d-type-%d",
> net/ipv4/af_inet.c: request_module("net-pf-%d-proto-%d",
> net/ipv4/inet_diag.c: request_module("net-pf-%d-proto-%d-type-%d",
> PF_NETLINK, net/ipv6/af_inet6.c:
> request_module("net-pf-%d-proto-%d-type-%d",
> net/ipv6/af_inet6.c: request_module("net-pf-%d-proto-%d",
> net/netlink/af_netlink.c: request_module("net-pf-%d-proto-%d", PF_NETLINK,
> protocol);
> [acme@doppio linux-2.6]$
>
> Nah, perhaps not...
Looking at it, I could not find a "vrequest_module", which seems to preclude a
functional implementation?? Not sure if a macro makes much sense here.
--
Rémi Denis-Courmont
Maemo Software, Nokia Devices R&D
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH 6/6] Phonet: pipe end-point protocol documentation
2008-10-03 14:09 [PATCH 0/6] Phonet pipes protocol (take 3) Rémi Denis-Courmont
@ 2008-10-03 14:09 ` Remi Denis-Courmont
0 siblings, 0 replies; 22+ messages in thread
From: Remi Denis-Courmont @ 2008-10-03 14:09 UTC (permalink / raw)
To: netdev; +Cc: Rémi Denis-Courmont
From: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
Signed-off-by: Rémi Denis-Courmont <remi.denis-courmont@nokia.com>
---
Documentation/networking/phonet.txt | 54 +++++++++++++++++++++++++++++++++++
1 files changed, 54 insertions(+), 0 deletions(-)
diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt
index 57d3e59..0e6e592 100644
--- a/Documentation/networking/phonet.txt
+++ b/Documentation/networking/phonet.txt
@@ -112,6 +112,60 @@ However, connect() and getpeername() are not supported, as they did
not seem useful with Phonet usages (could be added easily).
+Phonet Pipe protocol
+--------------------
+
+The Phonet Pipe protocol is a simple sequenced packets protocol
+with end-to-end congestion control. It uses the passive listening
+socket paradigm. The listening socket is bound to an unique free object
+ID. Each listening socket can handle up to 255 simultaneous
+connections, one per accept()'d socket.
+
+ int lfd, cfd;
+
+ lfd = socket(PF_PHONET, SOCK_SEQPACKET, PN_PROTO_PIPE);
+ listen (lfd, INT_MAX);
+
+ /* ... */
+ cfd = accept(lfd, NULL, NULL);
+ for (;;)
+ {
+ char buf[...];
+ ssize_t len = read(cfd, buf, sizeof(buf));
+
+ /* ... */
+
+ write(cfd, msg, msglen);
+ }
+
+Connections are established between two endpoints by a "third party"
+application. This means that both endpoints are passive; so connect()
+is not possible.
+
+WARNING:
+When polling a connected pipe socket for writability, there is an
+intrinsic race condition whereby writability might be lost between the
+polling and the writing system calls. In this case, the socket will
+block until write because possible again, unless non-blocking mode
+becomes enabled.
+
+
+The pipe protocol provides two socket options at the SOL_PNPIPE level:
+
+ PNPIPE_ENCAP accepts one integer value (int) of:
+
+ PNPIPE_ENCAP_NONE: The socket operates normally (default).
+
+ PNPIPE_ENCAP_IP: The socket is used as a backend for a virtual IP
+ interface. This requires CAP_NET_ADMIN capability. GPRS data
+ support on Nokia modems can use this. Note that the socket cannot
+ be reliably poll()'d or read() from while in this mode.
+
+ PNPIPE_IFINDEX is a read-only integer value. It contains the
+ interface index of the network interface created by PNPIPE_ENCAP,
+ or zero if encapsulation is off.
+
+
Authors
-------
--
1.5.4.3
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 6/6] Phonet: pipe end-point protocol documentation
2008-10-01 10:13 ` [PATCH 6/6] Phonet: pipe end-point protocol documentation Remi Denis-Courmont
2008-10-01 15:19 ` Randy Macleod
@ 2008-10-10 18:24 ` Randy Macleod
2008-10-11 19:30 ` David Miller
1 sibling, 1 reply; 22+ messages in thread
From: Randy Macleod @ 2008-10-10 18:24 UTC (permalink / raw)
To: netdev; +Cc: Remi Denis-Courmont
Minor documentation fix.
Is this patch formatted as desired (first patch!)?
I'm using Thunderbird.
// Randy
Signed-off-by: Randy MacLeod <macleodr@nortel.com>
git diff Documentation/networking/phonet.txt
diff --git a/Documentation/networking/phonet.txt
b/Documentation/networking/phonet.txt
index 0e6e592..6a07e45 100644
--- a/Documentation/networking/phonet.txt
+++ b/Documentation/networking/phonet.txt
@@ -146,8 +146,8 @@ WARNING:
When polling a connected pipe socket for writability, there is an
intrinsic race condition whereby writability might be lost between the
polling and the writing system calls. In this case, the socket will
-block until write because possible again, unless non-blocking mode
-becomes enabled.
+block until write becomes possible again, unless non-blocking mode
+is enabled.
The pipe protocol provides two socket options at the SOL_PNPIPE level:
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 6/6] Phonet: pipe end-point protocol documentation
2008-10-10 18:24 ` Randy Macleod
@ 2008-10-11 19:30 ` David Miller
2008-10-14 15:06 ` Randy Macleod
2008-10-14 15:11 ` Randy Macleod
0 siblings, 2 replies; 22+ messages in thread
From: David Miller @ 2008-10-11 19:30 UTC (permalink / raw)
To: macleodr; +Cc: netdev, remi.denis-courmont
From: "Randy Macleod" <macleodr@nortel.com>
Date: Fri, 10 Oct 2008 14:24:39 -0400
> Minor documentation fix.
> Is this patch formatted as desired (first patch!)?
> I'm using Thunderbird.
It corrupted the patch, chopping up long lines and such.
There are directions in linux/Documentation/email-clients.txt on
how to fix this.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH 6/6] Phonet: pipe end-point protocol documentation
2008-10-11 19:30 ` David Miller
@ 2008-10-14 15:06 ` Randy Macleod
2008-10-14 20:49 ` David Miller
2008-10-14 15:11 ` Randy Macleod
1 sibling, 1 reply; 22+ messages in thread
From: Randy Macleod @ 2008-10-14 15:06 UTC (permalink / raw)
To: David Miller; +Cc: netdev, remi.denis-courmont
David Miller wrote:
> It corrupted the patch, chopping up long lines and such.
>
> There are directions in linux/Documentation/email-clients.txt on
> how to fix this.
>
Fixed the line wrapping and RTFM.
Sending a test message to myself worked.
Here's the simple documentation patch, it's against:
git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
// Randy
diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt
index 0e6e592..6a07e45 100644
--- a/Documentation/networking/phonet.txt
+++ b/Documentation/networking/phonet.txt
@@ -146,8 +146,8 @@ WARNING:
When polling a connected pipe socket for writability, there is an
intrinsic race condition whereby writability might be lost between the
polling and the writing system calls. In this case, the socket will
-block until write because possible again, unless non-blocking mode
-becomes enabled.
+block until write becomes possible again, unless non-blocking mode
+is enabled.
The pipe protocol provides two socket options at the SOL_PNPIPE level:
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 6/6] Phonet: pipe end-point protocol documentation
2008-10-11 19:30 ` David Miller
2008-10-14 15:06 ` Randy Macleod
@ 2008-10-14 15:11 ` Randy Macleod
1 sibling, 0 replies; 22+ messages in thread
From: Randy Macleod @ 2008-10-14 15:11 UTC (permalink / raw)
To: David Miller; +Cc: netdev, remi.denis-courmont
Added the SOB line.
David Miller wrote:
> It corrupted the patch, chopping up long lines and such.
>
> There are directions in linux/Documentation/email-clients.txt on
> how to fix this.
>
Fixed the line wrapping and RTFM.
Sending a test message to myself worked.
Here's the simple documentation patch, it's against:
git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
Signed-off-by: Randy MacLeod <macleodr@nortel.com>
// Randy
diff --git a/Documentation/networking/phonet.txt b/Documentation/networking/phonet.txt
index 0e6e592..6a07e45 100644
--- a/Documentation/networking/phonet.txt
+++ b/Documentation/networking/phonet.txt
@@ -146,8 +146,8 @@ WARNING:
When polling a connected pipe socket for writability, there is an
intrinsic race condition whereby writability might be lost between the
polling and the writing system calls. In this case, the socket will
-block until write because possible again, unless non-blocking mode
-becomes enabled.
+block until write becomes possible again, unless non-blocking mode
+is enabled.
The pipe protocol provides two socket options at the SOL_PNPIPE level:
^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [PATCH 6/6] Phonet: pipe end-point protocol documentation
2008-10-14 15:06 ` Randy Macleod
@ 2008-10-14 20:49 ` David Miller
0 siblings, 0 replies; 22+ messages in thread
From: David Miller @ 2008-10-14 20:49 UTC (permalink / raw)
To: macleodr; +Cc: netdev, remi.denis-courmont
From: "Randy Macleod" <macleodr@nortel.com>
Date: Tue, 14 Oct 2008 11:06:18 -0400
> David Miller wrote:
> > It corrupted the patch, chopping up long lines and such.
> >
> > There are directions in linux/Documentation/email-clients.txt on
> > how to fix this.
> >
> Fixed the line wrapping and RTFM.
> Sending a test message to myself worked.
>
> Here's the simple documentation patch, it's against:
> git://git.kernel.org/pub/scm/linux/kernel/git/davem/net-next-2.6
Applied, please provide a proper "Signed-off-by:" line in future
submissions.
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2008-10-14 20:50 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-10-01 10:12 [PATCH 0/6] [RFC] Phonet pipes protocol (v2) Rémi Denis-Courmont
2008-10-01 10:13 ` [PATCH 1/6] Phonet: transport protocol auto-loading Remi Denis-Courmont
2008-10-01 12:45 ` Arnaldo Carvalho de Melo
2008-10-01 15:01 ` Marcel Holtmann
2008-10-03 13:49 ` Rémi Denis-Courmont
2008-10-01 10:13 ` [PATCH 2/6] Phonet: connected sockets glue Remi Denis-Courmont
2008-10-01 12:48 ` Arnaldo Carvalho de Melo
2008-10-01 10:13 ` [PATCH 3/6] Phonet: Pipe End Point for Phonet Pipes protocol Remi Denis-Courmont
2008-10-01 13:18 ` Arnaldo Carvalho de Melo
2008-10-02 10:50 ` Rémi Denis-Courmont
2008-10-02 13:41 ` Arnaldo Carvalho de Melo
2008-10-01 10:13 ` [PATCH 4/6] Phonet: receive pipe control requests as out-of-band data Remi Denis-Courmont
2008-10-01 10:13 ` [PATCH 5/6] Phonet: implement GPRS virtual interface over PEP socket Remi Denis-Courmont
2008-10-01 13:32 ` Arnaldo Carvalho de Melo
2008-10-01 10:13 ` [PATCH 6/6] Phonet: pipe end-point protocol documentation Remi Denis-Courmont
2008-10-01 15:19 ` Randy Macleod
2008-10-10 18:24 ` Randy Macleod
2008-10-11 19:30 ` David Miller
2008-10-14 15:06 ` Randy Macleod
2008-10-14 20:49 ` David Miller
2008-10-14 15:11 ` Randy Macleod
-- strict thread matches above, loose matches on Subject: below --
2008-10-03 14:09 [PATCH 0/6] Phonet pipes protocol (take 3) Rémi Denis-Courmont
2008-10-03 14:09 ` [PATCH 6/6] Phonet: pipe end-point protocol documentation Remi Denis-Courmont
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).