From aed20a53a7d91e45c6a8cb8920c77aaaa88f76ee Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Sun, 16 Jul 2017 16:43:46 -0700 Subject: rds: cancel send/recv work before queuing connection shutdown We could end up executing rds_conn_shutdown before the rds_recv_worker thread, then rds_conn_shutdown -> rds_tcp_conn_shutdown can do a sock_release and set sock->sk to null, which may interleave in bad ways with rds_recv_worker, e.g., it could result in: "BUG: unable to handle kernel NULL pointer dereference at 0000000000000078" [ffff881769f6fd70] release_sock at ffffffff815f337b [ffff881769f6fd90] rds_tcp_recv at ffffffffa043c888 [rds_tcp] [ffff881769f6fdb0] rds_recv_worker at ffffffffa04a4810 [rds] [ffff881769f6fde0] process_one_work at ffffffff810a14c1 [ffff881769f6fe40] worker_thread at ffffffff810a1940 [ffff881769f6fec0] kthread at ffffffff810a6b1e Also, do not enqueue any new shutdown workq items when the connection is shutting down (this may happen for rds-tcp in softirq mode, if a FIN or CLOSE is received while the modules is in the middle of an unload) Signed-off-by: Sowmini Varadhan Signed-off-by: David S. Miller --- net/rds/connection.c | 16 ++++++++++------ net/rds/rds.h | 2 +- net/rds/tcp.c | 2 +- net/rds/tcp_connect.c | 4 ++-- net/rds/tcp_send.c | 2 +- net/rds/threads.c | 2 +- 6 files changed, 16 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/rds/connection.c b/net/rds/connection.c index 50a3789ac23e..005bca68aa94 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -374,13 +374,13 @@ static void rds_conn_path_destroy(struct rds_conn_path *cp) if (!cp->cp_transport_data) return; - rds_conn_path_drop(cp); - flush_work(&cp->cp_down_w); - /* make sure lingering queued work won't try to ref the conn */ cancel_delayed_work_sync(&cp->cp_send_w); cancel_delayed_work_sync(&cp->cp_recv_w); + rds_conn_path_drop(cp, true); + flush_work(&cp->cp_down_w); + /* tear down queued messages */ list_for_each_entry_safe(rm, rtmp, &cp->cp_send_queue, @@ -664,9 +664,13 @@ void rds_conn_exit(void) /* * Force a disconnect */ -void rds_conn_path_drop(struct rds_conn_path *cp) +void rds_conn_path_drop(struct rds_conn_path *cp, bool destroy) { atomic_set(&cp->cp_state, RDS_CONN_ERROR); + + if (!destroy && cp->cp_conn->c_destroy_in_prog) + return; + queue_work(rds_wq, &cp->cp_down_w); } EXPORT_SYMBOL_GPL(rds_conn_path_drop); @@ -674,7 +678,7 @@ EXPORT_SYMBOL_GPL(rds_conn_path_drop); void rds_conn_drop(struct rds_connection *conn) { WARN_ON(conn->c_trans->t_mp_capable); - rds_conn_path_drop(&conn->c_path[0]); + rds_conn_path_drop(&conn->c_path[0], false); } EXPORT_SYMBOL_GPL(rds_conn_drop); @@ -706,5 +710,5 @@ __rds_conn_path_error(struct rds_conn_path *cp, const char *fmt, ...) vprintk(fmt, ap); va_end(ap); - rds_conn_path_drop(cp); + rds_conn_path_drop(cp, false); } diff --git a/net/rds/rds.h b/net/rds/rds.h index 516bcc89b46f..3382695bf46c 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -700,7 +700,7 @@ struct rds_connection *rds_conn_create_outgoing(struct net *net, void rds_conn_shutdown(struct rds_conn_path *cpath); void rds_conn_destroy(struct rds_connection *conn); void rds_conn_drop(struct rds_connection *conn); -void rds_conn_path_drop(struct rds_conn_path *cpath); +void rds_conn_path_drop(struct rds_conn_path *cpath, bool destroy); void rds_conn_connect_if_down(struct rds_connection *conn); void rds_conn_path_connect_if_down(struct rds_conn_path *cp); void rds_for_each_conn_info(struct socket *sock, unsigned int len, diff --git a/net/rds/tcp.c b/net/rds/tcp.c index 431404dbdad1..6b7ee71f40c6 100644 --- a/net/rds/tcp.c +++ b/net/rds/tcp.c @@ -592,7 +592,7 @@ static void rds_tcp_sysctl_reset(struct net *net) continue; /* reconnect with new parameters */ - rds_conn_path_drop(tc->t_cpath); + rds_conn_path_drop(tc->t_cpath, false); } spin_unlock_irq(&rds_tcp_conn_lock); } diff --git a/net/rds/tcp_connect.c b/net/rds/tcp_connect.c index cbe08a1fa4c7..46f74dad0e16 100644 --- a/net/rds/tcp_connect.c +++ b/net/rds/tcp_connect.c @@ -69,14 +69,14 @@ void rds_tcp_state_change(struct sock *sk) if (!IS_CANONICAL(cp->cp_conn->c_laddr, cp->cp_conn->c_faddr) && rds_conn_path_transition(cp, RDS_CONN_CONNECTING, RDS_CONN_ERROR)) { - rds_conn_path_drop(cp); + rds_conn_path_drop(cp, false); } else { rds_connect_path_complete(cp, RDS_CONN_CONNECTING); } break; case TCP_CLOSE_WAIT: case TCP_CLOSE: - rds_conn_path_drop(cp); + rds_conn_path_drop(cp, false); default: break; } diff --git a/net/rds/tcp_send.c b/net/rds/tcp_send.c index 0d8616aa5bad..dc860d1bb608 100644 --- a/net/rds/tcp_send.c +++ b/net/rds/tcp_send.c @@ -157,7 +157,7 @@ out: "returned %d, " "disconnecting and reconnecting\n", &conn->c_faddr, cp->cp_index, ret); - rds_conn_path_drop(cp); + rds_conn_path_drop(cp, false); } } } diff --git a/net/rds/threads.c b/net/rds/threads.c index 2852bc1d37d4..f121daa402c8 100644 --- a/net/rds/threads.c +++ b/net/rds/threads.c @@ -78,7 +78,7 @@ void rds_connect_path_complete(struct rds_conn_path *cp, int curr) "current state is %d\n", __func__, atomic_read(&cp->cp_state)); - rds_conn_path_drop(cp); + rds_conn_path_drop(cp, false); return; } -- cgit v1.2.3 From a38905e6aa1a758af003b80f3318196eadb86dfe Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 17 Jul 2017 11:29:49 +0800 Subject: sctp: remove the typedef sctp_ipv4addr_param_t This patch is to remove the typedef sctp_ipv4addr_param_t, and replace with struct sctp_ipv4addr_param in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- net/sctp/protocol.c | 2 +- net/sctp/sm_make_chunk.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 99e866487e2f..e42095d7ce57 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -273,10 +273,10 @@ struct sctp_init_chunk { /* Section 3.3.2.1. IPv4 Address Parameter (5) */ -typedef struct sctp_ipv4addr_param { +struct sctp_ipv4addr_param { struct sctp_paramhdr param_hdr; - struct in_addr addr; -} sctp_ipv4addr_param_t; + struct in_addr addr; +}; /* Section 3.3.2.1. IPv6 Address Parameter (6) */ typedef struct sctp_ipv6addr_param { diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 989a900383b5..852556d67ae3 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -292,7 +292,7 @@ static void sctp_v4_from_addr_param(union sctp_addr *addr, static int sctp_v4_to_addr_param(const union sctp_addr *addr, union sctp_addr_param *param) { - int length = sizeof(sctp_ipv4addr_param_t); + int length = sizeof(struct sctp_ipv4addr_param); param->v4.param_hdr.type = SCTP_PARAM_IPV4_ADDRESS; param->v4.param_hdr.length = htons(length); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 4e16b02ed832..0dc64da74d55 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3153,7 +3153,7 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, case SCTP_PARAM_ERR_CAUSE: break; case SCTP_PARAM_IPV4_ADDRESS: - if (length != sizeof(sctp_ipv4addr_param_t)) + if (length != sizeof(struct sctp_ipv4addr_param)) return false; /* ensure there is only one addr param and it's in the * beginning of addip_hdr params, or we reject it. -- cgit v1.2.3 From 00987cc07e3f0f01699800cd89adf13a908cdee5 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 17 Jul 2017 11:29:50 +0800 Subject: sctp: remove the typedef sctp_ipv6addr_param_t This patch is to remove the typedef sctp_ipv6addr_param_t, and replace with struct sctp_ipv6addr_param in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/ipv6.c | 2 +- net/sctp/sm_make_chunk.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index e42095d7ce57..6b45c8a38642 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -279,10 +279,10 @@ struct sctp_ipv4addr_param { }; /* Section 3.3.2.1. IPv6 Address Parameter (6) */ -typedef struct sctp_ipv6addr_param { +struct sctp_ipv6addr_param { struct sctp_paramhdr param_hdr; struct in6_addr addr; -} sctp_ipv6addr_param_t; +}; /* Section 3.3.2.1 Cookie Preservative (9) */ typedef struct sctp_cookie_preserve_param { diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 2a186b201ad2..107d7c912922 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -497,7 +497,7 @@ static void sctp_v6_from_addr_param(union sctp_addr *addr, static int sctp_v6_to_addr_param(const union sctp_addr *addr, union sctp_addr_param *param) { - int length = sizeof(sctp_ipv6addr_param_t); + int length = sizeof(struct sctp_ipv6addr_param); param->v6.param_hdr.type = SCTP_PARAM_IPV6_ADDRESS; param->v6.param_hdr.length = htons(length); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 0dc64da74d55..e5be305072b5 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3163,7 +3163,7 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, addr_param_seen = true; break; case SCTP_PARAM_IPV6_ADDRESS: - if (length != sizeof(sctp_ipv6addr_param_t)) + if (length != sizeof(struct sctp_ipv6addr_param)) return false; if (param.v != addip->addip_hdr.params) return false; -- cgit v1.2.3 From 365ddb65e77f6b99d4aba09e0d8a096aada57815 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 17 Jul 2017 11:29:51 +0800 Subject: sctp: remove the typedef sctp_cookie_preserve_param_t This patch is to remove the typedef sctp_cookie_preserve_param_t, and replace with struct sctp_cookie_preserve_param in the places where it's using this typedef. It is also to fix some indents in sctp_sf_do_5_2_6_stale(). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- net/sctp/sm_statefuns.c | 11 +++++------ 2 files changed, 8 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 6b45c8a38642..d8f9d8f8649b 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -285,10 +285,10 @@ struct sctp_ipv6addr_param { }; /* Section 3.3.2.1 Cookie Preservative (9) */ -typedef struct sctp_cookie_preserve_param { +struct sctp_cookie_preserve_param { struct sctp_paramhdr param_hdr; - __be32 lifespan_increment; -} sctp_cookie_preserve_param_t; + __be32 lifespan_increment; +}; /* Section 3.3.2.1 Host Name Address (11) */ typedef struct sctp_hostname_param { diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index b2a74c3823ee..ae4c48c4f657 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2336,13 +2336,12 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, void *arg, sctp_cmd_seq_t *commands) { - struct sctp_chunk *chunk = arg; - u32 stale; - sctp_cookie_preserve_param_t bht; - sctp_errhdr_t *err; - struct sctp_chunk *reply; - struct sctp_bind_addr *bp; int attempts = asoc->init_err_counter + 1; + struct sctp_chunk *chunk = arg, *reply; + struct sctp_cookie_preserve_param bht; + struct sctp_bind_addr *bp; + sctp_errhdr_t *err; + u32 stale; if (attempts > asoc->max_init_attempts) { sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, -- cgit v1.2.3 From e925d506f1a21f7fd24a8fdd3e73e0810c655de4 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 17 Jul 2017 11:29:53 +0800 Subject: sctp: remove the typedef sctp_supported_addrs_param_t This patch is to remove the typedef sctp_supported_addrs_param_t, and replace with struct sctp_supported_addrs_param in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_make_chunk.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index c43e9067d41a..3ca3ab7302a6 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -297,10 +297,10 @@ struct sctp_hostname_param { }; /* Section 3.3.2.1 Supported Address Types (12) */ -typedef struct sctp_supported_addrs_param { +struct sctp_supported_addrs_param { struct sctp_paramhdr param_hdr; __be16 types[0]; -} sctp_supported_addrs_param_t; +}; /* Appendix A. ECN Capable (32768) */ typedef struct sctp_ecn_capable_param { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index e5be305072b5..fb06d4fd0515 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -223,7 +223,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, struct sctp_chunk *retval = NULL; int num_types, addrs_len = 0; struct sctp_sock *sp; - sctp_supported_addrs_param_t sat; + struct sctp_supported_addrs_param sat; __be16 types[2]; sctp_adaptation_ind_param_t aiparam; sctp_supported_ext_param_t ext_param; -- cgit v1.2.3 From 85f6bd24ac579ef0926eb4c564ba1f3c8a7f8563 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 17 Jul 2017 11:29:55 +0800 Subject: sctp: remove the typedef sctp_adaptation_ind_param_t This patch is to remove the typedef sctp_adaptation_ind_param_t, and replace with struct sctp_adaptation_ind_param in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_make_chunk.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 75524829aa81..72b87874ea76 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -303,10 +303,10 @@ struct sctp_supported_addrs_param { }; /* ADDIP Section 3.2.6 Adaptation Layer Indication */ -typedef struct sctp_adaptation_ind_param { +struct sctp_adaptation_ind_param { struct sctp_paramhdr param_hdr; __be32 adaptation_ind; -} sctp_adaptation_ind_param_t; +}; /* ADDIP Section 4.2.7 Supported Extensions Parameter */ typedef struct sctp_supported_ext_param { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index fb06d4fd0515..d5f82c2f8c84 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -225,7 +225,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, struct sctp_sock *sp; struct sctp_supported_addrs_param sat; __be16 types[2]; - sctp_adaptation_ind_param_t aiparam; + struct sctp_adaptation_ind_param aiparam; sctp_supported_ext_param_t ext_param; int num_ext = 0; __u8 extensions[3]; @@ -393,7 +393,7 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, sctp_cookie_param_t *cookie; int cookie_len; size_t chunksize; - sctp_adaptation_ind_param_t aiparam; + struct sctp_adaptation_ind_param aiparam; sctp_supported_ext_param_t ext_param; int num_ext = 0; __u8 extensions[3]; -- cgit v1.2.3 From 15328d9feede450d64ff77cac5d25bc734ec8b27 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 17 Jul 2017 11:29:56 +0800 Subject: sctp: remove the typedef sctp_supported_ext_param_t This patch is to remove the typedef sctp_supported_ext_param_t, and replace with struct sctp_supported_ext_param in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_make_chunk.c | 22 ++++++++-------------- 2 files changed, 10 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 72b87874ea76..76245685f923 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -309,10 +309,10 @@ struct sctp_adaptation_ind_param { }; /* ADDIP Section 4.2.7 Supported Extensions Parameter */ -typedef struct sctp_supported_ext_param { +struct sctp_supported_ext_param { struct sctp_paramhdr param_hdr; __u8 chunks[0]; -} sctp_supported_ext_param_t; +}; /* AUTH Section 3.1 Random */ typedef struct sctp_random_param { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index d5f82c2f8c84..06dc351b6ba1 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -226,7 +226,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, struct sctp_supported_addrs_param sat; __be16 types[2]; struct sctp_adaptation_ind_param aiparam; - sctp_supported_ext_param_t ext_param; + struct sctp_supported_ext_param ext_param; int num_ext = 0; __u8 extensions[3]; struct sctp_paramhdr *auth_chunks = NULL, @@ -305,8 +305,7 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, /* If we have any extensions to report, account for that */ if (num_ext) - chunksize += SCTP_PAD4(sizeof(sctp_supported_ext_param_t) + - num_ext); + chunksize += SCTP_PAD4(sizeof(ext_param) + num_ext); /* RFC 2960 3.3.2 Initiation (INIT) (1) * @@ -348,10 +347,8 @@ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, */ if (num_ext) { ext_param.param_hdr.type = SCTP_PARAM_SUPPORTED_EXT; - ext_param.param_hdr.length = - htons(sizeof(sctp_supported_ext_param_t) + num_ext); - sctp_addto_chunk(retval, sizeof(sctp_supported_ext_param_t), - &ext_param); + ext_param.param_hdr.length = htons(sizeof(ext_param) + num_ext); + sctp_addto_chunk(retval, sizeof(ext_param), &ext_param); sctp_addto_param(retval, num_ext, extensions); } @@ -394,7 +391,7 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, int cookie_len; size_t chunksize; struct sctp_adaptation_ind_param aiparam; - sctp_supported_ext_param_t ext_param; + struct sctp_supported_ext_param ext_param; int num_ext = 0; __u8 extensions[3]; struct sctp_paramhdr *auth_chunks = NULL, @@ -468,8 +465,7 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, } if (num_ext) - chunksize += SCTP_PAD4(sizeof(sctp_supported_ext_param_t) + - num_ext); + chunksize += SCTP_PAD4(sizeof(ext_param) + num_ext); /* Now allocate and fill out the chunk. */ retval = sctp_make_control(asoc, SCTP_CID_INIT_ACK, 0, chunksize, gfp); @@ -495,10 +491,8 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, sctp_addto_chunk(retval, sizeof(ecap_param), &ecap_param); if (num_ext) { ext_param.param_hdr.type = SCTP_PARAM_SUPPORTED_EXT; - ext_param.param_hdr.length = - htons(sizeof(sctp_supported_ext_param_t) + num_ext); - sctp_addto_chunk(retval, sizeof(sctp_supported_ext_param_t), - &ext_param); + ext_param.param_hdr.length = htons(sizeof(ext_param) + num_ext); + sctp_addto_chunk(retval, sizeof(ext_param), &ext_param); sctp_addto_param(retval, num_ext, extensions); } if (asoc->peer.prsctp_capable) -- cgit v1.2.3 From b02db702face3791889a4fcf06691c086648ee89 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 17 Jul 2017 11:29:57 +0800 Subject: sctp: remove the typedef sctp_random_param_t This patch is to remove the typedef sctp_random_param_t, and replace with struct sctp_random_param in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- include/net/sctp/structs.h | 2 +- net/sctp/auth.c | 9 ++++----- 3 files changed, 7 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 76245685f923..9b1aa3907c9e 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -315,10 +315,10 @@ struct sctp_supported_ext_param { }; /* AUTH Section 3.1 Random */ -typedef struct sctp_random_param { +struct sctp_random_param { struct sctp_paramhdr param_hdr; __u8 random_val[0]; -} sctp_random_param_t; +}; /* AUTH Section 3.2 Chunk List */ typedef struct sctp_chunks_param { diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 5ab29af8ca8a..f22c079fd1f1 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1556,7 +1556,7 @@ struct sctp_association { * and authenticated chunk list. All that is part of the * cookie and these are just pointers to those locations */ - sctp_random_param_t *peer_random; + struct sctp_random_param *peer_random; sctp_chunks_param_t *peer_chunks; sctp_hmac_algo_param_t *peer_hmacs; } peer; diff --git a/net/sctp/auth.c b/net/sctp/auth.c index e001b01b0e68..0d9c63eba978 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -185,7 +185,7 @@ static int sctp_auth_compare_vectors(struct sctp_auth_bytes *vector1, * are called the two key vectors. */ static struct sctp_auth_bytes *sctp_auth_make_key_vector( - sctp_random_param_t *random, + struct sctp_random_param *random, sctp_chunks_param_t *chunks, sctp_hmac_algo_param_t *hmacs, gfp_t gfp) @@ -226,10 +226,9 @@ static struct sctp_auth_bytes *sctp_auth_make_local_vector( gfp_t gfp) { return sctp_auth_make_key_vector( - (sctp_random_param_t *)asoc->c.auth_random, - (sctp_chunks_param_t *)asoc->c.auth_chunks, - (sctp_hmac_algo_param_t *)asoc->c.auth_hmacs, - gfp); + (struct sctp_random_param *)asoc->c.auth_random, + (sctp_chunks_param_t *)asoc->c.auth_chunks, + (sctp_hmac_algo_param_t *)asoc->c.auth_hmacs, gfp); } /* Make a key vector based on peer's parameters */ -- cgit v1.2.3 From a762a9d94d44980e3690f9de87b918376daa6428 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 17 Jul 2017 11:29:58 +0800 Subject: sctp: remove the typedef sctp_chunks_param_t This patch is to remove the typedef sctp_chunks_param_t, and replace with struct sctp_chunks_param in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- include/net/sctp/structs.h | 2 +- net/sctp/auth.c | 4 ++-- net/sctp/endpointola.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 9b1aa3907c9e..b52def9bcfa1 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -321,10 +321,10 @@ struct sctp_random_param { }; /* AUTH Section 3.2 Chunk List */ -typedef struct sctp_chunks_param { +struct sctp_chunks_param { struct sctp_paramhdr param_hdr; __u8 chunks[0]; -} sctp_chunks_param_t; +}; /* AUTH Section 3.3 HMAC Algorithm */ typedef struct sctp_hmac_algo_param { diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index f22c079fd1f1..8042e6380b0e 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1557,7 +1557,7 @@ struct sctp_association { * cookie and these are just pointers to those locations */ struct sctp_random_param *peer_random; - sctp_chunks_param_t *peer_chunks; + struct sctp_chunks_param *peer_chunks; sctp_hmac_algo_param_t *peer_hmacs; } peer; diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 0d9c63eba978..367994d9712a 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -186,7 +186,7 @@ static int sctp_auth_compare_vectors(struct sctp_auth_bytes *vector1, */ static struct sctp_auth_bytes *sctp_auth_make_key_vector( struct sctp_random_param *random, - sctp_chunks_param_t *chunks, + struct sctp_chunks_param *chunks, sctp_hmac_algo_param_t *hmacs, gfp_t gfp) { @@ -227,7 +227,7 @@ static struct sctp_auth_bytes *sctp_auth_make_local_vector( { return sctp_auth_make_key_vector( (struct sctp_random_param *)asoc->c.auth_random, - (sctp_chunks_param_t *)asoc->c.auth_chunks, + (struct sctp_chunks_param *)asoc->c.auth_chunks, (sctp_hmac_algo_param_t *)asoc->c.auth_hmacs, gfp); } diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 0e86f988f836..35bf5af124fc 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -78,8 +78,8 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, if (!auth_hmacs) goto nomem; - auth_chunks = kzalloc(sizeof(sctp_chunks_param_t) + - SCTP_NUM_CHUNK_TYPES, gfp); + auth_chunks = kzalloc(sizeof(*auth_chunks) + + SCTP_NUM_CHUNK_TYPES, gfp); if (!auth_chunks) goto nomem; -- cgit v1.2.3 From 1474774a7f0daf9878fd9537a24714f419e744ed Mon Sep 17 00:00:00 2001 From: Xin Long Date: Mon, 17 Jul 2017 11:29:59 +0800 Subject: sctp: remove the typedef sctp_hmac_algo_param_t This patch is to remove the typedef sctp_hmac_algo_param_t, and replace with struct sctp_hmac_algo_param in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- include/net/sctp/structs.h | 2 +- net/sctp/auth.c | 4 ++-- net/sctp/endpointola.c | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index b52def9bcfa1..913474dfc96c 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -327,10 +327,10 @@ struct sctp_chunks_param { }; /* AUTH Section 3.3 HMAC Algorithm */ -typedef struct sctp_hmac_algo_param { +struct sctp_hmac_algo_param { struct sctp_paramhdr param_hdr; __be16 hmac_ids[0]; -} sctp_hmac_algo_param_t; +}; /* RFC 2960. Section 3.3.3 Initiation Acknowledgement (INIT ACK) (2): * The INIT ACK chunk is used to acknowledge the initiation of an SCTP diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 8042e6380b0e..66cd7639b912 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1558,7 +1558,7 @@ struct sctp_association { */ struct sctp_random_param *peer_random; struct sctp_chunks_param *peer_chunks; - sctp_hmac_algo_param_t *peer_hmacs; + struct sctp_hmac_algo_param *peer_hmacs; } peer; /* State : A state variable indicating what state the diff --git a/net/sctp/auth.c b/net/sctp/auth.c index 367994d9712a..00667c50efa7 100644 --- a/net/sctp/auth.c +++ b/net/sctp/auth.c @@ -187,7 +187,7 @@ static int sctp_auth_compare_vectors(struct sctp_auth_bytes *vector1, static struct sctp_auth_bytes *sctp_auth_make_key_vector( struct sctp_random_param *random, struct sctp_chunks_param *chunks, - sctp_hmac_algo_param_t *hmacs, + struct sctp_hmac_algo_param *hmacs, gfp_t gfp) { struct sctp_auth_bytes *new; @@ -228,7 +228,7 @@ static struct sctp_auth_bytes *sctp_auth_make_local_vector( return sctp_auth_make_key_vector( (struct sctp_random_param *)asoc->c.auth_random, (struct sctp_chunks_param *)asoc->c.auth_chunks, - (sctp_hmac_algo_param_t *)asoc->c.auth_hmacs, gfp); + (struct sctp_hmac_algo_param *)asoc->c.auth_hmacs, gfp); } /* Make a key vector based on peer's parameters */ diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 35bf5af124fc..3d506b2f6193 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -73,8 +73,8 @@ static struct sctp_endpoint *sctp_endpoint_init(struct sctp_endpoint *ep, * variables. There are arrays that we encode directly * into parameters to make the rest of the operations easier. */ - auth_hmacs = kzalloc(sizeof(sctp_hmac_algo_param_t) + - sizeof(__u16) * SCTP_AUTH_NUM_HMACS, gfp); + auth_hmacs = kzalloc(sizeof(*auth_hmacs) + + sizeof(__u16) * SCTP_AUTH_NUM_HMACS, gfp); if (!auth_hmacs) goto nomem; -- cgit v1.2.3 From 3ccc6c6faaa93da70989177b91c7c3ef0df10937 Mon Sep 17 00:00:00 2001 From: linzhang Date: Mon, 17 Jul 2017 17:25:02 +0800 Subject: skbuff: optimize the pull_pages code in __pskb_pull_tail() In the pull_pages code block, if the first frag size > eat, we can end the loop in advance to avoid extra copy. Signed-off-by: Lin Zhang Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/core/skbuff.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 8b11341ed69a..b352c6bcfb31 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1719,6 +1719,8 @@ pull_pages: if (eat) { skb_shinfo(skb)->frags[k].page_offset += eat; skb_frag_size_sub(&skb_shinfo(skb)->frags[k], eat); + if (!i) + goto end; eat = 0; } k++; @@ -1726,6 +1728,7 @@ pull_pages: } skb_shinfo(skb)->nr_frags = k; +end: skb->tail += delta; skb->data_len -= delta; -- cgit v1.2.3 From 27eac47b00789522ba00501b0838026e1ecb6f05 Mon Sep 17 00:00:00 2001 From: David Herrmann Date: Mon, 17 Jul 2017 11:35:54 +0200 Subject: net/unix: drop obsolete fd-recursion limits All unix sockets now account inflight FDs to the respective sender. This was introduced in: commit 712f4aad406bb1ed67f3f98d04c044191f0ff593 Author: willy tarreau Date: Sun Jan 10 07:54:56 2016 +0100 unix: properly account for FDs passed over unix sockets and further refined in: commit 415e3d3e90ce9e18727e8843ae343eda5a58fad6 Author: Hannes Frederic Sowa Date: Wed Feb 3 02:11:03 2016 +0100 unix: correctly track in-flight fds in sending process user_struct Hence, regardless of the stacking depth of FDs, the total number of inflight FDs is limited, and accounted. There is no known way for a local user to exceed those limits or exploit the accounting. Furthermore, the GC logic is independent of the recursion/stacking depth as well. It solely depends on the total number of inflight FDs, regardless of their layout. Lastly, the current `recursion_level' suffers a TOCTOU race, since it checks and inherits depths only at queue time. If we consider `A<-B' to mean `queue-B-on-A', the following sequence circumvents the recursion level easily: A<-B B<-C C<-D ... Y<-Z resulting in: A<-B<-C<-...<-Z With all of this in mind, lets drop the recursion limit. It has no additional security value, anymore. On the contrary, it randomly confuses message brokers that try to forward file-descriptors, since any sendmsg(2) call can fail spuriously with ETOOMANYREFS if a client maliciously modifies the FD while inflight. Cc: Alban Crequy Cc: Simon McVittie Signed-off-by: David Herrmann Reviewed-by: Tom Gundersen Signed-off-by: David S. Miller --- include/net/af_unix.h | 1 - net/unix/af_unix.c | 24 +----------------------- 2 files changed, 1 insertion(+), 24 deletions(-) (limited to 'net') diff --git a/include/net/af_unix.h b/include/net/af_unix.h index 678e4d6fa317..3b3194b2fc65 100644 --- a/include/net/af_unix.h +++ b/include/net/af_unix.h @@ -58,7 +58,6 @@ struct unix_sock { struct list_head link; atomic_long_t inflight; spinlock_t lock; - unsigned char recursion_level; unsigned long gc_flags; #define UNIX_GC_CANDIDATE 0 #define UNIX_GC_MAYBE_CYCLE 1 diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c index 7b52a380d710..5c53f22d62e8 100644 --- a/net/unix/af_unix.c +++ b/net/unix/af_unix.c @@ -1528,26 +1528,13 @@ static inline bool too_many_unix_fds(struct task_struct *p) return false; } -#define MAX_RECURSION_LEVEL 4 - static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) { int i; - unsigned char max_level = 0; if (too_many_unix_fds(current)) return -ETOOMANYREFS; - for (i = scm->fp->count - 1; i >= 0; i--) { - struct sock *sk = unix_get_socket(scm->fp->fp[i]); - - if (sk) - max_level = max(max_level, - unix_sk(sk)->recursion_level); - } - if (unlikely(max_level > MAX_RECURSION_LEVEL)) - return -ETOOMANYREFS; - /* * Need to duplicate file references for the sake of garbage * collection. Otherwise a socket in the fps might become a @@ -1559,7 +1546,7 @@ static int unix_attach_fds(struct scm_cookie *scm, struct sk_buff *skb) for (i = scm->fp->count - 1; i >= 0; i--) unix_inflight(scm->fp->user, scm->fp->fp[i]); - return max_level; + return 0; } static int unix_scm_to_skb(struct scm_cookie *scm, struct sk_buff *skb, bool send_fds) @@ -1649,7 +1636,6 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, struct sk_buff *skb; long timeo; struct scm_cookie scm; - int max_level; int data_len = 0; int sk_locked; @@ -1701,7 +1687,6 @@ static int unix_dgram_sendmsg(struct socket *sock, struct msghdr *msg, err = unix_scm_to_skb(&scm, skb, true); if (err < 0) goto out_free; - max_level = err + 1; skb_put(skb, len - data_len); skb->data_len = data_len; @@ -1819,8 +1804,6 @@ restart_locked: __net_timestamp(skb); maybe_add_creds(skb, sock, other); skb_queue_tail(&other->sk_receive_queue, skb); - if (max_level > unix_sk(other)->recursion_level) - unix_sk(other)->recursion_level = max_level; unix_state_unlock(other); other->sk_data_ready(other); sock_put(other); @@ -1855,7 +1838,6 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, int sent = 0; struct scm_cookie scm; bool fds_sent = false; - int max_level; int data_len; wait_for_unix_gc(); @@ -1905,7 +1887,6 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, kfree_skb(skb); goto out_err; } - max_level = err + 1; fds_sent = true; skb_put(skb, size - data_len); @@ -1925,8 +1906,6 @@ static int unix_stream_sendmsg(struct socket *sock, struct msghdr *msg, maybe_add_creds(skb, sock, other); skb_queue_tail(&other->sk_receive_queue, skb); - if (max_level > unix_sk(other)->recursion_level) - unix_sk(other)->recursion_level = max_level; unix_state_unlock(other); other->sk_data_ready(other); sent += size; @@ -2324,7 +2303,6 @@ redo: last_len = last ? last->len : 0; again: if (skb == NULL) { - unix_sk(sk)->recursion_level = 0; if (copied >= target) goto unlock; -- cgit v1.2.3 From b145425f269a17ed344d737f746b844dfac60c82 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Mon, 17 Jul 2017 02:56:10 -0700 Subject: inetpeer: remove AVL implementation in favor of RB tree As discussed in Faro during Netfilter Workshop 2017, RB trees can be used with RCU, using a seqlock. Note that net/rxrpc/conn_service.c is already using this. This patch converts inetpeer from AVL tree to RB tree, since it allows to remove private AVL implementation in favor of shared RB code. $ size net/ipv4/inetpeer.before net/ipv4/inetpeer.after text data bss dec hex filename 3195 40 128 3363 d23 net/ipv4/inetpeer.before 1562 24 0 1586 632 net/ipv4/inetpeer.after The same technique can be used to speed up net/netfilter/nft_set_rbtree.c (removing rwlock contention in fast path) Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/inetpeer.h | 11 +- net/ipv4/inetpeer.c | 428 +++++++++++-------------------------------------- 2 files changed, 92 insertions(+), 347 deletions(-) (limited to 'net') diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h index f2a215fc78e4..950ed182f62f 100644 --- a/include/net/inetpeer.h +++ b/include/net/inetpeer.h @@ -33,18 +33,12 @@ struct inetpeer_addr { }; struct inet_peer { - /* group together avl_left,avl_right,v4daddr to speedup lookups */ - struct inet_peer __rcu *avl_left, *avl_right; + struct rb_node rb_node; struct inetpeer_addr daddr; - __u32 avl_height; u32 metrics[RTAX_MAX]; u32 rate_tokens; /* rate limiting for ICMP */ unsigned long rate_last; - union { - struct list_head gc_list; - struct rcu_head gc_rcu; - }; /* * Once inet_peer is queued for deletion (refcnt == 0), following field * is not available: rid @@ -55,7 +49,6 @@ struct inet_peer { atomic_t rid; /* Frag reception counter */ }; struct rcu_head rcu; - struct inet_peer *gc_next; }; /* following fields might be frequently dirtied */ @@ -64,7 +57,7 @@ struct inet_peer { }; struct inet_peer_base { - struct inet_peer __rcu *root; + struct rb_root rb_root; seqlock_t lock; int total; }; diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index c5a117cc6619..337ad41bb80a 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -33,7 +33,7 @@ * also be removed if the pool is overloaded i.e. if the total amount of * entries is greater-or-equal than the threshold. * - * Node pool is organised as an AVL tree. + * Node pool is organised as an RB tree. * Such an implementation has been chosen not just for fun. It's a way to * prevent easy and efficient DoS attacks by creating hash collisions. A huge * amount of long living nodes in a single hash slot would significantly delay @@ -45,7 +45,7 @@ * AND reference count being 0. * 3. Global variable peer_total is modified under the pool lock. * 4. struct inet_peer fields modification: - * avl_left, avl_right, avl_parent, avl_height: pool lock + * rb_node: pool lock * refcnt: atomically against modifications on other CPU; * usually under some other lock to prevent node disappearing * daddr: unchangeable @@ -53,30 +53,15 @@ static struct kmem_cache *peer_cachep __read_mostly; -static LIST_HEAD(gc_list); -static const int gc_delay = 60 * HZ; -static struct delayed_work gc_work; -static DEFINE_SPINLOCK(gc_lock); - -#define node_height(x) x->avl_height - -#define peer_avl_empty ((struct inet_peer *)&peer_fake_node) -#define peer_avl_empty_rcu ((struct inet_peer __rcu __force *)&peer_fake_node) -static const struct inet_peer peer_fake_node = { - .avl_left = peer_avl_empty_rcu, - .avl_right = peer_avl_empty_rcu, - .avl_height = 0 -}; - void inet_peer_base_init(struct inet_peer_base *bp) { - bp->root = peer_avl_empty_rcu; + bp->rb_root = RB_ROOT; seqlock_init(&bp->lock); bp->total = 0; } EXPORT_SYMBOL_GPL(inet_peer_base_init); -#define PEER_MAXDEPTH 40 /* sufficient for about 2^27 nodes */ +#define PEER_MAX_GC 32 /* Exported for sysctl_net_ipv4. */ int inet_peer_threshold __read_mostly = 65536 + 128; /* start to throw entries more @@ -84,53 +69,6 @@ int inet_peer_threshold __read_mostly = 65536 + 128; /* start to throw entries m int inet_peer_minttl __read_mostly = 120 * HZ; /* TTL under high load: 120 sec */ int inet_peer_maxttl __read_mostly = 10 * 60 * HZ; /* usual time to live: 10 min */ -static void inetpeer_gc_worker(struct work_struct *work) -{ - struct inet_peer *p, *n, *c; - struct list_head list; - - spin_lock_bh(&gc_lock); - list_replace_init(&gc_list, &list); - spin_unlock_bh(&gc_lock); - - if (list_empty(&list)) - return; - - list_for_each_entry_safe(p, n, &list, gc_list) { - - if (need_resched()) - cond_resched(); - - c = rcu_dereference_protected(p->avl_left, 1); - if (c != peer_avl_empty) { - list_add_tail(&c->gc_list, &list); - p->avl_left = peer_avl_empty_rcu; - } - - c = rcu_dereference_protected(p->avl_right, 1); - if (c != peer_avl_empty) { - list_add_tail(&c->gc_list, &list); - p->avl_right = peer_avl_empty_rcu; - } - - n = list_entry(p->gc_list.next, struct inet_peer, gc_list); - - if (refcount_read(&p->refcnt) == 1) { - list_del(&p->gc_list); - kmem_cache_free(peer_cachep, p); - } - } - - if (list_empty(&list)) - return; - - spin_lock_bh(&gc_lock); - list_splice(&list, &gc_list); - spin_unlock_bh(&gc_lock); - - schedule_delayed_work(&gc_work, gc_delay); -} - /* Called from ip_output.c:ip_init */ void __init inet_initpeers(void) { @@ -153,225 +91,62 @@ void __init inet_initpeers(void) sizeof(struct inet_peer), 0, SLAB_HWCACHE_ALIGN | SLAB_PANIC, NULL); - - INIT_DEFERRABLE_WORK(&gc_work, inetpeer_gc_worker); } -#define rcu_deref_locked(X, BASE) \ - rcu_dereference_protected(X, lockdep_is_held(&(BASE)->lock.lock)) - -/* - * Called with local BH disabled and the pool lock held. - */ -#define lookup(_daddr, _stack, _base) \ -({ \ - struct inet_peer *u; \ - struct inet_peer __rcu **v; \ - \ - stackptr = _stack; \ - *stackptr++ = &_base->root; \ - for (u = rcu_deref_locked(_base->root, _base); \ - u != peer_avl_empty;) { \ - int cmp = inetpeer_addr_cmp(_daddr, &u->daddr); \ - if (cmp == 0) \ - break; \ - if (cmp == -1) \ - v = &u->avl_left; \ - else \ - v = &u->avl_right; \ - *stackptr++ = v; \ - u = rcu_deref_locked(*v, _base); \ - } \ - u; \ -}) - -/* - * Called with rcu_read_lock() - * Because we hold no lock against a writer, its quite possible we fall - * in an endless loop. - * But every pointer we follow is guaranteed to be valid thanks to RCU. - * We exit from this function if number of links exceeds PEER_MAXDEPTH - */ -static struct inet_peer *lookup_rcu(const struct inetpeer_addr *daddr, - struct inet_peer_base *base) +/* Called with rcu_read_lock() or base->lock held */ +static struct inet_peer *lookup(const struct inetpeer_addr *daddr, + struct inet_peer_base *base, + unsigned int seq, + struct inet_peer *gc_stack[], + unsigned int *gc_cnt, + struct rb_node **parent_p, + struct rb_node ***pp_p) { - struct inet_peer *u = rcu_dereference(base->root); - int count = 0; + struct rb_node **pp, *parent; + struct inet_peer *p; + + pp = &base->rb_root.rb_node; + parent = NULL; + while (*pp) { + int cmp; - while (u != peer_avl_empty) { - int cmp = inetpeer_addr_cmp(daddr, &u->daddr); + parent = rcu_dereference_raw(*pp); + p = rb_entry(parent, struct inet_peer, rb_node); + cmp = inetpeer_addr_cmp(daddr, &p->daddr); if (cmp == 0) { - /* Before taking a reference, check if this entry was - * deleted (refcnt=0) - */ - if (!refcount_inc_not_zero(&u->refcnt)) { - u = NULL; - } - return u; + if (!refcount_inc_not_zero(&p->refcnt)) + break; + return p; + } + if (gc_stack) { + if (*gc_cnt < PEER_MAX_GC) + gc_stack[(*gc_cnt)++] = p; + } else if (unlikely(read_seqretry(&base->lock, seq))) { + break; } if (cmp == -1) - u = rcu_dereference(u->avl_left); + pp = &(*pp)->rb_left; else - u = rcu_dereference(u->avl_right); - if (unlikely(++count == PEER_MAXDEPTH)) - break; + pp = &(*pp)->rb_right; } + *parent_p = parent; + *pp_p = pp; return NULL; } -/* Called with local BH disabled and the pool lock held. */ -#define lookup_rightempty(start, base) \ -({ \ - struct inet_peer *u; \ - struct inet_peer __rcu **v; \ - *stackptr++ = &start->avl_left; \ - v = &start->avl_left; \ - for (u = rcu_deref_locked(*v, base); \ - u->avl_right != peer_avl_empty_rcu;) { \ - v = &u->avl_right; \ - *stackptr++ = v; \ - u = rcu_deref_locked(*v, base); \ - } \ - u; \ -}) - -/* Called with local BH disabled and the pool lock held. - * Variable names are the proof of operation correctness. - * Look into mm/map_avl.c for more detail description of the ideas. - */ -static void peer_avl_rebalance(struct inet_peer __rcu **stack[], - struct inet_peer __rcu ***stackend, - struct inet_peer_base *base) -{ - struct inet_peer __rcu **nodep; - struct inet_peer *node, *l, *r; - int lh, rh; - - while (stackend > stack) { - nodep = *--stackend; - node = rcu_deref_locked(*nodep, base); - l = rcu_deref_locked(node->avl_left, base); - r = rcu_deref_locked(node->avl_right, base); - lh = node_height(l); - rh = node_height(r); - if (lh > rh + 1) { /* l: RH+2 */ - struct inet_peer *ll, *lr, *lrl, *lrr; - int lrh; - ll = rcu_deref_locked(l->avl_left, base); - lr = rcu_deref_locked(l->avl_right, base); - lrh = node_height(lr); - if (lrh <= node_height(ll)) { /* ll: RH+1 */ - RCU_INIT_POINTER(node->avl_left, lr); /* lr: RH or RH+1 */ - RCU_INIT_POINTER(node->avl_right, r); /* r: RH */ - node->avl_height = lrh + 1; /* RH+1 or RH+2 */ - RCU_INIT_POINTER(l->avl_left, ll); /* ll: RH+1 */ - RCU_INIT_POINTER(l->avl_right, node); /* node: RH+1 or RH+2 */ - l->avl_height = node->avl_height + 1; - RCU_INIT_POINTER(*nodep, l); - } else { /* ll: RH, lr: RH+1 */ - lrl = rcu_deref_locked(lr->avl_left, base);/* lrl: RH or RH-1 */ - lrr = rcu_deref_locked(lr->avl_right, base);/* lrr: RH or RH-1 */ - RCU_INIT_POINTER(node->avl_left, lrr); /* lrr: RH or RH-1 */ - RCU_INIT_POINTER(node->avl_right, r); /* r: RH */ - node->avl_height = rh + 1; /* node: RH+1 */ - RCU_INIT_POINTER(l->avl_left, ll); /* ll: RH */ - RCU_INIT_POINTER(l->avl_right, lrl); /* lrl: RH or RH-1 */ - l->avl_height = rh + 1; /* l: RH+1 */ - RCU_INIT_POINTER(lr->avl_left, l); /* l: RH+1 */ - RCU_INIT_POINTER(lr->avl_right, node); /* node: RH+1 */ - lr->avl_height = rh + 2; - RCU_INIT_POINTER(*nodep, lr); - } - } else if (rh > lh + 1) { /* r: LH+2 */ - struct inet_peer *rr, *rl, *rlr, *rll; - int rlh; - rr = rcu_deref_locked(r->avl_right, base); - rl = rcu_deref_locked(r->avl_left, base); - rlh = node_height(rl); - if (rlh <= node_height(rr)) { /* rr: LH+1 */ - RCU_INIT_POINTER(node->avl_right, rl); /* rl: LH or LH+1 */ - RCU_INIT_POINTER(node->avl_left, l); /* l: LH */ - node->avl_height = rlh + 1; /* LH+1 or LH+2 */ - RCU_INIT_POINTER(r->avl_right, rr); /* rr: LH+1 */ - RCU_INIT_POINTER(r->avl_left, node); /* node: LH+1 or LH+2 */ - r->avl_height = node->avl_height + 1; - RCU_INIT_POINTER(*nodep, r); - } else { /* rr: RH, rl: RH+1 */ - rlr = rcu_deref_locked(rl->avl_right, base);/* rlr: LH or LH-1 */ - rll = rcu_deref_locked(rl->avl_left, base);/* rll: LH or LH-1 */ - RCU_INIT_POINTER(node->avl_right, rll); /* rll: LH or LH-1 */ - RCU_INIT_POINTER(node->avl_left, l); /* l: LH */ - node->avl_height = lh + 1; /* node: LH+1 */ - RCU_INIT_POINTER(r->avl_right, rr); /* rr: LH */ - RCU_INIT_POINTER(r->avl_left, rlr); /* rlr: LH or LH-1 */ - r->avl_height = lh + 1; /* r: LH+1 */ - RCU_INIT_POINTER(rl->avl_right, r); /* r: LH+1 */ - RCU_INIT_POINTER(rl->avl_left, node); /* node: LH+1 */ - rl->avl_height = lh + 2; - RCU_INIT_POINTER(*nodep, rl); - } - } else { - node->avl_height = (lh > rh ? lh : rh) + 1; - } - } -} - -/* Called with local BH disabled and the pool lock held. */ -#define link_to_pool(n, base) \ -do { \ - n->avl_height = 1; \ - n->avl_left = peer_avl_empty_rcu; \ - n->avl_right = peer_avl_empty_rcu; \ - /* lockless readers can catch us now */ \ - rcu_assign_pointer(**--stackptr, n); \ - peer_avl_rebalance(stack, stackptr, base); \ -} while (0) - static void inetpeer_free_rcu(struct rcu_head *head) { kmem_cache_free(peer_cachep, container_of(head, struct inet_peer, rcu)); } -static void unlink_from_pool(struct inet_peer *p, struct inet_peer_base *base, - struct inet_peer __rcu **stack[PEER_MAXDEPTH]) -{ - struct inet_peer __rcu ***stackptr, ***delp; - - if (lookup(&p->daddr, stack, base) != p) - BUG(); - delp = stackptr - 1; /* *delp[0] == p */ - if (p->avl_left == peer_avl_empty_rcu) { - *delp[0] = p->avl_right; - --stackptr; - } else { - /* look for a node to insert instead of p */ - struct inet_peer *t; - t = lookup_rightempty(p, base); - BUG_ON(rcu_deref_locked(*stackptr[-1], base) != t); - **--stackptr = t->avl_left; - /* t is removed, t->daddr > x->daddr for any - * x in p->avl_left subtree. - * Put t in the old place of p. */ - RCU_INIT_POINTER(*delp[0], t); - t->avl_left = p->avl_left; - t->avl_right = p->avl_right; - t->avl_height = p->avl_height; - BUG_ON(delp[1] != &p->avl_left); - delp[1] = &t->avl_left; /* was &p->avl_left */ - } - peer_avl_rebalance(stack, stackptr, base); - base->total--; - call_rcu(&p->rcu, inetpeer_free_rcu); -} - /* perform garbage collect on all items stacked during a lookup */ -static int inet_peer_gc(struct inet_peer_base *base, - struct inet_peer __rcu **stack[PEER_MAXDEPTH], - struct inet_peer __rcu ***stackptr) +static void inet_peer_gc(struct inet_peer_base *base, + struct inet_peer *gc_stack[], + unsigned int gc_cnt) { - struct inet_peer *p, *gchead = NULL; + struct inet_peer *p; __u32 delta, ttl; - int cnt = 0; + int i; if (base->total >= inet_peer_threshold) ttl = 0; /* be aggressive */ @@ -379,43 +154,38 @@ static int inet_peer_gc(struct inet_peer_base *base, ttl = inet_peer_maxttl - (inet_peer_maxttl - inet_peer_minttl) / HZ * base->total / inet_peer_threshold * HZ; - stackptr--; /* last stack slot is peer_avl_empty */ - while (stackptr > stack) { - stackptr--; - p = rcu_deref_locked(**stackptr, base); - if (refcount_read(&p->refcnt) == 1) { - smp_rmb(); - delta = (__u32)jiffies - p->dtime; - if (delta >= ttl && refcount_dec_if_one(&p->refcnt)) { - p->gc_next = gchead; - gchead = p; - } - } + for (i = 0; i < gc_cnt; i++) { + p = gc_stack[i]; + delta = (__u32)jiffies - p->dtime; + if (delta < ttl || !refcount_dec_if_one(&p->refcnt)) + gc_stack[i] = NULL; } - while ((p = gchead) != NULL) { - gchead = p->gc_next; - cnt++; - unlink_from_pool(p, base, stack); + for (i = 0; i < gc_cnt; i++) { + p = gc_stack[i]; + if (p) { + rb_erase(&p->rb_node, &base->rb_root); + base->total--; + call_rcu(&p->rcu, inetpeer_free_rcu); + } } - return cnt; } struct inet_peer *inet_getpeer(struct inet_peer_base *base, const struct inetpeer_addr *daddr, int create) { - struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr; - struct inet_peer *p; - unsigned int sequence; - int invalidated, gccnt = 0; + struct inet_peer *p, *gc_stack[PEER_MAX_GC]; + struct rb_node **pp, *parent; + unsigned int gc_cnt, seq; + int invalidated; /* Attempt a lockless lookup first. * Because of a concurrent writer, we might not find an existing entry. */ rcu_read_lock(); - sequence = read_seqbegin(&base->lock); - p = lookup_rcu(daddr, base); - invalidated = read_seqretry(&base->lock, sequence); + seq = read_seqbegin(&base->lock); + p = lookup(daddr, base, seq, NULL, &gc_cnt, &parent, &pp); + invalidated = read_seqretry(&base->lock, seq); rcu_read_unlock(); if (p) @@ -428,36 +198,31 @@ struct inet_peer *inet_getpeer(struct inet_peer_base *base, /* retry an exact lookup, taking the lock before. * At least, nodes should be hot in our cache. */ + parent = NULL; write_seqlock_bh(&base->lock); -relookup: - p = lookup(daddr, stack, base); - if (p != peer_avl_empty) { - refcount_inc(&p->refcnt); - write_sequnlock_bh(&base->lock); - return p; - } - if (!gccnt) { - gccnt = inet_peer_gc(base, stack, stackptr); - if (gccnt && create) - goto relookup; - } - p = create ? kmem_cache_alloc(peer_cachep, GFP_ATOMIC) : NULL; - if (p) { - p->daddr = *daddr; - refcount_set(&p->refcnt, 2); - atomic_set(&p->rid, 0); - p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; - p->rate_tokens = 0; - /* 60*HZ is arbitrary, but chosen enough high so that the first - * calculation of tokens is at its maximum. - */ - p->rate_last = jiffies - 60*HZ; - INIT_LIST_HEAD(&p->gc_list); - /* Link the node. */ - link_to_pool(p, base); - base->total++; + gc_cnt = 0; + p = lookup(daddr, base, seq, gc_stack, &gc_cnt, &parent, &pp); + if (!p && create) { + p = kmem_cache_alloc(peer_cachep, GFP_ATOMIC); + if (p) { + p->daddr = *daddr; + refcount_set(&p->refcnt, 2); + atomic_set(&p->rid, 0); + p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW; + p->rate_tokens = 0; + /* 60*HZ is arbitrary, but chosen enough high so that the first + * calculation of tokens is at its maximum. + */ + p->rate_last = jiffies - 60*HZ; + + rb_link_node(&p->rb_node, parent, pp); + rb_insert_color(&p->rb_node, &base->rb_root); + base->total++; + } } + if (gc_cnt) + inet_peer_gc(base, gc_stack, gc_cnt); write_sequnlock_bh(&base->lock); return p; @@ -467,8 +232,9 @@ EXPORT_SYMBOL_GPL(inet_getpeer); void inet_putpeer(struct inet_peer *p) { p->dtime = (__u32)jiffies; - smp_mb__before_atomic(); - refcount_dec(&p->refcnt); + + if (refcount_dec_and_test(&p->refcnt)) + call_rcu(&p->rcu, inetpeer_free_rcu); } EXPORT_SYMBOL_GPL(inet_putpeer); @@ -513,30 +279,16 @@ bool inet_peer_xrlim_allow(struct inet_peer *peer, int timeout) } EXPORT_SYMBOL(inet_peer_xrlim_allow); -static void inetpeer_inval_rcu(struct rcu_head *head) -{ - struct inet_peer *p = container_of(head, struct inet_peer, gc_rcu); - - spin_lock_bh(&gc_lock); - list_add_tail(&p->gc_list, &gc_list); - spin_unlock_bh(&gc_lock); - - schedule_delayed_work(&gc_work, gc_delay); -} - void inetpeer_invalidate_tree(struct inet_peer_base *base) { - struct inet_peer *root; - - write_seqlock_bh(&base->lock); + struct inet_peer *p, *n; - root = rcu_deref_locked(base->root, base); - if (root != peer_avl_empty) { - base->root = peer_avl_empty_rcu; - base->total = 0; - call_rcu(&root->gc_rcu, inetpeer_inval_rcu); + rbtree_postorder_for_each_entry_safe(p, n, &base->rb_root, rb_node) { + inet_putpeer(p); + cond_resched(); } - write_sequnlock_bh(&base->lock); + base->rb_root = RB_ROOT; + base->total = 0; } EXPORT_SYMBOL(inetpeer_invalidate_tree); -- cgit v1.2.3 From d445516966dcb2924741b13b27738b54df2af01a Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:26:45 -0700 Subject: net: xdp: support xdp generic on virtual devices XDP generic allows users to test XDP programs and/or run them with degraded performance on devices that do not yet support XDP. For testing I typically test eBPF programs using a set of veth devices. This allows testing topologies that would otherwise be difficult to setup especially in the early stages of development. This patch adds a xdp generic hook to the netif_rx_internal() function which is called from dev_forward_skb(). With this addition attaching XDP programs to veth devices works as expected! Also I noticed multiple drivers using netif_rx(). These devices will also benefit and generic XDP will work for them as well. Signed-off-by: John Fastabend Tested-by: Andy Gospodarek Acked-by: Daniel Borkmann Acked-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- net/core/dev.c | 208 +++++++++++++++++++++++++++++++-------------------------- 1 file changed, 113 insertions(+), 95 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 02440518dd69..a1ed7b41b3e8 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3865,6 +3865,107 @@ drop: return NET_RX_DROP; } +static u32 netif_receive_generic_xdp(struct sk_buff *skb, + struct bpf_prog *xdp_prog) +{ + struct xdp_buff xdp; + u32 act = XDP_DROP; + void *orig_data; + int hlen, off; + u32 mac_len; + + /* Reinjected packets coming from act_mirred or similar should + * not get XDP generic processing. + */ + if (skb_cloned(skb)) + return XDP_PASS; + + if (skb_linearize(skb)) + goto do_drop; + + /* The XDP program wants to see the packet starting at the MAC + * header. + */ + mac_len = skb->data - skb_mac_header(skb); + hlen = skb_headlen(skb) + mac_len; + xdp.data = skb->data - mac_len; + xdp.data_end = xdp.data + hlen; + xdp.data_hard_start = skb->data - skb_headroom(skb); + orig_data = xdp.data; + + act = bpf_prog_run_xdp(xdp_prog, &xdp); + + off = xdp.data - orig_data; + if (off > 0) + __skb_pull(skb, off); + else if (off < 0) + __skb_push(skb, -off); + + switch (act) { + case XDP_TX: + __skb_push(skb, mac_len); + /* fall through */ + case XDP_PASS: + break; + + default: + bpf_warn_invalid_xdp_action(act); + /* fall through */ + case XDP_ABORTED: + trace_xdp_exception(skb->dev, xdp_prog, act); + /* fall through */ + case XDP_DROP: + do_drop: + kfree_skb(skb); + break; + } + + return act; +} + +/* When doing generic XDP we have to bypass the qdisc layer and the + * network taps in order to match in-driver-XDP behavior. + */ +static void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog) +{ + struct net_device *dev = skb->dev; + struct netdev_queue *txq; + bool free_skb = true; + int cpu, rc; + + txq = netdev_pick_tx(dev, skb, NULL); + cpu = smp_processor_id(); + HARD_TX_LOCK(dev, txq, cpu); + if (!netif_xmit_stopped(txq)) { + rc = netdev_start_xmit(skb, dev, txq, 0); + if (dev_xmit_complete(rc)) + free_skb = false; + } + HARD_TX_UNLOCK(dev, txq); + if (free_skb) { + trace_xdp_exception(dev, xdp_prog, XDP_TX); + kfree_skb(skb); + } +} + +static struct static_key generic_xdp_needed __read_mostly; + +static int do_xdp_generic(struct sk_buff *skb) +{ + struct bpf_prog *xdp_prog = rcu_dereference(skb->dev->xdp_prog); + + if (xdp_prog) { + u32 act = netif_receive_generic_xdp(skb, xdp_prog); + + if (act != XDP_PASS) { + if (act == XDP_TX) + generic_xdp_tx(skb, xdp_prog); + return XDP_DROP; + } + } + return XDP_PASS; +} + static int netif_rx_internal(struct sk_buff *skb) { int ret; @@ -3872,6 +3973,14 @@ static int netif_rx_internal(struct sk_buff *skb) net_timestamp_check(netdev_tstamp_prequeue, skb); trace_netif_rx(skb); + + if (static_key_false(&generic_xdp_needed)) { + int ret = do_xdp_generic(skb); + + if (ret != XDP_PASS) + return NET_RX_DROP; + } + #ifdef CONFIG_RPS if (static_key_false(&rps_needed)) { struct rps_dev_flow voidflow, *rflow = &voidflow; @@ -4338,8 +4447,6 @@ static int __netif_receive_skb(struct sk_buff *skb) return ret; } -static struct static_key generic_xdp_needed __read_mostly; - static int generic_xdp_install(struct net_device *dev, struct netdev_xdp *xdp) { struct bpf_prog *old = rtnl_dereference(dev->xdp_prog); @@ -4373,89 +4480,6 @@ static int generic_xdp_install(struct net_device *dev, struct netdev_xdp *xdp) return ret; } -static u32 netif_receive_generic_xdp(struct sk_buff *skb, - struct bpf_prog *xdp_prog) -{ - struct xdp_buff xdp; - u32 act = XDP_DROP; - void *orig_data; - int hlen, off; - u32 mac_len; - - /* Reinjected packets coming from act_mirred or similar should - * not get XDP generic processing. - */ - if (skb_cloned(skb)) - return XDP_PASS; - - if (skb_linearize(skb)) - goto do_drop; - - /* The XDP program wants to see the packet starting at the MAC - * header. - */ - mac_len = skb->data - skb_mac_header(skb); - hlen = skb_headlen(skb) + mac_len; - xdp.data = skb->data - mac_len; - xdp.data_end = xdp.data + hlen; - xdp.data_hard_start = skb->data - skb_headroom(skb); - orig_data = xdp.data; - - act = bpf_prog_run_xdp(xdp_prog, &xdp); - - off = xdp.data - orig_data; - if (off > 0) - __skb_pull(skb, off); - else if (off < 0) - __skb_push(skb, -off); - - switch (act) { - case XDP_TX: - __skb_push(skb, mac_len); - /* fall through */ - case XDP_PASS: - break; - - default: - bpf_warn_invalid_xdp_action(act); - /* fall through */ - case XDP_ABORTED: - trace_xdp_exception(skb->dev, xdp_prog, act); - /* fall through */ - case XDP_DROP: - do_drop: - kfree_skb(skb); - break; - } - - return act; -} - -/* When doing generic XDP we have to bypass the qdisc layer and the - * network taps in order to match in-driver-XDP behavior. - */ -static void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog) -{ - struct net_device *dev = skb->dev; - struct netdev_queue *txq; - bool free_skb = true; - int cpu, rc; - - txq = netdev_pick_tx(dev, skb, NULL); - cpu = smp_processor_id(); - HARD_TX_LOCK(dev, txq, cpu); - if (!netif_xmit_stopped(txq)) { - rc = netdev_start_xmit(skb, dev, txq, 0); - if (dev_xmit_complete(rc)) - free_skb = false; - } - HARD_TX_UNLOCK(dev, txq); - if (free_skb) { - trace_xdp_exception(dev, xdp_prog, XDP_TX); - kfree_skb(skb); - } -} - static int netif_receive_skb_internal(struct sk_buff *skb) { int ret; @@ -4468,17 +4492,11 @@ static int netif_receive_skb_internal(struct sk_buff *skb) rcu_read_lock(); if (static_key_false(&generic_xdp_needed)) { - struct bpf_prog *xdp_prog = rcu_dereference(skb->dev->xdp_prog); - - if (xdp_prog) { - u32 act = netif_receive_generic_xdp(skb, xdp_prog); + int ret = do_xdp_generic(skb); - if (act != XDP_PASS) { - rcu_read_unlock(); - if (act == XDP_TX) - generic_xdp_tx(skb, xdp_prog); - return NET_RX_DROP; - } + if (ret != XDP_PASS) { + rcu_read_unlock(); + return NET_RX_DROP; } } -- cgit v1.2.3 From 814abfabef3ceed390c10d06a0cc69a86454b6cf Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:27:07 -0700 Subject: xdp: add bpf_redirect helper function This adds support for a bpf_redirect helper function to the XDP infrastructure. For now this only supports redirecting to the egress path of a port. In order to support drivers handling a xdp_buff natively this patches uses a new ndo operation ndo_xdp_xmit() that takes pushes a xdp_buff to the specified device. If the program specifies either (a) an unknown device or (b) a device that does not support the operation a BPF warning is thrown and the XDP_ABORTED error code is returned. Signed-off-by: John Fastabend Acked-by: Daniel Borkmann Acked-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/linux/filter.h | 4 ++++ include/linux/netdevice.h | 6 ++++++ include/uapi/linux/bpf.h | 1 + net/core/filter.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 63 insertions(+) (limited to 'net') diff --git a/include/linux/filter.h b/include/linux/filter.h index bfef1e5734f8..64cae7a08148 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -711,7 +711,11 @@ bool bpf_helper_changes_pkt_data(void *func); struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, const struct bpf_insn *patch, u32 len); + +int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp); + void bpf_warn_invalid_xdp_action(u32 act); +void bpf_warn_invalid_xdp_redirect(u32 ifindex); #ifdef CONFIG_BPF_JIT extern int bpf_jit_enable; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 779b23595596..77f5376005e6 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -66,6 +66,7 @@ struct mpls_dev; /* UDP Tunnel offloads */ struct udp_tunnel_info; struct bpf_prog; +struct xdp_buff; void netdev_set_default_ethtool_ops(struct net_device *dev, const struct ethtool_ops *ops); @@ -1138,6 +1139,9 @@ struct xfrmdev_ops { * int (*ndo_xdp)(struct net_device *dev, struct netdev_xdp *xdp); * This function is used to set or query state related to XDP on the * netdevice. See definition of enum xdp_netdev_command for details. + * int (*ndo_xdp_xmit)(struct net_device *dev, struct xdp_buff *xdp); + * This function is used to submit a XDP packet for transmit on a + * netdevice. * */ struct net_device_ops { @@ -1323,6 +1327,8 @@ struct net_device_ops { int needed_headroom); int (*ndo_xdp)(struct net_device *dev, struct netdev_xdp *xdp); + int (*ndo_xdp_xmit)(struct net_device *dev, + struct xdp_buff *xdp); }; /** diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index e99e3e6f8b37..4dbb7a3f4677 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -717,6 +717,7 @@ enum xdp_action { XDP_DROP, XDP_PASS, XDP_TX, + XDP_REDIRECT, }; /* user accessible metadata for XDP packet hook diff --git a/net/core/filter.c b/net/core/filter.c index c7f737058d89..d606a66d1040 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2412,6 +2412,51 @@ static const struct bpf_func_proto bpf_xdp_adjust_head_proto = { .arg2_type = ARG_ANYTHING, }; +static int __bpf_tx_xdp(struct net_device *dev, struct xdp_buff *xdp) +{ + if (dev->netdev_ops->ndo_xdp_xmit) { + dev->netdev_ops->ndo_xdp_xmit(dev, xdp); + return 0; + } + bpf_warn_invalid_xdp_redirect(dev->ifindex); + return -EOPNOTSUPP; +} + +int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + + dev = dev_get_by_index_rcu(dev_net(dev), ri->ifindex); + ri->ifindex = 0; + if (unlikely(!dev)) { + bpf_warn_invalid_xdp_redirect(ri->ifindex); + return -EINVAL; + } + + return __bpf_tx_xdp(dev, xdp); +} +EXPORT_SYMBOL_GPL(xdp_do_redirect); + +BPF_CALL_2(bpf_xdp_redirect, u32, ifindex, u64, flags) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + + if (unlikely(flags)) + return XDP_ABORTED; + + ri->ifindex = ifindex; + ri->flags = flags; + return XDP_REDIRECT; +} + +static const struct bpf_func_proto bpf_xdp_redirect_proto = { + .func = bpf_xdp_redirect, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_ANYTHING, + .arg2_type = ARG_ANYTHING, +}; + bool bpf_helper_changes_pkt_data(void *func) { if (func == bpf_skb_vlan_push || @@ -3011,6 +3056,8 @@ xdp_func_proto(enum bpf_func_id func_id) return &bpf_get_smp_processor_id_proto; case BPF_FUNC_xdp_adjust_head: return &bpf_xdp_adjust_head_proto; + case BPF_FUNC_redirect: + return &bpf_xdp_redirect_proto; default: return bpf_base_func_proto(func_id); } @@ -3310,6 +3357,11 @@ void bpf_warn_invalid_xdp_action(u32 act) } EXPORT_SYMBOL_GPL(bpf_warn_invalid_xdp_action); +void bpf_warn_invalid_xdp_redirect(u32 ifindex) +{ + WARN_ONCE(1, "Illegal XDP redirect to unsupported device ifindex(%i)\n", ifindex); +} + static bool __is_valid_sock_ops_access(int off, int size) { if (off < 0 || off >= sizeof(struct bpf_sock_ops)) -- cgit v1.2.3 From 6103aa96ec077c976e851e0b89cc2446cb76573d Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:27:50 -0700 Subject: net: implement XDP_REDIRECT for xdp generic Add support for redirect to xdp generic creating a fall back for devices that do not yet have support and allowing test infrastructure using veth pairs to be built. Signed-off-by: John Fastabend Tested-by: Andy Gospodarek Acked-by: Daniel Borkmann Acked-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/linux/filter.h | 1 + net/core/dev.c | 22 ++++++++++++++++++++-- net/core/filter.c | 26 ++++++++++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/linux/filter.h b/include/linux/filter.h index 64cae7a08148..10df7daf5ec6 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -712,6 +712,7 @@ bool bpf_helper_changes_pkt_data(void *func); struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, const struct bpf_insn *patch, u32 len); +int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb); int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp); void bpf_warn_invalid_xdp_action(u32 act); diff --git a/net/core/dev.c b/net/core/dev.c index a1ed7b41b3e8..9f3f4083ada5 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3902,6 +3902,7 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, __skb_push(skb, -off); switch (act) { + case XDP_REDIRECT: case XDP_TX: __skb_push(skb, mac_len); /* fall through */ @@ -3956,14 +3957,27 @@ static int do_xdp_generic(struct sk_buff *skb) if (xdp_prog) { u32 act = netif_receive_generic_xdp(skb, xdp_prog); + int err; if (act != XDP_PASS) { - if (act == XDP_TX) + switch (act) { + case XDP_REDIRECT: + err = xdp_do_generic_redirect(skb->dev, skb); + if (err) + goto out_redir; + /* fallthru to submit skb */ + case XDP_TX: generic_xdp_tx(skb, xdp_prog); + break; + } return XDP_DROP; } } return XDP_PASS; +out_redir: + trace_xdp_exception(skb->dev, xdp_prog, XDP_REDIRECT); + kfree_skb(skb); + return XDP_DROP; } static int netif_rx_internal(struct sk_buff *skb) @@ -3977,8 +3991,12 @@ static int netif_rx_internal(struct sk_buff *skb) if (static_key_false(&generic_xdp_needed)) { int ret = do_xdp_generic(skb); + /* Consider XDP consuming the packet a success from + * the netdev point of view we do not want to count + * this as an error. + */ if (ret != XDP_PASS) - return NET_RX_DROP; + return NET_RX_SUCCESS; } #ifdef CONFIG_RPS diff --git a/net/core/filter.c b/net/core/filter.c index d606a66d1040..eeb713461c25 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2437,6 +2437,32 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp) } EXPORT_SYMBOL_GPL(xdp_do_redirect); +int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + unsigned int len; + + dev = dev_get_by_index_rcu(dev_net(dev), ri->ifindex); + ri->ifindex = 0; + if (unlikely(!dev)) { + bpf_warn_invalid_xdp_redirect(ri->ifindex); + goto err; + } + + if (unlikely(!(dev->flags & IFF_UP))) + goto err; + + len = dev->mtu + dev->hard_header_len + VLAN_HLEN; + if (skb->len > len) + goto err; + + skb->dev = dev; + return 0; +err: + return -EINVAL; +} +EXPORT_SYMBOL_GPL(xdp_do_generic_redirect); + BPF_CALL_2(bpf_xdp_redirect, u32, ifindex, u64, flags) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); -- cgit v1.2.3 From 5acaee0a8964c9bab7775ab8bedcd1f66a2a1011 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:28:35 -0700 Subject: xdp: add trace event for xdp redirect This adds a trace event for xdp redirect which may help when debugging XDP programs that use redirect bpf commands. Signed-off-by: John Fastabend Acked-by: Daniel Borkmann Acked-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- include/linux/filter.h | 4 +++- include/trace/events/xdp.h | 31 ++++++++++++++++++++++++++- net/core/filter.c | 13 +++++++---- 4 files changed, 43 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 3db04736a048..38f7ff97d636 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2232,7 +2232,7 @@ static struct sk_buff *ixgbe_run_xdp(struct ixgbe_adapter *adapter, result = ixgbe_xmit_xdp_ring(adapter, xdp); break; case XDP_REDIRECT: - err = xdp_do_redirect(adapter->netdev, xdp); + err = xdp_do_redirect(adapter->netdev, xdp, xdp_prog); if (!err) result = IXGBE_XDP_TX; else diff --git a/include/linux/filter.h b/include/linux/filter.h index 10df7daf5ec6..ce8211fa91c7 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -713,7 +713,9 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, const struct bpf_insn *patch, u32 len); int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb); -int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp); +int xdp_do_redirect(struct net_device *dev, + struct xdp_buff *xdp, + struct bpf_prog *prog); void bpf_warn_invalid_xdp_action(u32 act); void bpf_warn_invalid_xdp_redirect(u32 ifindex); diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index 1b61357d3f57..7b1eb7b4be41 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -12,7 +12,8 @@ FN(ABORTED) \ FN(DROP) \ FN(PASS) \ - FN(TX) + FN(TX) \ + FN(REDIRECT) #define __XDP_ACT_TP_FN(x) \ TRACE_DEFINE_ENUM(XDP_##x); @@ -48,6 +49,34 @@ TRACE_EVENT(xdp_exception, __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB)) ); +TRACE_EVENT(xdp_redirect, + + TP_PROTO(const struct net_device *from, + const struct net_device *to, + const struct bpf_prog *xdp, u32 act), + + TP_ARGS(from, to, xdp, act), + + TP_STRUCT__entry( + __string(name_from, from->name) + __string(name_to, to->name) + __array(u8, prog_tag, 8) + __field(u32, act) + ), + + TP_fast_assign( + BUILD_BUG_ON(sizeof(__entry->prog_tag) != sizeof(xdp->tag)); + memcpy(__entry->prog_tag, xdp->tag, sizeof(xdp->tag)); + __assign_str(name_from, from->name); + __assign_str(name_to, to->name); + __entry->act = act; + ), + + TP_printk("prog=%s from=%s to=%s action=%s", + __print_hex_str(__entry->prog_tag, 8), + __get_str(name_from), __get_str(name_to), + __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB)) +); #endif /* _TRACE_XDP_H */ #include diff --git a/net/core/filter.c b/net/core/filter.c index eeb713461c25..e30d38b27f21 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -55,6 +55,7 @@ #include #include #include +#include /** * sk_filter_trim_cap - run a packet through a socket filter @@ -2422,18 +2423,22 @@ static int __bpf_tx_xdp(struct net_device *dev, struct xdp_buff *xdp) return -EOPNOTSUPP; } -int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp) +int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, + struct bpf_prog *xdp_prog) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); + struct net_device *fwd; - dev = dev_get_by_index_rcu(dev_net(dev), ri->ifindex); + fwd = dev_get_by_index_rcu(dev_net(dev), ri->ifindex); ri->ifindex = 0; - if (unlikely(!dev)) { + if (unlikely(!fwd)) { bpf_warn_invalid_xdp_redirect(ri->ifindex); return -EINVAL; } - return __bpf_tx_xdp(dev, xdp); + trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); + + return __bpf_tx_xdp(fwd, xdp); } EXPORT_SYMBOL_GPL(xdp_do_redirect); -- cgit v1.2.3 From 97f91a7cf04ff605845c20948b8a80e54cbd3376 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:29:18 -0700 Subject: bpf: add bpf_redirect_map helper routine BPF programs can use the devmap with a bpf_redirect_map() helper routine to forward packets to netdevice in map. Signed-off-by: John Fastabend Signed-off-by: Jesper Dangaard Brouer Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/bpf.h | 3 +++ include/uapi/linux/bpf.h | 8 +++++++- kernel/bpf/devmap.c | 12 +++++++++++ kernel/bpf/verifier.c | 4 ++++ net/core/filter.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 78 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index b69e7a5869ff..d0d3281ac678 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -379,4 +379,7 @@ extern const struct bpf_func_proto bpf_get_stackid_proto; void bpf_user_rnd_init_once(void); u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); +/* Map specifics */ +struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key); + #endif /* _LINUX_BPF_H */ diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index ecbb0e7e15bc..1106a8c4cd36 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -348,6 +348,11 @@ union bpf_attr { * @flags: bit 0 - if set, redirect to ingress instead of egress * other bits - reserved * Return: TC_ACT_REDIRECT + * int bpf_redirect_map(key, map, flags) + * redirect to endpoint in map + * @key: index in map to lookup + * @map: fd of map to do lookup in + * @flags: -- * * u32 bpf_get_route_realm(skb) * retrieve a dst's tclassid @@ -592,7 +597,8 @@ union bpf_attr { FN(get_socket_uid), \ FN(set_hash), \ FN(setsockopt), \ - FN(skb_adjust_room), + FN(skb_adjust_room), \ + FN(redirect_map), /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 1a878356bd37..36dc13deb2e1 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -159,6 +159,18 @@ static int dev_map_get_next_key(struct bpf_map *map, void *key, void *next_key) return 0; } +struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key) +{ + struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); + struct bpf_dtab_netdev *dev; + + if (key >= map->max_entries) + return NULL; + + dev = READ_ONCE(dtab->netdev_map[key]); + return dev ? dev->dev : NULL; +} + /* rcu_read_lock (from syscall and BPF contexts) ensures that if a delete and/or * update happens in parallel here a dev_put wont happen until after reading the * ifindex. diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 4016774d5cca..df05d65f0c87 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1312,6 +1312,10 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id) if (map->map_type != BPF_MAP_TYPE_CGROUP_ARRAY) goto error; break; + case BPF_FUNC_redirect_map: + if (map->map_type != BPF_MAP_TYPE_DEVMAP) + goto error; + break; default: break; } diff --git a/net/core/filter.c b/net/core/filter.c index e30d38b27f21..e93a558324b5 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1779,6 +1779,7 @@ static const struct bpf_func_proto bpf_clone_redirect_proto = { struct redirect_info { u32 ifindex; u32 flags; + struct bpf_map *map; }; static DEFINE_PER_CPU(struct redirect_info, redirect_info); @@ -1792,6 +1793,7 @@ BPF_CALL_2(bpf_redirect, u32, ifindex, u64, flags) ri->ifindex = ifindex; ri->flags = flags; + ri->map = NULL; return TC_ACT_REDIRECT; } @@ -1819,6 +1821,29 @@ static const struct bpf_func_proto bpf_redirect_proto = { .arg2_type = ARG_ANYTHING, }; +BPF_CALL_3(bpf_redirect_map, struct bpf_map *, map, u32, ifindex, u64, flags) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + + if (unlikely(flags)) + return XDP_ABORTED; + + ri->ifindex = ifindex; + ri->flags = flags; + ri->map = map; + + return XDP_REDIRECT; +} + +static const struct bpf_func_proto bpf_redirect_map_proto = { + .func = bpf_redirect_map, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_CONST_MAP_PTR, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +}; + BPF_CALL_1(bpf_get_cgroup_classid, const struct sk_buff *, skb) { return task_get_classid(skb); @@ -2423,14 +2448,39 @@ static int __bpf_tx_xdp(struct net_device *dev, struct xdp_buff *xdp) return -EOPNOTSUPP; } +int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, + struct bpf_prog *xdp_prog) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + struct bpf_map *map = ri->map; + struct net_device *fwd; + int err = -EINVAL; + + ri->ifindex = 0; + ri->map = NULL; + + fwd = __dev_map_lookup_elem(map, ri->ifindex); + if (!fwd) + goto out; + + trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); + err = __bpf_tx_xdp(fwd, xdp); +out: + return err; +} + int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, struct bpf_prog *xdp_prog) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); struct net_device *fwd; + if (ri->map) + return xdp_do_redirect_map(dev, xdp, xdp_prog); + fwd = dev_get_by_index_rcu(dev_net(dev), ri->ifindex); ri->ifindex = 0; + ri->map = NULL; if (unlikely(!fwd)) { bpf_warn_invalid_xdp_redirect(ri->ifindex); return -EINVAL; @@ -3089,6 +3139,8 @@ xdp_func_proto(enum bpf_func_id func_id) return &bpf_xdp_adjust_head_proto; case BPF_FUNC_redirect: return &bpf_xdp_redirect_proto; + case BPF_FUNC_redirect_map: + return &bpf_redirect_map_proto; default: return bpf_base_func_proto(func_id); } -- cgit v1.2.3 From 11393cc9b9be2a1f61559e6fb9c27bc8fa20b1ff Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 17 Jul 2017 09:29:40 -0700 Subject: xdp: Add batching support to redirect map For performance reasons we want to avoid updating the tail pointer in the driver tx ring as much as possible. To accomplish this we add batching support to the redirect path in XDP. This adds another ndo op "xdp_flush" that is used to inform the driver that it should bump the tail pointer on the TX ring. Signed-off-by: John Fastabend Signed-off-by: Jesper Dangaard Brouer Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 28 ++++++++- include/linux/bpf.h | 2 + include/linux/filter.h | 7 +++ include/linux/netdevice.h | 5 +- kernel/bpf/devmap.c | 84 ++++++++++++++++++++++++++- net/core/filter.c | 55 ++++++++++++++---- 6 files changed, 166 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 38f7ff97d636..0f867dcda65f 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -2415,6 +2415,8 @@ static int ixgbe_clean_rx_irq(struct ixgbe_q_vector *q_vector, */ wmb(); writel(ring->next_to_use, ring->tail); + + xdp_do_flush_map(); } u64_stats_update_begin(&rx_ring->syncp); @@ -5817,6 +5819,9 @@ void ixgbe_down(struct ixgbe_adapter *adapter) usleep_range(10000, 20000); + /* synchronize_sched() needed for pending XDP buffers to drain */ + if (adapter->xdp_ring[0]) + synchronize_sched(); netif_tx_stop_all_queues(netdev); /* call carrier off first to avoid false dev_watchdog timeouts */ @@ -9850,15 +9855,31 @@ static int ixgbe_xdp_xmit(struct net_device *dev, struct xdp_buff *xdp) if (err != IXGBE_XDP_TX) return -ENOMEM; + return 0; +} + +static void ixgbe_xdp_flush(struct net_device *dev) +{ + struct ixgbe_adapter *adapter = netdev_priv(dev); + struct ixgbe_ring *ring; + + /* Its possible the device went down between xdp xmit and flush so + * we need to ensure device is still up. + */ + if (unlikely(test_bit(__IXGBE_DOWN, &adapter->state))) + return; + + ring = adapter->xdp_prog ? adapter->xdp_ring[smp_processor_id()] : NULL; + if (unlikely(!ring)) + return; + /* Force memory writes to complete before letting h/w know there * are new descriptors to fetch. */ wmb(); - - ring = adapter->xdp_ring[smp_processor_id()]; writel(ring->next_to_use, ring->tail); - return 0; + return; } static const struct net_device_ops ixgbe_netdev_ops = { @@ -9908,6 +9929,7 @@ static const struct net_device_ops ixgbe_netdev_ops = { .ndo_features_check = ixgbe_features_check, .ndo_xdp = ixgbe_xdp, .ndo_xdp_xmit = ixgbe_xdp_xmit, + .ndo_xdp_flush = ixgbe_xdp_flush, }; /** diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d0d3281ac678..6850a760dc94 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -381,5 +381,7 @@ u64 bpf_user_rnd_u32(u64 r1, u64 r2, u64 r3, u64 r4, u64 r5); /* Map specifics */ struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key); +void __dev_map_insert_ctx(struct bpf_map *map, u32 index); +void __dev_map_flush(struct bpf_map *map); #endif /* _LINUX_BPF_H */ diff --git a/include/linux/filter.h b/include/linux/filter.h index ce8211fa91c7..3323ee91c172 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -712,10 +712,17 @@ bool bpf_helper_changes_pkt_data(void *func); struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, const struct bpf_insn *patch, u32 len); +/* The pair of xdp_do_redirect and xdp_do_flush_map MUST be called in the + * same cpu context. Further for best results no more than a single map + * for the do_redirect/do_flush pair should be used. This limitation is + * because we only track one map and force a flush when the map changes. + * This does not appear to be a real limiation for existing software. + */ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb); int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, struct bpf_prog *prog); +void xdp_do_flush_map(void); void bpf_warn_invalid_xdp_action(u32 act); void bpf_warn_invalid_xdp_redirect(u32 ifindex); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 77f5376005e6..03b104908235 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1142,7 +1142,9 @@ struct xfrmdev_ops { * int (*ndo_xdp_xmit)(struct net_device *dev, struct xdp_buff *xdp); * This function is used to submit a XDP packet for transmit on a * netdevice. - * + * void (*ndo_xdp_flush)(struct net_device *dev); + * This function is used to inform the driver to flush a paticular + * xpd tx queue. Must be called on same CPU as xdp_xmit. */ struct net_device_ops { int (*ndo_init)(struct net_device *dev); @@ -1329,6 +1331,7 @@ struct net_device_ops { struct netdev_xdp *xdp); int (*ndo_xdp_xmit)(struct net_device *dev, struct xdp_buff *xdp); + void (*ndo_xdp_flush)(struct net_device *dev); }; /** diff --git a/kernel/bpf/devmap.c b/kernel/bpf/devmap.c index 36dc13deb2e1..b2ef04a1c86a 100644 --- a/kernel/bpf/devmap.c +++ b/kernel/bpf/devmap.c @@ -53,6 +53,7 @@ struct bpf_dtab_netdev { struct bpf_dtab { struct bpf_map map; struct bpf_dtab_netdev **netdev_map; + unsigned long int __percpu *flush_needed; }; static struct bpf_map *dev_map_alloc(union bpf_attr *attr) @@ -87,6 +88,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) /* make sure page count doesn't overflow */ cost = (u64) dtab->map.max_entries * sizeof(struct bpf_dtab_netdev *); + cost += BITS_TO_LONGS(attr->max_entries) * sizeof(unsigned long); if (cost >= U32_MAX - PAGE_SIZE) goto free_dtab; @@ -97,6 +99,14 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) if (err) goto free_dtab; + /* A per cpu bitfield with a bit per possible net device */ + dtab->flush_needed = __alloc_percpu( + BITS_TO_LONGS(attr->max_entries) * + sizeof(unsigned long), + __alignof__(unsigned long)); + if (!dtab->flush_needed) + goto free_dtab; + dtab->netdev_map = bpf_map_area_alloc(dtab->map.max_entries * sizeof(struct bpf_dtab_netdev *)); if (!dtab->netdev_map) @@ -105,6 +115,7 @@ static struct bpf_map *dev_map_alloc(union bpf_attr *attr) return &dtab->map; free_dtab: + free_percpu(dtab->flush_needed); kfree(dtab); return ERR_PTR(err); } @@ -112,7 +123,7 @@ free_dtab: static void dev_map_free(struct bpf_map *map) { struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); - int i; + int i, cpu; /* At this point bpf_prog->aux->refcnt == 0 and this map->refcnt == 0, * so the programs (can be more than one that used this map) were @@ -123,6 +134,18 @@ static void dev_map_free(struct bpf_map *map) */ synchronize_rcu(); + /* To ensure all pending flush operations have completed wait for flush + * bitmap to indicate all flush_needed bits to be zero on _all_ cpus. + * Because the above synchronize_rcu() ensures the map is disconnected + * from the program we can assume no new bits will be set. + */ + for_each_online_cpu(cpu) { + unsigned long *bitmap = per_cpu_ptr(dtab->flush_needed, cpu); + + while (!bitmap_empty(bitmap, dtab->map.max_entries)) + cpu_relax(); + } + for (i = 0; i < dtab->map.max_entries; i++) { struct bpf_dtab_netdev *dev; @@ -137,6 +160,7 @@ static void dev_map_free(struct bpf_map *map) /* At this point bpf program is detached and all pending operations * _must_ be complete */ + free_percpu(dtab->flush_needed); bpf_map_area_free(dtab->netdev_map); kfree(dtab); } @@ -159,6 +183,14 @@ static int dev_map_get_next_key(struct bpf_map *map, void *key, void *next_key) return 0; } +void __dev_map_insert_ctx(struct bpf_map *map, u32 key) +{ + struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); + unsigned long *bitmap = this_cpu_ptr(dtab->flush_needed); + + __set_bit(key, bitmap); +} + struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key) { struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); @@ -171,6 +203,39 @@ struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key) return dev ? dev->dev : NULL; } +/* __dev_map_flush is called from xdp_do_flush_map() which _must_ be signaled + * from the driver before returning from its napi->poll() routine. The poll() + * routine is called either from busy_poll context or net_rx_action signaled + * from NET_RX_SOFTIRQ. Either way the poll routine must complete before the + * net device can be torn down. On devmap tear down we ensure the ctx bitmap + * is zeroed before completing to ensure all flush operations have completed. + */ +void __dev_map_flush(struct bpf_map *map) +{ + struct bpf_dtab *dtab = container_of(map, struct bpf_dtab, map); + unsigned long *bitmap = this_cpu_ptr(dtab->flush_needed); + u32 bit; + + for_each_set_bit(bit, bitmap, map->max_entries) { + struct bpf_dtab_netdev *dev = READ_ONCE(dtab->netdev_map[bit]); + struct net_device *netdev; + + /* This is possible if the dev entry is removed by user space + * between xdp redirect and flush op. + */ + if (unlikely(!dev)) + continue; + + netdev = dev->dev; + + __clear_bit(bit, bitmap); + if (unlikely(!netdev || !netdev->netdev_ops->ndo_xdp_flush)) + continue; + + netdev->netdev_ops->ndo_xdp_flush(netdev); + } +} + /* rcu_read_lock (from syscall and BPF contexts) ensures that if a delete and/or * update happens in parallel here a dev_put wont happen until after reading the * ifindex. @@ -188,11 +253,28 @@ static void *dev_map_lookup_elem(struct bpf_map *map, void *key) return dev ? &dev->dev->ifindex : NULL; } +static void dev_map_flush_old(struct bpf_dtab_netdev *old_dev) +{ + if (old_dev->dev->netdev_ops->ndo_xdp_flush) { + struct net_device *fl = old_dev->dev; + unsigned long *bitmap; + int cpu; + + for_each_online_cpu(cpu) { + bitmap = per_cpu_ptr(old_dev->dtab->flush_needed, cpu); + __clear_bit(old_dev->key, bitmap); + + fl->netdev_ops->ndo_xdp_flush(old_dev->dev); + } + } +} + static void __dev_map_entry_free(struct rcu_head *rcu) { struct bpf_dtab_netdev *old_dev; old_dev = container_of(rcu, struct bpf_dtab_netdev, rcu); + dev_map_flush_old(old_dev); dev_put(old_dev->dev); kfree(old_dev); } diff --git a/net/core/filter.c b/net/core/filter.c index e93a558324b5..e23aa6fa1119 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1780,6 +1780,7 @@ struct redirect_info { u32 ifindex; u32 flags; struct bpf_map *map; + struct bpf_map *map_to_flush; }; static DEFINE_PER_CPU(struct redirect_info, redirect_info); @@ -2438,34 +2439,68 @@ static const struct bpf_func_proto bpf_xdp_adjust_head_proto = { .arg2_type = ARG_ANYTHING, }; -static int __bpf_tx_xdp(struct net_device *dev, struct xdp_buff *xdp) +static int __bpf_tx_xdp(struct net_device *dev, + struct bpf_map *map, + struct xdp_buff *xdp, + u32 index) { - if (dev->netdev_ops->ndo_xdp_xmit) { - dev->netdev_ops->ndo_xdp_xmit(dev, xdp); - return 0; + int err; + + if (!dev->netdev_ops->ndo_xdp_xmit) { + bpf_warn_invalid_xdp_redirect(dev->ifindex); + return -EOPNOTSUPP; } - bpf_warn_invalid_xdp_redirect(dev->ifindex); - return -EOPNOTSUPP; + + err = dev->netdev_ops->ndo_xdp_xmit(dev, xdp); + if (err) + return err; + + if (map) + __dev_map_insert_ctx(map, index); + else + dev->netdev_ops->ndo_xdp_flush(dev); + + return err; } +void xdp_do_flush_map(void) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + struct bpf_map *map = ri->map_to_flush; + + ri->map = NULL; + ri->map_to_flush = NULL; + + if (map) + __dev_map_flush(map); +} +EXPORT_SYMBOL_GPL(xdp_do_flush_map); + int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, struct bpf_prog *xdp_prog) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); struct bpf_map *map = ri->map; + u32 index = ri->ifindex; struct net_device *fwd; int err = -EINVAL; ri->ifindex = 0; ri->map = NULL; - fwd = __dev_map_lookup_elem(map, ri->ifindex); + fwd = __dev_map_lookup_elem(map, index); if (!fwd) goto out; - trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); - err = __bpf_tx_xdp(fwd, xdp); + if (ri->map_to_flush && (ri->map_to_flush != map)) + xdp_do_flush_map(); + + err = __bpf_tx_xdp(fwd, map, xdp, index); + if (likely(!err)) + ri->map_to_flush = map; + out: + trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); return err; } @@ -2488,7 +2523,7 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); - return __bpf_tx_xdp(fwd, xdp); + return __bpf_tx_xdp(fwd, NULL, xdp, 0); } EXPORT_SYMBOL_GPL(xdp_do_redirect); -- cgit v1.2.3 From d4c023f4f3dd96734ef53d4b588136a872300046 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 07:04:22 -0700 Subject: net: Remove references to NETIF_F_UFO in netdev_fix_features(). It is going away. Signed-off-by: David S. Miller --- net/core/dev.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 9f3f4083ada5..467420eda02e 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -7271,24 +7271,6 @@ static netdev_features_t netdev_fix_features(struct net_device *dev, features &= ~NETIF_F_GSO; } - /* UFO needs SG and checksumming */ - if (features & NETIF_F_UFO) { - /* maybe split UFO into V4 and V6? */ - if (!(features & NETIF_F_HW_CSUM) && - ((features & (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM)) != - (NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM))) { - netdev_dbg(dev, - "Dropping NETIF_F_UFO since no checksum offload features.\n"); - features &= ~NETIF_F_UFO; - } - - if (!(features & NETIF_F_SG)) { - netdev_dbg(dev, - "Dropping NETIF_F_UFO since no NETIF_F_SG feature.\n"); - features &= ~NETIF_F_UFO; - } - } - /* GSO partial features require GSO partial be set */ if ((features & dev->gso_partial_features) && !(features & NETIF_F_GSO_PARTIAL)) { -- cgit v1.2.3 From 08a00fea6de277df12ccfadc21bf779df2e83705 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 07:04:34 -0700 Subject: net: Remove references to NETIF_F_UFO from ethtool. It is going away. Signed-off-by: David S. Miller --- net/core/ethtool.c | 7 ------- 1 file changed, 7 deletions(-) (limited to 'net') diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 674b6c9cec18..78408ab77a10 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -76,7 +76,6 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_LRO_BIT] = "rx-lro", [NETIF_F_TSO_BIT] = "tx-tcp-segmentation", - [NETIF_F_UFO_BIT] = "tx-udp-fragmentation", [NETIF_F_GSO_ROBUST_BIT] = "tx-gso-robust", [NETIF_F_TSO_ECN_BIT] = "tx-tcp-ecn-segmentation", [NETIF_F_TSO_MANGLEID_BIT] = "tx-tcp-mangleid-segmentation", @@ -299,9 +298,6 @@ static netdev_features_t ethtool_get_feature_mask(u32 eth_cmd) case ETHTOOL_GTSO: case ETHTOOL_STSO: return NETIF_F_ALL_TSO; - case ETHTOOL_GUFO: - case ETHTOOL_SUFO: - return NETIF_F_UFO; case ETHTOOL_GGSO: case ETHTOOL_SGSO: return NETIF_F_GSO; @@ -2555,7 +2551,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GPHYSTATS: case ETHTOOL_GTSO: case ETHTOOL_GPERMADDR: - case ETHTOOL_GUFO: case ETHTOOL_GGSO: case ETHTOOL_GGRO: case ETHTOOL_GFLAGS: @@ -2723,7 +2718,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GRXCSUM: case ETHTOOL_GSG: case ETHTOOL_GTSO: - case ETHTOOL_GUFO: case ETHTOOL_GGSO: case ETHTOOL_GGRO: rc = ethtool_get_one_feature(dev, useraddr, ethcmd); @@ -2732,7 +2726,6 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_SRXCSUM: case ETHTOOL_SSG: case ETHTOOL_STSO: - case ETHTOOL_SUFO: case ETHTOOL_SGSO: case ETHTOOL_SGRO: rc = ethtool_set_one_feature(dev, useraddr, ethcmd); -- cgit v1.2.3 From 988cf74deb45bd6ee27433b7b5d1be6004d842b8 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 07:07:18 -0700 Subject: inet: Stop generating UFO packets. Signed-off-by: David S. Miller --- net/ipv4/ip_output.c | 76 --------------------------------------------------- net/ipv6/ip6_output.c | 76 --------------------------------------------------- 2 files changed, 152 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 7eb252dcecee..d338f865951a 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -853,61 +853,6 @@ csum_page(struct page *page, int offset, int copy) return csum; } -static inline int ip_ufo_append_data(struct sock *sk, - struct sk_buff_head *queue, - int getfrag(void *from, char *to, int offset, int len, - int odd, struct sk_buff *skb), - void *from, int length, int hh_len, int fragheaderlen, - int transhdrlen, int maxfraglen, unsigned int flags) -{ - struct sk_buff *skb; - int err; - - /* There is support for UDP fragmentation offload by network - * device, so create one single skb packet containing complete - * udp datagram - */ - skb = skb_peek_tail(queue); - if (!skb) { - skb = sock_alloc_send_skb(sk, - hh_len + fragheaderlen + transhdrlen + 20, - (flags & MSG_DONTWAIT), &err); - - if (!skb) - return err; - - /* reserve space for Hardware header */ - skb_reserve(skb, hh_len); - - /* create space for UDP/IP header */ - skb_put(skb, fragheaderlen + transhdrlen); - - /* initialize network header pointer */ - skb_reset_network_header(skb); - - /* initialize protocol header pointer */ - skb->transport_header = skb->network_header + fragheaderlen; - - skb->csum = 0; - - if (flags & MSG_CONFIRM) - skb_set_dst_pending_confirm(skb, 1); - - __skb_queue_tail(queue, skb); - } else if (skb_is_gso(skb)) { - goto append; - } - - skb->ip_summed = CHECKSUM_PARTIAL; - /* specify the length of each IP datagram fragment */ - skb_shinfo(skb)->gso_size = maxfraglen - fragheaderlen; - skb_shinfo(skb)->gso_type = SKB_GSO_UDP; - -append: - return skb_append_datato_frags(sk, skb, getfrag, from, - (length - transhdrlen)); -} - static int __ip_append_data(struct sock *sk, struct flowi4 *fl4, struct sk_buff_head *queue, @@ -965,18 +910,6 @@ static int __ip_append_data(struct sock *sk, csummode = CHECKSUM_PARTIAL; cork->length += length; - if ((((length + (skb ? skb->len : fragheaderlen)) > mtu) || - (skb && skb_is_gso(skb))) && - (sk->sk_protocol == IPPROTO_UDP) && - (rt->dst.dev->features & NETIF_F_UFO) && !dst_xfrm(&rt->dst) && - (sk->sk_type == SOCK_DGRAM) && !sk->sk_no_check_tx) { - err = ip_ufo_append_data(sk, queue, getfrag, from, length, - hh_len, fragheaderlen, transhdrlen, - maxfraglen, flags); - if (err) - goto error; - return 0; - } /* So, what's going on in the loop below? * @@ -1287,15 +1220,6 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, if (!skb) return -EINVAL; - if ((size + skb->len > mtu) && - (sk->sk_protocol == IPPROTO_UDP) && - (rt->dst.dev->features & NETIF_F_UFO)) { - if (skb->ip_summed != CHECKSUM_PARTIAL) - return -EOPNOTSUPP; - - skb_shinfo(skb)->gso_size = mtu - fragheaderlen; - skb_shinfo(skb)->gso_type = SKB_GSO_UDP; - } cork->length += size; while (size > 0) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index 1422d6c08377..c6ec06465ce0 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -1114,69 +1114,6 @@ struct dst_entry *ip6_sk_dst_lookup_flow(struct sock *sk, struct flowi6 *fl6, } EXPORT_SYMBOL_GPL(ip6_sk_dst_lookup_flow); -static inline int ip6_ufo_append_data(struct sock *sk, - struct sk_buff_head *queue, - int getfrag(void *from, char *to, int offset, int len, - int odd, struct sk_buff *skb), - void *from, int length, int hh_len, int fragheaderlen, - int exthdrlen, int transhdrlen, int mtu, - unsigned int flags, const struct flowi6 *fl6) - -{ - struct sk_buff *skb; - int err; - - /* There is support for UDP large send offload by network - * device, so create one single skb packet containing complete - * udp datagram - */ - skb = skb_peek_tail(queue); - if (!skb) { - skb = sock_alloc_send_skb(sk, - hh_len + fragheaderlen + transhdrlen + 20, - (flags & MSG_DONTWAIT), &err); - if (!skb) - return err; - - /* reserve space for Hardware header */ - skb_reserve(skb, hh_len); - - /* create space for UDP/IP header */ - skb_put(skb, fragheaderlen + transhdrlen); - - /* initialize network header pointer */ - skb_set_network_header(skb, exthdrlen); - - /* initialize protocol header pointer */ - skb->transport_header = skb->network_header + fragheaderlen; - - skb->protocol = htons(ETH_P_IPV6); - skb->csum = 0; - - if (flags & MSG_CONFIRM) - skb_set_dst_pending_confirm(skb, 1); - - __skb_queue_tail(queue, skb); - } else if (skb_is_gso(skb)) { - goto append; - } - - skb->ip_summed = CHECKSUM_PARTIAL; - /* Specify the length of each IPv6 datagram fragment. - * It has to be a multiple of 8. - */ - skb_shinfo(skb)->gso_size = (mtu - fragheaderlen - - sizeof(struct frag_hdr)) & ~7; - skb_shinfo(skb)->gso_type = SKB_GSO_UDP; - skb_shinfo(skb)->ip6_frag_id = ipv6_select_ident(sock_net(sk), - &fl6->daddr, - &fl6->saddr); - -append: - return skb_append_datato_frags(sk, skb, getfrag, from, - (length - transhdrlen)); -} - static inline struct ipv6_opt_hdr *ip6_opt_dup(struct ipv6_opt_hdr *src, gfp_t gfp) { @@ -1385,19 +1322,6 @@ emsgsize: */ cork->length += length; - if ((((length + (skb ? skb->len : headersize)) > mtu) || - (skb && skb_is_gso(skb))) && - (sk->sk_protocol == IPPROTO_UDP) && - (rt->dst.dev->features & NETIF_F_UFO) && !dst_xfrm(&rt->dst) && - (sk->sk_type == SOCK_DGRAM) && !udp_get_no_check6_tx(sk)) { - err = ip6_ufo_append_data(sk, queue, getfrag, from, length, - hh_len, fragheaderlen, exthdrlen, - transhdrlen, mtu, flags, fl6); - if (err) - goto error; - return 0; - } - if (!skb) goto alloc_new_skb; -- cgit v1.2.3 From 880388aa3c07fdea4f9b85e35641753017b1852f Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Mon, 3 Jul 2017 07:29:12 -0700 Subject: net: Remove all references to SKB_GSO_UDP. Such packets are no longer possible. Signed-off-by: David S. Miller --- include/linux/virtio_net.h | 5 ----- net/core/filter.c | 8 ++++---- net/ipv4/af_inet.c | 12 ++---------- net/ipv4/gre_offload.c | 14 +------------- net/ipv4/udp_offload.c | 6 ++---- net/openvswitch/datapath.c | 14 -------------- net/openvswitch/flow.c | 6 +----- net/sched/act_csum.c | 6 ------ 8 files changed, 10 insertions(+), 61 deletions(-) (limited to 'net') diff --git a/include/linux/virtio_net.h b/include/linux/virtio_net.h index 5209b5ed2a64..32fb046f2173 100644 --- a/include/linux/virtio_net.h +++ b/include/linux/virtio_net.h @@ -18,9 +18,6 @@ static inline int virtio_net_hdr_to_skb(struct sk_buff *skb, case VIRTIO_NET_HDR_GSO_TCPV6: gso_type = SKB_GSO_TCPV6; break; - case VIRTIO_NET_HDR_GSO_UDP: - gso_type = SKB_GSO_UDP; - break; default: return -EINVAL; } @@ -73,8 +70,6 @@ static inline int virtio_net_hdr_from_skb(const struct sk_buff *skb, hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV4; else if (sinfo->gso_type & SKB_GSO_TCPV6) hdr->gso_type = VIRTIO_NET_HDR_GSO_TCPV6; - else if (sinfo->gso_type & SKB_GSO_UDP) - hdr->gso_type = VIRTIO_NET_HDR_GSO_UDP; else return -EINVAL; if (sinfo->gso_type & SKB_GSO_TCP_ECN) diff --git a/net/core/filter.c b/net/core/filter.c index e23aa6fa1119..29e690cbe820 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2051,8 +2051,8 @@ static int bpf_skb_proto_4_to_6(struct sk_buff *skb) return ret; if (skb_is_gso(skb)) { - /* SKB_GSO_UDP stays as is. SKB_GSO_TCPV4 needs to - * be changed into SKB_GSO_TCPV6. + /* SKB_GSO_TCPV4 needs to be changed into + * SKB_GSO_TCPV6. */ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4) { skb_shinfo(skb)->gso_type &= ~SKB_GSO_TCPV4; @@ -2087,8 +2087,8 @@ static int bpf_skb_proto_6_to_4(struct sk_buff *skb) return ret; if (skb_is_gso(skb)) { - /* SKB_GSO_UDP stays as is. SKB_GSO_TCPV6 needs to - * be changed into SKB_GSO_TCPV4. + /* SKB_GSO_TCPV6 needs to be changed into + * SKB_GSO_TCPV4. */ if (skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6) { skb_shinfo(skb)->gso_type &= ~SKB_GSO_TCPV6; diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 76c2077c3f5b..5ce44fb7d498 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1219,10 +1219,9 @@ EXPORT_SYMBOL(inet_sk_rebuild_header); struct sk_buff *inet_gso_segment(struct sk_buff *skb, netdev_features_t features) { - bool udpfrag = false, fixedid = false, gso_partial, encap; + bool fixedid = false, gso_partial, encap; struct sk_buff *segs = ERR_PTR(-EINVAL); const struct net_offload *ops; - unsigned int offset = 0; struct iphdr *iph; int proto, tot_len; int nhoff; @@ -1257,7 +1256,6 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb, segs = ERR_PTR(-EPROTONOSUPPORT); if (!skb->encapsulation || encap) { - udpfrag = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP); fixedid = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TCP_FIXEDID); /* fixed ID is invalid if DF bit is not set */ @@ -1277,13 +1275,7 @@ struct sk_buff *inet_gso_segment(struct sk_buff *skb, skb = segs; do { iph = (struct iphdr *)(skb_mac_header(skb) + nhoff); - if (udpfrag) { - iph->frag_off = htons(offset >> 3); - if (skb->next) - iph->frag_off |= htons(IP_MF); - offset += skb->len - nhoff - ihl; - tot_len = skb->len - nhoff; - } else if (skb_is_gso(skb)) { + if (skb_is_gso(skb)) { if (!fixedid) { iph->id = htons(id); id += skb_shinfo(skb)->gso_segs; diff --git a/net/ipv4/gre_offload.c b/net/ipv4/gre_offload.c index d5cac99170b1..416bb304a281 100644 --- a/net/ipv4/gre_offload.c +++ b/net/ipv4/gre_offload.c @@ -24,7 +24,7 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb, __be16 protocol = skb->protocol; u16 mac_len = skb->mac_len; int gre_offset, outer_hlen; - bool need_csum, ufo, gso_partial; + bool need_csum, gso_partial; if (!skb->encapsulation) goto out; @@ -47,20 +47,8 @@ static struct sk_buff *gre_gso_segment(struct sk_buff *skb, need_csum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_GRE_CSUM); skb->encap_hdr_csum = need_csum; - ufo = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP); - features &= skb->dev->hw_enc_features; - /* The only checksum offload we care about from here on out is the - * outer one so strip the existing checksum feature flags based - * on the fact that we will be computing our checksum in software. - */ - if (ufo) { - features &= ~NETIF_F_CSUM_MASK; - if (!need_csum) - features |= NETIF_F_HW_CSUM; - } - /* segment inner packet. */ segs = skb_mac_gso_segment(skb, features); if (IS_ERR_OR_NULL(segs)) { diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 781250151d40..4fedce3d5733 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -21,7 +21,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, __be16 new_protocol, bool is_ipv6) { int tnl_hlen = skb_inner_mac_header(skb) - skb_transport_header(skb); - bool remcsum, need_csum, offload_csum, ufo, gso_partial; + bool remcsum, need_csum, offload_csum, gso_partial; struct sk_buff *segs = ERR_PTR(-EINVAL); struct udphdr *uh = udp_hdr(skb); u16 mac_offset = skb->mac_header; @@ -61,8 +61,6 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, remcsum = !!(skb_shinfo(skb)->gso_type & SKB_GSO_TUNNEL_REMCSUM); skb->remcsum_offload = remcsum; - ufo = !!(skb_shinfo(skb)->gso_type & SKB_GSO_UDP); - need_ipsec = skb_dst(skb) && dst_xfrm(skb_dst(skb)); /* Try to offload checksum if possible */ offload_csum = !!(need_csum && @@ -77,7 +75,7 @@ static struct sk_buff *__skb_udp_tunnel_segment(struct sk_buff *skb, * outer one so strip the existing checksum feature flags and * instead set the flag based on our outer checksum offload value. */ - if (remcsum || ufo) { + if (remcsum) { features &= ~NETIF_F_CSUM_MASK; if (!need_csum || offload_csum) features |= NETIF_F_HW_CSUM; diff --git a/net/openvswitch/datapath.c b/net/openvswitch/datapath.c index 45fe8c8a884d..f6e229b51dfb 100644 --- a/net/openvswitch/datapath.c +++ b/net/openvswitch/datapath.c @@ -335,8 +335,6 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, const struct dp_upcall_info *upcall_info, uint32_t cutlen) { - unsigned short gso_type = skb_shinfo(skb)->gso_type; - struct sw_flow_key later_key; struct sk_buff *segs, *nskb; int err; @@ -347,21 +345,9 @@ static int queue_gso_packets(struct datapath *dp, struct sk_buff *skb, if (segs == NULL) return -EINVAL; - if (gso_type & SKB_GSO_UDP) { - /* The initial flow key extracted by ovs_flow_key_extract() - * in this case is for a first fragment, so we need to - * properly mark later fragments. - */ - later_key = *key; - later_key.ip.frag = OVS_FRAG_TYPE_LATER; - } - /* Queue all of the segments. */ skb = segs; do { - if (gso_type & SKB_GSO_UDP && skb != segs) - key = &later_key; - err = queue_userspace_packet(dp, skb, key, upcall_info, cutlen); if (err) break; diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 3f76cb765e5b..597d96faca45 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -584,8 +584,7 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) key->ip.frag = OVS_FRAG_TYPE_LATER; return 0; } - if (nh->frag_off & htons(IP_MF) || - skb_shinfo(skb)->gso_type & SKB_GSO_UDP) + if (nh->frag_off & htons(IP_MF)) key->ip.frag = OVS_FRAG_TYPE_FIRST; else key->ip.frag = OVS_FRAG_TYPE_NONE; @@ -701,9 +700,6 @@ static int key_extract(struct sk_buff *skb, struct sw_flow_key *key) if (key->ip.frag == OVS_FRAG_TYPE_LATER) return 0; - if (skb_shinfo(skb)->gso_type & SKB_GSO_UDP) - key->ip.frag = OVS_FRAG_TYPE_FIRST; - /* Transport layer. */ if (key->ip.proto == NEXTHDR_TCP) { if (tcphdr_ok(skb)) { diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 3317a2f579da..67afc12df88b 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -231,9 +231,6 @@ static int tcf_csum_ipv4_udp(struct sk_buff *skb, unsigned int ihl, const struct iphdr *iph; u16 ul; - if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) - return 1; - /* * Support both UDP and UDPLITE checksum algorithms, Don't use * udph->len to get the real length without any protocol check, @@ -287,9 +284,6 @@ static int tcf_csum_ipv6_udp(struct sk_buff *skb, unsigned int ihl, const struct ipv6hdr *ip6h; u16 ul; - if (skb_is_gso(skb) && skb_shinfo(skb)->gso_type & SKB_GSO_UDP) - return 1; - /* * Support both UDP and UDPLITE checksum algorithms, Don't use * udph->len to get the real length without any protocol check, -- cgit v1.2.3 From 6800b2e040edda01f593aba28203c2ebf1679f4c Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Fri, 7 Jul 2017 10:30:55 +0100 Subject: inet: Remove software UFO fragmenting code. Rename udp{4,6}_ufo_fragment() to udp{4,6}_tunnel_segment() and only handle tunnel segmentation. Signed-off-by: David S. Miller --- net/ipv4/udp_offload.c | 58 ++-------------------------- net/ipv6/udp_offload.c | 100 ++----------------------------------------------- 2 files changed, 7 insertions(+), 151 deletions(-) (limited to 'net') diff --git a/net/ipv4/udp_offload.c b/net/ipv4/udp_offload.c index 4fedce3d5733..97658bfc1b58 100644 --- a/net/ipv4/udp_offload.c +++ b/net/ipv4/udp_offload.c @@ -187,66 +187,16 @@ out_unlock: } EXPORT_SYMBOL(skb_udp_tunnel_segment); -static struct sk_buff *udp4_ufo_fragment(struct sk_buff *skb, - netdev_features_t features) +static struct sk_buff *udp4_tunnel_segment(struct sk_buff *skb, + netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); - unsigned int mss; - __wsum csum; - struct udphdr *uh; - struct iphdr *iph; if (skb->encapsulation && (skb_shinfo(skb)->gso_type & - (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) { + (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM))) segs = skb_udp_tunnel_segment(skb, features, false); - goto out; - } - - if (!pskb_may_pull(skb, sizeof(struct udphdr))) - goto out; - - mss = skb_shinfo(skb)->gso_size; - if (unlikely(skb->len <= mss)) - goto out; - - if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { - /* Packet is from an untrusted source, reset gso_segs. */ - - skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); - - segs = NULL; - goto out; - } - - /* Do software UFO. Complete and fill in the UDP checksum as - * HW cannot do checksum of UDP packets sent as multiple - * IP fragments. - */ - - uh = udp_hdr(skb); - iph = ip_hdr(skb); - - uh->check = 0; - csum = skb_checksum(skb, 0, skb->len, 0); - uh->check = udp_v4_check(skb->len, iph->saddr, iph->daddr, csum); - if (uh->check == 0) - uh->check = CSUM_MANGLED_0; - skb->ip_summed = CHECKSUM_NONE; - - /* If there is no outer header we can fake a checksum offload - * due to the fact that we have already done the checksum in - * software prior to segmenting the frame. - */ - if (!skb->encap_hdr_csum) - features |= NETIF_F_HW_CSUM; - - /* Fragment the skb. IP headers of the fragments are updated in - * inet_gso_segment() - */ - segs = skb_segment(skb, features); -out: return segs; } @@ -380,7 +330,7 @@ static int udp4_gro_complete(struct sk_buff *skb, int nhoff) static const struct net_offload udpv4_offload = { .callbacks = { - .gso_segment = udp4_ufo_fragment, + .gso_segment = udp4_tunnel_segment, .gro_receive = udp4_gro_receive, .gro_complete = udp4_gro_complete, }, diff --git a/net/ipv6/udp_offload.c b/net/ipv6/udp_offload.c index a2267f80febb..455fd4e39333 100644 --- a/net/ipv6/udp_offload.c +++ b/net/ipv6/udp_offload.c @@ -17,109 +17,15 @@ #include #include "ip6_offload.h" -static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, - netdev_features_t features) +static struct sk_buff *udp6_tunnel_segment(struct sk_buff *skb, + netdev_features_t features) { struct sk_buff *segs = ERR_PTR(-EINVAL); - unsigned int mss; - unsigned int unfrag_ip6hlen, unfrag_len; - struct frag_hdr *fptr; - u8 *packet_start, *prevhdr; - u8 nexthdr; - u8 frag_hdr_sz = sizeof(struct frag_hdr); - __wsum csum; - int tnl_hlen; - int err; - - mss = skb_shinfo(skb)->gso_size; - if (unlikely(skb->len <= mss)) - goto out; - - if (skb_gso_ok(skb, features | NETIF_F_GSO_ROBUST)) { - /* Packet is from an untrusted source, reset gso_segs. */ - - skb_shinfo(skb)->gso_segs = DIV_ROUND_UP(skb->len, mss); - - /* Set the IPv6 fragment id if not set yet */ - if (!skb_shinfo(skb)->ip6_frag_id) - ipv6_proxy_select_ident(dev_net(skb->dev), skb); - - segs = NULL; - goto out; - } if (skb->encapsulation && skb_shinfo(skb)->gso_type & (SKB_GSO_UDP_TUNNEL|SKB_GSO_UDP_TUNNEL_CSUM)) segs = skb_udp_tunnel_segment(skb, features, true); - else { - const struct ipv6hdr *ipv6h; - struct udphdr *uh; - - if (!pskb_may_pull(skb, sizeof(struct udphdr))) - goto out; - - /* Do software UFO. Complete and fill in the UDP checksum as HW cannot - * do checksum of UDP packets sent as multiple IP fragments. - */ - - uh = udp_hdr(skb); - ipv6h = ipv6_hdr(skb); - - uh->check = 0; - csum = skb_checksum(skb, 0, skb->len, 0); - uh->check = udp_v6_check(skb->len, &ipv6h->saddr, - &ipv6h->daddr, csum); - if (uh->check == 0) - uh->check = CSUM_MANGLED_0; - - skb->ip_summed = CHECKSUM_NONE; - - /* If there is no outer header we can fake a checksum offload - * due to the fact that we have already done the checksum in - * software prior to segmenting the frame. - */ - if (!skb->encap_hdr_csum) - features |= NETIF_F_HW_CSUM; - - /* Check if there is enough headroom to insert fragment header. */ - tnl_hlen = skb_tnl_header_len(skb); - if (skb->mac_header < (tnl_hlen + frag_hdr_sz)) { - if (gso_pskb_expand_head(skb, tnl_hlen + frag_hdr_sz)) - goto out; - } - - /* Find the unfragmentable header and shift it left by frag_hdr_sz - * bytes to insert fragment header. - */ - err = ip6_find_1stfragopt(skb, &prevhdr); - if (err < 0) - return ERR_PTR(err); - unfrag_ip6hlen = err; - nexthdr = *prevhdr; - *prevhdr = NEXTHDR_FRAGMENT; - unfrag_len = (skb_network_header(skb) - skb_mac_header(skb)) + - unfrag_ip6hlen + tnl_hlen; - packet_start = (u8 *) skb->head + SKB_GSO_CB(skb)->mac_offset; - memmove(packet_start-frag_hdr_sz, packet_start, unfrag_len); - - SKB_GSO_CB(skb)->mac_offset -= frag_hdr_sz; - skb->mac_header -= frag_hdr_sz; - skb->network_header -= frag_hdr_sz; - - fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen); - fptr->nexthdr = nexthdr; - fptr->reserved = 0; - if (!skb_shinfo(skb)->ip6_frag_id) - ipv6_proxy_select_ident(dev_net(skb->dev), skb); - fptr->identification = skb_shinfo(skb)->ip6_frag_id; - - /* Fragment the skb. ipv6 header and the remaining fields of the - * fragment header are updated in ipv6_gso_segment() - */ - segs = skb_segment(skb, features); - } -out: return segs; } @@ -169,7 +75,7 @@ static int udp6_gro_complete(struct sk_buff *skb, int nhoff) static const struct net_offload udpv6_offload = { .callbacks = { - .gso_segment = udp6_ufo_fragment, + .gso_segment = udp6_tunnel_segment, .gro_receive = udp6_gro_receive, .gro_complete = udp6_gro_complete, }, -- cgit v1.2.3 From 06dc75ab06943fcc126a951a0680980ad5cb75c6 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 18:56:54 +0200 Subject: net: Revert "net: add function to allocate sk_buff head without data area" It was added for netlink mmap tx, there are no callers in the tree. The commit also added a check for skb->head != NULL in kfree_skb path, remove that too -- all skbs ought to have skb->head set. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/skbuff.h | 6 ------ net/core/skbuff.c | 31 ++----------------------------- 2 files changed, 2 insertions(+), 35 deletions(-) (limited to 'net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 4d7a284ba3ee..4093552be1de 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -944,12 +944,6 @@ static inline struct sk_buff *alloc_skb_fclone(unsigned int size, return __alloc_skb(size, priority, SKB_ALLOC_FCLONE, NUMA_NO_NODE); } -struct sk_buff *__alloc_skb_head(gfp_t priority, int node); -static inline struct sk_buff *alloc_skb_head(gfp_t priority) -{ - return __alloc_skb_head(priority, -1); -} - struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src); int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask); struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t priority); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index b352c6bcfb31..6bc19c80c210 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -158,31 +158,6 @@ out: * */ -struct sk_buff *__alloc_skb_head(gfp_t gfp_mask, int node) -{ - struct sk_buff *skb; - - /* Get the HEAD */ - skb = kmem_cache_alloc_node(skbuff_head_cache, - gfp_mask & ~__GFP_DMA, node); - if (!skb) - goto out; - - /* - * Only clear those fields we need to clear, not those that we will - * actually initialise below. Hence, don't put any more fields after - * the tail pointer in struct sk_buff! - */ - memset(skb, 0, offsetof(struct sk_buff, tail)); - skb->head = NULL; - skb->truesize = sizeof(struct sk_buff); - refcount_set(&skb->users, 1); - - skb->mac_header = (typeof(skb->mac_header))~0U; -out: - return skb; -} - /** * __alloc_skb - allocate a network buffer * @size: size to allocate @@ -663,8 +638,7 @@ void skb_release_head_state(struct sk_buff *skb) static void skb_release_all(struct sk_buff *skb) { skb_release_head_state(skb); - if (likely(skb->head)) - skb_release_data(skb); + skb_release_data(skb); } /** @@ -762,8 +736,7 @@ void consume_stateless_skb(struct sk_buff *skb) return; trace_consume_skb(skb); - if (likely(skb->head)) - skb_release_data(skb); + skb_release_data(skb); kfree_skbmem(skb); } -- cgit v1.2.3 From 6b1c42e9726bdb00370342909d95efdc331d10ac Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:19 +0200 Subject: vti: revert flush x-netns xfrm cache when vti interface is removed flow cache is removed in next commit. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/ipv4/ip_vti.c | 31 ------------------------------- net/ipv6/ip6_vti.c | 31 ------------------------------- 2 files changed, 62 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_vti.c b/net/ipv4/ip_vti.c index 0192c255e508..5ed63d250950 100644 --- a/net/ipv4/ip_vti.c +++ b/net/ipv4/ip_vti.c @@ -584,33 +584,6 @@ static struct rtnl_link_ops vti_link_ops __read_mostly = { .get_link_net = ip_tunnel_get_link_net, }; -static bool is_vti_tunnel(const struct net_device *dev) -{ - return dev->netdev_ops == &vti_netdev_ops; -} - -static int vti_device_event(struct notifier_block *unused, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct ip_tunnel *tunnel = netdev_priv(dev); - - if (!is_vti_tunnel(dev)) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_DOWN: - if (!net_eq(tunnel->net, dev_net(dev))) - xfrm_garbage_collect(tunnel->net); - break; - } - return NOTIFY_DONE; -} - -static struct notifier_block vti_notifier_block __read_mostly = { - .notifier_call = vti_device_event, -}; - static int __init vti_init(void) { const char *msg; @@ -618,8 +591,6 @@ static int __init vti_init(void) pr_info("IPv4 over IPsec tunneling driver\n"); - register_netdevice_notifier(&vti_notifier_block); - msg = "tunnel device"; err = register_pernet_device(&vti_net_ops); if (err < 0) @@ -652,7 +623,6 @@ xfrm_proto_ah_failed: xfrm_proto_esp_failed: unregister_pernet_device(&vti_net_ops); pernet_dev_failed: - unregister_netdevice_notifier(&vti_notifier_block); pr_err("vti init: failed to register %s\n", msg); return err; } @@ -664,7 +634,6 @@ static void __exit vti_fini(void) xfrm4_protocol_deregister(&vti_ah4_protocol, IPPROTO_AH); xfrm4_protocol_deregister(&vti_esp4_protocol, IPPROTO_ESP); unregister_pernet_device(&vti_net_ops); - unregister_netdevice_notifier(&vti_notifier_block); } module_init(vti_init); diff --git a/net/ipv6/ip6_vti.c b/net/ipv6/ip6_vti.c index 486c2305f53c..79444a4bfd6d 100644 --- a/net/ipv6/ip6_vti.c +++ b/net/ipv6/ip6_vti.c @@ -1145,33 +1145,6 @@ static struct xfrm6_protocol vti_ipcomp6_protocol __read_mostly = { .priority = 100, }; -static bool is_vti6_tunnel(const struct net_device *dev) -{ - return dev->netdev_ops == &vti6_netdev_ops; -} - -static int vti6_device_event(struct notifier_block *unused, - unsigned long event, void *ptr) -{ - struct net_device *dev = netdev_notifier_info_to_dev(ptr); - struct ip6_tnl *t = netdev_priv(dev); - - if (!is_vti6_tunnel(dev)) - return NOTIFY_DONE; - - switch (event) { - case NETDEV_DOWN: - if (!net_eq(t->net, dev_net(dev))) - xfrm_garbage_collect(t->net); - break; - } - return NOTIFY_DONE; -} - -static struct notifier_block vti6_notifier_block __read_mostly = { - .notifier_call = vti6_device_event, -}; - /** * vti6_tunnel_init - register protocol and reserve needed resources * @@ -1182,8 +1155,6 @@ static int __init vti6_tunnel_init(void) const char *msg; int err; - register_netdevice_notifier(&vti6_notifier_block); - msg = "tunnel device"; err = register_pernet_device(&vti6_net_ops); if (err < 0) @@ -1216,7 +1187,6 @@ xfrm_proto_ah_failed: xfrm_proto_esp_failed: unregister_pernet_device(&vti6_net_ops); pernet_dev_failed: - unregister_netdevice_notifier(&vti6_notifier_block); pr_err("vti6 init: failed to register %s\n", msg); return err; } @@ -1231,7 +1201,6 @@ static void __exit vti6_tunnel_cleanup(void) xfrm6_protocol_deregister(&vti_ah6_protocol, IPPROTO_AH); xfrm6_protocol_deregister(&vti_esp6_protocol, IPPROTO_ESP); unregister_pernet_device(&vti6_net_ops); - unregister_netdevice_notifier(&vti6_notifier_block); } module_init(vti6_tunnel_init); -- cgit v1.2.3 From 3c2a89ddc11896cf5498115c0380ab54b1c424b7 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:20 +0200 Subject: net: xfrm: revert to lower xfrm dst gc limit revert c386578f1cdb4dac230395 ("xfrm: Let the flowcache handle its size by default."). Once we remove flow cache, we don't have a flow cache limit anymore. We must not allow (virtually) unlimited allocations of xfrm dst entries. Revert back to the old xfrm dst gc limits. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 6 ++---- net/ipv4/xfrm4_policy.c | 2 +- net/ipv6/xfrm6_policy.c | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 974ab47ae53a..f485d553e65c 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1291,8 +1291,7 @@ tag - INTEGER xfrm4_gc_thresh - INTEGER The threshold at which we will start garbage collecting for IPv4 destination cache entries. At twice this value the system will - refuse new allocations. The value must be set below the flowcache - limit (4096 * number of online cpus) to take effect. + refuse new allocations. igmp_link_local_mcast_reports - BOOLEAN Enable IGMP reports for link local multicast groups in the @@ -1778,8 +1777,7 @@ ratelimit - INTEGER xfrm6_gc_thresh - INTEGER The threshold at which we will start garbage collecting for IPv6 destination cache entries. At twice this value the system will - refuse new allocations. The value must be set below the flowcache - limit (4096 * number of online cpus) to take effect. + refuse new allocations. IPv6 Update by: diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 71b4ecc195c7..19455a5fc328 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -266,7 +266,7 @@ static struct dst_ops xfrm4_dst_ops_template = { .destroy = xfrm4_dst_destroy, .ifdown = xfrm4_dst_ifdown, .local_out = __ip_local_out, - .gc_thresh = INT_MAX, + .gc_thresh = 32768, }; static const struct xfrm_policy_afinfo xfrm4_policy_afinfo = { diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index 79651bc71bf0..ae30dc4973e8 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -286,7 +286,7 @@ static struct dst_ops xfrm6_dst_ops_template = { .destroy = xfrm6_dst_destroy, .ifdown = xfrm6_dst_ifdown, .local_out = __ip6_local_out, - .gc_thresh = INT_MAX, + .gc_thresh = 32768, }; static const struct xfrm_policy_afinfo xfrm6_policy_afinfo = { -- cgit v1.2.3 From 3ca28286ea809685d273d41674da34f45111482a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:21 +0200 Subject: xfrm_policy: bypass flow_cache_lookup Instead of consulting flow cache, call the xfrm bundle/policy lookup functions directly. This pretends the flow cache had no entry. This helps to gradually remove flow cache integration, followup commit will remove the dead code that this change adds. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/xfrm/xfrm_policy.c | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index ff61d8557929..1c7126ab752c 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2052,13 +2052,12 @@ free_dst: } static struct flow_cache_object * -xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, - struct flow_cache_object *oldflo, void *ctx) +xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct xfrm_flo *xflo) { - struct xfrm_flo *xflo = (struct xfrm_flo *)ctx; struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; struct xfrm_dst *xdst, *new_xdst; int num_pols = 0, num_xfrms = 0, i, err, pol_dead; + struct flow_cache_object *oldflo = NULL; /* Check if the policies from old bundle are usable */ xdst = NULL; @@ -2128,8 +2127,6 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, dst_release_immediate(&xdst->u.dst); } - /* We do need to return one reference for original caller */ - dst_hold(&new_xdst->u.dst); return &new_xdst->flo; make_dummy_bundle: @@ -2242,8 +2239,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, !net->xfrm.policy_count[XFRM_POLICY_OUT]) goto nopol; - flo = flow_cache_lookup(net, fl, family, dir, - xfrm_bundle_lookup, &xflo); + flo = xfrm_bundle_lookup(net, fl, family, dir, &xflo); if (flo == NULL) goto nopol; if (IS_ERR(flo)) { @@ -2489,8 +2485,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, if (!pol) { struct flow_cache_object *flo; - flo = flow_cache_lookup(net, &fl, family, fl_dir, - xfrm_policy_lookup, NULL); + flo = xfrm_policy_lookup(net, &fl, family, dir, NULL, NULL); + if (IS_ERR_OR_NULL(flo)) pol = ERR_CAST(flo); else -- cgit v1.2.3 From 855dad99c07434065c0f21be6185e9cd8a6daab0 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:22 +0200 Subject: xfrm_policy: remove always true/false branches after previous change oldflo and xdst are always NULL. These branches were already removed by gcc, this doesn't change code. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/xfrm/xfrm_policy.c | 74 ++++++++++---------------------------------------- 1 file changed, 14 insertions(+), 60 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 1c7126ab752c..19d457db3a09 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -2056,48 +2056,23 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, { struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; struct xfrm_dst *xdst, *new_xdst; - int num_pols = 0, num_xfrms = 0, i, err, pol_dead; - struct flow_cache_object *oldflo = NULL; + int num_pols = 0, num_xfrms = 0, err; /* Check if the policies from old bundle are usable */ xdst = NULL; - if (oldflo) { - xdst = container_of(oldflo, struct xfrm_dst, flo); - num_pols = xdst->num_pols; - num_xfrms = xdst->num_xfrms; - pol_dead = 0; - for (i = 0; i < num_pols; i++) { - pols[i] = xdst->pols[i]; - pol_dead |= pols[i]->walk.dead; - } - if (pol_dead) { - /* Mark DST_OBSOLETE_DEAD to fail the next - * xfrm_dst_check() - */ - xdst->u.dst.obsolete = DST_OBSOLETE_DEAD; - dst_release_immediate(&xdst->u.dst); - xdst = NULL; - num_pols = 0; - num_xfrms = 0; - oldflo = NULL; - } - } - /* Resolve policies to use if we couldn't get them from * previous cache entry */ - if (xdst == NULL) { - num_pols = 1; - pols[0] = __xfrm_policy_lookup(net, fl, family, - flow_to_policy_dir(dir)); - err = xfrm_expand_policies(fl, family, pols, + num_pols = 1; + pols[0] = __xfrm_policy_lookup(net, fl, family, + flow_to_policy_dir(dir)); + err = xfrm_expand_policies(fl, family, pols, &num_pols, &num_xfrms); - if (err < 0) - goto inc_error; - if (num_pols == 0) - return NULL; - if (num_xfrms <= 0) - goto make_dummy_bundle; - } + if (err < 0) + goto inc_error; + if (num_pols == 0) + return NULL; + if (num_xfrms <= 0) + goto make_dummy_bundle; new_xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family, xflo->dst_orig); @@ -2105,26 +2080,10 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, err = PTR_ERR(new_xdst); if (err != -EAGAIN) goto error; - if (oldflo == NULL) - goto make_dummy_bundle; - dst_hold(&xdst->u.dst); - return oldflo; + goto make_dummy_bundle; } else if (new_xdst == NULL) { num_xfrms = 0; - if (oldflo == NULL) - goto make_dummy_bundle; - xdst->num_xfrms = 0; - dst_hold(&xdst->u.dst); - return oldflo; - } - - /* Kill the previous bundle */ - if (xdst) { - /* The policies were stolen for newly generated bundle */ - xdst->num_pols = 0; - /* Mark DST_OBSOLETE_DEAD to fail the next xfrm_dst_check() */ - xdst->u.dst.obsolete = DST_OBSOLETE_DEAD; - dst_release_immediate(&xdst->u.dst); + goto make_dummy_bundle; } return &new_xdst->flo; @@ -2148,12 +2107,7 @@ make_dummy_bundle: inc_error: XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR); error: - if (xdst != NULL) { - /* Mark DST_OBSOLETE_DEAD to fail the next xfrm_dst_check() */ - xdst->u.dst.obsolete = DST_OBSOLETE_DEAD; - dst_release_immediate(&xdst->u.dst); - } else - xfrm_pols_put(pols, num_pols); + xfrm_pols_put(pols, num_pols); return ERR_PTR(err); } -- cgit v1.2.3 From aff669bc286eb3a459acb6e192ae7d2adc3967a3 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:23 +0200 Subject: xfrm_policy: kill flow to policy dir conversion XFRM_POLICY_IN/OUT/FWD are identical to FLOW_DIR_*, so gcc already removed this function as its just returns the argument. Again, no code change. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/xfrm/xfrm_policy.c | 46 ++++------------------------------------------ 1 file changed, 4 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 19d457db3a09..9f724a688475 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1187,24 +1187,6 @@ __xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir); } -static int flow_to_policy_dir(int dir) -{ - if (XFRM_POLICY_IN == FLOW_DIR_IN && - XFRM_POLICY_OUT == FLOW_DIR_OUT && - XFRM_POLICY_FWD == FLOW_DIR_FWD) - return dir; - - switch (dir) { - default: - case FLOW_DIR_IN: - return XFRM_POLICY_IN; - case FLOW_DIR_OUT: - return XFRM_POLICY_OUT; - case FLOW_DIR_FWD: - return XFRM_POLICY_FWD; - } -} - static struct flow_cache_object * xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct flow_cache_object *old_obj, void *ctx) @@ -1214,7 +1196,7 @@ xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, if (old_obj) xfrm_pol_put(container_of(old_obj, struct xfrm_policy, flo)); - pol = __xfrm_policy_lookup(net, fl, family, flow_to_policy_dir(dir)); + pol = __xfrm_policy_lookup(net, fl, family, dir); if (IS_ERR_OR_NULL(pol)) return ERR_CAST(pol); @@ -1225,23 +1207,6 @@ xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, return &pol->flo; } -static inline int policy_to_flow_dir(int dir) -{ - if (XFRM_POLICY_IN == FLOW_DIR_IN && - XFRM_POLICY_OUT == FLOW_DIR_OUT && - XFRM_POLICY_FWD == FLOW_DIR_FWD) - return dir; - switch (dir) { - default: - case XFRM_POLICY_IN: - return FLOW_DIR_IN; - case XFRM_POLICY_OUT: - return FLOW_DIR_OUT; - case XFRM_POLICY_FWD: - return FLOW_DIR_FWD; - } -} - static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, const struct flowi *fl, u16 family) { @@ -1261,7 +1226,7 @@ static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, } err = security_xfrm_policy_lookup(pol->security, fl->flowi_secid, - policy_to_flow_dir(dir)); + dir); if (!err) { if (!xfrm_pol_hold_rcu(pol)) goto again; @@ -2063,8 +2028,7 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, /* Resolve policies to use if we couldn't get them from * previous cache entry */ num_pols = 1; - pols[0] = __xfrm_policy_lookup(net, fl, family, - flow_to_policy_dir(dir)); + pols[0] = __xfrm_policy_lookup(net, fl, family, dir); err = xfrm_expand_policies(fl, family, pols, &num_pols, &num_xfrms); if (err < 0) @@ -2142,7 +2106,7 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, struct xfrm_dst *xdst; struct dst_entry *dst, *route; u16 family = dst_orig->ops->family; - u8 dir = policy_to_flow_dir(XFRM_POLICY_OUT); + u8 dir = XFRM_POLICY_OUT; int i, err, num_pols, num_xfrms = 0, drop_pols = 0; dst = NULL; @@ -2399,12 +2363,10 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, int pi; int reverse; struct flowi fl; - u8 fl_dir; int xerr_idx = -1; reverse = dir & ~XFRM_POLICY_MASK; dir &= XFRM_POLICY_MASK; - fl_dir = policy_to_flow_dir(dir); if (__xfrm_decode_session(skb, &fl, family, reverse) < 0) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINHDRERROR); -- cgit v1.2.3 From 86dc8ee0b2c524d47864ee1bdf2b36ea157405a8 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:24 +0200 Subject: xfrm_policy: remove xfrm_policy_lookup This removes the wrapper and renames the __xfrm_policy_lookup variant to get rid of another place that used flow cache objects. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/xfrm/xfrm_policy.c | 36 ++++-------------------------------- 1 file changed, 4 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 9f724a688475..339bb3ac2797 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1175,7 +1175,7 @@ fail: } static struct xfrm_policy * -__xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir) +xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir) { #ifdef CONFIG_XFRM_SUB_POLICY struct xfrm_policy *pol; @@ -1187,26 +1187,6 @@ __xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir return xfrm_policy_lookup_bytype(net, XFRM_POLICY_TYPE_MAIN, fl, family, dir); } -static struct flow_cache_object * -xfrm_policy_lookup(struct net *net, const struct flowi *fl, u16 family, - u8 dir, struct flow_cache_object *old_obj, void *ctx) -{ - struct xfrm_policy *pol; - - if (old_obj) - xfrm_pol_put(container_of(old_obj, struct xfrm_policy, flo)); - - pol = __xfrm_policy_lookup(net, fl, family, dir); - if (IS_ERR_OR_NULL(pol)) - return ERR_CAST(pol); - - /* Resolver returns two references: - * one for cache and one for caller of flow_cache_lookup() */ - xfrm_pol_hold(pol); - - return &pol->flo; -} - static struct xfrm_policy *xfrm_sk_policy_lookup(const struct sock *sk, int dir, const struct flowi *fl, u16 family) { @@ -2028,7 +2008,7 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, /* Resolve policies to use if we couldn't get them from * previous cache entry */ num_pols = 1; - pols[0] = __xfrm_policy_lookup(net, fl, family, dir); + pols[0] = xfrm_policy_lookup(net, fl, family, dir); err = xfrm_expand_policies(fl, family, pols, &num_pols, &num_xfrms); if (err < 0) @@ -2398,16 +2378,8 @@ int __xfrm_policy_check(struct sock *sk, int dir, struct sk_buff *skb, } } - if (!pol) { - struct flow_cache_object *flo; - - flo = xfrm_policy_lookup(net, &fl, family, dir, NULL, NULL); - - if (IS_ERR_OR_NULL(flo)) - pol = ERR_CAST(flo); - else - pol = container_of(flo, struct xfrm_policy, flo); - } + if (!pol) + pol = xfrm_policy_lookup(net, &fl, family, dir); if (IS_ERR(pol)) { XFRM_INC_STATS(net, LINUX_MIB_XFRMINPOLERROR); -- cgit v1.2.3 From bd45c539bf56650fb8fbab09c36f4b9afcbd4e1c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:25 +0200 Subject: xfrm_policy: make xfrm_bundle_lookup return xfrm dst object This allows to remove flow cache object embedded in struct xfrm_dst. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/xfrm/xfrm_policy.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 339bb3ac2797..145d2395f3c0 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1996,15 +1996,13 @@ free_dst: goto out; } -static struct flow_cache_object * +static struct xfrm_dst * xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, struct xfrm_flo *xflo) { struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; - struct xfrm_dst *xdst, *new_xdst; int num_pols = 0, num_xfrms = 0, err; + struct xfrm_dst *xdst; - /* Check if the policies from old bundle are usable */ - xdst = NULL; /* Resolve policies to use if we couldn't get them from * previous cache entry */ num_pols = 1; @@ -2018,19 +2016,19 @@ xfrm_bundle_lookup(struct net *net, const struct flowi *fl, u16 family, u8 dir, if (num_xfrms <= 0) goto make_dummy_bundle; - new_xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family, + xdst = xfrm_resolve_and_create_bundle(pols, num_pols, fl, family, xflo->dst_orig); - if (IS_ERR(new_xdst)) { - err = PTR_ERR(new_xdst); + if (IS_ERR(xdst)) { + err = PTR_ERR(xdst); if (err != -EAGAIN) goto error; goto make_dummy_bundle; - } else if (new_xdst == NULL) { + } else if (xdst == NULL) { num_xfrms = 0; goto make_dummy_bundle; } - return &new_xdst->flo; + return xdst; make_dummy_bundle: /* We found policies, but there's no bundles to instantiate: @@ -2046,7 +2044,7 @@ make_dummy_bundle: memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols); dst_hold(&xdst->u.dst); - return &xdst->flo; + return xdst; inc_error: XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTPOLERROR); @@ -2082,7 +2080,6 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, const struct sock *sk, int flags) { struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; - struct flow_cache_object *flo; struct xfrm_dst *xdst; struct dst_entry *dst, *route; u16 family = dst_orig->ops->family; @@ -2137,14 +2134,13 @@ struct dst_entry *xfrm_lookup(struct net *net, struct dst_entry *dst_orig, !net->xfrm.policy_count[XFRM_POLICY_OUT]) goto nopol; - flo = xfrm_bundle_lookup(net, fl, family, dir, &xflo); - if (flo == NULL) + xdst = xfrm_bundle_lookup(net, fl, family, dir, &xflo); + if (xdst == NULL) goto nopol; - if (IS_ERR(flo)) { - err = PTR_ERR(flo); + if (IS_ERR(xdst)) { + err = PTR_ERR(xdst); goto dropdst; } - xdst = container_of(flo, struct xfrm_dst, flo); num_pols = xdst->num_pols; num_xfrms = xdst->num_xfrms; -- cgit v1.2.3 From 09c7570480f7544ffbf8e6db365208b0b0c154c6 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:26 +0200 Subject: xfrm: remove flow cache After rcu conversions performance degradation in forward tests isn't that noticeable anymore. See next patch for some numbers. A followup patcg could then also remove genid from the policies as we do not cache bundles anymore. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/flow.h | 34 --- include/net/flowcache.h | 25 -- include/net/netns/xfrm.h | 11 - include/net/xfrm.h | 8 - net/core/Makefile | 1 - net/core/flow.c | 516 ---------------------------------------- net/ipv4/xfrm4_policy.c | 9 - net/ipv6/xfrm6_policy.c | 9 - net/key/af_key.c | 6 - net/xfrm/xfrm_device.c | 2 - net/xfrm/xfrm_policy.c | 108 +-------- net/xfrm/xfrm_user.c | 3 - security/selinux/include/xfrm.h | 4 +- 13 files changed, 2 insertions(+), 734 deletions(-) delete mode 100644 include/net/flowcache.h delete mode 100644 net/core/flow.c (limited to 'net') diff --git a/include/net/flow.h b/include/net/flow.h index bae198b3039e..f3dc61b29bb5 100644 --- a/include/net/flow.h +++ b/include/net/flow.h @@ -218,40 +218,6 @@ static inline unsigned int flow_key_size(u16 family) return 0; } -#define FLOW_DIR_IN 0 -#define FLOW_DIR_OUT 1 -#define FLOW_DIR_FWD 2 - -struct net; -struct sock; -struct flow_cache_ops; - -struct flow_cache_object { - const struct flow_cache_ops *ops; -}; - -struct flow_cache_ops { - struct flow_cache_object *(*get)(struct flow_cache_object *); - int (*check)(struct flow_cache_object *); - void (*delete)(struct flow_cache_object *); -}; - -typedef struct flow_cache_object *(*flow_resolve_t)( - struct net *net, const struct flowi *key, u16 family, - u8 dir, struct flow_cache_object *oldobj, void *ctx); - -struct flow_cache_object *flow_cache_lookup(struct net *net, - const struct flowi *key, u16 family, - u8 dir, flow_resolve_t resolver, - void *ctx); -int flow_cache_init(struct net *net); -void flow_cache_fini(struct net *net); -void flow_cache_hp_init(void); - -void flow_cache_flush(struct net *net); -void flow_cache_flush_deferred(struct net *net); -extern atomic_t flow_cache_genid; - __u32 __get_hash_from_flowi6(const struct flowi6 *fl6, struct flow_keys *keys); static inline __u32 get_hash_from_flowi6(const struct flowi6 *fl6) diff --git a/include/net/flowcache.h b/include/net/flowcache.h deleted file mode 100644 index 51eb971e8973..000000000000 --- a/include/net/flowcache.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef _NET_FLOWCACHE_H -#define _NET_FLOWCACHE_H - -#include -#include -#include -#include - -struct flow_cache_percpu { - struct hlist_head *hash_table; - unsigned int hash_count; - u32 hash_rnd; - int hash_rnd_recalc; - struct tasklet_struct flush_tasklet; -}; - -struct flow_cache { - u32 hash_shift; - struct flow_cache_percpu __percpu *percpu; - struct hlist_node node; - unsigned int low_watermark; - unsigned int high_watermark; - struct timer_list rnd_timer; -}; -#endif /* _NET_FLOWCACHE_H */ diff --git a/include/net/netns/xfrm.h b/include/net/netns/xfrm.h index 27bb9633c69d..611521646dd4 100644 --- a/include/net/netns/xfrm.h +++ b/include/net/netns/xfrm.h @@ -6,7 +6,6 @@ #include #include #include -#include struct ctl_table_header; @@ -73,16 +72,6 @@ struct netns_xfrm { spinlock_t xfrm_state_lock; spinlock_t xfrm_policy_lock; struct mutex xfrm_cfg_mutex; - - /* flow cache part */ - struct flow_cache flow_cache_global; - atomic_t flow_cache_genid; - struct list_head flow_cache_gc_list; - atomic_t flow_cache_gc_count; - spinlock_t flow_cache_gc_lock; - struct work_struct flow_cache_gc_work; - struct work_struct flow_cache_flush_work; - struct mutex flow_flush_sem; }; #endif diff --git a/include/net/xfrm.h b/include/net/xfrm.h index c0916ab18d32..e0feba2ce76a 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -563,7 +563,6 @@ struct xfrm_policy { refcount_t refcnt; struct timer_list timer; - struct flow_cache_object flo; atomic_t genid; u32 priority; u32 index; @@ -978,7 +977,6 @@ struct xfrm_dst { struct rt6_info rt6; } u; struct dst_entry *route; - struct flow_cache_object flo; struct xfrm_policy *pols[XFRM_POLICY_TYPE_MAX]; int num_pols, num_xfrms; u32 xfrm_genid; @@ -1226,9 +1224,6 @@ static inline void xfrm_sk_free_policy(struct sock *sk) } } -void xfrm_garbage_collect(struct net *net); -void xfrm_garbage_collect_deferred(struct net *net); - #else static inline void xfrm_sk_free_policy(struct sock *sk) {} @@ -1263,9 +1258,6 @@ static inline int xfrm6_policy_check_reverse(struct sock *sk, int dir, { return 1; } -static inline void xfrm_garbage_collect(struct net *net) -{ -} #endif static __inline__ diff --git a/net/core/Makefile b/net/core/Makefile index 79f9479e9658..d501c4278015 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -11,7 +11,6 @@ obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ sock_diag.o dev_ioctl.o tso.o sock_reuseport.o -obj-$(CONFIG_XFRM) += flow.o obj-y += net-sysfs.o obj-$(CONFIG_PROC_FS) += net-procfs.o obj-$(CONFIG_NET_PKTGEN) += pktgen.o diff --git a/net/core/flow.c b/net/core/flow.c deleted file mode 100644 index f7f5d1932a27..000000000000 --- a/net/core/flow.c +++ /dev/null @@ -1,516 +0,0 @@ -/* flow.c: Generic flow cache. - * - * Copyright (C) 2003 Alexey N. Kuznetsov (kuznet@ms2.inr.ac.ru) - * Copyright (C) 2003 David S. Miller (davem@redhat.com) - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -struct flow_cache_entry { - union { - struct hlist_node hlist; - struct list_head gc_list; - } u; - struct net *net; - u16 family; - u8 dir; - u32 genid; - struct flowi key; - struct flow_cache_object *object; -}; - -struct flow_flush_info { - struct flow_cache *cache; - atomic_t cpuleft; - struct completion completion; -}; - -static struct kmem_cache *flow_cachep __read_mostly; - -#define flow_cache_hash_size(cache) (1U << (cache)->hash_shift) -#define FLOW_HASH_RND_PERIOD (10 * 60 * HZ) - -static void flow_cache_new_hashrnd(unsigned long arg) -{ - struct flow_cache *fc = (void *) arg; - int i; - - for_each_possible_cpu(i) - per_cpu_ptr(fc->percpu, i)->hash_rnd_recalc = 1; - - fc->rnd_timer.expires = jiffies + FLOW_HASH_RND_PERIOD; - add_timer(&fc->rnd_timer); -} - -static int flow_entry_valid(struct flow_cache_entry *fle, - struct netns_xfrm *xfrm) -{ - if (atomic_read(&xfrm->flow_cache_genid) != fle->genid) - return 0; - if (fle->object && !fle->object->ops->check(fle->object)) - return 0; - return 1; -} - -static void flow_entry_kill(struct flow_cache_entry *fle, - struct netns_xfrm *xfrm) -{ - if (fle->object) - fle->object->ops->delete(fle->object); - kmem_cache_free(flow_cachep, fle); -} - -static void flow_cache_gc_task(struct work_struct *work) -{ - struct list_head gc_list; - struct flow_cache_entry *fce, *n; - struct netns_xfrm *xfrm = container_of(work, struct netns_xfrm, - flow_cache_gc_work); - - INIT_LIST_HEAD(&gc_list); - spin_lock_bh(&xfrm->flow_cache_gc_lock); - list_splice_tail_init(&xfrm->flow_cache_gc_list, &gc_list); - spin_unlock_bh(&xfrm->flow_cache_gc_lock); - - list_for_each_entry_safe(fce, n, &gc_list, u.gc_list) { - flow_entry_kill(fce, xfrm); - atomic_dec(&xfrm->flow_cache_gc_count); - } -} - -static void flow_cache_queue_garbage(struct flow_cache_percpu *fcp, - unsigned int deleted, - struct list_head *gc_list, - struct netns_xfrm *xfrm) -{ - if (deleted) { - atomic_add(deleted, &xfrm->flow_cache_gc_count); - fcp->hash_count -= deleted; - spin_lock_bh(&xfrm->flow_cache_gc_lock); - list_splice_tail(gc_list, &xfrm->flow_cache_gc_list); - spin_unlock_bh(&xfrm->flow_cache_gc_lock); - schedule_work(&xfrm->flow_cache_gc_work); - } -} - -static void __flow_cache_shrink(struct flow_cache *fc, - struct flow_cache_percpu *fcp, - unsigned int shrink_to) -{ - struct flow_cache_entry *fle; - struct hlist_node *tmp; - LIST_HEAD(gc_list); - unsigned int deleted = 0; - struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm, - flow_cache_global); - unsigned int i; - - for (i = 0; i < flow_cache_hash_size(fc); i++) { - unsigned int saved = 0; - - hlist_for_each_entry_safe(fle, tmp, - &fcp->hash_table[i], u.hlist) { - if (saved < shrink_to && - flow_entry_valid(fle, xfrm)) { - saved++; - } else { - deleted++; - hlist_del(&fle->u.hlist); - list_add_tail(&fle->u.gc_list, &gc_list); - } - } - } - - flow_cache_queue_garbage(fcp, deleted, &gc_list, xfrm); -} - -static void flow_cache_shrink(struct flow_cache *fc, - struct flow_cache_percpu *fcp) -{ - unsigned int shrink_to = fc->low_watermark / flow_cache_hash_size(fc); - - __flow_cache_shrink(fc, fcp, shrink_to); -} - -static void flow_new_hash_rnd(struct flow_cache *fc, - struct flow_cache_percpu *fcp) -{ - get_random_bytes(&fcp->hash_rnd, sizeof(u32)); - fcp->hash_rnd_recalc = 0; - __flow_cache_shrink(fc, fcp, 0); -} - -static u32 flow_hash_code(struct flow_cache *fc, - struct flow_cache_percpu *fcp, - const struct flowi *key, - unsigned int keysize) -{ - const u32 *k = (const u32 *) key; - const u32 length = keysize * sizeof(flow_compare_t) / sizeof(u32); - - return jhash2(k, length, fcp->hash_rnd) - & (flow_cache_hash_size(fc) - 1); -} - -/* I hear what you're saying, use memcmp. But memcmp cannot make - * important assumptions that we can here, such as alignment. - */ -static int flow_key_compare(const struct flowi *key1, const struct flowi *key2, - unsigned int keysize) -{ - const flow_compare_t *k1, *k1_lim, *k2; - - k1 = (const flow_compare_t *) key1; - k1_lim = k1 + keysize; - - k2 = (const flow_compare_t *) key2; - - do { - if (*k1++ != *k2++) - return 1; - } while (k1 < k1_lim); - - return 0; -} - -struct flow_cache_object * -flow_cache_lookup(struct net *net, const struct flowi *key, u16 family, u8 dir, - flow_resolve_t resolver, void *ctx) -{ - struct flow_cache *fc = &net->xfrm.flow_cache_global; - struct flow_cache_percpu *fcp; - struct flow_cache_entry *fle, *tfle; - struct flow_cache_object *flo; - unsigned int keysize; - unsigned int hash; - - local_bh_disable(); - fcp = this_cpu_ptr(fc->percpu); - - fle = NULL; - flo = NULL; - - keysize = flow_key_size(family); - if (!keysize) - goto nocache; - - /* Packet really early in init? Making flow_cache_init a - * pre-smp initcall would solve this. --RR */ - if (!fcp->hash_table) - goto nocache; - - if (fcp->hash_rnd_recalc) - flow_new_hash_rnd(fc, fcp); - - hash = flow_hash_code(fc, fcp, key, keysize); - hlist_for_each_entry(tfle, &fcp->hash_table[hash], u.hlist) { - if (tfle->net == net && - tfle->family == family && - tfle->dir == dir && - flow_key_compare(key, &tfle->key, keysize) == 0) { - fle = tfle; - break; - } - } - - if (unlikely(!fle)) { - if (fcp->hash_count > fc->high_watermark) - flow_cache_shrink(fc, fcp); - - if (atomic_read(&net->xfrm.flow_cache_gc_count) > - 2 * num_online_cpus() * fc->high_watermark) { - flo = ERR_PTR(-ENOBUFS); - goto ret_object; - } - - fle = kmem_cache_alloc(flow_cachep, GFP_ATOMIC); - if (fle) { - fle->net = net; - fle->family = family; - fle->dir = dir; - memcpy(&fle->key, key, keysize * sizeof(flow_compare_t)); - fle->object = NULL; - hlist_add_head(&fle->u.hlist, &fcp->hash_table[hash]); - fcp->hash_count++; - } - } else if (likely(fle->genid == atomic_read(&net->xfrm.flow_cache_genid))) { - flo = fle->object; - if (!flo) - goto ret_object; - flo = flo->ops->get(flo); - if (flo) - goto ret_object; - } else if (fle->object) { - flo = fle->object; - flo->ops->delete(flo); - fle->object = NULL; - } - -nocache: - flo = NULL; - if (fle) { - flo = fle->object; - fle->object = NULL; - } - flo = resolver(net, key, family, dir, flo, ctx); - if (fle) { - fle->genid = atomic_read(&net->xfrm.flow_cache_genid); - if (!IS_ERR(flo)) - fle->object = flo; - else - fle->genid--; - } else { - if (!IS_ERR_OR_NULL(flo)) - flo->ops->delete(flo); - } -ret_object: - local_bh_enable(); - return flo; -} -EXPORT_SYMBOL(flow_cache_lookup); - -static void flow_cache_flush_tasklet(unsigned long data) -{ - struct flow_flush_info *info = (void *)data; - struct flow_cache *fc = info->cache; - struct flow_cache_percpu *fcp; - struct flow_cache_entry *fle; - struct hlist_node *tmp; - LIST_HEAD(gc_list); - unsigned int deleted = 0; - struct netns_xfrm *xfrm = container_of(fc, struct netns_xfrm, - flow_cache_global); - unsigned int i; - - fcp = this_cpu_ptr(fc->percpu); - for (i = 0; i < flow_cache_hash_size(fc); i++) { - hlist_for_each_entry_safe(fle, tmp, - &fcp->hash_table[i], u.hlist) { - if (flow_entry_valid(fle, xfrm)) - continue; - - deleted++; - hlist_del(&fle->u.hlist); - list_add_tail(&fle->u.gc_list, &gc_list); - } - } - - flow_cache_queue_garbage(fcp, deleted, &gc_list, xfrm); - - if (atomic_dec_and_test(&info->cpuleft)) - complete(&info->completion); -} - -/* - * Return whether a cpu needs flushing. Conservatively, we assume - * the presence of any entries means the core may require flushing, - * since the flow_cache_ops.check() function may assume it's running - * on the same core as the per-cpu cache component. - */ -static int flow_cache_percpu_empty(struct flow_cache *fc, int cpu) -{ - struct flow_cache_percpu *fcp; - unsigned int i; - - fcp = per_cpu_ptr(fc->percpu, cpu); - for (i = 0; i < flow_cache_hash_size(fc); i++) - if (!hlist_empty(&fcp->hash_table[i])) - return 0; - return 1; -} - -static void flow_cache_flush_per_cpu(void *data) -{ - struct flow_flush_info *info = data; - struct tasklet_struct *tasklet; - - tasklet = &this_cpu_ptr(info->cache->percpu)->flush_tasklet; - tasklet->data = (unsigned long)info; - tasklet_schedule(tasklet); -} - -void flow_cache_flush(struct net *net) -{ - struct flow_flush_info info; - cpumask_var_t mask; - int i, self; - - /* Track which cpus need flushing to avoid disturbing all cores. */ - if (!alloc_cpumask_var(&mask, GFP_KERNEL)) - return; - cpumask_clear(mask); - - /* Don't want cpus going down or up during this. */ - get_online_cpus(); - mutex_lock(&net->xfrm.flow_flush_sem); - info.cache = &net->xfrm.flow_cache_global; - for_each_online_cpu(i) - if (!flow_cache_percpu_empty(info.cache, i)) - cpumask_set_cpu(i, mask); - atomic_set(&info.cpuleft, cpumask_weight(mask)); - if (atomic_read(&info.cpuleft) == 0) - goto done; - - init_completion(&info.completion); - - local_bh_disable(); - self = cpumask_test_and_clear_cpu(smp_processor_id(), mask); - on_each_cpu_mask(mask, flow_cache_flush_per_cpu, &info, 0); - if (self) - flow_cache_flush_tasklet((unsigned long)&info); - local_bh_enable(); - - wait_for_completion(&info.completion); - -done: - mutex_unlock(&net->xfrm.flow_flush_sem); - put_online_cpus(); - free_cpumask_var(mask); -} - -static void flow_cache_flush_task(struct work_struct *work) -{ - struct netns_xfrm *xfrm = container_of(work, struct netns_xfrm, - flow_cache_flush_work); - struct net *net = container_of(xfrm, struct net, xfrm); - - flow_cache_flush(net); -} - -void flow_cache_flush_deferred(struct net *net) -{ - schedule_work(&net->xfrm.flow_cache_flush_work); -} - -static int flow_cache_cpu_prepare(struct flow_cache *fc, int cpu) -{ - struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, cpu); - unsigned int sz = sizeof(struct hlist_head) * flow_cache_hash_size(fc); - - if (!fcp->hash_table) { - fcp->hash_table = kzalloc_node(sz, GFP_KERNEL, cpu_to_node(cpu)); - if (!fcp->hash_table) { - pr_err("NET: failed to allocate flow cache sz %u\n", sz); - return -ENOMEM; - } - fcp->hash_rnd_recalc = 1; - fcp->hash_count = 0; - tasklet_init(&fcp->flush_tasklet, flow_cache_flush_tasklet, 0); - } - return 0; -} - -static int flow_cache_cpu_up_prep(unsigned int cpu, struct hlist_node *node) -{ - struct flow_cache *fc = hlist_entry_safe(node, struct flow_cache, node); - - return flow_cache_cpu_prepare(fc, cpu); -} - -static int flow_cache_cpu_dead(unsigned int cpu, struct hlist_node *node) -{ - struct flow_cache *fc = hlist_entry_safe(node, struct flow_cache, node); - struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, cpu); - - __flow_cache_shrink(fc, fcp, 0); - return 0; -} - -int flow_cache_init(struct net *net) -{ - int i; - struct flow_cache *fc = &net->xfrm.flow_cache_global; - - if (!flow_cachep) - flow_cachep = kmem_cache_create("flow_cache", - sizeof(struct flow_cache_entry), - 0, SLAB_PANIC, NULL); - spin_lock_init(&net->xfrm.flow_cache_gc_lock); - INIT_LIST_HEAD(&net->xfrm.flow_cache_gc_list); - INIT_WORK(&net->xfrm.flow_cache_gc_work, flow_cache_gc_task); - INIT_WORK(&net->xfrm.flow_cache_flush_work, flow_cache_flush_task); - mutex_init(&net->xfrm.flow_flush_sem); - atomic_set(&net->xfrm.flow_cache_gc_count, 0); - - fc->hash_shift = 10; - fc->low_watermark = 2 * flow_cache_hash_size(fc); - fc->high_watermark = 4 * flow_cache_hash_size(fc); - - fc->percpu = alloc_percpu(struct flow_cache_percpu); - if (!fc->percpu) - return -ENOMEM; - - if (cpuhp_state_add_instance(CPUHP_NET_FLOW_PREPARE, &fc->node)) - goto err; - - setup_timer(&fc->rnd_timer, flow_cache_new_hashrnd, - (unsigned long) fc); - fc->rnd_timer.expires = jiffies + FLOW_HASH_RND_PERIOD; - add_timer(&fc->rnd_timer); - - return 0; - -err: - for_each_possible_cpu(i) { - struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, i); - kfree(fcp->hash_table); - fcp->hash_table = NULL; - } - - free_percpu(fc->percpu); - fc->percpu = NULL; - - return -ENOMEM; -} -EXPORT_SYMBOL(flow_cache_init); - -void flow_cache_fini(struct net *net) -{ - int i; - struct flow_cache *fc = &net->xfrm.flow_cache_global; - - del_timer_sync(&fc->rnd_timer); - - cpuhp_state_remove_instance_nocalls(CPUHP_NET_FLOW_PREPARE, &fc->node); - - for_each_possible_cpu(i) { - struct flow_cache_percpu *fcp = per_cpu_ptr(fc->percpu, i); - kfree(fcp->hash_table); - fcp->hash_table = NULL; - } - - free_percpu(fc->percpu); - fc->percpu = NULL; -} -EXPORT_SYMBOL(flow_cache_fini); - -void __init flow_cache_hp_init(void) -{ - int ret; - - ret = cpuhp_setup_state_multi(CPUHP_NET_FLOW_PREPARE, - "net/flow:prepare", - flow_cache_cpu_up_prep, - flow_cache_cpu_dead); - WARN_ON(ret < 0); -} diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 19455a5fc328..4aefb149fe0a 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -213,14 +213,6 @@ _decode_session4(struct sk_buff *skb, struct flowi *fl, int reverse) fl4->flowi4_tos = iph->tos; } -static inline int xfrm4_garbage_collect(struct dst_ops *ops) -{ - struct net *net = container_of(ops, struct net, xfrm.xfrm4_dst_ops); - - xfrm_garbage_collect_deferred(net); - return (dst_entries_get_slow(ops) > ops->gc_thresh * 2); -} - static void xfrm4_update_pmtu(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, u32 mtu) { @@ -259,7 +251,6 @@ static void xfrm4_dst_ifdown(struct dst_entry *dst, struct net_device *dev, static struct dst_ops xfrm4_dst_ops_template = { .family = AF_INET, - .gc = xfrm4_garbage_collect, .update_pmtu = xfrm4_update_pmtu, .redirect = xfrm4_redirect, .cow_metrics = dst_cow_metrics_generic, diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index ae30dc4973e8..f44b25a48478 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -214,14 +214,6 @@ _decode_session6(struct sk_buff *skb, struct flowi *fl, int reverse) } } -static inline int xfrm6_garbage_collect(struct dst_ops *ops) -{ - struct net *net = container_of(ops, struct net, xfrm.xfrm6_dst_ops); - - xfrm_garbage_collect_deferred(net); - return dst_entries_get_fast(ops) > ops->gc_thresh * 2; -} - static void xfrm6_update_pmtu(struct dst_entry *dst, struct sock *sk, struct sk_buff *skb, u32 mtu) { @@ -279,7 +271,6 @@ static void xfrm6_dst_ifdown(struct dst_entry *dst, struct net_device *dev, static struct dst_ops xfrm6_dst_ops_template = { .family = AF_INET6, - .gc = xfrm6_garbage_collect, .update_pmtu = xfrm6_update_pmtu, .redirect = xfrm6_redirect, .cow_metrics = dst_cow_metrics_generic, diff --git a/net/key/af_key.c b/net/key/af_key.c index ca9d3ae665e7..10d7133e4fe9 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -2398,8 +2398,6 @@ static int pfkey_spddelete(struct sock *sk, struct sk_buff *skb, const struct sa out: xfrm_pol_put(xp); - if (err == 0) - xfrm_garbage_collect(net); return err; } @@ -2650,8 +2648,6 @@ static int pfkey_spdget(struct sock *sk, struct sk_buff *skb, const struct sadb_ out: xfrm_pol_put(xp); - if (delete && err == 0) - xfrm_garbage_collect(net); return err; } @@ -2751,8 +2747,6 @@ static int pfkey_spdflush(struct sock *sk, struct sk_buff *skb, const struct sad int err, err2; err = xfrm_policy_flush(net, XFRM_POLICY_TYPE_MAIN, true); - if (!err) - xfrm_garbage_collect(net); err2 = unicast_flush_resp(sk, hdr); if (err || err2) { if (err == -ESRCH) /* empty table - old silent behavior */ diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 5f7e8bfa0c2d..1f9a079e08b0 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -175,8 +175,6 @@ static int xfrm_dev_down(struct net_device *dev) if (dev->features & NETIF_F_HW_ESP) xfrm_dev_state_flush(dev_net(dev), dev, true); - xfrm_garbage_collect(dev_net(dev)); - return NOTIFY_DONE; } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 145d2395f3c0..0f1db4c18b22 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -246,36 +246,6 @@ expired: xfrm_pol_put(xp); } -static struct flow_cache_object *xfrm_policy_flo_get(struct flow_cache_object *flo) -{ - struct xfrm_policy *pol = container_of(flo, struct xfrm_policy, flo); - - if (unlikely(pol->walk.dead)) - flo = NULL; - else - xfrm_pol_hold(pol); - - return flo; -} - -static int xfrm_policy_flo_check(struct flow_cache_object *flo) -{ - struct xfrm_policy *pol = container_of(flo, struct xfrm_policy, flo); - - return !pol->walk.dead; -} - -static void xfrm_policy_flo_delete(struct flow_cache_object *flo) -{ - xfrm_pol_put(container_of(flo, struct xfrm_policy, flo)); -} - -static const struct flow_cache_ops xfrm_policy_fc_ops = { - .get = xfrm_policy_flo_get, - .check = xfrm_policy_flo_check, - .delete = xfrm_policy_flo_delete, -}; - /* Allocate xfrm_policy. Not used here, it is supposed to be used by pfkeyv2 * SPD calls. */ @@ -298,7 +268,6 @@ struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp) (unsigned long)policy); setup_timer(&policy->polq.hold_timer, xfrm_policy_queue_process, (unsigned long)policy); - policy->flo.ops = &xfrm_policy_fc_ops; } return policy; } @@ -798,7 +767,6 @@ int xfrm_policy_insert(int dir, struct xfrm_policy *policy, int excl) else hlist_add_head(&policy->bydst, chain); __xfrm_policy_link(policy, dir); - atomic_inc(&net->xfrm.flow_cache_genid); /* After previous checking, family can either be AF_INET or AF_INET6 */ if (policy->family == AF_INET) @@ -1490,58 +1458,6 @@ static int xfrm_get_tos(const struct flowi *fl, int family) return tos; } -static struct flow_cache_object *xfrm_bundle_flo_get(struct flow_cache_object *flo) -{ - struct xfrm_dst *xdst = container_of(flo, struct xfrm_dst, flo); - struct dst_entry *dst = &xdst->u.dst; - - if (xdst->route == NULL) { - /* Dummy bundle - if it has xfrms we were not - * able to build bundle as template resolution failed. - * It means we need to try again resolving. */ - if (xdst->num_xfrms > 0) - return NULL; - } else if (dst->flags & DST_XFRM_QUEUE) { - return NULL; - } else { - /* Real bundle */ - if (stale_bundle(dst)) - return NULL; - } - - dst_hold(dst); - return flo; -} - -static int xfrm_bundle_flo_check(struct flow_cache_object *flo) -{ - struct xfrm_dst *xdst = container_of(flo, struct xfrm_dst, flo); - struct dst_entry *dst = &xdst->u.dst; - - if (!xdst->route) - return 0; - if (stale_bundle(dst)) - return 0; - - return 1; -} - -static void xfrm_bundle_flo_delete(struct flow_cache_object *flo) -{ - struct xfrm_dst *xdst = container_of(flo, struct xfrm_dst, flo); - struct dst_entry *dst = &xdst->u.dst; - - /* Mark DST_OBSOLETE_DEAD to fail the next xfrm_dst_check() */ - dst->obsolete = DST_OBSOLETE_DEAD; - dst_release_immediate(dst); -} - -static const struct flow_cache_ops xfrm_bundle_fc_ops = { - .get = xfrm_bundle_flo_get, - .check = xfrm_bundle_flo_check, - .delete = xfrm_bundle_flo_delete, -}; - static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) { const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); @@ -1569,7 +1485,6 @@ static inline struct xfrm_dst *xfrm_alloc_dst(struct net *net, int family) struct dst_entry *dst = &xdst->u.dst; memset(dst + 1, 0, sizeof(*xdst) - sizeof(*dst)); - xdst->flo.ops = &xfrm_bundle_fc_ops; } else xdst = ERR_PTR(-ENOBUFS); @@ -2521,11 +2436,9 @@ static struct dst_entry *xfrm_dst_check(struct dst_entry *dst, u32 cookie) * notice. That's what we are validating here via the * stale_bundle() check. * - * When an xdst is removed from flow cache, DST_OBSOLETE_DEAD will - * be marked on it. * When a dst is removed from the fib tree, DST_OBSOLETE_DEAD will * be marked on it. - * Both will force stable_bundle() to fail on any xdst bundle with + * This will force stale_bundle() to fail on any xdst bundle with * this dst linked in it. */ if (dst->obsolete < 0 && !stale_bundle(dst)) @@ -2565,18 +2478,6 @@ static struct dst_entry *xfrm_negative_advice(struct dst_entry *dst) return dst; } -void xfrm_garbage_collect(struct net *net) -{ - flow_cache_flush(net); -} -EXPORT_SYMBOL(xfrm_garbage_collect); - -void xfrm_garbage_collect_deferred(struct net *net) -{ - flow_cache_flush_deferred(net); -} -EXPORT_SYMBOL(xfrm_garbage_collect_deferred); - static void xfrm_init_pmtu(struct dst_entry *dst) { do { @@ -2914,14 +2815,9 @@ static int __net_init xfrm_net_init(struct net *net) rv = xfrm_sysctl_init(net); if (rv < 0) goto out_sysctl; - rv = flow_cache_init(net); - if (rv < 0) - goto out; return 0; -out: - xfrm_sysctl_fini(net); out_sysctl: xfrm_policy_fini(net); out_policy: @@ -2934,7 +2830,6 @@ out_statistics: static void __net_exit xfrm_net_exit(struct net *net) { - flow_cache_fini(net); xfrm_sysctl_fini(net); xfrm_policy_fini(net); xfrm_state_fini(net); @@ -2948,7 +2843,6 @@ static struct pernet_operations __net_initdata xfrm_net_ops = { void __init xfrm_init(void) { - flow_cache_hp_init(); register_pernet_subsys(&xfrm_net_ops); seqcount_init(&xfrm_policy_hash_generation); xfrm_input_init(); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 2be4c6af008a..1b539b7dcfab 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -1815,8 +1815,6 @@ static int xfrm_get_policy(struct sk_buff *skb, struct nlmsghdr *nlh, out: xfrm_pol_put(xp); - if (delete && err == 0) - xfrm_garbage_collect(net); return err; } @@ -2027,7 +2025,6 @@ static int xfrm_flush_policy(struct sk_buff *skb, struct nlmsghdr *nlh, return 0; return err; } - xfrm_garbage_collect(net); c.data.type = type; c.event = nlh->nlmsg_type; diff --git a/security/selinux/include/xfrm.h b/security/selinux/include/xfrm.h index 1450f85b946d..36a7ce9e11ff 100644 --- a/security/selinux/include/xfrm.h +++ b/security/selinux/include/xfrm.h @@ -47,10 +47,8 @@ static inline void selinux_xfrm_notify_policyload(void) struct net *net; rtnl_lock(); - for_each_net(net) { - atomic_inc(&net->xfrm.flow_cache_genid); + for_each_net(net) rt_genid_bump_all(net); - } rtnl_unlock(); } #else -- cgit v1.2.3 From ec30d78c14a813db39a647b6a348b4286ba4abf5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 17 Jul 2017 13:57:27 +0200 Subject: xfrm: add xdst pcpu cache retain last used xfrm_dst in a pcpu cache. On next request, reuse this dst if the policies are the same. The cache will not help with strict RR workloads as there is no hit. The cache packet-path part is reasonably small, the notifier part is needed so we do not add long hangs when a device is dismantled but some pcpu xdst still holds a reference, there are also calls to the flush operation when userspace deletes SAs so modules can be removed (there is no hit. We need to run the dst_release on the correct cpu to avoid races with packet path. This is done by adding a work_struct for each cpu and then doing the actual test/release on each affected cpu via schedule_work_on(). Test results using 4 network namespaces and null encryption: ns1 ns2 -> ns3 -> ns4 netperf -> xfrm/null enc -> xfrm/null dec -> netserver what TCP_STREAM UDP_STREAM UDP_RR Flow cache: 14644.61 294.35 327231.64 No flow cache: 14349.81 242.64 202301.72 Pcpu cache: 14629.70 292.21 205595.22 UDP tests used 64byte packets, tests ran for one minute each, value is average over ten iterations. 'Flow cache' is 'net-next', 'No flow cache' is net-next plus this series but without this patch. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/xfrm.h | 1 + net/xfrm/xfrm_device.c | 2 + net/xfrm/xfrm_policy.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++- net/xfrm/xfrm_state.c | 5 +- 4 files changed, 132 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index e0feba2ce76a..afb4929d7232 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -317,6 +317,7 @@ int xfrm_policy_register_afinfo(const struct xfrm_policy_afinfo *afinfo, int fam void xfrm_policy_unregister_afinfo(const struct xfrm_policy_afinfo *afinfo); void km_policy_notify(struct xfrm_policy *xp, int dir, const struct km_event *c); +void xfrm_policy_cache_flush(void); void km_state_notify(struct xfrm_state *x, const struct km_event *c); struct xfrm_tmpl; diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 1f9a079e08b0..5cd7a244e88d 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -153,6 +153,7 @@ static int xfrm_dev_register(struct net_device *dev) static int xfrm_dev_unregister(struct net_device *dev) { + xfrm_policy_cache_flush(); return NOTIFY_DONE; } @@ -175,6 +176,7 @@ static int xfrm_dev_down(struct net_device *dev) if (dev->features & NETIF_F_HW_ESP) xfrm_dev_state_flush(dev_net(dev), dev, true); + xfrm_policy_cache_flush(); return NOTIFY_DONE; } diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 0f1db4c18b22..06c3bf7ab86b 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ struct xfrm_flo { u8 flags; }; +static DEFINE_PER_CPU(struct xfrm_dst *, xfrm_last_dst); +static struct work_struct *xfrm_pcpu_work __read_mostly; static DEFINE_SPINLOCK(xfrm_policy_afinfo_lock); static struct xfrm_policy_afinfo const __rcu *xfrm_policy_afinfo[AF_INET6 + 1] __read_mostly; @@ -972,6 +975,8 @@ int xfrm_policy_flush(struct net *net, u8 type, bool task_valid) } if (!cnt) err = -ESRCH; + else + xfrm_policy_cache_flush(); out: spin_unlock_bh(&net->xfrm.xfrm_policy_lock); return err; @@ -1700,6 +1705,102 @@ static int xfrm_expand_policies(const struct flowi *fl, u16 family, } +static void xfrm_last_dst_update(struct xfrm_dst *xdst, struct xfrm_dst *old) +{ + this_cpu_write(xfrm_last_dst, xdst); + if (old) + dst_release(&old->u.dst); +} + +static void __xfrm_pcpu_work_fn(void) +{ + struct xfrm_dst *old; + + old = this_cpu_read(xfrm_last_dst); + if (old && !xfrm_bundle_ok(old)) + xfrm_last_dst_update(NULL, old); +} + +static void xfrm_pcpu_work_fn(struct work_struct *work) +{ + local_bh_disable(); + rcu_read_lock(); + __xfrm_pcpu_work_fn(); + rcu_read_unlock(); + local_bh_enable(); +} + +void xfrm_policy_cache_flush(void) +{ + struct xfrm_dst *old; + bool found = 0; + int cpu; + + local_bh_disable(); + rcu_read_lock(); + for_each_possible_cpu(cpu) { + old = per_cpu(xfrm_last_dst, cpu); + if (old && !xfrm_bundle_ok(old)) { + if (smp_processor_id() == cpu) { + __xfrm_pcpu_work_fn(); + continue; + } + found = true; + break; + } + } + + rcu_read_unlock(); + local_bh_enable(); + + if (!found) + return; + + get_online_cpus(); + + for_each_possible_cpu(cpu) { + bool bundle_release; + + rcu_read_lock(); + old = per_cpu(xfrm_last_dst, cpu); + bundle_release = old && !xfrm_bundle_ok(old); + rcu_read_unlock(); + + if (!bundle_release) + continue; + + if (cpu_online(cpu)) { + schedule_work_on(cpu, &xfrm_pcpu_work[cpu]); + continue; + } + + rcu_read_lock(); + old = per_cpu(xfrm_last_dst, cpu); + if (old && !xfrm_bundle_ok(old)) { + per_cpu(xfrm_last_dst, cpu) = NULL; + dst_release(&old->u.dst); + } + rcu_read_unlock(); + } + + put_online_cpus(); +} + +static bool xfrm_pol_dead(struct xfrm_dst *xdst) +{ + unsigned int num_pols = xdst->num_pols; + unsigned int pol_dead = 0, i; + + for (i = 0; i < num_pols; i++) + pol_dead |= xdst->pols[i]->walk.dead; + + /* Mark DST_OBSOLETE_DEAD to fail the next xfrm_dst_check() */ + if (pol_dead) + xdst->u.dst.obsolete = DST_OBSOLETE_DEAD; + + return pol_dead; +} + static struct xfrm_dst * xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols, const struct flowi *fl, u16 family, @@ -1707,10 +1808,22 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols, { struct net *net = xp_net(pols[0]); struct xfrm_state *xfrm[XFRM_MAX_DEPTH]; + struct xfrm_dst *xdst, *old; struct dst_entry *dst; - struct xfrm_dst *xdst; int err; + xdst = this_cpu_read(xfrm_last_dst); + if (xdst && + xdst->u.dst.dev == dst_orig->dev && + xdst->num_pols == num_pols && + !xfrm_pol_dead(xdst) && + memcmp(xdst->pols, pols, + sizeof(struct xfrm_policy *) * num_pols) == 0) { + dst_hold(&xdst->u.dst); + return xdst; + } + + old = xdst; /* Try to instantiate a bundle */ err = xfrm_tmpl_resolve(pols, num_pols, fl, xfrm, family); if (err <= 0) { @@ -1731,6 +1844,9 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols, memcpy(xdst->pols, pols, sizeof(struct xfrm_policy *) * num_pols); xdst->policy_genid = atomic_read(&pols[0]->genid); + atomic_set(&xdst->u.dst.__refcnt, 2); + xfrm_last_dst_update(xdst, old); + return xdst; } @@ -2843,6 +2959,15 @@ static struct pernet_operations __net_initdata xfrm_net_ops = { void __init xfrm_init(void) { + int i; + + xfrm_pcpu_work = kmalloc_array(NR_CPUS, sizeof(*xfrm_pcpu_work), + GFP_KERNEL); + BUG_ON(!xfrm_pcpu_work); + + for (i = 0; i < NR_CPUS; i++) + INIT_WORK(&xfrm_pcpu_work[i], xfrm_pcpu_work_fn); + register_pernet_subsys(&xfrm_net_ops); seqcount_init(&xfrm_policy_hash_generation); xfrm_input_init(); diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 6c0956d10db6..82cbbce69b79 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -724,9 +724,10 @@ restart: } } } - if (cnt) + if (cnt) { err = 0; - + xfrm_policy_cache_flush(); + } out: spin_unlock_bh(&net->xfrm.xfrm_state_lock); return err; -- cgit v1.2.3 From c57c054eb5b1ccf230c49f736f7a018fcbc3e952 Mon Sep 17 00:00:00 2001 From: Tonghao Zhang Date: Mon, 17 Jul 2017 23:28:05 -0700 Subject: openvswitch: Optimize updating for OvS flow_stats. In the ovs_flow_stats_update(), we only use the node var to alloc flow_stats struct. But this is not a common case, it is unnecessary to call the numa_node_id() everytime. This patch is not a bugfix, but there maybe a small increase. Signed-off-by: Tonghao Zhang Signed-off-by: David S. Miller --- net/openvswitch/flow.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 597d96faca45..6ef51e764367 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -72,7 +72,6 @@ void ovs_flow_stats_update(struct sw_flow *flow, __be16 tcp_flags, const struct sk_buff *skb) { struct flow_stats *stats; - int node = numa_node_id(); int cpu = smp_processor_id(); int len = skb->len + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); @@ -108,7 +107,7 @@ void ovs_flow_stats_update(struct sw_flow *flow, __be16 tcp_flags, __GFP_THISNODE | __GFP_NOWARN | __GFP_NOMEMALLOC, - node); + numa_node_id()); if (likely(new_stats)) { new_stats->used = jiffies; new_stats->packet_count = 1; -- cgit v1.2.3 From c4b2bf6b4a35348fe6d1eb06928eb68d7b9d99a9 Mon Sep 17 00:00:00 2001 From: Tonghao Zhang Date: Mon, 17 Jul 2017 23:28:06 -0700 Subject: openvswitch: Optimize operations for OvS flow_stats. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When calling the flow_free() to free the flow, we call many times (cpu_possible_mask, eg. 128 as default) cpumask_next(). That will take up our CPU usage if we call the flow_free() frequently. When we put all packets to userspace via upcall, and OvS will send them back via netlink to ovs_packet_cmd_execute(will call flow_free). The test topo is shown as below. VM01 sends TCP packets to VM02, and OvS forward packtets. When testing, we use perf to report the system performance. VM01 --- OvS-VM --- VM02 Without this patch, perf-top show as below: The flow_free() is 3.02% CPU usage. 4.23% [kernel] [k] _raw_spin_unlock_irqrestore 3.62% [kernel] [k] __do_softirq 3.16% [kernel] [k] __memcpy 3.02% [kernel] [k] flow_free 2.42% libc-2.17.so [.] __memcpy_ssse3_back 2.18% [kernel] [k] copy_user_generic_unrolled 2.17% [kernel] [k] find_next_bit When applied this patch, perf-top show as below: Not shown on the list anymore. 4.11% [kernel] [k] _raw_spin_unlock_irqrestore 3.79% [kernel] [k] __do_softirq 3.46% [kernel] [k] __memcpy 2.73% libc-2.17.so [.] __memcpy_ssse3_back 2.25% [kernel] [k] copy_user_generic_unrolled 1.89% libc-2.17.so [.] _int_malloc 1.53% ovs-vswitchd [.] xlate_actions With this patch, the TCP throughput(we dont use Megaflow Cache + Microflow Cache) between VMs is 1.18Gbs/sec up to 1.30Gbs/sec (maybe ~10% performance imporve). This patch adds cpumask struct, the cpu_used_mask stores the cpu_id that the flow used. And we only check the flow_stats on the cpu we used, and it is unncessary to check all possible cpu when getting, cleaning, and updating the flow_stats. Adding the cpu_used_mask to sw_flow struct does’t increase the cacheline number. Signed-off-by: Tonghao Zhang Acked-by: Pravin B Shelar Signed-off-by: David S. Miller --- net/openvswitch/flow.c | 7 ++++--- net/openvswitch/flow.h | 2 ++ net/openvswitch/flow_table.c | 4 +++- 3 files changed, 9 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/openvswitch/flow.c b/net/openvswitch/flow.c index 6ef51e764367..8c94cef25a72 100644 --- a/net/openvswitch/flow.c +++ b/net/openvswitch/flow.c @@ -72,7 +72,7 @@ void ovs_flow_stats_update(struct sw_flow *flow, __be16 tcp_flags, const struct sk_buff *skb) { struct flow_stats *stats; - int cpu = smp_processor_id(); + unsigned int cpu = smp_processor_id(); int len = skb->len + (skb_vlan_tag_present(skb) ? VLAN_HLEN : 0); stats = rcu_dereference(flow->stats[cpu]); @@ -117,6 +117,7 @@ void ovs_flow_stats_update(struct sw_flow *flow, __be16 tcp_flags, rcu_assign_pointer(flow->stats[cpu], new_stats); + cpumask_set_cpu(cpu, &flow->cpu_used_mask); goto unlock; } } @@ -144,7 +145,7 @@ void ovs_flow_stats_get(const struct sw_flow *flow, memset(ovs_stats, 0, sizeof(*ovs_stats)); /* We open code this to make sure cpu 0 is always considered */ - for (cpu = 0; cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpu_possible_mask)) { + for (cpu = 0; cpu < nr_cpu_ids; cpu = cpumask_next(cpu, &flow->cpu_used_mask)) { struct flow_stats *stats = rcu_dereference_ovsl(flow->stats[cpu]); if (stats) { @@ -168,7 +169,7 @@ void ovs_flow_stats_clear(struct sw_flow *flow) int cpu; /* We open code this to make sure cpu 0 is always considered */ - for (cpu = 0; cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpu_possible_mask)) { + for (cpu = 0; cpu < nr_cpu_ids; cpu = cpumask_next(cpu, &flow->cpu_used_mask)) { struct flow_stats *stats = ovsl_dereference(flow->stats[cpu]); if (stats) { diff --git a/net/openvswitch/flow.h b/net/openvswitch/flow.h index a9bc1c875965..1875bba4f865 100644 --- a/net/openvswitch/flow.h +++ b/net/openvswitch/flow.h @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -219,6 +220,7 @@ struct sw_flow { */ struct sw_flow_key key; struct sw_flow_id id; + struct cpumask cpu_used_mask; struct sw_flow_mask *mask; struct sw_flow_actions __rcu *sf_acts; struct flow_stats __rcu *stats[]; /* One for each CPU. First one diff --git a/net/openvswitch/flow_table.c b/net/openvswitch/flow_table.c index ea7a8073fa02..80ea2a71852e 100644 --- a/net/openvswitch/flow_table.c +++ b/net/openvswitch/flow_table.c @@ -98,6 +98,8 @@ struct sw_flow *ovs_flow_alloc(void) RCU_INIT_POINTER(flow->stats[0], stats); + cpumask_set_cpu(0, &flow->cpu_used_mask); + return flow; err: kmem_cache_free(flow_cache, flow); @@ -141,7 +143,7 @@ static void flow_free(struct sw_flow *flow) if (flow->sf_acts) ovs_nla_free_flow_actions((struct sw_flow_actions __force *)flow->sf_acts); /* We open code this to make sure cpu 0 is always considered */ - for (cpu = 0; cpu < nr_cpu_ids; cpu = cpumask_next(cpu, cpu_possible_mask)) + for (cpu = 0; cpu < nr_cpu_ids; cpu = cpumask_next(cpu, &flow->cpu_used_mask)) if (flow->stats[cpu]) kmem_cache_free(flow_stats_cache, (struct flow_stats __force *)flow->stats[cpu]); -- cgit v1.2.3 From bb4d991a28cc86a2dfbeefeff32911ca9f779c18 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Wed, 19 Jul 2017 15:41:26 -0700 Subject: tcp: adjust tail loss probe timeout This patch adjusts the timeout formula to schedule the TCP loss probe (TLP). The previous formula uses 2*SRTT or 1.5*RTT + DelayACKMax if only one packet is in flight. It keeps a lower bound of 10 msec which is too large for short RTT connections (e.g. within a data-center). The new formula = 2*RTT + (inflight == 1 ? 200ms : 2ticks) which performs better for short and fast connections. Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- include/net/tcp.h | 3 +-- net/ipv4/tcp_output.c | 17 ++++++++++------- net/ipv4/tcp_recovery.c | 2 +- 3 files changed, 12 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/net/tcp.h b/include/net/tcp.h index 70483296157f..4f056ea79df2 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -139,6 +139,7 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #endif #define TCP_RTO_MAX ((unsigned)(120*HZ)) #define TCP_RTO_MIN ((unsigned)(HZ/5)) +#define TCP_TIMEOUT_MIN (2U) /* Min timeout for TCP timers in jiffies */ #define TCP_TIMEOUT_INIT ((unsigned)(1*HZ)) /* RFC6298 2.1 initial RTO value */ #define TCP_TIMEOUT_FALLBACK ((unsigned)(3*HZ)) /* RFC 1122 initial RTO value, now * used as a fallback RTO for the @@ -150,8 +151,6 @@ void tcp_time_wait(struct sock *sk, int state, int timeo); #define TCP_RESOURCE_PROBE_INTERVAL ((unsigned)(HZ/2U)) /* Maximal interval between probes * for local resources. */ -#define TCP_REO_TIMEOUT_MIN (2000) /* Min RACK reordering timeout in usec */ - #define TCP_KEEPALIVE_TIME (120*60*HZ) /* two hours */ #define TCP_KEEPALIVE_PROBES 9 /* Max of 9 keepalive probes */ #define TCP_KEEPALIVE_INTVL (75*HZ) diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 4e985dea1dd2..886d874775df 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -2377,7 +2377,6 @@ bool tcp_schedule_loss_probe(struct sock *sk) struct inet_connection_sock *icsk = inet_csk(sk); struct tcp_sock *tp = tcp_sk(sk); u32 timeout, tlp_time_stamp, rto_time_stamp; - u32 rtt = usecs_to_jiffies(tp->srtt_us >> 3); /* No consecutive loss probes. */ if (WARN_ON(icsk->icsk_pending == ICSK_TIME_LOSS_PROBE)) { @@ -2406,15 +2405,19 @@ bool tcp_schedule_loss_probe(struct sock *sk) tcp_send_head(sk)) return false; - /* Probe timeout is at least 1.5*rtt + TCP_DELACK_MAX to account + /* Probe timeout is 2*rtt. Add minimum RTO to account * for delayed ack when there's one outstanding packet. If no RTT * sample is available then probe after TCP_TIMEOUT_INIT. */ - timeout = rtt << 1 ? : TCP_TIMEOUT_INIT; - if (tp->packets_out == 1) - timeout = max_t(u32, timeout, - (rtt + (rtt >> 1) + TCP_DELACK_MAX)); - timeout = max_t(u32, timeout, msecs_to_jiffies(10)); + if (tp->srtt_us) { + timeout = usecs_to_jiffies(tp->srtt_us >> 2); + if (tp->packets_out == 1) + timeout += TCP_RTO_MIN; + else + timeout += TCP_TIMEOUT_MIN; + } else { + timeout = TCP_TIMEOUT_INIT; + } /* If RTO is shorter, just schedule TLP in its place. */ tlp_time_stamp = tcp_jiffies32 + timeout; diff --git a/net/ipv4/tcp_recovery.c b/net/ipv4/tcp_recovery.c index fe9a493d0208..449cd914d58e 100644 --- a/net/ipv4/tcp_recovery.c +++ b/net/ipv4/tcp_recovery.c @@ -113,7 +113,7 @@ void tcp_rack_mark_lost(struct sock *sk) tp->rack.advanced = 0; tcp_rack_detect_loss(sk, &timeout); if (timeout) { - timeout = usecs_to_jiffies(timeout + TCP_REO_TIMEOUT_MIN); + timeout = usecs_to_jiffies(timeout) + TCP_TIMEOUT_MIN; inet_csk_reset_xmit_timer(sk, ICSK_TIME_REO_TIMEOUT, timeout, inet_csk(sk)->icsk_rto); } -- cgit v1.2.3 From 8410095730c0874eb265bbcc87e9ec0d0ff8c183 Mon Sep 17 00:00:00 2001 From: "Rosen, Rami" Date: Tue, 18 Jul 2017 22:23:30 +0300 Subject: net/packet: remove unused PGV_FROM_VMALLOC definition. This patch removes the definition of PGV_FROM_VMALLOC from af_packet.c. The PGV_FROM_VMALLOC definition was already removed by commit 441c793a5650 ("net: cleanup unused macros in net directory"), and its usage was removed even before by commit c56b4d90123b ("af_packet: remove pgv.flags"); but it was added back by mistake later on, in commit f6fb8f100b80 ("af-packet: TPACKET_V3 flexible buffer implementation"). Signed-off-by: Rami Rosen Signed-off-by: David S. Miller --- net/packet/af_packet.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c index e3beb28203eb..ee035cbe5621 100644 --- a/net/packet/af_packet.c +++ b/net/packet/af_packet.c @@ -177,8 +177,6 @@ static int packet_set_ring(struct sock *sk, union tpacket_req_u *req_u, #define BLK_PLUS_PRIV(sz_of_priv) \ (BLK_HDR_LEN + ALIGN((sz_of_priv), V3_ALIGNMENT)) -#define PGV_FROM_VMALLOC 1 - #define BLOCK_STATUS(x) ((x)->hdr.bh1.block_status) #define BLOCK_NUM_PKTS(x) ((x)->hdr.bh1.num_pkts) #define BLOCK_O2FP(x) ((x)->hdr.bh1.offset_to_first_pkt) -- cgit v1.2.3 From e7d53ad3239de636ea478fc003d3652b49b8e593 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 18 Jul 2017 16:23:56 -0400 Subject: net: dsa: unexport dsa_is_port_initialized The dsa_is_port_initialized helper is only used by dsa_switch_resume and dsa_switch_suspend, if CONFIG_PM_SLEEP is enabled. Make it static to dsa.c. Signed-off-by: Vivien Didelot Signed-off-by: David S. Miller --- include/net/dsa.h | 5 ----- net/dsa/dsa.c | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/dsa.h b/include/net/dsa.h index 58969b9a090c..88da272d20d0 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -256,11 +256,6 @@ static inline bool dsa_is_normal_port(struct dsa_switch *ds, int p) return !dsa_is_cpu_port(ds, p) && !dsa_is_dsa_port(ds, p); } -static inline bool dsa_is_port_initialized(struct dsa_switch *ds, int p) -{ - return ds->enabled_port_mask & (1 << p) && ds->ports[p].netdev; -} - static inline u8 dsa_upstream_port(struct dsa_switch *ds) { struct dsa_switch_tree *dst = ds->dst; diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 416ac4ef9ba9..a55e2e4087a4 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -220,6 +220,11 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, } #ifdef CONFIG_PM_SLEEP +static bool dsa_is_port_initialized(struct dsa_switch *ds, int p) +{ + return ds->enabled_port_mask & (1 << p) && ds->ports[p].netdev; +} + int dsa_switch_suspend(struct dsa_switch *ds) { int i, ret = 0; -- cgit v1.2.3 From ddee3103eeafd1d9dbe831274c3813d5cac30bc3 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 18 Jul 2017 15:59:25 -0700 Subject: bluetooth: 6lowpan dev_close never returns error The function dev_close in current kernel will never return an error. Later changes will make it void. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/bluetooth/6lowpan.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'net') diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index ab3b654b05cc..2af4f1cc0ab4 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -618,12 +618,8 @@ static void ifup(struct net_device *netdev) static void ifdown(struct net_device *netdev) { - int err; - rtnl_lock(); - err = dev_close(netdev); - if (err < 0) - BT_INFO("iface %s cannot be closed (%d)", netdev->name, err); + dev_close(netdev); rtnl_unlock(); } -- cgit v1.2.3 From 7051b88a35c7dde5705923833117e14f9cc17d92 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Tue, 18 Jul 2017 15:59:27 -0700 Subject: net: make dev_close and related functions void There is no useful return value from dev_close. All paths return 0. Change dev_close and helper functions to void. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/netdevice.h | 4 ++-- net/core/dev.c | 26 +++++++++++--------------- 2 files changed, 13 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c60351b84323..614642eb7eb7 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2432,8 +2432,8 @@ struct net_device *dev_get_by_name_rcu(struct net *net, const char *name); struct net_device *__dev_get_by_name(struct net *net, const char *name); int dev_alloc_name(struct net_device *dev, const char *name); int dev_open(struct net_device *dev); -int dev_close(struct net_device *dev); -int dev_close_many(struct list_head *head, bool unlink); +void dev_close(struct net_device *dev); +void dev_close_many(struct list_head *head, bool unlink); void dev_disable_lro(struct net_device *dev); int dev_loopback_xmit(struct net *net, struct sock *sk, struct sk_buff *newskb); int dev_queue_xmit(struct sk_buff *skb); diff --git a/net/core/dev.c b/net/core/dev.c index 467420eda02e..d1b9c9b6c970 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1413,7 +1413,7 @@ int dev_open(struct net_device *dev) } EXPORT_SYMBOL(dev_open); -static int __dev_close_many(struct list_head *head) +static void __dev_close_many(struct list_head *head) { struct net_device *dev; @@ -1455,23 +1455,18 @@ static int __dev_close_many(struct list_head *head) dev->flags &= ~IFF_UP; netpoll_poll_enable(dev); } - - return 0; } -static int __dev_close(struct net_device *dev) +static void __dev_close(struct net_device *dev) { - int retval; LIST_HEAD(single); list_add(&dev->close_list, &single); - retval = __dev_close_many(&single); + __dev_close_many(&single); list_del(&single); - - return retval; } -int dev_close_many(struct list_head *head, bool unlink) +void dev_close_many(struct list_head *head, bool unlink) { struct net_device *dev, *tmp; @@ -1488,8 +1483,6 @@ int dev_close_many(struct list_head *head, bool unlink) if (unlink) list_del_init(&dev->close_list); } - - return 0; } EXPORT_SYMBOL(dev_close_many); @@ -1502,7 +1495,7 @@ EXPORT_SYMBOL(dev_close_many); * is then deactivated and finally a %NETDEV_DOWN is sent to the notifier * chain. */ -int dev_close(struct net_device *dev) +void dev_close(struct net_device *dev) { if (dev->flags & IFF_UP) { LIST_HEAD(single); @@ -1511,7 +1504,6 @@ int dev_close(struct net_device *dev) dev_close_many(&single, true); list_del(&single); } - return 0; } EXPORT_SYMBOL(dev_close); @@ -6725,8 +6717,12 @@ int __dev_change_flags(struct net_device *dev, unsigned int flags) */ ret = 0; - if ((old_flags ^ flags) & IFF_UP) - ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev); + if ((old_flags ^ flags) & IFF_UP) { + if (old_flags & IFF_UP) + __dev_close(dev); + else + ret = __dev_open(dev); + } if ((flags ^ dev->gflags) & IFF_PROMISC) { int inc = (flags & IFF_PROMISC) ? 1 : -1; -- cgit v1.2.3 From ddc6c70f07bb1f6dd39a2c6c430f7b4fa95199c8 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 21 Jul 2017 10:07:10 +0100 Subject: rxrpc: Move the packet.h include file into net/rxrpc/ Move the protocol description header file into net/rxrpc/ and rename it to protocol.h. It's no longer necessary to expose it as packets are no longer exposed to kernel services (such as AFS) that use the facility. The abort codes are transferred to the UAPI header instead as we pass these back to userspace and also to kernel services. Signed-off-by: David Howells --- fs/afs/misc.c | 1 - fs/afs/rxrpc.c | 1 - include/rxrpc/packet.h | 235 --------------------------------------------- include/uapi/linux/rxrpc.h | 44 +++++++++ net/rxrpc/ar-internal.h | 2 +- net/rxrpc/protocol.h | 190 ++++++++++++++++++++++++++++++++++++ 6 files changed, 235 insertions(+), 238 deletions(-) delete mode 100644 include/rxrpc/packet.h create mode 100644 net/rxrpc/protocol.h (limited to 'net') diff --git a/fs/afs/misc.c b/fs/afs/misc.c index 100b207efc9e..c05f1f1c0d41 100644 --- a/fs/afs/misc.c +++ b/fs/afs/misc.c @@ -12,7 +12,6 @@ #include #include #include -#include #include "internal.h" #include "afs_fs.h" diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 02781e78ffb6..10743043d431 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -14,7 +14,6 @@ #include #include -#include #include "internal.h" #include "afs_cm.h" diff --git a/include/rxrpc/packet.h b/include/rxrpc/packet.h deleted file mode 100644 index a2dcfb850b9f..000000000000 --- a/include/rxrpc/packet.h +++ /dev/null @@ -1,235 +0,0 @@ -/* packet.h: Rx packet layout and definitions - * - * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. - * Written by David Howells (dhowells@redhat.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version - * 2 of the License, or (at your option) any later version. - */ - -#ifndef _LINUX_RXRPC_PACKET_H -#define _LINUX_RXRPC_PACKET_H - -typedef u32 rxrpc_seq_t; /* Rx message sequence number */ -typedef u32 rxrpc_serial_t; /* Rx message serial number */ -typedef __be32 rxrpc_seq_net_t; /* on-the-wire Rx message sequence number */ -typedef __be32 rxrpc_serial_net_t; /* on-the-wire Rx message serial number */ - -/*****************************************************************************/ -/* - * on-the-wire Rx packet header - * - all multibyte fields should be in network byte order - */ -struct rxrpc_wire_header { - __be32 epoch; /* client boot timestamp */ -#define RXRPC_RANDOM_EPOCH 0x80000000 /* Random if set, date-based if not */ - - __be32 cid; /* connection and channel ID */ -#define RXRPC_MAXCALLS 4 /* max active calls per conn */ -#define RXRPC_CHANNELMASK (RXRPC_MAXCALLS-1) /* mask for channel ID */ -#define RXRPC_CIDMASK (~RXRPC_CHANNELMASK) /* mask for connection ID */ -#define RXRPC_CIDSHIFT ilog2(RXRPC_MAXCALLS) /* shift for connection ID */ -#define RXRPC_CID_INC (1 << RXRPC_CIDSHIFT) /* connection ID increment */ - - __be32 callNumber; /* call ID (0 for connection-level packets) */ - __be32 seq; /* sequence number of pkt in call stream */ - __be32 serial; /* serial number of pkt sent to network */ - - uint8_t type; /* packet type */ -#define RXRPC_PACKET_TYPE_DATA 1 /* data */ -#define RXRPC_PACKET_TYPE_ACK 2 /* ACK */ -#define RXRPC_PACKET_TYPE_BUSY 3 /* call reject */ -#define RXRPC_PACKET_TYPE_ABORT 4 /* call/connection abort */ -#define RXRPC_PACKET_TYPE_ACKALL 5 /* ACK all outstanding packets on call */ -#define RXRPC_PACKET_TYPE_CHALLENGE 6 /* connection security challenge (SRVR->CLNT) */ -#define RXRPC_PACKET_TYPE_RESPONSE 7 /* connection secutity response (CLNT->SRVR) */ -#define RXRPC_PACKET_TYPE_DEBUG 8 /* debug info request */ -#define RXRPC_PACKET_TYPE_VERSION 13 /* version string request */ -#define RXRPC_N_PACKET_TYPES 14 /* number of packet types (incl type 0) */ - - uint8_t flags; /* packet flags */ -#define RXRPC_CLIENT_INITIATED 0x01 /* signifies a packet generated by a client */ -#define RXRPC_REQUEST_ACK 0x02 /* request an unconditional ACK of this packet */ -#define RXRPC_LAST_PACKET 0x04 /* the last packet from this side for this call */ -#define RXRPC_MORE_PACKETS 0x08 /* more packets to come */ -#define RXRPC_JUMBO_PACKET 0x20 /* [DATA] this is a jumbo packet */ -#define RXRPC_SLOW_START_OK 0x20 /* [ACK] slow start supported */ - - uint8_t userStatus; /* app-layer defined status */ -#define RXRPC_USERSTATUS_SERVICE_UPGRADE 0x01 /* AuriStor service upgrade request */ - - uint8_t securityIndex; /* security protocol ID */ - union { - __be16 _rsvd; /* reserved */ - __be16 cksum; /* kerberos security checksum */ - }; - __be16 serviceId; /* service ID */ - -} __packed; - -#define RXRPC_SUPPORTED_PACKET_TYPES ( \ - (1 << RXRPC_PACKET_TYPE_DATA) | \ - (1 << RXRPC_PACKET_TYPE_ACK) | \ - (1 << RXRPC_PACKET_TYPE_BUSY) | \ - (1 << RXRPC_PACKET_TYPE_ABORT) | \ - (1 << RXRPC_PACKET_TYPE_ACKALL) | \ - (1 << RXRPC_PACKET_TYPE_CHALLENGE) | \ - (1 << RXRPC_PACKET_TYPE_RESPONSE) | \ - /*(1 << RXRPC_PACKET_TYPE_DEBUG) | */ \ - (1 << RXRPC_PACKET_TYPE_VERSION)) - -/*****************************************************************************/ -/* - * jumbo packet secondary header - * - can be mapped to read header by: - * - new_serial = serial + 1 - * - new_seq = seq + 1 - * - new_flags = j_flags - * - new__rsvd = j__rsvd - * - duplicating all other fields - */ -struct rxrpc_jumbo_header { - uint8_t flags; /* packet flags (as per rxrpc_header) */ - uint8_t pad; - union { - __be16 _rsvd; /* reserved */ - __be16 cksum; /* kerberos security checksum */ - }; -}; - -#define RXRPC_JUMBO_DATALEN 1412 /* non-terminal jumbo packet data length */ -#define RXRPC_JUMBO_SUBPKTLEN (RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header)) - -/*****************************************************************************/ -/* - * on-the-wire Rx ACK packet data payload - * - all multibyte fields should be in network byte order - */ -struct rxrpc_ackpacket { - __be16 bufferSpace; /* number of packet buffers available */ - __be16 maxSkew; /* diff between serno being ACK'd and highest serial no - * received */ - __be32 firstPacket; /* sequence no of first ACK'd packet in attached list */ - __be32 previousPacket; /* sequence no of previous packet received */ - __be32 serial; /* serial no of packet that prompted this ACK */ - - uint8_t reason; /* reason for ACK */ -#define RXRPC_ACK_REQUESTED 1 /* ACK was requested on packet */ -#define RXRPC_ACK_DUPLICATE 2 /* duplicate packet received */ -#define RXRPC_ACK_OUT_OF_SEQUENCE 3 /* out of sequence packet received */ -#define RXRPC_ACK_EXCEEDS_WINDOW 4 /* packet received beyond end of ACK window */ -#define RXRPC_ACK_NOSPACE 5 /* packet discarded due to lack of buffer space */ -#define RXRPC_ACK_PING 6 /* keep alive ACK */ -#define RXRPC_ACK_PING_RESPONSE 7 /* response to RXRPC_ACK_PING */ -#define RXRPC_ACK_DELAY 8 /* nothing happened since received packet */ -#define RXRPC_ACK_IDLE 9 /* ACK due to fully received ACK window */ -#define RXRPC_ACK__INVALID 10 /* Representation of invalid ACK reason */ - - uint8_t nAcks; /* number of ACKs */ -#define RXRPC_MAXACKS 255 - - uint8_t acks[0]; /* list of ACK/NAKs */ -#define RXRPC_ACK_TYPE_NACK 0 -#define RXRPC_ACK_TYPE_ACK 1 - -} __packed; - -/* Some ACKs refer to specific packets and some are general and can be updated. */ -#define RXRPC_ACK_UPDATEABLE ((1 << RXRPC_ACK_REQUESTED) | \ - (1 << RXRPC_ACK_PING_RESPONSE) | \ - (1 << RXRPC_ACK_DELAY) | \ - (1 << RXRPC_ACK_IDLE)) - - -/* - * ACK packets can have a further piece of information tagged on the end - */ -struct rxrpc_ackinfo { - __be32 rxMTU; /* maximum Rx MTU size (bytes) [AFS 3.3] */ - __be32 maxMTU; /* maximum interface MTU size (bytes) [AFS 3.3] */ - __be32 rwind; /* Rx window size (packets) [AFS 3.4] */ - __be32 jumbo_max; /* max packets to stick into a jumbo packet [AFS 3.5] */ -}; - -/*****************************************************************************/ -/* - * Kerberos security type-2 challenge packet - */ -struct rxkad_challenge { - __be32 version; /* version of this challenge type */ - __be32 nonce; /* encrypted random number */ - __be32 min_level; /* minimum security level */ - __be32 __padding; /* padding to 8-byte boundary */ -} __packed; - -/*****************************************************************************/ -/* - * Kerberos security type-2 response packet - */ -struct rxkad_response { - __be32 version; /* version of this response type */ - __be32 __pad; - - /* encrypted bit of the response */ - struct { - __be32 epoch; /* current epoch */ - __be32 cid; /* parent connection ID */ - __be32 checksum; /* checksum */ - __be32 securityIndex; /* security type */ - __be32 call_id[4]; /* encrypted call IDs */ - __be32 inc_nonce; /* challenge nonce + 1 */ - __be32 level; /* desired level */ - } encrypted; - - __be32 kvno; /* Kerberos key version number */ - __be32 ticket_len; /* Kerberos ticket length */ -} __packed; - -/*****************************************************************************/ -/* - * RxRPC-level abort codes - */ -#define RX_CALL_DEAD -1 /* call/conn has been inactive and is shut down */ -#define RX_INVALID_OPERATION -2 /* invalid operation requested / attempted */ -#define RX_CALL_TIMEOUT -3 /* call timeout exceeded */ -#define RX_EOF -4 /* unexpected end of data on read op */ -#define RX_PROTOCOL_ERROR -5 /* low-level protocol error */ -#define RX_USER_ABORT -6 /* generic user abort */ -#define RX_ADDRINUSE -7 /* UDP port in use */ -#define RX_DEBUGI_BADTYPE -8 /* bad debugging packet type */ - -/* - * (un)marshalling abort codes (rxgen) - */ -#define RXGEN_CC_MARSHAL -450 -#define RXGEN_CC_UNMARSHAL -451 -#define RXGEN_SS_MARSHAL -452 -#define RXGEN_SS_UNMARSHAL -453 -#define RXGEN_DECODE -454 -#define RXGEN_OPCODE -455 -#define RXGEN_SS_XDRFREE -456 -#define RXGEN_CC_XDRFREE -457 - -/* - * Rx kerberos security abort codes - * - unfortunately we have no generalised security abort codes to say things - * like "unsupported security", so we have to use these instead and hope the - * other side understands - */ -#define RXKADINCONSISTENCY 19270400 /* security module structure inconsistent */ -#define RXKADPACKETSHORT 19270401 /* packet too short for security challenge */ -#define RXKADLEVELFAIL 19270402 /* security level negotiation failed */ -#define RXKADTICKETLEN 19270403 /* ticket length too short or too long */ -#define RXKADOUTOFSEQUENCE 19270404 /* packet had bad sequence number */ -#define RXKADNOAUTH 19270405 /* caller not authorised */ -#define RXKADBADKEY 19270406 /* illegal key: bad parity or weak */ -#define RXKADBADTICKET 19270407 /* security object was passed a bad ticket */ -#define RXKADUNKNOWNKEY 19270408 /* ticket contained unknown key version number */ -#define RXKADEXPIRED 19270409 /* authentication expired */ -#define RXKADSEALEDINCON 19270410 /* sealed data inconsistent */ -#define RXKADDATALEN 19270411 /* user data too long */ -#define RXKADILLEGALLEVEL 19270412 /* caller not authorised to use encrypted conns */ - -#endif /* _LINUX_RXRPC_PACKET_H */ diff --git a/include/uapi/linux/rxrpc.h b/include/uapi/linux/rxrpc.h index 08e2fb9c70ae..9656aad8f8f7 100644 --- a/include/uapi/linux/rxrpc.h +++ b/include/uapi/linux/rxrpc.h @@ -77,4 +77,48 @@ enum rxrpc_cmsg_type { #define RXRPC_SECURITY_RXGK 4 /* gssapi-based */ #define RXRPC_SECURITY_RXK5 5 /* kerberos 5 */ +/* + * RxRPC-level abort codes + */ +#define RX_CALL_DEAD -1 /* call/conn has been inactive and is shut down */ +#define RX_INVALID_OPERATION -2 /* invalid operation requested / attempted */ +#define RX_CALL_TIMEOUT -3 /* call timeout exceeded */ +#define RX_EOF -4 /* unexpected end of data on read op */ +#define RX_PROTOCOL_ERROR -5 /* low-level protocol error */ +#define RX_USER_ABORT -6 /* generic user abort */ +#define RX_ADDRINUSE -7 /* UDP port in use */ +#define RX_DEBUGI_BADTYPE -8 /* bad debugging packet type */ + +/* + * (un)marshalling abort codes (rxgen) + */ +#define RXGEN_CC_MARSHAL -450 +#define RXGEN_CC_UNMARSHAL -451 +#define RXGEN_SS_MARSHAL -452 +#define RXGEN_SS_UNMARSHAL -453 +#define RXGEN_DECODE -454 +#define RXGEN_OPCODE -455 +#define RXGEN_SS_XDRFREE -456 +#define RXGEN_CC_XDRFREE -457 + +/* + * Rx kerberos security abort codes + * - unfortunately we have no generalised security abort codes to say things + * like "unsupported security", so we have to use these instead and hope the + * other side understands + */ +#define RXKADINCONSISTENCY 19270400 /* security module structure inconsistent */ +#define RXKADPACKETSHORT 19270401 /* packet too short for security challenge */ +#define RXKADLEVELFAIL 19270402 /* security level negotiation failed */ +#define RXKADTICKETLEN 19270403 /* ticket length too short or too long */ +#define RXKADOUTOFSEQUENCE 19270404 /* packet had bad sequence number */ +#define RXKADNOAUTH 19270405 /* caller not authorised */ +#define RXKADBADKEY 19270406 /* illegal key: bad parity or weak */ +#define RXKADBADTICKET 19270407 /* security object was passed a bad ticket */ +#define RXKADUNKNOWNKEY 19270408 /* ticket contained unknown key version number */ +#define RXKADEXPIRED 19270409 /* authentication expired */ +#define RXKADSEALEDINCON 19270410 /* sealed data inconsistent */ +#define RXKADDATALEN 19270411 /* user data too long */ +#define RXKADILLEGALLEVEL 19270412 /* caller not authorised to use encrypted conns */ + #endif /* _UAPI_LINUX_RXRPC_H */ diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 69b97339ff9d..8c0db9b3e4ab 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -15,7 +15,7 @@ #include #include #include -#include +#include "protocol.h" #if 0 #define CHECK_SLAB_OKAY(X) \ diff --git a/net/rxrpc/protocol.h b/net/rxrpc/protocol.h new file mode 100644 index 000000000000..4bddcf3face3 --- /dev/null +++ b/net/rxrpc/protocol.h @@ -0,0 +1,190 @@ +/* packet.h: Rx packet layout and definitions + * + * Copyright (C) 2002, 2007 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _LINUX_RXRPC_PACKET_H +#define _LINUX_RXRPC_PACKET_H + +typedef u32 rxrpc_seq_t; /* Rx message sequence number */ +typedef u32 rxrpc_serial_t; /* Rx message serial number */ +typedef __be32 rxrpc_seq_net_t; /* on-the-wire Rx message sequence number */ +typedef __be32 rxrpc_serial_net_t; /* on-the-wire Rx message serial number */ + +/*****************************************************************************/ +/* + * on-the-wire Rx packet header + * - all multibyte fields should be in network byte order + */ +struct rxrpc_wire_header { + __be32 epoch; /* client boot timestamp */ +#define RXRPC_RANDOM_EPOCH 0x80000000 /* Random if set, date-based if not */ + + __be32 cid; /* connection and channel ID */ +#define RXRPC_MAXCALLS 4 /* max active calls per conn */ +#define RXRPC_CHANNELMASK (RXRPC_MAXCALLS-1) /* mask for channel ID */ +#define RXRPC_CIDMASK (~RXRPC_CHANNELMASK) /* mask for connection ID */ +#define RXRPC_CIDSHIFT ilog2(RXRPC_MAXCALLS) /* shift for connection ID */ +#define RXRPC_CID_INC (1 << RXRPC_CIDSHIFT) /* connection ID increment */ + + __be32 callNumber; /* call ID (0 for connection-level packets) */ + __be32 seq; /* sequence number of pkt in call stream */ + __be32 serial; /* serial number of pkt sent to network */ + + uint8_t type; /* packet type */ +#define RXRPC_PACKET_TYPE_DATA 1 /* data */ +#define RXRPC_PACKET_TYPE_ACK 2 /* ACK */ +#define RXRPC_PACKET_TYPE_BUSY 3 /* call reject */ +#define RXRPC_PACKET_TYPE_ABORT 4 /* call/connection abort */ +#define RXRPC_PACKET_TYPE_ACKALL 5 /* ACK all outstanding packets on call */ +#define RXRPC_PACKET_TYPE_CHALLENGE 6 /* connection security challenge (SRVR->CLNT) */ +#define RXRPC_PACKET_TYPE_RESPONSE 7 /* connection secutity response (CLNT->SRVR) */ +#define RXRPC_PACKET_TYPE_DEBUG 8 /* debug info request */ +#define RXRPC_PACKET_TYPE_VERSION 13 /* version string request */ +#define RXRPC_N_PACKET_TYPES 14 /* number of packet types (incl type 0) */ + + uint8_t flags; /* packet flags */ +#define RXRPC_CLIENT_INITIATED 0x01 /* signifies a packet generated by a client */ +#define RXRPC_REQUEST_ACK 0x02 /* request an unconditional ACK of this packet */ +#define RXRPC_LAST_PACKET 0x04 /* the last packet from this side for this call */ +#define RXRPC_MORE_PACKETS 0x08 /* more packets to come */ +#define RXRPC_JUMBO_PACKET 0x20 /* [DATA] this is a jumbo packet */ +#define RXRPC_SLOW_START_OK 0x20 /* [ACK] slow start supported */ + + uint8_t userStatus; /* app-layer defined status */ +#define RXRPC_USERSTATUS_SERVICE_UPGRADE 0x01 /* AuriStor service upgrade request */ + + uint8_t securityIndex; /* security protocol ID */ + union { + __be16 _rsvd; /* reserved */ + __be16 cksum; /* kerberos security checksum */ + }; + __be16 serviceId; /* service ID */ + +} __packed; + +#define RXRPC_SUPPORTED_PACKET_TYPES ( \ + (1 << RXRPC_PACKET_TYPE_DATA) | \ + (1 << RXRPC_PACKET_TYPE_ACK) | \ + (1 << RXRPC_PACKET_TYPE_BUSY) | \ + (1 << RXRPC_PACKET_TYPE_ABORT) | \ + (1 << RXRPC_PACKET_TYPE_ACKALL) | \ + (1 << RXRPC_PACKET_TYPE_CHALLENGE) | \ + (1 << RXRPC_PACKET_TYPE_RESPONSE) | \ + /*(1 << RXRPC_PACKET_TYPE_DEBUG) | */ \ + (1 << RXRPC_PACKET_TYPE_VERSION)) + +/*****************************************************************************/ +/* + * jumbo packet secondary header + * - can be mapped to read header by: + * - new_serial = serial + 1 + * - new_seq = seq + 1 + * - new_flags = j_flags + * - new__rsvd = j__rsvd + * - duplicating all other fields + */ +struct rxrpc_jumbo_header { + uint8_t flags; /* packet flags (as per rxrpc_header) */ + uint8_t pad; + union { + __be16 _rsvd; /* reserved */ + __be16 cksum; /* kerberos security checksum */ + }; +}; + +#define RXRPC_JUMBO_DATALEN 1412 /* non-terminal jumbo packet data length */ +#define RXRPC_JUMBO_SUBPKTLEN (RXRPC_JUMBO_DATALEN + sizeof(struct rxrpc_jumbo_header)) + +/*****************************************************************************/ +/* + * on-the-wire Rx ACK packet data payload + * - all multibyte fields should be in network byte order + */ +struct rxrpc_ackpacket { + __be16 bufferSpace; /* number of packet buffers available */ + __be16 maxSkew; /* diff between serno being ACK'd and highest serial no + * received */ + __be32 firstPacket; /* sequence no of first ACK'd packet in attached list */ + __be32 previousPacket; /* sequence no of previous packet received */ + __be32 serial; /* serial no of packet that prompted this ACK */ + + uint8_t reason; /* reason for ACK */ +#define RXRPC_ACK_REQUESTED 1 /* ACK was requested on packet */ +#define RXRPC_ACK_DUPLICATE 2 /* duplicate packet received */ +#define RXRPC_ACK_OUT_OF_SEQUENCE 3 /* out of sequence packet received */ +#define RXRPC_ACK_EXCEEDS_WINDOW 4 /* packet received beyond end of ACK window */ +#define RXRPC_ACK_NOSPACE 5 /* packet discarded due to lack of buffer space */ +#define RXRPC_ACK_PING 6 /* keep alive ACK */ +#define RXRPC_ACK_PING_RESPONSE 7 /* response to RXRPC_ACK_PING */ +#define RXRPC_ACK_DELAY 8 /* nothing happened since received packet */ +#define RXRPC_ACK_IDLE 9 /* ACK due to fully received ACK window */ +#define RXRPC_ACK__INVALID 10 /* Representation of invalid ACK reason */ + + uint8_t nAcks; /* number of ACKs */ +#define RXRPC_MAXACKS 255 + + uint8_t acks[0]; /* list of ACK/NAKs */ +#define RXRPC_ACK_TYPE_NACK 0 +#define RXRPC_ACK_TYPE_ACK 1 + +} __packed; + +/* Some ACKs refer to specific packets and some are general and can be updated. */ +#define RXRPC_ACK_UPDATEABLE ((1 << RXRPC_ACK_REQUESTED) | \ + (1 << RXRPC_ACK_PING_RESPONSE) | \ + (1 << RXRPC_ACK_DELAY) | \ + (1 << RXRPC_ACK_IDLE)) + + +/* + * ACK packets can have a further piece of information tagged on the end + */ +struct rxrpc_ackinfo { + __be32 rxMTU; /* maximum Rx MTU size (bytes) [AFS 3.3] */ + __be32 maxMTU; /* maximum interface MTU size (bytes) [AFS 3.3] */ + __be32 rwind; /* Rx window size (packets) [AFS 3.4] */ + __be32 jumbo_max; /* max packets to stick into a jumbo packet [AFS 3.5] */ +}; + +/*****************************************************************************/ +/* + * Kerberos security type-2 challenge packet + */ +struct rxkad_challenge { + __be32 version; /* version of this challenge type */ + __be32 nonce; /* encrypted random number */ + __be32 min_level; /* minimum security level */ + __be32 __padding; /* padding to 8-byte boundary */ +} __packed; + +/*****************************************************************************/ +/* + * Kerberos security type-2 response packet + */ +struct rxkad_response { + __be32 version; /* version of this response type */ + __be32 __pad; + + /* encrypted bit of the response */ + struct { + __be32 epoch; /* current epoch */ + __be32 cid; /* parent connection ID */ + __be32 checksum; /* checksum */ + __be32 securityIndex; /* security type */ + __be32 call_id[4]; /* encrypted call IDs */ + __be32 inc_nonce; /* challenge nonce + 1 */ + __be32 level; /* desired level */ + } encrypted; + + __be32 kvno; /* Kerberos key version number */ + __be32 ticket_len; /* Kerberos ticket length */ +} __packed; + +#endif /* _LINUX_RXRPC_PACKET_H */ -- cgit v1.2.3 From 56a97e701c49ff66dd1a5e5e02775209ab5147d3 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 10 Jul 2017 15:06:39 +0200 Subject: netfilter: expect: add to hash table after expect init assuming we have lockless readers we should make sure they can only see expectations that have already been initialized. hlist_add_head_rcu acts as memory barrier, move it after timer setup. Theoretically we could crash due to a del_timer() on other cpu seeing garbage data. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_expect.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 899c2c36da13..2c63808bea96 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -368,12 +368,6 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) /* two references : one for hash insert, one for the timer */ refcount_add(2, &exp->use); - hlist_add_head_rcu(&exp->lnode, &master_help->expectations); - master_help->expecting[exp->class]++; - - hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]); - net->ct.expect_count++; - setup_timer(&exp->timeout, nf_ct_expectation_timed_out, (unsigned long)exp); helper = rcu_dereference_protected(master_help->helper, @@ -384,6 +378,12 @@ static void nf_ct_expect_insert(struct nf_conntrack_expect *exp) } add_timer(&exp->timeout); + hlist_add_head_rcu(&exp->lnode, &master_help->expectations); + master_help->expecting[exp->class]++; + + hlist_add_head_rcu(&exp->hnode, &nf_ct_expect_hash[h]); + net->ct.expect_count++; + NF_CT_STAT_INC(net, expect_create); } -- cgit v1.2.3 From 9f08ea848117ab521efcfd3e004d8e1a0edc640c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Tue, 18 Jul 2017 20:18:09 +0200 Subject: netfilter: nf_tables: keep chain counters away from hot path These chain counters are only used by the iptables-compat tool, that allow users to use the x_tables extensions from the existing nf_tables framework. This patch makes nf_tables by ~5% for the general usecase, ie. native nft users, where no chain counters are used at all. Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables_core.h | 2 ++ net/netfilter/nf_tables_api.c | 11 +++-------- net/netfilter/nf_tables_core.c | 26 ++++++++++++++++++-------- 3 files changed, 23 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_tables_core.h b/include/net/netfilter/nf_tables_core.h index 8f690effec37..424684c33771 100644 --- a/include/net/netfilter/nf_tables_core.h +++ b/include/net/netfilter/nf_tables_core.h @@ -49,6 +49,8 @@ struct nft_payload_set { }; extern const struct nft_expr_ops nft_payload_fast_ops; + +extern struct static_key_false nft_counters_enabled; extern struct static_key_false nft_trace_enabled; #endif /* _NET_NF_TABLES_CORE_H */ diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 7843efa33c59..7fbf0070aba1 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1240,6 +1240,8 @@ static void nf_tables_chain_destroy(struct nft_chain *chain) module_put(basechain->type->owner); free_percpu(basechain->stats); + if (basechain->stats) + static_branch_dec(&nft_counters_enabled); if (basechain->ops[0].dev != NULL) dev_put(basechain->ops[0].dev); kfree(basechain); @@ -1504,14 +1506,7 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, return PTR_ERR(stats); } basechain->stats = stats; - } else { - stats = netdev_alloc_pcpu_stats(struct nft_stats); - if (stats == NULL) { - nft_chain_release_hook(&hook); - kfree(basechain); - return -ENOMEM; - } - rcu_assign_pointer(basechain->stats, stats); + static_branch_inc(&nft_counters_enabled); } hookfn = hook.type->hooks[hook.num]; diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index 65dbeadcb118..c5bab08b0d73 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -114,6 +114,22 @@ static bool nft_payload_fast_eval(const struct nft_expr *expr, return true; } +DEFINE_STATIC_KEY_FALSE(nft_counters_enabled); + +static noinline void nft_update_chain_stats(const struct nft_chain *chain, + const struct nft_pktinfo *pkt) +{ + struct nft_stats *stats; + + local_bh_disable(); + stats = this_cpu_ptr(rcu_dereference(nft_base_chain(chain)->stats)); + u64_stats_update_begin(&stats->syncp); + stats->pkts++; + stats->bytes += pkt->skb->len; + u64_stats_update_end(&stats->syncp); + local_bh_enable(); +} + struct nft_jumpstack { const struct nft_chain *chain; const struct nft_rule *rule; @@ -130,7 +146,6 @@ nft_do_chain(struct nft_pktinfo *pkt, void *priv) struct nft_regs regs; unsigned int stackptr = 0; struct nft_jumpstack jumpstack[NFT_JUMP_STACK_SIZE]; - struct nft_stats *stats; int rulenum; unsigned int gencursor = nft_genmask_cur(net); struct nft_traceinfo info; @@ -220,13 +235,8 @@ next_rule: nft_trace_packet(&info, basechain, NULL, -1, NFT_TRACETYPE_POLICY); - rcu_read_lock_bh(); - stats = this_cpu_ptr(rcu_dereference(nft_base_chain(basechain)->stats)); - u64_stats_update_begin(&stats->syncp); - stats->pkts++; - stats->bytes += pkt->skb->len; - u64_stats_update_end(&stats->syncp); - rcu_read_unlock_bh(); + if (static_branch_unlikely(&nft_counters_enabled)) + nft_update_chain_stats(basechain, pkt); return nft_base_chain(basechain)->policy; } -- cgit v1.2.3 From 0b35f6031a00329800bacc04085188c300c3a4d8 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Wed, 19 Jul 2017 14:27:33 +0900 Subject: netfilter: Remove duplicated rcu_read_lock. This patch removes duplicate rcu_read_lock(). 1. IPVS part: According to Julian Anastasov's mention, contexts of ipvs are described at: http://marc.info/?l=netfilter-devel&m=149562884514072&w=2, in summary: - packet RX/TX: does not need locks because packets come from hooks. - sync msg RX: backup server uses RCU locks while registering new connections. - ip_vs_ctl.c: configuration get/set, RCU locks needed. - xt_ipvs.c: It is a netfilter match, running from hook context. As result, rcu_read_lock and rcu_read_unlock can be removed from: - ip_vs_core.c: all - ip_vs_ctl.c: - only from ip_vs_has_real_service - ip_vs_ftp.c: all - ip_vs_proto_sctp.c: all - ip_vs_proto_tcp.c: all - ip_vs_proto_udp.c: all - ip_vs_xmit.c: all (contains only packet processing) 2. Netfilter part: There are three types of functions that are guaranteed the rcu_read_lock(). First, as result, functions are only called by nf_hook(): - nf_conntrack_broadcast_help(), pptp_expectfn(), set_expected_rtp_rtcp(). - tcpmss_reverse_mtu(), tproxy_laddr4(), tproxy_laddr6(). - match_lookup_rt6(), check_hlist(), hashlimit_mt_common(). - xt_osf_match_packet(). Second, functions that caller already held the rcu_read_lock(). - destroy_conntrack(), ctnetlink_conntrack_event(). - ctnl_timeout_find_get(), nfqnl_nf_hook_drop(). Third, functions that are mixed with type1 and type2. These functions are called by nf_hook() also these are called by ordinary functions that already held the rcu_read_lock(): - __ctnetlink_glue_build(), ctnetlink_expect_event(). - ctnetlink_proto_size(). Applied files are below: - nf_conntrack_broadcast.c, nf_conntrack_core.c, nf_conntrack_netlink.c. - nf_conntrack_pptp.c, nf_conntrack_sip.c, nfnetlink_cttimeout.c. - nfnetlink_queue.c, xt_TCPMSS.c, xt_TPROXY.c, xt_addrtype.c. - xt_connlimit.c, xt_hashlimit.c, xt_osf.c Detailed calltrace can be found at: http://marc.info/?l=netfilter-devel&m=149667610710350&w=2 Signed-off-by: Taehee Yoo Acked-by: Julian Anastasov Signed-off-by: Pablo Neira Ayuso --- net/netfilter/ipvs/ip_vs_core.c | 8 ------ net/netfilter/ipvs/ip_vs_ctl.c | 3 --- net/netfilter/ipvs/ip_vs_ftp.c | 2 -- net/netfilter/ipvs/ip_vs_proto_sctp.c | 11 ++------ net/netfilter/ipvs/ip_vs_proto_tcp.c | 10 +------- net/netfilter/ipvs/ip_vs_proto_udp.c | 10 +------- net/netfilter/ipvs/ip_vs_xmit.c | 46 +++------------------------------- net/netfilter/nf_conntrack_broadcast.c | 2 -- net/netfilter/nf_conntrack_core.c | 3 --- net/netfilter/nf_conntrack_netlink.c | 12 --------- net/netfilter/nf_conntrack_pptp.c | 2 -- net/netfilter/nf_conntrack_sip.c | 6 +---- net/netfilter/nfnetlink_cttimeout.c | 2 -- net/netfilter/nfnetlink_queue.c | 2 -- net/netfilter/xt_TCPMSS.c | 2 -- net/netfilter/xt_TPROXY.c | 4 --- net/netfilter/xt_addrtype.c | 3 --- net/netfilter/xt_connlimit.c | 3 --- net/netfilter/xt_hashlimit.c | 8 +++--- net/netfilter/xt_osf.c | 2 -- 20 files changed, 13 insertions(+), 128 deletions(-) (limited to 'net') diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index e31956b58aba..2ff9d9070c95 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -125,14 +125,12 @@ ip_vs_in_stats(struct ip_vs_conn *cp, struct sk_buff *skb) s->cnt.inbytes += skb->len; u64_stats_update_end(&s->syncp); - rcu_read_lock(); svc = rcu_dereference(dest->svc); s = this_cpu_ptr(svc->stats.cpustats); u64_stats_update_begin(&s->syncp); s->cnt.inpkts++; s->cnt.inbytes += skb->len; u64_stats_update_end(&s->syncp); - rcu_read_unlock(); s = this_cpu_ptr(ipvs->tot_stats.cpustats); u64_stats_update_begin(&s->syncp); @@ -159,14 +157,12 @@ ip_vs_out_stats(struct ip_vs_conn *cp, struct sk_buff *skb) s->cnt.outbytes += skb->len; u64_stats_update_end(&s->syncp); - rcu_read_lock(); svc = rcu_dereference(dest->svc); s = this_cpu_ptr(svc->stats.cpustats); u64_stats_update_begin(&s->syncp); s->cnt.outpkts++; s->cnt.outbytes += skb->len; u64_stats_update_end(&s->syncp); - rcu_read_unlock(); s = this_cpu_ptr(ipvs->tot_stats.cpustats); u64_stats_update_begin(&s->syncp); @@ -1222,7 +1218,6 @@ static struct ip_vs_conn *__ip_vs_rs_conn_out(unsigned int hooknum, if (!pptr) return NULL; - rcu_read_lock(); dest = ip_vs_find_real_service(ipvs, af, iph->protocol, &iph->saddr, pptr[0]); if (dest) { @@ -1237,7 +1232,6 @@ static struct ip_vs_conn *__ip_vs_rs_conn_out(unsigned int hooknum, pptr[0], pptr[1]); } } - rcu_read_unlock(); return cp; } @@ -1689,11 +1683,9 @@ ip_vs_in_icmp(struct netns_ipvs *ipvs, struct sk_buff *skb, int *related, if (dest) { struct ip_vs_dest_dst *dest_dst; - rcu_read_lock(); dest_dst = rcu_dereference(dest->dest_dst); if (dest_dst) mtu = dst_mtu(dest_dst->dst_cache); - rcu_read_unlock(); } if (mtu > 68 + sizeof(struct iphdr)) mtu -= sizeof(struct iphdr); diff --git a/net/netfilter/ipvs/ip_vs_ctl.c b/net/netfilter/ipvs/ip_vs_ctl.c index 1fa3c2307b6e..4f940d7eb2f7 100644 --- a/net/netfilter/ipvs/ip_vs_ctl.c +++ b/net/netfilter/ipvs/ip_vs_ctl.c @@ -550,18 +550,15 @@ bool ip_vs_has_real_service(struct netns_ipvs *ipvs, int af, __u16 protocol, /* Check for "full" addressed entries */ hash = ip_vs_rs_hashkey(af, daddr, dport); - rcu_read_lock(); hlist_for_each_entry_rcu(dest, &ipvs->rs_table[hash], d_list) { if (dest->port == dport && dest->af == af && ip_vs_addr_equal(af, &dest->addr, daddr) && (dest->protocol == protocol || dest->vfwmark)) { /* HIT */ - rcu_read_unlock(); return true; } } - rcu_read_unlock(); return false; } diff --git a/net/netfilter/ipvs/ip_vs_ftp.c b/net/netfilter/ipvs/ip_vs_ftp.c index fb780be76d15..3e17d32b629d 100644 --- a/net/netfilter/ipvs/ip_vs_ftp.c +++ b/net/netfilter/ipvs/ip_vs_ftp.c @@ -269,13 +269,11 @@ static int ip_vs_ftp_out(struct ip_vs_app *app, struct ip_vs_conn *cp, * hopefully it will succeed on the retransmitted * packet. */ - rcu_read_lock(); mangled = nf_nat_mangle_tcp_packet(skb, ct, ctinfo, iph->ihl * 4, start - data, end - start, buf, buf_len); - rcu_read_unlock(); if (mangled) { ip_vs_nfct_expect_related(skb, ct, n_cp, IPPROTO_TCP, 0, 0); diff --git a/net/netfilter/ipvs/ip_vs_proto_sctp.c b/net/netfilter/ipvs/ip_vs_proto_sctp.c index 3ffad4adaddf..e1efa446b305 100644 --- a/net/netfilter/ipvs/ip_vs_proto_sctp.c +++ b/net/netfilter/ipvs/ip_vs_proto_sctp.c @@ -38,7 +38,6 @@ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, return 0; } - rcu_read_lock(); if (likely(!ip_vs_iph_inverse(iph))) svc = ip_vs_service_find(ipvs, af, skb->mark, iph->protocol, &iph->daddr, ports[1]); @@ -53,7 +52,6 @@ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, * It seems that we are very loaded. * We have to drop this packet :( */ - rcu_read_unlock(); *verdict = NF_DROP; return 0; } @@ -67,11 +65,9 @@ sctp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, *verdict = ip_vs_leave(svc, skb, pd, iph); else *verdict = NF_DROP; - rcu_read_unlock(); return 0; } } - rcu_read_unlock(); /* NF_ACCEPT */ return 1; } @@ -526,12 +522,10 @@ static int sctp_app_conn_bind(struct ip_vs_conn *cp) /* Lookup application incarnations and bind the right one */ hash = sctp_app_hashkey(cp->vport); - rcu_read_lock(); list_for_each_entry_rcu(inc, &ipvs->sctp_apps[hash], p_list) { if (inc->port == cp->vport) { if (unlikely(!ip_vs_app_inc_get(inc))) break; - rcu_read_unlock(); IP_VS_DBG_BUF(9, "%s: Binding conn %s:%u->" "%s:%u to app %s on port %u\n", @@ -544,11 +538,10 @@ static int sctp_app_conn_bind(struct ip_vs_conn *cp) cp->app = inc; if (inc->init_conn) result = inc->init_conn(inc, cp); - goto out; + break; } } - rcu_read_unlock(); -out: + return result; } diff --git a/net/netfilter/ipvs/ip_vs_proto_tcp.c b/net/netfilter/ipvs/ip_vs_proto_tcp.c index 12dc8d5bc37d..121a321b91be 100644 --- a/net/netfilter/ipvs/ip_vs_proto_tcp.c +++ b/net/netfilter/ipvs/ip_vs_proto_tcp.c @@ -63,7 +63,6 @@ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, } /* No !th->ack check to allow scheduling on SYN+ACK for Active FTP */ - rcu_read_lock(); if (likely(!ip_vs_iph_inverse(iph))) svc = ip_vs_service_find(ipvs, af, skb->mark, iph->protocol, @@ -80,7 +79,6 @@ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, * It seems that we are very loaded. * We have to drop this packet :( */ - rcu_read_unlock(); *verdict = NF_DROP; return 0; } @@ -95,11 +93,9 @@ tcp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, *verdict = ip_vs_leave(svc, skb, pd, iph); else *verdict = NF_DROP; - rcu_read_unlock(); return 0; } } - rcu_read_unlock(); /* NF_ACCEPT */ return 1; } @@ -661,12 +657,10 @@ tcp_app_conn_bind(struct ip_vs_conn *cp) /* Lookup application incarnations and bind the right one */ hash = tcp_app_hashkey(cp->vport); - rcu_read_lock(); list_for_each_entry_rcu(inc, &ipvs->tcp_apps[hash], p_list) { if (inc->port == cp->vport) { if (unlikely(!ip_vs_app_inc_get(inc))) break; - rcu_read_unlock(); IP_VS_DBG_BUF(9, "%s(): Binding conn %s:%u->" "%s:%u to app %s on port %u\n", @@ -680,12 +674,10 @@ tcp_app_conn_bind(struct ip_vs_conn *cp) cp->app = inc; if (inc->init_conn) result = inc->init_conn(inc, cp); - goto out; + break; } } - rcu_read_unlock(); - out: return result; } diff --git a/net/netfilter/ipvs/ip_vs_proto_udp.c b/net/netfilter/ipvs/ip_vs_proto_udp.c index e494e9a88c7f..30e11cd6aa8a 100644 --- a/net/netfilter/ipvs/ip_vs_proto_udp.c +++ b/net/netfilter/ipvs/ip_vs_proto_udp.c @@ -53,7 +53,6 @@ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, return 0; } - rcu_read_lock(); if (likely(!ip_vs_iph_inverse(iph))) svc = ip_vs_service_find(ipvs, af, skb->mark, iph->protocol, &iph->daddr, ports[1]); @@ -69,7 +68,6 @@ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, * It seems that we are very loaded. * We have to drop this packet :( */ - rcu_read_unlock(); *verdict = NF_DROP; return 0; } @@ -84,11 +82,9 @@ udp_conn_schedule(struct netns_ipvs *ipvs, int af, struct sk_buff *skb, *verdict = ip_vs_leave(svc, skb, pd, iph); else *verdict = NF_DROP; - rcu_read_unlock(); return 0; } } - rcu_read_unlock(); /* NF_ACCEPT */ return 1; } @@ -410,12 +406,10 @@ static int udp_app_conn_bind(struct ip_vs_conn *cp) /* Lookup application incarnations and bind the right one */ hash = udp_app_hashkey(cp->vport); - rcu_read_lock(); list_for_each_entry_rcu(inc, &ipvs->udp_apps[hash], p_list) { if (inc->port == cp->vport) { if (unlikely(!ip_vs_app_inc_get(inc))) break; - rcu_read_unlock(); IP_VS_DBG_BUF(9, "%s(): Binding conn %s:%u->" "%s:%u to app %s on port %u\n", @@ -429,12 +423,10 @@ static int udp_app_conn_bind(struct ip_vs_conn *cp) cp->app = inc; if (inc->init_conn) result = inc->init_conn(inc, cp); - goto out; + break; } } - rcu_read_unlock(); - out: return result; } diff --git a/net/netfilter/ipvs/ip_vs_xmit.c b/net/netfilter/ipvs/ip_vs_xmit.c index 2eab1e0400f4..90d396814798 100644 --- a/net/netfilter/ipvs/ip_vs_xmit.c +++ b/net/netfilter/ipvs/ip_vs_xmit.c @@ -678,7 +678,6 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - rcu_read_lock(); if (__ip_vs_get_out_rt(cp->ipvs, cp->af, skb, NULL, iph->daddr, IP_VS_RT_MODE_NON_LOCAL, NULL, ipvsh) < 0) goto tx_error; @@ -689,14 +688,12 @@ ip_vs_bypass_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, skb->ignore_df = 1; ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; tx_error: kfree_skb(skb); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; } @@ -710,7 +707,6 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - rcu_read_lock(); if (__ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, NULL, &iph->daddr, NULL, ipvsh, 0, IP_VS_RT_MODE_NON_LOCAL) < 0) @@ -720,14 +716,12 @@ ip_vs_bypass_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, skb->ignore_df = 1; ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; tx_error: kfree_skb(skb); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; } @@ -746,7 +740,6 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - rcu_read_lock(); /* check if it is a connection of no-client-port */ if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT)) { __be16 _pt, *p; @@ -815,14 +808,12 @@ ip_vs_nat_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, skb->ignore_df = 1; rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local); - rcu_read_unlock(); LeaveFunction(10); return rc; tx_error: kfree_skb(skb); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; } @@ -837,7 +828,6 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - rcu_read_lock(); /* check if it is a connection of no-client-port */ if (unlikely(cp->flags & IP_VS_CONN_F_NO_CPORT && !ipvsh->fragoffs)) { __be16 _pt, *p; @@ -906,7 +896,6 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, skb->ignore_df = 1; rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local); - rcu_read_unlock(); LeaveFunction(10); return rc; @@ -914,7 +903,6 @@ ip_vs_nat_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, tx_error: LeaveFunction(10); kfree_skb(skb); - rcu_read_unlock(); return NF_STOLEN; } #endif @@ -1035,7 +1023,6 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - rcu_read_lock(); local = __ip_vs_get_out_rt(ipvs, cp->af, skb, cp->dest, cp->daddr.ip, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | @@ -1043,10 +1030,8 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, IP_VS_RT_MODE_TUNNEL, &saddr, ipvsh); if (local < 0) goto tx_error; - if (local) { - rcu_read_unlock(); + if (local) return ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 1); - } rt = skb_rtable(skb); tdev = rt->dst.dev; @@ -1095,7 +1080,6 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, ip_local_out(net, skb->sk, skb); else if (ret == NF_DROP) kfree_skb(skb); - rcu_read_unlock(); LeaveFunction(10); @@ -1104,7 +1088,6 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, tx_error: if (!IS_ERR(skb)) kfree_skb(skb); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; } @@ -1127,7 +1110,6 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - rcu_read_lock(); local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, &cp->daddr.in6, &saddr, ipvsh, 1, @@ -1136,10 +1118,8 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, IP_VS_RT_MODE_TUNNEL); if (local < 0) goto tx_error; - if (local) { - rcu_read_unlock(); + if (local) return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1); - } rt = (struct rt6_info *) skb_dst(skb); tdev = rt->dst.dev; @@ -1185,7 +1165,6 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, ip6_local_out(cp->ipvs->net, skb->sk, skb); else if (ret == NF_DROP) kfree_skb(skb); - rcu_read_unlock(); LeaveFunction(10); @@ -1194,7 +1173,6 @@ ip_vs_tunnel_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, tx_error: if (!IS_ERR(skb)) kfree_skb(skb); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; } @@ -1213,17 +1191,14 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - rcu_read_lock(); local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_KNOWN_NH, NULL, ipvsh); if (local < 0) goto tx_error; - if (local) { - rcu_read_unlock(); + if (local) return ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 1); - } ip_send_check(ip_hdr(skb)); @@ -1231,14 +1206,12 @@ ip_vs_dr_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, skb->ignore_df = 1; ip_vs_send_or_cont(NFPROTO_IPV4, skb, cp, 0); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; tx_error: kfree_skb(skb); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; } @@ -1252,7 +1225,6 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, EnterFunction(10); - rcu_read_lock(); local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, &cp->daddr.in6, NULL, ipvsh, 0, @@ -1261,23 +1233,19 @@ ip_vs_dr_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, IP_VS_RT_MODE_KNOWN_NH); if (local < 0) goto tx_error; - if (local) { - rcu_read_unlock(); + if (local) return ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 1); - } /* Another hack: avoid icmp_send in ip_fragment */ skb->ignore_df = 1; ip_vs_send_or_cont(NFPROTO_IPV6, skb, cp, 0); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; tx_error: kfree_skb(skb); - rcu_read_unlock(); LeaveFunction(10); return NF_STOLEN; } @@ -1322,7 +1290,6 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, rt_mode = (hooknum != NF_INET_FORWARD) ? IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; - rcu_read_lock(); local = __ip_vs_get_out_rt(cp->ipvs, cp->af, skb, cp->dest, cp->daddr.ip, rt_mode, NULL, iph); if (local < 0) @@ -1368,12 +1335,10 @@ ip_vs_icmp_xmit(struct sk_buff *skb, struct ip_vs_conn *cp, skb->ignore_df = 1; rc = ip_vs_nat_send_or_cont(NFPROTO_IPV4, skb, cp, local); - rcu_read_unlock(); goto out; tx_error: kfree_skb(skb); - rcu_read_unlock(); rc = NF_STOLEN; out: LeaveFunction(10); @@ -1414,7 +1379,6 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, rt_mode = (hooknum != NF_INET_FORWARD) ? IP_VS_RT_MODE_LOCAL | IP_VS_RT_MODE_NON_LOCAL | IP_VS_RT_MODE_RDR : IP_VS_RT_MODE_NON_LOCAL; - rcu_read_lock(); local = __ip_vs_get_out_rt_v6(cp->ipvs, cp->af, skb, cp->dest, &cp->daddr.in6, NULL, ipvsh, 0, rt_mode); if (local < 0) @@ -1460,12 +1424,10 @@ ip_vs_icmp_xmit_v6(struct sk_buff *skb, struct ip_vs_conn *cp, skb->ignore_df = 1; rc = ip_vs_nat_send_or_cont(NFPROTO_IPV6, skb, cp, local); - rcu_read_unlock(); goto out; tx_error: kfree_skb(skb); - rcu_read_unlock(); rc = NF_STOLEN; out: LeaveFunction(10); diff --git a/net/netfilter/nf_conntrack_broadcast.c b/net/netfilter/nf_conntrack_broadcast.c index 4e99cca61612..ecc3ab784633 100644 --- a/net/netfilter/nf_conntrack_broadcast.c +++ b/net/netfilter/nf_conntrack_broadcast.c @@ -40,7 +40,6 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, if (CTINFO2DIR(ctinfo) != IP_CT_DIR_ORIGINAL) goto out; - rcu_read_lock(); in_dev = __in_dev_get_rcu(rt->dst.dev); if (in_dev != NULL) { for_primary_ifa(in_dev) { @@ -50,7 +49,6 @@ int nf_conntrack_broadcast_help(struct sk_buff *skb, } } endfor_ifa(in_dev); } - rcu_read_unlock(); if (mask == 0) goto out; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 9979f46c81dc..69746928cc0a 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -407,13 +407,10 @@ destroy_conntrack(struct nf_conntrack *nfct) nf_ct_tmpl_free(ct); return; } - rcu_read_lock(); l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); if (l4proto->destroy) l4proto->destroy(ct); - rcu_read_unlock(); - local_bh_disable(); /* Expectations will have been removed in clean_from_lists, * except TFTP can create an expectation on the first packet, diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 7999e70c3bfb..4dba71de4de7 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -539,13 +539,11 @@ static inline size_t ctnetlink_proto_size(const struct nf_conn *ct) struct nf_conntrack_l4proto *l4proto; size_t len = 0; - rcu_read_lock(); l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); len += l3proto->nla_size; l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); len += l4proto->nla_size; - rcu_read_unlock(); return len; } @@ -664,7 +662,6 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) nfmsg->version = NFNETLINK_V0; nfmsg->res_id = 0; - rcu_read_lock(); zone = nf_ct_zone(ct); nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED); @@ -736,8 +733,6 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) && ctnetlink_dump_mark(skb, ct) < 0) goto nla_put_failure; #endif - rcu_read_unlock(); - nlmsg_end(skb, nlh); err = nfnetlink_send(skb, net, item->portid, group, item->report, GFP_ATOMIC); @@ -747,7 +742,6 @@ ctnetlink_conntrack_event(unsigned int events, struct nf_ct_event *item) return 0; nla_put_failure: - rcu_read_unlock(); nlmsg_cancel(skb, nlh); nlmsg_failure: kfree_skb(skb); @@ -2213,7 +2207,6 @@ static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct) const struct nf_conntrack_zone *zone; struct nlattr *nest_parms; - rcu_read_lock(); zone = nf_ct_zone(ct); nest_parms = nla_nest_start(skb, CTA_TUPLE_ORIG | NLA_F_NESTED); @@ -2272,11 +2265,9 @@ static int __ctnetlink_glue_build(struct sk_buff *skb, struct nf_conn *ct) #endif if (ctnetlink_dump_labels(skb, ct) < 0) goto nla_put_failure; - rcu_read_unlock(); return 0; nla_put_failure: - rcu_read_unlock(); return -ENOSPC; } @@ -2661,17 +2652,14 @@ ctnetlink_expect_event(unsigned int events, struct nf_exp_event *item) nfmsg->version = NFNETLINK_V0; nfmsg->res_id = 0; - rcu_read_lock(); if (ctnetlink_exp_dump_expect(skb, exp) < 0) goto nla_put_failure; - rcu_read_unlock(); nlmsg_end(skb, nlh); nfnetlink_send(skb, net, item->portid, group, item->report, GFP_ATOMIC); return 0; nla_put_failure: - rcu_read_unlock(); nlmsg_cancel(skb, nlh); nlmsg_failure: kfree_skb(skb); diff --git a/net/netfilter/nf_conntrack_pptp.c b/net/netfilter/nf_conntrack_pptp.c index 6959e93063d4..11562f2a08bb 100644 --- a/net/netfilter/nf_conntrack_pptp.c +++ b/net/netfilter/nf_conntrack_pptp.c @@ -113,7 +113,6 @@ static void pptp_expectfn(struct nf_conn *ct, /* Can you see how rusty this code is, compared with the pre-2.6.11 * one? That's what happened to my shiny newnat of 2002 ;( -HW */ - rcu_read_lock(); nf_nat_pptp_expectfn = rcu_dereference(nf_nat_pptp_hook_expectfn); if (nf_nat_pptp_expectfn && ct->master->status & IPS_NAT_MASK) nf_nat_pptp_expectfn(ct, exp); @@ -136,7 +135,6 @@ static void pptp_expectfn(struct nf_conn *ct, pr_debug("not found\n"); } } - rcu_read_unlock(); } static int destroy_sibling_or_exp(struct net *net, struct nf_conn *ct, diff --git a/net/netfilter/nf_conntrack_sip.c b/net/netfilter/nf_conntrack_sip.c index d38af4274335..4dbb5bad4363 100644 --- a/net/netfilter/nf_conntrack_sip.c +++ b/net/netfilter/nf_conntrack_sip.c @@ -884,7 +884,6 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, tuple.dst.u3 = *daddr; tuple.dst.u.udp.port = port; - rcu_read_lock(); do { exp = __nf_ct_expect_find(net, nf_ct_zone(ct), &tuple); @@ -918,10 +917,8 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, goto err1; } - if (skip_expect) { - rcu_read_unlock(); + if (skip_expect) return NF_ACCEPT; - } rtp_exp = nf_ct_expect_alloc(ct); if (rtp_exp == NULL) @@ -952,7 +949,6 @@ static int set_expected_rtp_rtcp(struct sk_buff *skb, unsigned int protoff, err2: nf_ct_expect_put(rtp_exp); err1: - rcu_read_unlock(); return ret; } diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 400e9ae97153..7ce9e86d374c 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -505,7 +505,6 @@ ctnl_timeout_find_get(struct net *net, const char *name) { struct ctnl_timeout *timeout, *matching = NULL; - rcu_read_lock(); list_for_each_entry_rcu(timeout, &net->nfct_timeout_list, head) { if (strncmp(timeout->name, name, CTNL_TIMEOUT_NAME_MAX) != 0) continue; @@ -521,7 +520,6 @@ ctnl_timeout_find_get(struct net *net, const char *name) break; } err: - rcu_read_unlock(); return matching; } diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 16fa04086880..7c543bfbf624 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -928,7 +928,6 @@ static unsigned int nfqnl_nf_hook_drop(struct net *net) unsigned int instances = 0; int i; - rcu_read_lock(); for (i = 0; i < INSTANCE_BUCKETS; i++) { struct nfqnl_instance *inst; struct hlist_head *head = &q->instance_table[i]; @@ -938,7 +937,6 @@ static unsigned int nfqnl_nf_hook_drop(struct net *net) instances++; } } - rcu_read_unlock(); return instances; } diff --git a/net/netfilter/xt_TCPMSS.c b/net/netfilter/xt_TCPMSS.c index c64aca611ac5..9dae4d665965 100644 --- a/net/netfilter/xt_TCPMSS.c +++ b/net/netfilter/xt_TCPMSS.c @@ -62,11 +62,9 @@ static u_int32_t tcpmss_reverse_mtu(struct net *net, memset(fl6, 0, sizeof(*fl6)); fl6->daddr = ipv6_hdr(skb)->saddr; } - rcu_read_lock(); ai = nf_get_afinfo(family); if (ai != NULL) ai->route(net, (struct dst_entry **)&rt, &fl, false); - rcu_read_unlock(); if (rt != NULL) { mtu = dst_mtu(&rt->dst); diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index d767e35fff6b..2b74f37132fc 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -70,13 +70,11 @@ tproxy_laddr4(struct sk_buff *skb, __be32 user_laddr, __be32 daddr) return user_laddr; laddr = 0; - rcu_read_lock(); indev = __in_dev_get_rcu(skb->dev); for_primary_ifa(indev) { laddr = ifa->ifa_local; break; } endfor_ifa(indev); - rcu_read_unlock(); return laddr ? laddr : daddr; } @@ -391,7 +389,6 @@ tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr, return user_laddr; laddr = NULL; - rcu_read_lock(); indev = __in6_dev_get(skb->dev); if (indev) { read_lock_bh(&indev->lock); @@ -404,7 +401,6 @@ tproxy_laddr6(struct sk_buff *skb, const struct in6_addr *user_laddr, } read_unlock_bh(&indev->lock); } - rcu_read_unlock(); return laddr ? laddr : daddr; } diff --git a/net/netfilter/xt_addrtype.c b/net/netfilter/xt_addrtype.c index e329dabde35f..3b2be2ae6987 100644 --- a/net/netfilter/xt_addrtype.c +++ b/net/netfilter/xt_addrtype.c @@ -47,8 +47,6 @@ static u32 match_lookup_rt6(struct net *net, const struct net_device *dev, if (dev) flow.flowi6_oif = dev->ifindex; - rcu_read_lock(); - afinfo = nf_get_afinfo(NFPROTO_IPV6); if (afinfo != NULL) { const struct nf_ipv6_ops *v6ops; @@ -63,7 +61,6 @@ static u32 match_lookup_rt6(struct net *net, const struct net_device *dev, } else { route_err = 1; } - rcu_read_unlock(); if (route_err) return XT_ADDRTYPE_UNREACHABLE; diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index b8fd4ab762ed..97589b8a2a40 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -144,7 +144,6 @@ static unsigned int check_hlist(struct net *net, unsigned int length = 0; *addit = true; - rcu_read_lock(); /* check the saved connections */ hlist_for_each_entry_safe(conn, n, head, node) { @@ -179,8 +178,6 @@ static unsigned int check_hlist(struct net *net, length++; } - rcu_read_unlock(); - return length; } diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index 762e1874f28b..ffdb611e54a2 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -659,12 +659,12 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par, if (hashlimit_init_dst(hinfo, &dst, skb, par->thoff) < 0) goto hotdrop; - rcu_read_lock_bh(); + local_bh_disable(); dh = dsthash_find(hinfo, &dst); if (dh == NULL) { dh = dsthash_alloc_init(hinfo, &dst, &race); if (dh == NULL) { - rcu_read_unlock_bh(); + local_bh_enable(); goto hotdrop; } else if (race) { /* Already got an entry, update expiration timeout */ @@ -689,12 +689,12 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par, /* below the limit */ dh->rateinfo.credit -= cost; spin_unlock(&dh->lock); - rcu_read_unlock_bh(); + local_bh_enable(); return !(cfg->mode & XT_HASHLIMIT_INVERT); } spin_unlock(&dh->lock); - rcu_read_unlock_bh(); + local_bh_enable(); /* default match is underlimit - so over the limit, we need to invert */ return cfg->mode & XT_HASHLIMIT_INVERT; diff --git a/net/netfilter/xt_osf.c b/net/netfilter/xt_osf.c index 71cfa9551d08..36e14b1f061d 100644 --- a/net/netfilter/xt_osf.c +++ b/net/netfilter/xt_osf.c @@ -226,7 +226,6 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) sizeof(struct tcphdr), optsize, opts); } - rcu_read_lock(); list_for_each_entry_rcu(kf, &xt_osf_fingers[df], finger_entry) { int foptsize, optnum; @@ -340,7 +339,6 @@ xt_osf_match_packet(const struct sk_buff *skb, struct xt_action_param *p) info->loglevel == XT_OSF_LOGLEVEL_FIRST) break; } - rcu_read_unlock(); if (!fcount && (info->flags & XT_OSF_LOG)) nf_log_packet(net, xt_family(p), xt_hooknum(p), skb, xt_in(p), -- cgit v1.2.3 From 784b4e612d42a2b7578d7fab2ed78940e10536bc Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Wed, 19 Jul 2017 16:32:23 +0200 Subject: netfilter: nf_tables: Attach process info to NFT_MSG_NEWGEN notifications This is helpful for 'nft monitor' to track which process caused a given change to the ruleset. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 2 ++ net/netfilter/nf_tables_api.c | 5 ++++- 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 683f6f88fcac..6f0a950e21c3 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1221,6 +1221,8 @@ enum nft_objref_attributes { enum nft_gen_attributes { NFTA_GEN_UNSPEC, NFTA_GEN_ID, + NFTA_GEN_PROC_PID, + NFTA_GEN_PROC_NAME, __NFTA_GEN_MAX }; #define NFTA_GEN_MAX (__NFTA_GEN_MAX - 1) diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 7fbf0070aba1..b77ad0813564 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4657,6 +4657,7 @@ static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; + char buf[TASK_COMM_LEN]; int event = nfnl_msg_type(NFNL_SUBSYS_NFTABLES, NFT_MSG_NEWGEN); nlh = nlmsg_put(skb, portid, seq, event, sizeof(struct nfgenmsg), 0); @@ -4668,7 +4669,9 @@ static int nf_tables_fill_gen_info(struct sk_buff *skb, struct net *net, nfmsg->version = NFNETLINK_V0; nfmsg->res_id = htons(net->nft.base_seq & 0xffff); - if (nla_put_be32(skb, NFTA_GEN_ID, htonl(net->nft.base_seq))) + if (nla_put_be32(skb, NFTA_GEN_ID, htonl(net->nft.base_seq)) || + nla_put_be32(skb, NFTA_GEN_PROC_PID, htonl(task_pid_nr(current))) || + nla_put_string(skb, NFTA_GEN_PROC_NAME, get_task_comm(buf, current))) goto nla_put_failure; nlmsg_end(skb, nlh); -- cgit v1.2.3 From d764a122cc7af7ab1c40c08745f0fcd33cc2f7db Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 21 Jul 2017 12:49:28 +0200 Subject: net: add new netdevice feature for offload of RX port for UDP tunnels This adds a new netdevice feature, so that the offloading of RX port for UDP tunnels can be disabled by the administrator on some netdevices, using the "rx-udp_tunnel-port-offload" feature in ethtool. This feature is set for all devices that provide ndo_udp_tunnel_add. Signed-off-by: Sabrina Dubroca Signed-off-by: David S. Miller --- include/linux/netdev_features.h | 2 ++ net/core/dev.c | 6 ++++++ net/core/ethtool.c | 1 + 3 files changed, 9 insertions(+) (limited to 'net') diff --git a/include/linux/netdev_features.h b/include/linux/netdev_features.h index ebd273627334..dc8b4896b77b 100644 --- a/include/linux/netdev_features.h +++ b/include/linux/netdev_features.h @@ -75,6 +75,7 @@ enum { NETIF_F_HW_TC_BIT, /* Offload TC infrastructure */ NETIF_F_HW_ESP_BIT, /* Hardware ESP transformation offload */ NETIF_F_HW_ESP_TX_CSUM_BIT, /* ESP with TX checksum offload */ + NETIF_F_RX_UDP_TUNNEL_PORT_BIT, /* Offload of RX port for UDP tunnels */ /* * Add your fresh new feature above and remember to update @@ -138,6 +139,7 @@ enum { #define NETIF_F_HW_TC __NETIF_F(HW_TC) #define NETIF_F_HW_ESP __NETIF_F(HW_ESP) #define NETIF_F_HW_ESP_TX_CSUM __NETIF_F(HW_ESP_TX_CSUM) +#define NETIF_F_RX_UDP_TUNNEL_PORT __NETIF_F(RX_UDP_TUNNEL_PORT) #define for_each_netdev_feature(mask_addr, bit) \ for_each_set_bit(bit, (unsigned long *)mask_addr, NETDEV_FEATURE_COUNT) diff --git a/net/core/dev.c b/net/core/dev.c index 509af6ce8831..9081134adc0d 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -7530,6 +7530,12 @@ int register_netdevice(struct net_device *dev) */ dev->hw_features |= NETIF_F_SOFT_FEATURES; dev->features |= NETIF_F_SOFT_FEATURES; + + if (dev->netdev_ops->ndo_udp_tunnel_add) { + dev->features |= NETIF_F_RX_UDP_TUNNEL_PORT; + dev->hw_features |= NETIF_F_RX_UDP_TUNNEL_PORT; + } + dev->wanted_features = dev->features & dev->hw_features; if (!(dev->flags & IFF_LOOPBACK)) diff --git a/net/core/ethtool.c b/net/core/ethtool.c index 78408ab77a10..b987bc475fc8 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -105,6 +105,7 @@ static const char netdev_features_strings[NETDEV_FEATURE_COUNT][ETH_GSTRING_LEN] [NETIF_F_HW_TC_BIT] = "hw-tc-offload", [NETIF_F_HW_ESP_BIT] = "esp-hw-offload", [NETIF_F_HW_ESP_TX_CSUM_BIT] = "esp-tx-csum-hw-offload", + [NETIF_F_RX_UDP_TUNNEL_PORT_BIT] = "rx-udp_tunnel-port-offload", }; static const char -- cgit v1.2.3 From 7a27fc6d536b36a29c0ed4bfff7035420f4df216 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 21 Jul 2017 12:49:29 +0200 Subject: net: check UDP tunnel RX port offload feature before calling tunnel ndo ndo If NETIF_F_RX_UDP_TUNNEL_PORT was disabled on a given netdevice, skip the tunnel offload ndo call during tunnel port creation and deletion. Signed-off-by: Sabrina Dubroca Signed-off-by: David S. Miller --- net/ipv4/udp_tunnel.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c index 58bd39fb14b4..0d3f14cdc524 100644 --- a/net/ipv4/udp_tunnel.c +++ b/net/ipv4/udp_tunnel.c @@ -82,7 +82,8 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, struct sock *sk = sock->sk; struct udp_tunnel_info ti; - if (!dev->netdev_ops->ndo_udp_tunnel_add) + if (!dev->netdev_ops->ndo_udp_tunnel_add || + !(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT)) return; ti.type = type; @@ -109,6 +110,8 @@ void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type) for_each_netdev_rcu(net, dev) { if (!dev->netdev_ops->ndo_udp_tunnel_add) continue; + if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT)) + continue; dev->netdev_ops->ndo_udp_tunnel_add(dev, &ti); } rcu_read_unlock(); @@ -131,6 +134,8 @@ void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type) for_each_netdev_rcu(net, dev) { if (!dev->netdev_ops->ndo_udp_tunnel_del) continue; + if (!(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT)) + continue; dev->netdev_ops->ndo_udp_tunnel_del(dev, &ti); } rcu_read_unlock(); -- cgit v1.2.3 From 296d8ee37c50f139d934bdefbab85509b2e4a525 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 21 Jul 2017 12:49:30 +0200 Subject: net: add infrastructure to un-offload UDP tunnel port This adds a new NETDEV_UDP_TUNNEL_DROP_INFO event, similar to NETDEV_UDP_TUNNEL_PUSH_INFO, to signal to un-offload ports. This also adds udp_tunnel_drop_rx_port(), which calls ndo_udp_tunnel_del. Signed-off-by: Sabrina Dubroca Signed-off-by: David S. Miller --- include/linux/netdevice.h | 1 + include/net/udp_tunnel.h | 8 ++++++++ net/ipv4/udp_tunnel.c | 18 ++++++++++++++++++ 3 files changed, 27 insertions(+) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 614642eb7eb7..3a3cdc1b1f31 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -2317,6 +2317,7 @@ struct netdev_lag_lower_state_info { #define NETDEV_PRECHANGEUPPER 0x001A #define NETDEV_CHANGELOWERSTATE 0x001B #define NETDEV_UDP_TUNNEL_PUSH_INFO 0x001C +#define NETDEV_UDP_TUNNEL_DROP_INFO 0x001D #define NETDEV_CHANGE_TX_QUEUE_LEN 0x001E int register_netdevice_notifier(struct notifier_block *nb); diff --git a/include/net/udp_tunnel.h b/include/net/udp_tunnel.h index 02c5be037451..10cce0dd4450 100644 --- a/include/net/udp_tunnel.h +++ b/include/net/udp_tunnel.h @@ -115,6 +115,8 @@ struct udp_tunnel_info { /* Notify network devices of offloadable types */ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, unsigned short type); +void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock, + unsigned short type); void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type); void udp_tunnel_notify_del_rx_port(struct socket *sock, unsigned short type); @@ -124,6 +126,12 @@ static inline void udp_tunnel_get_rx_info(struct net_device *dev) call_netdevice_notifiers(NETDEV_UDP_TUNNEL_PUSH_INFO, dev); } +static inline void udp_tunnel_drop_rx_info(struct net_device *dev) +{ + ASSERT_RTNL(); + call_netdevice_notifiers(NETDEV_UDP_TUNNEL_DROP_INFO, dev); +} + /* Transmit the skb using UDP encapsulation. */ void udp_tunnel_xmit_skb(struct rtable *rt, struct sock *sk, struct sk_buff *skb, __be32 src, __be32 dst, __u8 tos, __u8 ttl, diff --git a/net/ipv4/udp_tunnel.c b/net/ipv4/udp_tunnel.c index 0d3f14cdc524..6539ff15e9a3 100644 --- a/net/ipv4/udp_tunnel.c +++ b/net/ipv4/udp_tunnel.c @@ -94,6 +94,24 @@ void udp_tunnel_push_rx_port(struct net_device *dev, struct socket *sock, } EXPORT_SYMBOL_GPL(udp_tunnel_push_rx_port); +void udp_tunnel_drop_rx_port(struct net_device *dev, struct socket *sock, + unsigned short type) +{ + struct sock *sk = sock->sk; + struct udp_tunnel_info ti; + + if (!dev->netdev_ops->ndo_udp_tunnel_del || + !(dev->features & NETIF_F_RX_UDP_TUNNEL_PORT)) + return; + + ti.type = type; + ti.sa_family = sk->sk_family; + ti.port = inet_sk(sk)->inet_sport; + + dev->netdev_ops->ndo_udp_tunnel_del(dev, &ti); +} +EXPORT_SYMBOL_GPL(udp_tunnel_drop_rx_port); + /* Notify netdevs that UDP port started listening */ void udp_tunnel_notify_add_rx_port(struct socket *sock, unsigned short type) { -- cgit v1.2.3 From ae847f40b6418a7d6e197f6ef0d85f40e313c4d4 Mon Sep 17 00:00:00 2001 From: Sabrina Dubroca Date: Fri, 21 Jul 2017 12:49:31 +0200 Subject: net: call udp_tunnel_get_rx_info when NETIF_F_RX_UDP_TUNNEL_PORT is toggled NETIF_F_RX_UDP_TUNNEL_PORT is special, in that we need to do more than just flip the bit in dev->features. When disabling we must also clear currently offloaded ports from the device, and when enabling we must tell the device to offload the ports it can. Because vxlan stores its sockets in a hashtable and they are inserted at the head of per-bucket lists, switching the feature off and then on can result in a different set of ports being offloaded (depending on the HW's limits). Signed-off-by: Sabrina Dubroca Signed-off-by: David S. Miller --- net/core/dev.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 9081134adc0d..8ea6b4b42611 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -144,6 +144,7 @@ #include #include #include +#include #include "net-sysfs.h" @@ -7327,8 +7328,27 @@ sync_lower: netdev_for_each_lower_dev(dev, lower, iter) netdev_sync_lower_features(dev, lower, features); - if (!err) + if (!err) { + netdev_features_t diff = features ^ dev->features; + + if (diff & NETIF_F_RX_UDP_TUNNEL_PORT) { + /* udp_tunnel_{get,drop}_rx_info both need + * NETIF_F_RX_UDP_TUNNEL_PORT enabled on the + * device, or they won't do anything. + * Thus we need to update dev->features + * *before* calling udp_tunnel_get_rx_info, + * but *after* calling udp_tunnel_drop_rx_info. + */ + if (features & NETIF_F_RX_UDP_TUNNEL_PORT) { + dev->features = features; + udp_tunnel_get_rx_info(dev); + } else { + udp_tunnel_drop_rx_info(dev); + } + } + dev->features = features; + } return err < 0 ? 0 : 1; } -- cgit v1.2.3 From cb1844c47279fb59129f8a021a0b09bcf2011ad7 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 23 Jul 2017 09:34:26 +0800 Subject: sctp: remove the typedef sctp_initack_chunk_t This patch is to remove the typedef sctp_initack_chunk_t, and replace with struct sctp_initack_chunk in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 5 ++++- net/sctp/sm_statefuns.c | 13 ++++++------- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 913474dfc96c..05c2099be537 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -336,7 +336,10 @@ struct sctp_hmac_algo_param { * The INIT ACK chunk is used to acknowledge the initiation of an SCTP * association. */ -typedef struct sctp_init_chunk sctp_initack_chunk_t; +struct sctp_initack_chunk { + struct sctp_chunkhdr chunk_hdr; + struct sctp_inithdr init_hdr; +}; /* Section 3.3.3.1 State Cookie (7) */ typedef struct sctp_cookie_param { diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index ae4c48c4f657..6568fc395901 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -518,7 +518,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, return sctp_sf_violation_chunk(net, ep, asoc, type, arg, commands); /* Make sure that the INIT-ACK chunk has a valid length */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_initack_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_initack_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); /* Grab the INIT header. */ @@ -4453,11 +4453,10 @@ static sctp_disposition_t sctp_sf_abort_violation( /* Treat INIT-ACK as a special case during COOKIE-WAIT. */ if (chunk->chunk_hdr->type == SCTP_CID_INIT_ACK && !asoc->peer.i.init_tag) { - sctp_initack_chunk_t *initack; + struct sctp_initack_chunk *initack; - initack = (sctp_initack_chunk_t *)chunk->chunk_hdr; - if (!sctp_chunk_length_valid(chunk, - sizeof(sctp_initack_chunk_t))) + initack = (struct sctp_initack_chunk *)chunk->chunk_hdr; + if (!sctp_chunk_length_valid(chunk, sizeof(*initack))) abort->chunk_hdr->flags |= SCTP_CHUNK_FLAG_T; else { unsigned int inittag; @@ -6106,9 +6105,9 @@ static struct sctp_packet *sctp_ootb_pkt_new(struct net *net, switch (chunk->chunk_hdr->type) { case SCTP_CID_INIT_ACK: { - sctp_initack_chunk_t *initack; + struct sctp_initack_chunk *initack; - initack = (sctp_initack_chunk_t *)chunk->chunk_hdr; + initack = (struct sctp_initack_chunk *)chunk->chunk_hdr; vtag = ntohl(initack->init_hdr.init_tag); break; } -- cgit v1.2.3 From f48ef4c7f7979e8e658b7e038a82f096ab292d70 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 23 Jul 2017 09:34:27 +0800 Subject: sctp: remove the typedef sctp_cookie_param_t This patch is to remove the typedef sctp_cookie_param_t, and replace with struct sctp_cookie_param in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_make_chunk.c | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 05c2099be537..9e77abda2111 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -342,10 +342,10 @@ struct sctp_initack_chunk { }; /* Section 3.3.3.1 State Cookie (7) */ -typedef struct sctp_cookie_param { +struct sctp_cookie_param { struct sctp_paramhdr p; __u8 body[0]; -} sctp_cookie_param_t; +}; /* Section 3.3.3.1 Unrecognized Parameters (8) */ typedef struct sctp_unrecognized_param { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 0b36e96cb0df..163004e7047c 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -69,7 +69,8 @@ static struct sctp_chunk *sctp_make_data(const struct sctp_association *asoc, static struct sctp_chunk *_sctp_make_chunk(const struct sctp_association *asoc, __u8 type, __u8 flags, int paylen, gfp_t gfp); -static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, +static struct sctp_cookie_param *sctp_pack_cookie( + const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *init_chunk, int *cookie_len, @@ -387,7 +388,7 @@ struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, union sctp_params addrs; struct sctp_sock *sp; int addrs_len; - sctp_cookie_param_t *cookie; + struct sctp_cookie_param *cookie; int cookie_len; size_t chunksize; struct sctp_adaptation_ind_param aiparam; @@ -1595,14 +1596,15 @@ nodata: /* Build a cookie representing asoc. * This INCLUDES the param header needed to put the cookie in the INIT ACK. */ -static sctp_cookie_param_t *sctp_pack_cookie(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const struct sctp_chunk *init_chunk, - int *cookie_len, - const __u8 *raw_addrs, int addrs_len) +static struct sctp_cookie_param *sctp_pack_cookie( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const struct sctp_chunk *init_chunk, + int *cookie_len, + const __u8 *raw_addrs, int addrs_len) { - sctp_cookie_param_t *retval; struct sctp_signed_cookie *cookie; + struct sctp_cookie_param *retval; int headersize, bodysize; /* Header size is static data prior to the actual cookie, including -- cgit v1.2.3 From 62e6b7e4ee244f8043c169c049d3b2c2c798cd60 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 23 Jul 2017 09:34:28 +0800 Subject: sctp: remove the typedef sctp_unrecognized_param_t This patch is to remove the typedef sctp_unrecognized_param_t, and replace with struct sctp_unrecognized_param in the places where it's using this typedef. It is also to fix some indents in sctp_sf_do_unexpected_init() and sctp_sf_do_5_1B_init(). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_statefuns.c | 18 +++++++----------- 2 files changed, 9 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 9e77abda2111..c323b3e3ecdb 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -348,10 +348,10 @@ struct sctp_cookie_param { }; /* Section 3.3.3.1 Unrecognized Parameters (8) */ -typedef struct sctp_unrecognized_param { +struct sctp_unrecognized_param { struct sctp_paramhdr param_hdr; struct sctp_paramhdr unrecognized; -} sctp_unrecognized_param_t; +}; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 6568fc395901..7f852392f56a 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -306,12 +306,10 @@ sctp_disposition_t sctp_sf_do_5_1B_init(struct net *net, void *arg, sctp_cmd_seq_t *commands) { - struct sctp_chunk *chunk = arg; - struct sctp_chunk *repl; + struct sctp_chunk *chunk = arg, *repl, *err_chunk; + struct sctp_unrecognized_param *unk_param; struct sctp_association *new_asoc; - struct sctp_chunk *err_chunk; struct sctp_packet *packet; - sctp_unrecognized_param_t *unk_param; int len; /* 6.10 Bundling @@ -435,7 +433,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(struct net *net, * construct the parameters in INIT ACK by copying the * ERROR causes over. */ - unk_param = (sctp_unrecognized_param_t *) + unk_param = (struct sctp_unrecognized_param *) ((__u8 *)(err_chunk->chunk_hdr) + sizeof(struct sctp_chunkhdr)); /* Replace the cause code with the "Unrecognized parameter" @@ -1419,13 +1417,11 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( const sctp_subtype_t type, void *arg, sctp_cmd_seq_t *commands) { - sctp_disposition_t retval; - struct sctp_chunk *chunk = arg; - struct sctp_chunk *repl; + struct sctp_chunk *chunk = arg, *repl, *err_chunk; + struct sctp_unrecognized_param *unk_param; struct sctp_association *new_asoc; - struct sctp_chunk *err_chunk; struct sctp_packet *packet; - sctp_unrecognized_param_t *unk_param; + sctp_disposition_t retval; int len; /* 6.10 Bundling @@ -1555,7 +1551,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( * construct the parameters in INIT ACK by copying the * ERROR causes over. */ - unk_param = (sctp_unrecognized_param_t *) + unk_param = (struct sctp_unrecognized_param *) ((__u8 *)(err_chunk->chunk_hdr) + sizeof(struct sctp_chunkhdr)); /* Replace the cause code with the "Unrecognized parameter" -- cgit v1.2.3 From afd93b7be6e24731d82d9fd84b8a5ea73a68214b Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 23 Jul 2017 09:34:31 +0800 Subject: sctp: remove the typedef sctp_sack_variable_t This patch is to remove the typedef sctp_sack_variable_t, and replace with union sctp_sack_variable in the places where it's using this typedef. It is also to fix some indents in sctp_acked(). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- net/sctp/outqueue.c | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 8faf74eff63d..8df6ac53f05b 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -368,17 +368,17 @@ struct sctp_gap_ack_block { __be16 end; }; -typedef union { +union sctp_sack_variable { struct sctp_gap_ack_block gab; __be32 dup; -} sctp_sack_variable_t; +}; typedef struct sctp_sackhdr { __be32 cum_tsn_ack; __be32 a_rwnd; __be16 num_gap_ack_blocks; __be16 num_dup_tsns; - sctp_sack_variable_t variable[0]; + union sctp_sack_variable variable[0]; } sctp_sackhdr_t; typedef struct sctp_sack_chunk { diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index e8762702a313..d2a8adfd4a6f 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -1197,7 +1197,7 @@ sctp_flush_out: static void sctp_sack_update_unack_data(struct sctp_association *assoc, struct sctp_sackhdr *sack) { - sctp_sack_variable_t *frags; + union sctp_sack_variable *frags; __u16 unack_data; int i; @@ -1224,7 +1224,7 @@ int sctp_outq_sack(struct sctp_outq *q, struct sctp_chunk *chunk) struct sctp_transport *transport; struct sctp_chunk *tchunk = NULL; struct list_head *lchunk, *transport_list, *temp; - sctp_sack_variable_t *frags = sack->variable; + union sctp_sack_variable *frags = sack->variable; __u32 sack_ctsn, ctsn, tsn; __u32 highest_tsn, highest_new_tsn; __u32 sack_a_rwnd; @@ -1736,10 +1736,10 @@ static void sctp_mark_missing(struct sctp_outq *q, /* Is the given TSN acked by this packet? */ static int sctp_acked(struct sctp_sackhdr *sack, __u32 tsn) { - int i; - sctp_sack_variable_t *frags; - __u16 tsn_offset, blocks; __u32 ctsn = ntohl(sack->cum_tsn_ack); + union sctp_sack_variable *frags; + __u16 tsn_offset, blocks; + int i; if (TSN_lte(tsn, ctsn)) goto pass; -- cgit v1.2.3 From 787310859d8d1a72545db2343fb3ac8f765b0f35 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 23 Jul 2017 09:34:32 +0800 Subject: sctp: remove the typedef sctp_sackhdr_t This patch is to remove the typedef sctp_sackhdr_t, and replace with struct sctp_sackhdr in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- include/net/sctp/command.h | 4 ++-- net/sctp/sm_statefuns.c | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 8df6ac53f05b..a2e43129d11a 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -373,17 +373,17 @@ union sctp_sack_variable { __be32 dup; }; -typedef struct sctp_sackhdr { +struct sctp_sackhdr { __be32 cum_tsn_ack; __be32 a_rwnd; __be16 num_gap_ack_blocks; __be16 num_dup_tsns; union sctp_sack_variable variable[0]; -} sctp_sackhdr_t; +}; typedef struct sctp_sack_chunk { struct sctp_chunkhdr chunk_hdr; - sctp_sackhdr_t sack_hdr; + struct sctp_sackhdr sack_hdr; } sctp_sack_chunk_t; diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index d4679e7a5ed5..1d5f6ff3f440 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -135,7 +135,7 @@ typedef union { struct sctp_init_chunk *init; struct sctp_ulpevent *ulpevent; struct sctp_packet *packet; - sctp_sackhdr_t *sackh; + struct sctp_sackhdr *sackh; struct sctp_datamsg *msg; } sctp_arg_t; @@ -176,7 +176,7 @@ SCTP_ARG_CONSTRUCTOR(BA, struct sctp_bind_addr *, bp) SCTP_ARG_CONSTRUCTOR(PEER_INIT, struct sctp_init_chunk *, init) SCTP_ARG_CONSTRUCTOR(ULPEVENT, struct sctp_ulpevent *, ulpevent) SCTP_ARG_CONSTRUCTOR(PACKET, struct sctp_packet *, packet) -SCTP_ARG_CONSTRUCTOR(SACKH, sctp_sackhdr_t *, sackh) +SCTP_ARG_CONSTRUCTOR(SACKH, struct sctp_sackhdr *, sackh) SCTP_ARG_CONSTRUCTOR(DATAMSG, struct sctp_datamsg *, msg) static inline sctp_arg_t SCTP_FORCE(void) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 7f852392f56a..c09dfe6ebac2 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3187,7 +3187,7 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; - sctp_sackhdr_t *sackh; + struct sctp_sackhdr *sackh; __u32 ctsn; if (!sctp_vtag_verify(chunk, asoc)) -- cgit v1.2.3 From d4d6c61489e7e4a8944360312e572988889558a8 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 23 Jul 2017 09:34:33 +0800 Subject: sctp: remove the typedef sctp_sack_chunk_t This patch is to remove the typedef sctp_sack_chunk_t, and replace with struct sctp_sack_chunk in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/chunk.c | 2 +- net/sctp/sm_statefuns.c | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index a2e43129d11a..48f6560dd880 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -381,10 +381,10 @@ struct sctp_sackhdr { union sctp_sack_variable variable[0]; }; -typedef struct sctp_sack_chunk { +struct sctp_sack_chunk { struct sctp_chunkhdr chunk_hdr; struct sctp_sackhdr sack_hdr; -} sctp_sack_chunk_t; +}; /* RFC 2960. Section 3.3.5 Heartbeat Request (HEARTBEAT) (4): diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 1323d41e68b8..681b181e7ae3 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -221,7 +221,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, asoc->outqueue.out_qlen == 0 && list_empty(&asoc->outqueue.retransmit) && msg_len > max_data) - first_len -= SCTP_PAD4(sizeof(sctp_sack_chunk_t)); + first_len -= SCTP_PAD4(sizeof(struct sctp_sack_chunk)); /* Encourage Cookie-ECHO bundling. */ if (asoc->state < SCTP_STATE_COOKIE_ECHOED) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index c09dfe6ebac2..08ebe8cd96c7 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3194,7 +3194,7 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net, return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the SACK chunk has a valid length. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_sack_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_sack_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); @@ -4515,7 +4515,7 @@ nomem: * Handle a protocol violation when the chunk length is invalid. * "Invalid" length is identified as smaller than the minimal length a * given chunk can be. For example, a SACK chunk has invalid length - * if its length is set to be smaller than the size of sctp_sack_chunk_t. + * if its length is set to be smaller than the size of struct sctp_sack_chunk. * * We inform the other end by sending an ABORT with a Protocol Violation * error code. -- cgit v1.2.3 From 4d2dcdf4e04c938b266f06f271000e4b0f3a288f Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 23 Jul 2017 09:34:34 +0800 Subject: sctp: remove the typedef sctp_heartbeathdr_t This patch is to remove the typedef sctp_heartbeathdr_t, and replace with struct sctp_heartbeathdr in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- net/sctp/sm_statefuns.c | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 48f6560dd880..6e26b86770f1 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -394,13 +394,13 @@ struct sctp_sack_chunk { * the present association. */ -typedef struct sctp_heartbeathdr { +struct sctp_heartbeathdr { struct sctp_paramhdr info; -} sctp_heartbeathdr_t; +}; typedef struct sctp_heartbeat_chunk { struct sctp_chunkhdr chunk_hdr; - sctp_heartbeathdr_t hb_hdr; + struct sctp_heartbeathdr hb_hdr; } sctp_heartbeat_chunk_t; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 08ebe8cd96c7..32ac90b22654 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1096,7 +1096,7 @@ sctp_disposition_t sctp_sf_beat_8_3(struct net *net, * respond with a HEARTBEAT ACK that contains the Heartbeat * Information field copied from the received HEARTBEAT chunk. */ - chunk->subh.hb_hdr = (sctp_heartbeathdr_t *)chunk->skb->data; + chunk->subh.hb_hdr = (struct sctp_heartbeathdr *)chunk->skb->data; param_hdr = (struct sctp_paramhdr *)chunk->subh.hb_hdr; paylen = ntohs(chunk->chunk_hdr->length) - sizeof(struct sctp_chunkhdr); -- cgit v1.2.3 From 38c00f7482281801d6f7fe410c7a4b61ae25218e Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 23 Jul 2017 09:34:35 +0800 Subject: sctp: remove the typedef sctp_heartbeat_chunk_t This patch is to remove the typedef sctp_heartbeat_chunk_t, and replace with struct sctp_heartbeat_chunk in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_statefuns.c | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 6e26b86770f1..bfda7c66960c 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -398,10 +398,10 @@ struct sctp_heartbeathdr { struct sctp_paramhdr info; }; -typedef struct sctp_heartbeat_chunk { +struct sctp_heartbeat_chunk { struct sctp_chunkhdr chunk_hdr; struct sctp_heartbeathdr hb_hdr; -} sctp_heartbeat_chunk_t; +}; /* For the abort and shutdown ACK we must carry the init tag in the diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 32ac90b22654..7bbee085b476 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1088,7 +1088,8 @@ sctp_disposition_t sctp_sf_beat_8_3(struct net *net, return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the HEARTBEAT chunk has a valid length. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_heartbeat_chunk_t))) + if (!sctp_chunk_length_valid(chunk, + sizeof(struct sctp_heartbeat_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); -- cgit v1.2.3 From 441ae65ae000dd325a609306bd0cd850df50cfc4 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sun, 23 Jul 2017 09:34:36 +0800 Subject: sctp: remove the typedef sctp_abort_chunk_t This patch is to remove the typedef sctp_abort_chunk_t, and replace with struct sctp_abort_chunk in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_statefuns.c | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index bfda7c66960c..ba007163acfd 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -408,9 +408,9 @@ struct sctp_heartbeat_chunk { * common header. Just the common header is all that is needed with a * chunk descriptor. */ -typedef struct sctp_abort_chunk { +struct sctp_abort_chunk { struct sctp_chunkhdr uh; -} sctp_abort_chunk_t; +}; /* For the graceful shutdown we must carry the tag (in common header) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 7bbee085b476..dc0c2c4188d8 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2164,7 +2164,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( * as we do not know its true length. So, to be safe, discard the * packet. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_abort_chunk))) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* ADD-IP: Special case for ABORT chunks @@ -2206,7 +2206,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(struct net *net, * as we do not know its true length. So, to be safe, discard the * packet. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_abort_chunk))) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* ADD-IP: Special case for ABORT chunks @@ -2470,7 +2470,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(struct net *net, * as we do not know its true length. So, to be safe, discard the * packet. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_abort_chunk))) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* ADD-IP: Special case for ABORT chunks @@ -2546,7 +2546,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(struct net *net, * as we do not know its true length. So, to be safe, discard the * packet. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_abort_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_abort_chunk))) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* See if we have an error cause code in the chunk. */ -- cgit v1.2.3 From a28b1b90de8322ecc45d58f1c08da12197dad17f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 23 Jul 2017 19:54:47 +0200 Subject: skbuff: re-add check for NULL skb->head in kfree_skb path A null check is needed after all. netlink skbs can have skb->head be backed by vmalloc. The netlink destructor vfree()s head, then sets it to NULL. We then panic in skb_release_data with a NULL dereference. Re-add such a test. Alternative would be to switch to kvfree to free skb->head memory and remove the special handling in netlink destructor. Reported-by: kernel test robot Fixes: 06dc75ab06943 ("net: Revert "net: add function to allocate sk_buff head without data area") Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/core/skbuff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 84bdfa229b0d..c27da51d14e4 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -638,7 +638,8 @@ void skb_release_head_state(struct sk_buff *skb) static void skb_release_all(struct sk_buff *skb) { skb_release_head_state(skb); - skb_release_data(skb); + if (likely(skb->head)) + skb_release_data(skb); } /** -- cgit v1.2.3 From e42e24c3cc072088756d84ef07b492ac2a3ae2e5 Mon Sep 17 00:00:00 2001 From: Matvejchikov Ilya Date: Mon, 24 Jul 2017 16:02:12 +0400 Subject: tcp: remove redundant argument from tcp_rcv_established() The last (4th) argument of tcp_rcv_established() is redundant as it always equals to skb->len and the skb itself is always passed as 2th agrument. There is no reason to have it. Signed-off-by: Ilya V. Matveychikov Signed-off-by: David S. Miller --- include/net/tcp.h | 2 +- net/ipv4/tcp_input.c | 3 ++- net/ipv4/tcp_ipv4.c | 2 +- net/ipv4/tcp_probe.c | 5 +++-- net/ipv6/tcp_ipv6.c | 2 +- 5 files changed, 8 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/net/tcp.h b/include/net/tcp.h index 4f056ea79df2..12d68335acd4 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -362,7 +362,7 @@ void tcp_delack_timer_handler(struct sock *sk); int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg); int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb); void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, - const struct tcphdr *th, unsigned int len); + const struct tcphdr *th); void tcp_rcv_space_adjust(struct sock *sk); int tcp_twsk_unique(struct sock *sk, struct sock *sktw, void *twp); void tcp_twsk_destructor(struct sock *sk); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 2920e0cb09f8..adc3f3e9468c 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5358,8 +5358,9 @@ discard: * tcp_data_queue when everything is OK. */ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, - const struct tcphdr *th, unsigned int len) + const struct tcphdr *th) { + unsigned int len = skb->len; struct tcp_sock *tp = tcp_sk(sk); tcp_mstamp_refresh(tp); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a20e7f03d5f7..3a19ea28339f 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1458,7 +1458,7 @@ int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb) sk->sk_rx_dst = NULL; } } - tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len); + tcp_rcv_established(sk, skb, tcp_hdr(skb)); return 0; } diff --git a/net/ipv4/tcp_probe.c b/net/ipv4/tcp_probe.c index f6c50af24a64..697f4c67b2e3 100644 --- a/net/ipv4/tcp_probe.c +++ b/net/ipv4/tcp_probe.c @@ -105,8 +105,9 @@ static inline int tcp_probe_avail(void) * Note: arguments must match tcp_rcv_established()! */ static void jtcp_rcv_established(struct sock *sk, struct sk_buff *skb, - const struct tcphdr *th, unsigned int len) + const struct tcphdr *th) { + unsigned int len = skb->len; const struct tcp_sock *tp = tcp_sk(sk); const struct inet_sock *inet = inet_sk(sk); @@ -145,7 +146,7 @@ static void jtcp_rcv_established(struct sock *sk, struct sk_buff *skb, BUG(); } - p->length = skb->len; + p->length = len; p->snd_nxt = tp->snd_nxt; p->snd_una = tp->snd_una; p->snd_cwnd = tp->snd_cwnd; diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2521690d62d6..90a32576c3d0 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1296,7 +1296,7 @@ static int tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) } } - tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len); + tcp_rcv_established(sk, skb, tcp_hdr(skb)); if (opt_skb) goto ipv6_pktoptions; return 0; -- cgit v1.2.3 From 3754b87a4e2d8cad644cea9713fed7842504991a Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:25:21 -0700 Subject: netfilter: remove unused variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit warning: ‘recent_old_fops’ defined but not used Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/netfilter/xt_recent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/netfilter/xt_recent.c b/net/netfilter/xt_recent.c index 3f6c4fa78bdb..245fa350a7a8 100644 --- a/net/netfilter/xt_recent.c +++ b/net/netfilter/xt_recent.c @@ -106,7 +106,7 @@ static DEFINE_SPINLOCK(recent_lock); static DEFINE_MUTEX(recent_mutex); #ifdef CONFIG_PROC_FS -static const struct file_operations recent_old_fops, recent_mt_fops; +static const struct file_operations recent_mt_fops; #endif static u_int32_t hash_rnd __read_mostly; -- cgit v1.2.3 From 614d79c09e03d4a421f6d7eab1804600db33e04e Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:25:22 -0700 Subject: socket: fix set not used warning The variable owned_by_user is always set, but only used when kernel is configured with LOCKDEP enabled. Get rid of the warning by moving the code to put the call to owned_by_user into the the rcu_protected call. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/socket.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/socket.c b/net/socket.c index bf2122691fba..79d9bb964cd8 100644 --- a/net/socket.c +++ b/net/socket.c @@ -3404,7 +3404,6 @@ u32 kernel_sock_ip_overhead(struct sock *sk) struct inet_sock *inet; struct ip_options_rcu *opt; u32 overhead = 0; - bool owned_by_user; #if IS_ENABLED(CONFIG_IPV6) struct ipv6_pinfo *np; struct ipv6_txoptions *optv6 = NULL; @@ -3413,13 +3412,12 @@ u32 kernel_sock_ip_overhead(struct sock *sk) if (!sk) return overhead; - owned_by_user = sock_owned_by_user(sk); switch (sk->sk_family) { case AF_INET: inet = inet_sk(sk); overhead += sizeof(struct iphdr); opt = rcu_dereference_protected(inet->inet_opt, - owned_by_user); + sock_owned_by_user(sk)); if (opt) overhead += opt->opt.optlen; return overhead; @@ -3429,7 +3427,7 @@ u32 kernel_sock_ip_overhead(struct sock *sk) overhead += sizeof(struct ipv6hdr); if (np) optv6 = rcu_dereference_protected(np->opt, - owned_by_user); + sock_owned_by_user(sk)); if (optv6) overhead += (optv6->opt_flen + optv6->opt_nflen); return overhead; -- cgit v1.2.3 From ce7426ca70de80b4a8c9ada8f14ce6d651cccf5e Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Mon, 24 Jul 2017 10:25:23 -0700 Subject: 6lowpan: fix set not used warning Signed-off-by: Stephen Hemminger Acked-by: Luiz Augusto von Dentz Signed-off-by: David S. Miller --- net/bluetooth/6lowpan.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c index 2af4f1cc0ab4..4e2576fc0c59 100644 --- a/net/bluetooth/6lowpan.c +++ b/net/bluetooth/6lowpan.c @@ -273,9 +273,6 @@ static int iphc_decompress(struct sk_buff *skb, struct net_device *netdev, struct lowpan_peer *peer) { const u8 *saddr; - struct lowpan_btle_dev *dev; - - dev = lowpan_btle_dev(netdev); saddr = peer->lladdr; -- cgit v1.2.3 From 3a3a4e3054137c5ff5d4d306ec834f6d25d7f95b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 28 Jul 2017 22:18:57 +0200 Subject: ipv6: constify inet6_protocol structures The inet6_protocol structure is only passed as the first argument to inet6_add_protocol or inet6_del_protocol, both of which are declared as const. Thus the inet6_protocol structure itself can be const. Also drop __read_mostly where present on the newly const structures. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- net/ipv6/ip6_gre.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- net/ipv6/udp.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 67ff2aaf5dcb..33865d67bcb4 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1080,7 +1080,7 @@ static void ip6gre_fb_tunnel_init(struct net_device *dev) } -static struct inet6_protocol ip6gre_protocol __read_mostly = { +static const struct inet6_protocol ip6gre_protocol = { .handler = gre_rcv, .err_handler = ip6gre_err, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 90a32576c3d0..2968a33cca7d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1945,7 +1945,7 @@ struct proto tcpv6_prot = { .diag_destroy = tcp_abort, }; -static struct inet6_protocol tcpv6_protocol = { +static const struct inet6_protocol tcpv6_protocol = { .early_demux = tcp_v6_early_demux, .early_demux_handler = tcp_v6_early_demux, .handler = tcp_v6_rcv, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 4a3e65626e8b..5f8b8d766c63 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1448,7 +1448,7 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, } #endif -static struct inet6_protocol udpv6_protocol = { +static const struct inet6_protocol udpv6_protocol = { .early_demux = udp_v6_early_demux, .early_demux_handler = udp_v6_early_demux, .handler = udpv6_rcv, -- cgit v1.2.3 From d04916a48ad4a3db892b664fa9c3a2a693c378ad Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 28 Jul 2017 22:18:58 +0200 Subject: l2tp: constify inet6_protocol structures The inet6_protocol structure is only passed as the first argument to inet6_add_protocol or inet6_del_protocol, both of which are declared as const. Thus the inet6_protocol structure itself can be const. Also drop __read_mostly on the newly const structure. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- net/l2tp/l2tp_ip6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index 88b397c30d86..d2efcd93e1e2 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -788,7 +788,7 @@ static struct inet_protosw l2tp_ip6_protosw = { .ops = &l2tp_ip6_ops, }; -static struct inet6_protocol l2tp_ip6_protocol __read_mostly = { +static const struct inet6_protocol l2tp_ip6_protocol = { .handler = l2tp_ip6_recv, }; -- cgit v1.2.3 From a311abdffb06b66b5160cb3305450a79a3918a8f Mon Sep 17 00:00:00 2001 From: Simon Wunderlich Date: Fri, 28 Jul 2017 13:39:52 +0200 Subject: batman-adv: Start new development cycle Signed-off-by: Simon Wunderlich --- net/batman-adv/main.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/batman-adv/main.h b/net/batman-adv/main.h index 2be8f1f46529..05cc7637c064 100644 --- a/net/batman-adv/main.h +++ b/net/batman-adv/main.h @@ -24,7 +24,7 @@ #define BATADV_DRIVER_DEVICE "batman-adv" #ifndef BATADV_SOURCE_VERSION -#define BATADV_SOURCE_VERSION "2017.2" +#define BATADV_SOURCE_VERSION "2017.3" #endif /* B.A.T.M.A.N. parameters */ -- cgit v1.2.3 From cd0edf3ada13e9c268713a913387a88be30634dc Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Wed, 14 Jun 2017 02:33:52 -0700 Subject: batman-adv: Remove unnecessary length qualifier in %14pM It's misleading and unnecessary. Signed-off-by: Joe Perches Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/distributed-arp-table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index 6930d6b50f99..b6cfa78e9381 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -834,7 +834,7 @@ int batadv_dat_cache_seq_print_text(struct seq_file *seq, void *offset) last_seen_msecs = last_seen_msecs % 60000; last_seen_secs = last_seen_msecs / 1000; - seq_printf(seq, " * %15pI4 %14pM %4i %6i:%02i\n", + seq_printf(seq, " * %15pI4 %pM %4i %6i:%02i\n", &dat_entry->ip, dat_entry->mac_addr, batadv_print_vid(dat_entry->vid), last_seen_mins, last_seen_secs); -- cgit v1.2.3 From 6a04be8d860e86ad84b99045395d9e46ed7304cf Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 18 Jun 2017 10:12:30 +0200 Subject: batman-adv: Remove too short %pM printk field width The string representation for a mac address produced by %pM is 17 characters long. Left-aligning the output in a 15 character wide field width %-15pM is therefore misleading and unnecessary. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_iv_ogm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index a3501173e200..c27001d38574 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -1281,7 +1281,7 @@ static bool batadv_iv_ogm_calc_tq(struct batadv_orig_node *orig_node, batadv_ogm_packet->tq = combined_tq; batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "bidirectional: orig = %-15pM neigh = %-15pM => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, iface_penalty: %3i, total tq: %3i, if_incoming = %s, if_outgoing = %s\n", + "bidirectional: orig = %pM neigh = %pM => own_bcast = %2i, real recv = %2i, local tq: %3i, asym_penalty: %3i, iface_penalty: %3i, total tq: %3i, if_incoming = %s, if_outgoing = %s\n", orig_node->orig, orig_neigh_node->orig, total_count, neigh_rq_count, tq_own, tq_asym_penalty, tq_iface_penalty, batadv_ogm_packet->tq, if_incoming->net_dev->name, -- cgit v1.2.3 From e04de4861cdebdfaa13507ad3d40e6542aeab857 Mon Sep 17 00:00:00 2001 From: Sven Eckelmann Date: Sun, 18 Jun 2017 09:59:28 +0200 Subject: batman-adv: Remove variable deprecated by skb_put_data skb_put_data makes it unnecessary to store the skb_put return value to copy some data to the packet. The returned pointer of skb_put_data should therefore not stored by functions which previously only used it to copy some data. Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_iv_ogm.c | 4 +--- net/batman-adv/bat_v_ogm.c | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bat_iv_ogm.c b/net/batman-adv/bat_iv_ogm.c index c27001d38574..83ba5483455a 100644 --- a/net/batman-adv/bat_iv_ogm.c +++ b/net/batman-adv/bat_iv_ogm.c @@ -729,11 +729,9 @@ static void batadv_iv_ogm_aggregate(struct batadv_forw_packet *forw_packet_aggr, const unsigned char *packet_buff, int packet_len, bool direct_link) { - unsigned char *skb_buff; unsigned long new_direct_link_flag; - skb_buff = skb_put_data(forw_packet_aggr->skb, packet_buff, - packet_len); + skb_put_data(forw_packet_aggr->skb, packet_buff, packet_len); forw_packet_aggr->packet_len += packet_len; forw_packet_aggr->num_packets++; diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 1e3dc374bfde..894c96371e41 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -137,7 +137,7 @@ static void batadv_v_ogm_send(struct work_struct *work) struct batadv_priv *bat_priv; struct batadv_ogm2_packet *ogm_packet; struct sk_buff *skb, *skb_tmp; - unsigned char *ogm_buff, *pkt_buff; + unsigned char *ogm_buff; int ogm_buff_len; u16 tvlv_len = 0; int ret; @@ -166,7 +166,7 @@ static void batadv_v_ogm_send(struct work_struct *work) goto reschedule; skb_reserve(skb, ETH_HLEN); - pkt_buff = skb_put_data(skb, ogm_buff, ogm_buff_len); + skb_put_data(skb, ogm_buff, ogm_buff_len); ogm_packet = (struct batadv_ogm2_packet *)skb->data; ogm_packet->seqno = htonl(atomic_read(&bat_priv->bat_v.ogm_seqno)); -- cgit v1.2.3 From f25cbb2a6ac1ce702d8c39222bc13c24a69bb5ef Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Mon, 26 Jun 2017 11:26:44 +0100 Subject: batman-adv: fix various spelling mistakes Trivial fix to spelling mistakes in batadv_dbg debug messages and also in a comment and ensure comment line is not wider than 80 characters "ourselve" -> "ourselves" "surpressed" -> "suppressed" "troughput" -> "throughput" Signed-off-by: Colin Ian King Signed-off-by: Sven Eckelmann Signed-off-by: Simon Wunderlich --- net/batman-adv/bat_v_ogm.c | 12 ++++++------ net/batman-adv/send.c | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/batman-adv/bat_v_ogm.c b/net/batman-adv/bat_v_ogm.c index 894c96371e41..8be61734fc43 100644 --- a/net/batman-adv/bat_v_ogm.c +++ b/net/batman-adv/bat_v_ogm.c @@ -200,7 +200,7 @@ static void batadv_v_ogm_send(struct work_struct *work) type = "unknown"; } - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "OGM2 from ourselve on %s surpressed: %s\n", + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "OGM2 from ourselves on %s suppressed: %s\n", hard_iface->net_dev->name, type); batadv_hardif_put(hard_iface); @@ -683,18 +683,18 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, ogm_throughput = ntohl(ogm_packet->throughput); batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Received OGM2 packet via NB: %pM, IF: %s [%pM] (from OG: %pM, seqno %u, troughput %u, TTL %u, V %u, tvlv_len %u)\n", + "Received OGM2 packet via NB: %pM, IF: %s [%pM] (from OG: %pM, seqno %u, throughput %u, TTL %u, V %u, tvlv_len %u)\n", ethhdr->h_source, if_incoming->net_dev->name, if_incoming->net_dev->dev_addr, ogm_packet->orig, ntohl(ogm_packet->seqno), ogm_throughput, ogm_packet->ttl, ogm_packet->version, ntohs(ogm_packet->tvlv_len)); - /* If the troughput metric is 0, immediately drop the packet. No need to - * create orig_node / neigh_node for an unusable route. + /* If the throughput metric is 0, immediately drop the packet. No need + * to create orig_node / neigh_node for an unusable route. */ if (ogm_throughput == 0) { batadv_dbg(BATADV_DBG_BATMAN, bat_priv, - "Drop packet: originator packet with troughput metric of 0\n"); + "Drop packet: originator packet with throughput metric of 0\n"); return; } @@ -762,7 +762,7 @@ static void batadv_v_ogm_process(const struct sk_buff *skb, int ogm_offset, type = "unknown"; } - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "OGM2 packet from %pM on %s surpressed: %s\n", + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "OGM2 packet from %pM on %s suppressed: %s\n", ogm_packet->orig, hard_iface->net_dev->name, type); diff --git a/net/batman-adv/send.c b/net/batman-adv/send.c index d239a9d72ac3..054a65e6eb68 100644 --- a/net/batman-adv/send.c +++ b/net/batman-adv/send.c @@ -911,7 +911,7 @@ static void batadv_send_outstanding_bcast_packet(struct work_struct *work) type = "unknown"; } - batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "BCAST packet from orig %pM on %s surpressed: %s\n", + batadv_dbg(BATADV_DBG_BATMAN, bat_priv, "BCAST packet from orig %pM on %s suppressed: %s\n", bcast_packet->orig, hard_iface->net_dev->name, type); -- cgit v1.2.3 From 977bb32440e7342e28838d9c0260681637ac97c4 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:13 +0200 Subject: net/smc: serialize connection creation in all cases If a link group for a new server connection exists already, the mutex serializing the determination of link groups is given up early. The coming registration of memory regions benefits from the serialization as well, if the mutex is held till connection creation is finished. This patch postpones the unlocking of the link group creation mutex. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/af_smc.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'net') diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 6793d7348cc8..120a7b9b4d8e 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -779,11 +779,6 @@ static void smc_listen_work(struct work_struct *work) mutex_lock(&smc_create_lgr_pending); local_contact = smc_conn_create(new_smc, peeraddr.sin_addr.s_addr, smcibdev, ibport, &pclc.lcl, 0); - if (local_contact == SMC_REUSE_CONTACT) - /* lock no longer needed, free it due to following - * smc_clc_wait_msg() call - */ - mutex_unlock(&smc_create_lgr_pending); if (local_contact < 0) { rc = local_contact; if (rc == -ENOMEM) @@ -853,8 +848,7 @@ out_connected: if (newsmcsk->sk_state == SMC_INIT) newsmcsk->sk_state = SMC_ACTIVE; enqueue: - if (local_contact == SMC_FIRST_CONTACT) - mutex_unlock(&smc_create_lgr_pending); + mutex_unlock(&smc_create_lgr_pending); lock_sock_nested(&lsmc->sk, SINGLE_DEPTH_NESTING); if (lsmc->sk.sk_state == SMC_LISTEN) { smc_accept_enqueue(&lsmc->sk, newsmcsk); -- cgit v1.2.3 From c45abf31e72f7640c5e84adda4683b72cb32a222 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:14 +0200 Subject: net/smc: shorten local bufsize variables Initiate the coming rework of SMC buffer handling with this small code cleanup. No functional changes here. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/smc_core.c | 54 +++++++++++++++++++++++++----------------------------- 1 file changed, 25 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 3ac09a629ea1..6159488cb94b 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -508,25 +508,25 @@ int smc_sndbuf_create(struct smc_sock *smc) { struct smc_connection *conn = &smc->conn; struct smc_link_group *lgr = conn->lgr; - int tmp_bufsize, tmp_bufsize_short; struct smc_buf_desc *sndbuf_desc; + int bufsize, bufsize_short; int rc; /* use socket send buffer size (w/o overhead) as start value */ - for (tmp_bufsize_short = smc_compress_bufsize(smc->sk.sk_sndbuf / 2); - tmp_bufsize_short >= 0; tmp_bufsize_short--) { - tmp_bufsize = smc_uncompress_bufsize(tmp_bufsize_short); + for (bufsize_short = smc_compress_bufsize(smc->sk.sk_sndbuf / 2); + bufsize_short >= 0; bufsize_short--) { + bufsize = smc_uncompress_bufsize(bufsize_short); /* check for reusable sndbuf_slot in the link group */ - sndbuf_desc = smc_sndbuf_get_slot(lgr, tmp_bufsize_short); + sndbuf_desc = smc_sndbuf_get_slot(lgr, bufsize_short); if (sndbuf_desc) { - memset(sndbuf_desc->cpu_addr, 0, tmp_bufsize); + memset(sndbuf_desc->cpu_addr, 0, bufsize); break; /* found reusable slot */ } /* try to alloc a new send buffer */ sndbuf_desc = kzalloc(sizeof(*sndbuf_desc), GFP_KERNEL); if (!sndbuf_desc) break; /* give up with -ENOMEM */ - sndbuf_desc->cpu_addr = kzalloc(tmp_bufsize, + sndbuf_desc->cpu_addr = kzalloc(bufsize, GFP_KERNEL | __GFP_NOWARN | __GFP_NOMEMALLOC | __GFP_NORETRY); @@ -539,8 +539,7 @@ int smc_sndbuf_create(struct smc_sock *smc) continue; } rc = smc_ib_buf_map(lgr->lnk[SMC_SINGLE_LINK].smcibdev, - tmp_bufsize, sndbuf_desc, - DMA_TO_DEVICE); + bufsize, sndbuf_desc, DMA_TO_DEVICE); if (rc) { kfree(sndbuf_desc->cpu_addr); kfree(sndbuf_desc); @@ -549,16 +548,15 @@ int smc_sndbuf_create(struct smc_sock *smc) } sndbuf_desc->used = 1; write_lock_bh(&lgr->sndbufs_lock); - list_add(&sndbuf_desc->list, - &lgr->sndbufs[tmp_bufsize_short]); + list_add(&sndbuf_desc->list, &lgr->sndbufs[bufsize_short]); write_unlock_bh(&lgr->sndbufs_lock); break; } if (sndbuf_desc && sndbuf_desc->cpu_addr) { conn->sndbuf_desc = sndbuf_desc; - conn->sndbuf_size = tmp_bufsize; - smc->sk.sk_sndbuf = tmp_bufsize * 2; - atomic_set(&conn->sndbuf_space, tmp_bufsize); + conn->sndbuf_size = bufsize; + smc->sk.sk_sndbuf = bufsize * 2; + atomic_set(&conn->sndbuf_space, bufsize); return 0; } else { return -ENOMEM; @@ -574,25 +572,25 @@ int smc_rmb_create(struct smc_sock *smc) { struct smc_connection *conn = &smc->conn; struct smc_link_group *lgr = conn->lgr; - int tmp_bufsize, tmp_bufsize_short; struct smc_buf_desc *rmb_desc; + int bufsize, bufsize_short; int rc; /* use socket recv buffer size (w/o overhead) as start value */ - for (tmp_bufsize_short = smc_compress_bufsize(smc->sk.sk_rcvbuf / 2); - tmp_bufsize_short >= 0; tmp_bufsize_short--) { - tmp_bufsize = smc_uncompress_bufsize(tmp_bufsize_short); + for (bufsize_short = smc_compress_bufsize(smc->sk.sk_rcvbuf / 2); + bufsize_short >= 0; bufsize_short--) { + bufsize = smc_uncompress_bufsize(bufsize_short); /* check for reusable rmb_slot in the link group */ - rmb_desc = smc_rmb_get_slot(lgr, tmp_bufsize_short); + rmb_desc = smc_rmb_get_slot(lgr, bufsize_short); if (rmb_desc) { - memset(rmb_desc->cpu_addr, 0, tmp_bufsize); + memset(rmb_desc->cpu_addr, 0, bufsize); break; /* found reusable slot */ } /* try to alloc a new RMB */ rmb_desc = kzalloc(sizeof(*rmb_desc), GFP_KERNEL); if (!rmb_desc) break; /* give up with -ENOMEM */ - rmb_desc->cpu_addr = kzalloc(tmp_bufsize, + rmb_desc->cpu_addr = kzalloc(bufsize, GFP_KERNEL | __GFP_NOWARN | __GFP_NOMEMALLOC | __GFP_NORETRY); @@ -605,8 +603,7 @@ int smc_rmb_create(struct smc_sock *smc) continue; } rc = smc_ib_buf_map(lgr->lnk[SMC_SINGLE_LINK].smcibdev, - tmp_bufsize, rmb_desc, - DMA_FROM_DEVICE); + bufsize, rmb_desc, DMA_FROM_DEVICE); if (rc) { kfree(rmb_desc->cpu_addr); kfree(rmb_desc); @@ -617,18 +614,17 @@ int smc_rmb_create(struct smc_sock *smc) lgr->lnk[SMC_SINGLE_LINK].roce_pd->unsafe_global_rkey; rmb_desc->used = 1; write_lock_bh(&lgr->rmbs_lock); - list_add(&rmb_desc->list, - &lgr->rmbs[tmp_bufsize_short]); + list_add(&rmb_desc->list, &lgr->rmbs[bufsize_short]); write_unlock_bh(&lgr->rmbs_lock); break; } if (rmb_desc && rmb_desc->cpu_addr) { conn->rmb_desc = rmb_desc; - conn->rmbe_size = tmp_bufsize; - conn->rmbe_size_short = tmp_bufsize_short; - smc->sk.sk_rcvbuf = tmp_bufsize * 2; + conn->rmbe_size = bufsize; + conn->rmbe_size_short = bufsize_short; + smc->sk.sk_rcvbuf = bufsize * 2; atomic_set(&conn->bytes_to_rcv, 0); - conn->rmbe_update_limit = smc_rmb_wnd_update_limit(tmp_bufsize); + conn->rmbe_update_limit = smc_rmb_wnd_update_limit(bufsize); return 0; } else { return -ENOMEM; -- cgit v1.2.3 From a3fe3d01bd0d7cd6ee7a5e3eebc0926c47954fe7 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:15 +0200 Subject: net/smc: introduce sg-logic for RMBs The follow-on patch makes use of ib_map_mr_sg() when introducing separate memory regions for RMBs. This function is based on scatterlists; thus this patch introduces scatterlists for RMBs. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/smc_clc.c | 12 ++++++------ net/smc/smc_core.c | 47 +++++++++++++++++++++++++++++++---------------- net/smc/smc_core.h | 6 ++---- net/smc/smc_ib.c | 31 +++++++++++++++++++++++++++++++ net/smc/smc_ib.h | 6 ++++++ 5 files changed, 76 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index 03ec058d18df..15cb76019009 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -204,13 +204,13 @@ int smc_clc_send_confirm(struct smc_sock *smc) memcpy(&cclc.lcl.mac, &link->smcibdev->mac[link->ibport - 1], ETH_ALEN); hton24(cclc.qpn, link->roce_qp->qp_num); cclc.rmb_rkey = - htonl(conn->rmb_desc->rkey[SMC_SINGLE_LINK]); + htonl(link->roce_pd->unsafe_global_rkey); cclc.conn_idx = 1; /* for now: 1 RMB = 1 RMBE */ cclc.rmbe_alert_token = htonl(conn->alert_token_local); cclc.qp_mtu = min(link->path_mtu, link->peer_mtu); cclc.rmbe_size = conn->rmbe_size_short; - cclc.rmb_dma_addr = - cpu_to_be64((u64)conn->rmb_desc->dma_addr[SMC_SINGLE_LINK]); + cclc.rmb_dma_addr = cpu_to_be64( + (u64)sg_dma_address(conn->rmb_desc->sgt[SMC_SINGLE_LINK].sgl)); hton24(cclc.psn, link->psn_initial); memcpy(cclc.trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); @@ -256,13 +256,13 @@ int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact) memcpy(&aclc.lcl.mac, link->smcibdev->mac[link->ibport - 1], ETH_ALEN); hton24(aclc.qpn, link->roce_qp->qp_num); aclc.rmb_rkey = - htonl(conn->rmb_desc->rkey[SMC_SINGLE_LINK]); + htonl(link->roce_pd->unsafe_global_rkey); aclc.conn_idx = 1; /* as long as 1 RMB = 1 RMBE */ aclc.rmbe_alert_token = htonl(conn->alert_token_local); aclc.qp_mtu = link->path_mtu; aclc.rmbe_size = conn->rmbe_size_short, - aclc.rmb_dma_addr = - cpu_to_be64((u64)conn->rmb_desc->dma_addr[SMC_SINGLE_LINK]); + aclc.rmb_dma_addr = cpu_to_be64( + (u64)sg_dma_address(conn->rmb_desc->sgt[SMC_SINGLE_LINK].sgl)); hton24(aclc.psn, link->psn_initial); memcpy(aclc.trl.eyecatcher, SMC_EYECATCHER, sizeof(SMC_EYECATCHER)); diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 6159488cb94b..bfdbda795f67 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -266,17 +266,16 @@ static void smc_lgr_free_sndbufs(struct smc_link_group *lgr) static void smc_lgr_free_rmbs(struct smc_link_group *lgr) { - struct smc_buf_desc *rmb_desc, *bf_desc; struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK]; + struct smc_buf_desc *rmb_desc, *bf_desc; int i; for (i = 0; i < SMC_RMBE_SIZES; i++) { list_for_each_entry_safe(rmb_desc, bf_desc, &lgr->rmbs[i], list) { list_del(&rmb_desc->list); - smc_ib_buf_unmap(lnk->smcibdev, - smc_uncompress_bufsize(i), - rmb_desc, DMA_FROM_DEVICE); + smc_ib_buf_unmap_sg(lnk->smcibdev, rmb_desc, + DMA_FROM_DEVICE); kfree(rmb_desc->cpu_addr); kfree(rmb_desc); } @@ -580,38 +579,54 @@ int smc_rmb_create(struct smc_sock *smc) for (bufsize_short = smc_compress_bufsize(smc->sk.sk_rcvbuf / 2); bufsize_short >= 0; bufsize_short--) { bufsize = smc_uncompress_bufsize(bufsize_short); + if ((1 << get_order(bufsize)) > SG_MAX_SINGLE_ALLOC) + continue; + /* check for reusable rmb_slot in the link group */ rmb_desc = smc_rmb_get_slot(lgr, bufsize_short); if (rmb_desc) { memset(rmb_desc->cpu_addr, 0, bufsize); break; /* found reusable slot */ } + /* try to alloc a new RMB */ rmb_desc = kzalloc(sizeof(*rmb_desc), GFP_KERNEL); if (!rmb_desc) break; /* give up with -ENOMEM */ - rmb_desc->cpu_addr = kzalloc(bufsize, - GFP_KERNEL | __GFP_NOWARN | - __GFP_NOMEMALLOC | - __GFP_NORETRY); + rmb_desc->cpu_addr = + (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | + __GFP_NOMEMALLOC | + __GFP_NORETRY | __GFP_ZERO, + get_order(bufsize)); if (!rmb_desc->cpu_addr) { kfree(rmb_desc); rmb_desc = NULL; - /* if RMB allocation has failed, - * try a smaller one - */ continue; } - rc = smc_ib_buf_map(lgr->lnk[SMC_SINGLE_LINK].smcibdev, - bufsize, rmb_desc, DMA_FROM_DEVICE); + rmb_desc->order = get_order(bufsize); + + rc = sg_alloc_table(&rmb_desc->sgt[SMC_SINGLE_LINK], 1, + GFP_KERNEL); if (rc) { - kfree(rmb_desc->cpu_addr); + free_pages((unsigned long)rmb_desc->cpu_addr, + rmb_desc->order); + kfree(rmb_desc); + rmb_desc = NULL; + continue; + } + sg_set_buf(rmb_desc->sgt[SMC_SINGLE_LINK].sgl, + rmb_desc->cpu_addr, bufsize); + + rc = smc_ib_buf_map_sg(lgr->lnk[SMC_SINGLE_LINK].smcibdev, + rmb_desc, DMA_FROM_DEVICE); + if (rc != 1) { + sg_free_table(&rmb_desc->sgt[SMC_SINGLE_LINK]); + free_pages((unsigned long)rmb_desc->cpu_addr, + rmb_desc->order); kfree(rmb_desc); rmb_desc = NULL; continue; /* if mapping failed, try smaller one */ } - rmb_desc->rkey[SMC_SINGLE_LINK] = - lgr->lnk[SMC_SINGLE_LINK].roce_pd->unsafe_global_rkey; rmb_desc->used = 1; write_lock_bh(&lgr->rmbs_lock); list_add(&rmb_desc->list, &lgr->rmbs[bufsize_short]); diff --git a/net/smc/smc_core.h b/net/smc/smc_core.h index b013cb43a327..0ee450d69907 100644 --- a/net/smc/smc_core.h +++ b/net/smc/smc_core.h @@ -93,10 +93,8 @@ struct smc_buf_desc { u64 dma_addr[SMC_LINKS_PER_LGR_MAX]; /* mapped address of buffer */ void *cpu_addr; /* virtual address of buffer */ - u32 rkey[SMC_LINKS_PER_LGR_MAX]; - /* for rmb only: - * rkey provided to peer - */ + struct sg_table sgt[SMC_LINKS_PER_LGR_MAX];/* virtual buffer */ + u32 order; /* allocation order */ u32 used; /* currently used / unused */ }; diff --git a/net/smc/smc_ib.c b/net/smc/smc_ib.c index b31715505a35..fcfeb89b05d9 100644 --- a/net/smc/smc_ib.c +++ b/net/smc/smc_ib.c @@ -283,6 +283,37 @@ void smc_ib_buf_unmap(struct smc_ib_device *smcibdev, int buf_size, buf_slot->dma_addr[SMC_SINGLE_LINK] = 0; } +/* Map a new TX or RX buffer SG-table to DMA */ +int smc_ib_buf_map_sg(struct smc_ib_device *smcibdev, + struct smc_buf_desc *buf_slot, + enum dma_data_direction data_direction) +{ + int mapped_nents; + + mapped_nents = ib_dma_map_sg(smcibdev->ibdev, + buf_slot->sgt[SMC_SINGLE_LINK].sgl, + buf_slot->sgt[SMC_SINGLE_LINK].orig_nents, + data_direction); + if (!mapped_nents) + return -ENOMEM; + + return mapped_nents; +} + +void smc_ib_buf_unmap_sg(struct smc_ib_device *smcibdev, + struct smc_buf_desc *buf_slot, + enum dma_data_direction data_direction) +{ + if (!buf_slot->sgt[SMC_SINGLE_LINK].sgl->dma_address) + return; /* already unmapped */ + + ib_dma_unmap_sg(smcibdev->ibdev, + buf_slot->sgt[SMC_SINGLE_LINK].sgl, + buf_slot->sgt[SMC_SINGLE_LINK].orig_nents, + data_direction); + buf_slot->sgt[SMC_SINGLE_LINK].sgl->dma_address = 0; +} + static int smc_ib_fill_gid_and_mac(struct smc_ib_device *smcibdev, u8 ibport) { struct net_device *ndev; diff --git a/net/smc/smc_ib.h b/net/smc/smc_ib.h index b567152a526d..b30e387854b6 100644 --- a/net/smc/smc_ib.h +++ b/net/smc/smc_ib.h @@ -57,6 +57,12 @@ int smc_ib_buf_map(struct smc_ib_device *smcibdev, int buf_size, void smc_ib_buf_unmap(struct smc_ib_device *smcibdev, int bufsize, struct smc_buf_desc *buf_slot, enum dma_data_direction data_direction); +int smc_ib_buf_map_sg(struct smc_ib_device *smcibdev, + struct smc_buf_desc *buf_slot, + enum dma_data_direction data_direction); +void smc_ib_buf_unmap_sg(struct smc_ib_device *smcibdev, + struct smc_buf_desc *buf_slot, + enum dma_data_direction data_direction); void smc_ib_dealloc_protection_domain(struct smc_link *lnk); int smc_ib_create_protection_domain(struct smc_link *lnk); void smc_ib_destroy_queue_pair(struct smc_link *lnk); -- cgit v1.2.3 From 897e1c245773d93f26f125a99674f585a3aeef5d Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:16 +0200 Subject: net/smc: use separate memory regions for RMBs SMC currently uses the unsafe_global_rkey of the protection domain, which exposes all memory for remote reads and writes once a connection is established. This patch introduces separate memory regions with separate rkeys for every RMB. Now the unsafe_global_rkey of the protection domain is no longer needed. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/smc_clc.c | 4 ++-- net/smc/smc_core.c | 18 ++++++++++++++++++ net/smc/smc_core.h | 6 +++++- net/smc/smc_ib.c | 45 +++++++++++++++++++++++++++++++++++++++++++-- net/smc/smc_ib.h | 5 +++-- 5 files changed, 71 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/smc/smc_clc.c b/net/smc/smc_clc.c index 15cb76019009..3934913ab835 100644 --- a/net/smc/smc_clc.c +++ b/net/smc/smc_clc.c @@ -204,7 +204,7 @@ int smc_clc_send_confirm(struct smc_sock *smc) memcpy(&cclc.lcl.mac, &link->smcibdev->mac[link->ibport - 1], ETH_ALEN); hton24(cclc.qpn, link->roce_qp->qp_num); cclc.rmb_rkey = - htonl(link->roce_pd->unsafe_global_rkey); + htonl(conn->rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey); cclc.conn_idx = 1; /* for now: 1 RMB = 1 RMBE */ cclc.rmbe_alert_token = htonl(conn->alert_token_local); cclc.qp_mtu = min(link->path_mtu, link->peer_mtu); @@ -256,7 +256,7 @@ int smc_clc_send_accept(struct smc_sock *new_smc, int srv_first_contact) memcpy(&aclc.lcl.mac, link->smcibdev->mac[link->ibport - 1], ETH_ALEN); hton24(aclc.qpn, link->roce_qp->qp_num); aclc.rmb_rkey = - htonl(link->roce_pd->unsafe_global_rkey); + htonl(conn->rmb_desc->mr_rx[SMC_SINGLE_LINK]->rkey); aclc.conn_idx = 1; /* as long as 1 RMB = 1 RMBE */ aclc.rmbe_alert_token = htonl(conn->alert_token_local); aclc.qp_mtu = link->path_mtu; diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index bfdbda795f67..f1dd4e1cd3e1 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -218,6 +218,7 @@ static void smc_sndbuf_unuse(struct smc_connection *conn) static void smc_rmb_unuse(struct smc_connection *conn) { if (conn->rmb_desc) { + conn->rmb_desc->reused = true; conn->rmb_desc->used = 0; conn->rmbe_size = 0; } @@ -274,6 +275,8 @@ static void smc_lgr_free_rmbs(struct smc_link_group *lgr) list_for_each_entry_safe(rmb_desc, bf_desc, &lgr->rmbs[i], list) { list_del(&rmb_desc->list); + smc_ib_put_memory_region( + rmb_desc->mr_rx[SMC_SINGLE_LINK]); smc_ib_buf_unmap_sg(lnk->smcibdev, rmb_desc, DMA_FROM_DEVICE); kfree(rmb_desc->cpu_addr); @@ -627,6 +630,21 @@ int smc_rmb_create(struct smc_sock *smc) rmb_desc = NULL; continue; /* if mapping failed, try smaller one */ } + rc = smc_ib_get_memory_region(lgr->lnk[SMC_SINGLE_LINK].roce_pd, + IB_ACCESS_REMOTE_WRITE | + IB_ACCESS_LOCAL_WRITE, + rmb_desc); + if (rc) { + smc_ib_buf_unmap_sg(lgr->lnk[SMC_SINGLE_LINK].smcibdev, + rmb_desc, DMA_FROM_DEVICE); + sg_free_table(&rmb_desc->sgt[SMC_SINGLE_LINK]); + free_pages((unsigned long)rmb_desc->cpu_addr, + rmb_desc->order); + kfree(rmb_desc); + rmb_desc = NULL; + continue; + } + rmb_desc->used = 1; write_lock_bh(&lgr->rmbs_lock); list_add(&rmb_desc->list, &lgr->rmbs[bufsize_short]); diff --git a/net/smc/smc_core.h b/net/smc/smc_core.h index 0ee450d69907..17b5fea09901 100644 --- a/net/smc/smc_core.h +++ b/net/smc/smc_core.h @@ -94,8 +94,13 @@ struct smc_buf_desc { /* mapped address of buffer */ void *cpu_addr; /* virtual address of buffer */ struct sg_table sgt[SMC_LINKS_PER_LGR_MAX];/* virtual buffer */ + struct ib_mr *mr_rx[SMC_LINKS_PER_LGR_MAX]; + /* for rmb only: memory region + * incl. rkey provided to peer + */ u32 order; /* allocation order */ u32 used; /* currently used / unused */ + bool reused; /* new created / reused */ }; struct smc_rtoken { /* address/key of remote RMB */ @@ -175,5 +180,4 @@ int smc_sndbuf_create(struct smc_sock *smc); int smc_rmb_create(struct smc_sock *smc); int smc_rmb_rtoken_handling(struct smc_connection *conn, struct smc_clc_msg_accept_confirm *clc); - #endif diff --git a/net/smc/smc_ib.c b/net/smc/smc_ib.c index fcfeb89b05d9..08233492ec45 100644 --- a/net/smc/smc_ib.c +++ b/net/smc/smc_ib.c @@ -192,8 +192,7 @@ int smc_ib_create_protection_domain(struct smc_link *lnk) { int rc; - lnk->roce_pd = ib_alloc_pd(lnk->smcibdev->ibdev, - IB_PD_UNSAFE_GLOBAL_RKEY); + lnk->roce_pd = ib_alloc_pd(lnk->smcibdev->ibdev, 0); rc = PTR_ERR_OR_ZERO(lnk->roce_pd); if (IS_ERR(lnk->roce_pd)) lnk->roce_pd = NULL; @@ -254,6 +253,48 @@ int smc_ib_create_queue_pair(struct smc_link *lnk) return rc; } +void smc_ib_put_memory_region(struct ib_mr *mr) +{ + ib_dereg_mr(mr); +} + +static int smc_ib_map_mr_sg(struct smc_buf_desc *buf_slot) +{ + unsigned int offset = 0; + int sg_num; + + /* map the largest prefix of a dma mapped SG list */ + sg_num = ib_map_mr_sg(buf_slot->mr_rx[SMC_SINGLE_LINK], + buf_slot->sgt[SMC_SINGLE_LINK].sgl, + buf_slot->sgt[SMC_SINGLE_LINK].orig_nents, + &offset, PAGE_SIZE); + + return sg_num; +} + +/* Allocate a memory region and map the dma mapped SG list of buf_slot */ +int smc_ib_get_memory_region(struct ib_pd *pd, int access_flags, + struct smc_buf_desc *buf_slot) +{ + if (buf_slot->mr_rx[SMC_SINGLE_LINK]) + return 0; /* already done */ + + buf_slot->mr_rx[SMC_SINGLE_LINK] = + ib_alloc_mr(pd, IB_MR_TYPE_MEM_REG, 1 << buf_slot->order); + if (IS_ERR(buf_slot->mr_rx[SMC_SINGLE_LINK])) { + int rc; + + rc = PTR_ERR(buf_slot->mr_rx[SMC_SINGLE_LINK]); + buf_slot->mr_rx[SMC_SINGLE_LINK] = NULL; + return rc; + } + + if (smc_ib_map_mr_sg(buf_slot) != 1) + return -EINVAL; + + return 0; +} + /* map a new TX or RX buffer to DMA */ int smc_ib_buf_map(struct smc_ib_device *smcibdev, int buf_size, struct smc_buf_desc *buf_slot, diff --git a/net/smc/smc_ib.h b/net/smc/smc_ib.h index b30e387854b6..b57d29f29042 100644 --- a/net/smc/smc_ib.h +++ b/net/smc/smc_ib.h @@ -71,6 +71,7 @@ int smc_ib_ready_link(struct smc_link *lnk); int smc_ib_modify_qp_rts(struct smc_link *lnk); int smc_ib_modify_qp_reset(struct smc_link *lnk); long smc_ib_setup_per_ibdev(struct smc_ib_device *smcibdev); - - +int smc_ib_get_memory_region(struct ib_pd *pd, int access_flags, + struct smc_buf_desc *buf_slot); +void smc_ib_put_memory_region(struct ib_mr *mr); #endif -- cgit v1.2.3 From 652a1e41eca7dfaacc47a79badb4a51aea570d35 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:17 +0200 Subject: net/smc: register RMB-related memory region A memory region created for a new RMB must be registered explicitly, before the peer can make use of it for remote DMA transfer. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/af_smc.c | 38 ++++++++++++++++++++++++++++++++ net/smc/smc_core.c | 1 - net/smc/smc_core.h | 12 +++++++++++ net/smc/smc_ib.c | 2 +- net/smc/smc_wr.c | 63 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ net/smc/smc_wr.h | 1 + 6 files changed, 115 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 120a7b9b4d8e..e0a95d50bf87 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -338,6 +338,12 @@ static int smc_clnt_conf_first_link(struct smc_sock *smc, union ib_gid *gid) return SMC_CLC_DECL_INTERR; smc_wr_remember_qp_attr(link); + + rc = smc_wr_reg_send(link, + smc->conn.rmb_desc->mr_rx[SMC_SINGLE_LINK]); + if (rc) + return SMC_CLC_DECL_INTERR; + /* send CONFIRM LINK response over RoCE fabric */ rc = smc_llc_send_confirm_link(link, link->smcibdev->mac[link->ibport - 1], @@ -459,6 +465,18 @@ static int smc_connect_rdma(struct smc_sock *smc) reason_code = SMC_CLC_DECL_INTERR; goto decline_rdma_unlock; } + } else { + struct smc_buf_desc *buf_desc = smc->conn.rmb_desc; + + if (!buf_desc->reused) { + /* register memory region for new rmb */ + rc = smc_wr_reg_send(link, + buf_desc->mr_rx[SMC_SINGLE_LINK]); + if (rc) { + reason_code = SMC_CLC_DECL_INTERR; + goto decline_rdma_unlock; + } + } } rc = smc_clc_send_confirm(smc); @@ -692,6 +710,12 @@ static int smc_serv_conf_first_link(struct smc_sock *smc) int rc; link = &lgr->lnk[SMC_SINGLE_LINK]; + + rc = smc_wr_reg_send(link, + smc->conn.rmb_desc->mr_rx[SMC_SINGLE_LINK]); + if (rc) + return SMC_CLC_DECL_INTERR; + /* send CONFIRM LINK request to client over the RoCE fabric */ rc = smc_llc_send_confirm_link(link, link->smcibdev->mac[link->ibport - 1], @@ -803,6 +827,20 @@ static void smc_listen_work(struct work_struct *work) smc_close_init(new_smc); smc_rx_init(new_smc); + if (local_contact != SMC_FIRST_CONTACT) { + struct smc_buf_desc *buf_desc = new_smc->conn.rmb_desc; + + if (!buf_desc->reused) { + /* register memory region for new rmb */ + rc = smc_wr_reg_send(link, + buf_desc->mr_rx[SMC_SINGLE_LINK]); + if (rc) { + reason_code = SMC_CLC_DECL_INTERR; + goto decline_rdma; + } + } + } + rc = smc_clc_send_accept(new_smc, local_contact); if (rc) goto out_err; diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index f1dd4e1cd3e1..87bb3e4771a8 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -175,7 +175,6 @@ static int smc_lgr_create(struct smc_sock *smc, __be32 peer_in_addr, rc = smc_wr_alloc_link_mem(lnk); if (rc) goto free_lgr; - init_waitqueue_head(&lnk->wr_tx_wait); rc = smc_ib_create_protection_domain(lnk); if (rc) goto free_link_mem; diff --git a/net/smc/smc_core.h b/net/smc/smc_core.h index 17b5fea09901..f7b40bdbf24c 100644 --- a/net/smc/smc_core.h +++ b/net/smc/smc_core.h @@ -37,6 +37,14 @@ struct smc_wr_buf { u8 raw[SMC_WR_BUF_SIZE]; }; +#define SMC_WR_REG_MR_WAIT_TIME (5 * HZ)/* wait time for ib_wr_reg_mr result */ + +enum smc_wr_reg_state { + POSTED, /* ib_wr_reg_mr request posted */ + CONFIRMED, /* ib_wr_reg_mr response: successful */ + FAILED /* ib_wr_reg_mr response: failure */ +}; + struct smc_link { struct smc_ib_device *smcibdev; /* ib-device */ u8 ibport; /* port - values 1 | 2 */ @@ -65,6 +73,10 @@ struct smc_link { u64 wr_rx_id; /* seq # of last recv WR */ u32 wr_rx_cnt; /* number of WR recv buffers */ + struct ib_reg_wr wr_reg; /* WR register memory region */ + wait_queue_head_t wr_reg_wait; /* wait for wr_reg result */ + enum smc_wr_reg_state wr_reg_state; /* state of wr_reg request */ + union ib_gid gid; /* gid matching used vlan id */ u32 peer_qpn; /* QP number of peer */ enum ib_mtu path_mtu; /* used mtu */ diff --git a/net/smc/smc_ib.c b/net/smc/smc_ib.c index 08233492ec45..85e1831f591e 100644 --- a/net/smc/smc_ib.c +++ b/net/smc/smc_ib.c @@ -231,10 +231,10 @@ int smc_ib_create_queue_pair(struct smc_link *lnk) .recv_cq = lnk->smcibdev->roce_cq_recv, .srq = NULL, .cap = { - .max_send_wr = SMC_WR_BUF_CNT, /* include unsolicited rdma_writes as well, * there are max. 2 RDMA_WRITE per 1 WR_SEND */ + .max_send_wr = SMC_WR_BUF_CNT * 3, .max_recv_wr = SMC_WR_BUF_CNT * 3, .max_send_sge = SMC_IB_MAX_SEND_SGE, .max_recv_sge = 1, diff --git a/net/smc/smc_wr.c b/net/smc/smc_wr.c index 874ee9f9d796..ab56bda66783 100644 --- a/net/smc/smc_wr.c +++ b/net/smc/smc_wr.c @@ -68,6 +68,16 @@ static inline void smc_wr_tx_process_cqe(struct ib_wc *wc) int i; link = wc->qp->qp_context; + + if (wc->opcode == IB_WC_REG_MR) { + if (wc->status) + link->wr_reg_state = FAILED; + else + link->wr_reg_state = CONFIRMED; + wake_up(&link->wr_reg_wait); + return; + } + pnd_snd_idx = smc_wr_tx_find_pending_index(link, wc->wr_id); if (pnd_snd_idx == link->wr_tx_cnt) return; @@ -243,6 +253,52 @@ int smc_wr_tx_send(struct smc_link *link, struct smc_wr_tx_pend_priv *priv) return rc; } +/* Register a memory region and wait for result. */ +int smc_wr_reg_send(struct smc_link *link, struct ib_mr *mr) +{ + struct ib_send_wr *failed_wr = NULL; + int rc; + + ib_req_notify_cq(link->smcibdev->roce_cq_send, + IB_CQ_NEXT_COMP | IB_CQ_REPORT_MISSED_EVENTS); + link->wr_reg_state = POSTED; + link->wr_reg.wr.wr_id = (u64)(uintptr_t)mr; + link->wr_reg.mr = mr; + link->wr_reg.key = mr->rkey; + failed_wr = &link->wr_reg.wr; + rc = ib_post_send(link->roce_qp, &link->wr_reg.wr, &failed_wr); + WARN_ON(failed_wr != &link->wr_reg.wr); + if (rc) + return rc; + + rc = wait_event_interruptible_timeout(link->wr_reg_wait, + (link->wr_reg_state != POSTED), + SMC_WR_REG_MR_WAIT_TIME); + if (!rc) { + /* timeout - terminate connections */ + struct smc_link_group *lgr; + + lgr = container_of(link, struct smc_link_group, + lnk[SMC_SINGLE_LINK]); + smc_lgr_terminate(lgr); + return -EPIPE; + } + if (rc == -ERESTARTSYS) + return -EINTR; + switch (link->wr_reg_state) { + case CONFIRMED: + rc = 0; + break; + case FAILED: + rc = -EIO; + break; + case POSTED: + rc = -EPIPE; + break; + } + return rc; +} + void smc_wr_tx_dismiss_slots(struct smc_link *link, u8 wr_rx_hdr_type, smc_wr_tx_filter filter, smc_wr_tx_dismisser dismisser, @@ -458,6 +514,11 @@ static void smc_wr_init_sge(struct smc_link *lnk) lnk->wr_rx_ibs[i].sg_list = &lnk->wr_rx_sges[i]; lnk->wr_rx_ibs[i].num_sge = 1; } + lnk->wr_reg.wr.next = NULL; + lnk->wr_reg.wr.num_sge = 0; + lnk->wr_reg.wr.send_flags = IB_SEND_SIGNALED; + lnk->wr_reg.wr.opcode = IB_WR_REG_MR; + lnk->wr_reg.access = IB_ACCESS_LOCAL_WRITE | IB_ACCESS_REMOTE_WRITE; } void smc_wr_free_link(struct smc_link *lnk) @@ -602,6 +663,8 @@ int smc_wr_create_link(struct smc_link *lnk) smc_wr_init_sge(lnk); memset(lnk->wr_tx_mask, 0, BITS_TO_LONGS(SMC_WR_BUF_CNT) * sizeof(*lnk->wr_tx_mask)); + init_waitqueue_head(&lnk->wr_tx_wait); + init_waitqueue_head(&lnk->wr_reg_wait); return rc; dma_unmap: diff --git a/net/smc/smc_wr.h b/net/smc/smc_wr.h index 0b9beeda6053..45eb53833052 100644 --- a/net/smc/smc_wr.h +++ b/net/smc/smc_wr.h @@ -102,5 +102,6 @@ void smc_wr_tx_dismiss_slots(struct smc_link *lnk, u8 wr_rx_hdr_type, int smc_wr_rx_register_handler(struct smc_wr_rx_handler *handler); int smc_wr_rx_post_init(struct smc_link *link); void smc_wr_rx_cq_handler(struct ib_cq *ib_cq, void *cq_context); +int smc_wr_reg_send(struct smc_link *link, struct ib_mr *mr); #endif /* SMC_WR_H */ -- cgit v1.2.3 From d5b361b0dc3aa319522ef593b9bbf19ac3db23c5 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:18 +0200 Subject: net/smc: remove Kconfig warning Now separate memory regions are created and registered for separate RMBs. The unsafe_global_rkey of the protection domain is no longer used. Thus the exposing memory warning can be removed. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/Kconfig | 4 ---- 1 file changed, 4 deletions(-) (limited to 'net') diff --git a/net/smc/Kconfig b/net/smc/Kconfig index 33954852f3f8..c717ef0896aa 100644 --- a/net/smc/Kconfig +++ b/net/smc/Kconfig @@ -8,10 +8,6 @@ config SMC The Linux implementation of the SMC-R solution is designed as a separate socket family SMC. - Warning: SMC will expose all memory for remote reads and writes - once a connection is established. Don't enable this option except - for tightly controlled lab environment. - Select this option if you want to run SMC socket applications config SMC_DIAG -- cgit v1.2.3 From 9d8fb6173477ad61364eeab652a87c2a295fa601 Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:19 +0200 Subject: net/smc: introduce sg-logic for send buffers SMC send buffers are processed the same way as RMBs. Since RMBs have been converted to sg-logic, do the same for send buffers. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/smc_core.c | 46 +++++++++++++++++++++++++++++++++++----------- net/smc/smc_core.h | 2 -- net/smc/smc_ib.c | 29 ----------------------------- net/smc/smc_ib.h | 6 ------ net/smc/smc_tx.c | 6 +++--- 5 files changed, 38 insertions(+), 51 deletions(-) (limited to 'net') diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 87bb3e4771a8..8795c7ed9ce4 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -248,6 +248,7 @@ static void smc_link_clear(struct smc_link *lnk) static void smc_lgr_free_sndbufs(struct smc_link_group *lgr) { + struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK]; struct smc_buf_desc *sndbuf_desc, *bf_desc; int i; @@ -255,10 +256,11 @@ static void smc_lgr_free_sndbufs(struct smc_link_group *lgr) list_for_each_entry_safe(sndbuf_desc, bf_desc, &lgr->sndbufs[i], list) { list_del(&sndbuf_desc->list); - smc_ib_buf_unmap(lgr->lnk[SMC_SINGLE_LINK].smcibdev, - smc_uncompress_bufsize(i), - sndbuf_desc, DMA_TO_DEVICE); - kfree(sndbuf_desc->cpu_addr); + smc_ib_buf_unmap_sg(lnk->smcibdev, sndbuf_desc, + DMA_TO_DEVICE); + sg_free_table(&sndbuf_desc->sgt[SMC_SINGLE_LINK]); + free_pages((unsigned long)sndbuf_desc->cpu_addr, + sndbuf_desc->order); kfree(sndbuf_desc); } } @@ -517,6 +519,9 @@ int smc_sndbuf_create(struct smc_sock *smc) for (bufsize_short = smc_compress_bufsize(smc->sk.sk_sndbuf / 2); bufsize_short >= 0; bufsize_short--) { bufsize = smc_uncompress_bufsize(bufsize_short); + if ((1 << get_order(bufsize)) > SG_MAX_SINGLE_ALLOC) + continue; + /* check for reusable sndbuf_slot in the link group */ sndbuf_desc = smc_sndbuf_get_slot(lgr, bufsize_short); if (sndbuf_desc) { @@ -527,10 +532,12 @@ int smc_sndbuf_create(struct smc_sock *smc) sndbuf_desc = kzalloc(sizeof(*sndbuf_desc), GFP_KERNEL); if (!sndbuf_desc) break; /* give up with -ENOMEM */ - sndbuf_desc->cpu_addr = kzalloc(bufsize, - GFP_KERNEL | __GFP_NOWARN | - __GFP_NOMEMALLOC | - __GFP_NORETRY); + + sndbuf_desc->cpu_addr = + (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | + __GFP_NOMEMALLOC | + __GFP_NORETRY | __GFP_ZERO, + get_order(bufsize)); if (!sndbuf_desc->cpu_addr) { kfree(sndbuf_desc); sndbuf_desc = NULL; @@ -539,14 +546,31 @@ int smc_sndbuf_create(struct smc_sock *smc) */ continue; } - rc = smc_ib_buf_map(lgr->lnk[SMC_SINGLE_LINK].smcibdev, - bufsize, sndbuf_desc, DMA_TO_DEVICE); + sndbuf_desc->order = get_order(bufsize); + + rc = sg_alloc_table(&sndbuf_desc->sgt[SMC_SINGLE_LINK], 1, + GFP_KERNEL); if (rc) { - kfree(sndbuf_desc->cpu_addr); + free_pages((unsigned long)sndbuf_desc->cpu_addr, + sndbuf_desc->order); + kfree(sndbuf_desc); + sndbuf_desc = NULL; + continue; + } + sg_set_buf(sndbuf_desc->sgt[SMC_SINGLE_LINK].sgl, + sndbuf_desc->cpu_addr, bufsize); + + rc = smc_ib_buf_map_sg(lgr->lnk[SMC_SINGLE_LINK].smcibdev, + sndbuf_desc, DMA_TO_DEVICE); + if (rc != 1) { + sg_free_table(&sndbuf_desc->sgt[SMC_SINGLE_LINK]); + free_pages((unsigned long)sndbuf_desc->cpu_addr, + sndbuf_desc->order); kfree(sndbuf_desc); sndbuf_desc = NULL; continue; /* if mapping failed, try smaller one */ } + sndbuf_desc->used = 1; write_lock_bh(&lgr->sndbufs_lock); list_add(&sndbuf_desc->list, &lgr->sndbufs[bufsize_short]); diff --git a/net/smc/smc_core.h b/net/smc/smc_core.h index f7b40bdbf24c..72c25cb3eb89 100644 --- a/net/smc/smc_core.h +++ b/net/smc/smc_core.h @@ -102,8 +102,6 @@ struct smc_link { /* tx/rx buffer list element for sndbufs list and rmbs list of a lgr */ struct smc_buf_desc { struct list_head list; - u64 dma_addr[SMC_LINKS_PER_LGR_MAX]; - /* mapped address of buffer */ void *cpu_addr; /* virtual address of buffer */ struct sg_table sgt[SMC_LINKS_PER_LGR_MAX];/* virtual buffer */ struct ib_mr *mr_rx[SMC_LINKS_PER_LGR_MAX]; diff --git a/net/smc/smc_ib.c b/net/smc/smc_ib.c index 85e1831f591e..021f061609f5 100644 --- a/net/smc/smc_ib.c +++ b/net/smc/smc_ib.c @@ -295,35 +295,6 @@ int smc_ib_get_memory_region(struct ib_pd *pd, int access_flags, return 0; } -/* map a new TX or RX buffer to DMA */ -int smc_ib_buf_map(struct smc_ib_device *smcibdev, int buf_size, - struct smc_buf_desc *buf_slot, - enum dma_data_direction data_direction) -{ - int rc = 0; - - if (buf_slot->dma_addr[SMC_SINGLE_LINK]) - return rc; /* already mapped */ - buf_slot->dma_addr[SMC_SINGLE_LINK] = - ib_dma_map_single(smcibdev->ibdev, buf_slot->cpu_addr, - buf_size, data_direction); - if (ib_dma_mapping_error(smcibdev->ibdev, - buf_slot->dma_addr[SMC_SINGLE_LINK])) - rc = -EIO; - return rc; -} - -void smc_ib_buf_unmap(struct smc_ib_device *smcibdev, int buf_size, - struct smc_buf_desc *buf_slot, - enum dma_data_direction data_direction) -{ - if (!buf_slot->dma_addr[SMC_SINGLE_LINK]) - return; /* already unmapped */ - ib_dma_unmap_single(smcibdev->ibdev, *buf_slot->dma_addr, buf_size, - data_direction); - buf_slot->dma_addr[SMC_SINGLE_LINK] = 0; -} - /* Map a new TX or RX buffer SG-table to DMA */ int smc_ib_buf_map_sg(struct smc_ib_device *smcibdev, struct smc_buf_desc *buf_slot, diff --git a/net/smc/smc_ib.h b/net/smc/smc_ib.h index b57d29f29042..72acb19ffc67 100644 --- a/net/smc/smc_ib.h +++ b/net/smc/smc_ib.h @@ -51,12 +51,6 @@ int smc_ib_register_client(void) __init; void smc_ib_unregister_client(void); bool smc_ib_port_active(struct smc_ib_device *smcibdev, u8 ibport); int smc_ib_remember_port_attr(struct smc_ib_device *smcibdev, u8 ibport); -int smc_ib_buf_map(struct smc_ib_device *smcibdev, int buf_size, - struct smc_buf_desc *buf_slot, - enum dma_data_direction data_direction); -void smc_ib_buf_unmap(struct smc_ib_device *smcibdev, int bufsize, - struct smc_buf_desc *buf_slot, - enum dma_data_direction data_direction); int smc_ib_buf_map_sg(struct smc_ib_device *smcibdev, struct smc_buf_desc *buf_slot, enum dma_data_direction data_direction); diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c index 21ec1832ab51..f4d58e2dd559 100644 --- a/net/smc/smc_tx.c +++ b/net/smc/smc_tx.c @@ -277,6 +277,7 @@ static int smc_tx_rdma_writes(struct smc_connection *conn) struct smc_link_group *lgr = conn->lgr; int to_send, rmbespace; struct smc_link *link; + dma_addr_t dma_addr; int num_sges; int rc; @@ -334,12 +335,11 @@ static int smc_tx_rdma_writes(struct smc_connection *conn) src_len = conn->sndbuf_size - sent.count; } src_len_sum = src_len; + dma_addr = sg_dma_address(conn->sndbuf_desc->sgt[SMC_SINGLE_LINK].sgl); for (dstchunk = 0; dstchunk < 2; dstchunk++) { num_sges = 0; for (srcchunk = 0; srcchunk < 2; srcchunk++) { - sges[srcchunk].addr = - conn->sndbuf_desc->dma_addr[SMC_SINGLE_LINK] + - src_off; + sges[srcchunk].addr = dma_addr + src_off; sges[srcchunk].length = src_len; sges[srcchunk].lkey = link->roce_pd->local_dma_lkey; num_sges++; -- cgit v1.2.3 From 3e034725c0d814370a75b249c0ba794262cacd0f Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:20 +0200 Subject: net/smc: common functions for RMBs and send buffers Creation and deletion of SMC receive and send buffers shares a high amount of common code . This patch introduces common functions to get rid of duplicate code. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/af_smc.c | 16 +-- net/smc/smc_core.c | 348 ++++++++++++++++++++++------------------------------- net/smc/smc_core.h | 3 +- 3 files changed, 148 insertions(+), 219 deletions(-) (limited to 'net') diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index e0a95d50bf87..75518879b68a 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -436,12 +436,8 @@ static int smc_connect_rdma(struct smc_sock *smc) smc_conn_save_peer_info(smc, &aclc); - rc = smc_sndbuf_create(smc); - if (rc) { - reason_code = SMC_CLC_DECL_MEM; - goto decline_rdma_unlock; - } - rc = smc_rmb_create(smc); + /* create send buffer and rmb */ + rc = smc_buf_create(smc); if (rc) { reason_code = SMC_CLC_DECL_MEM; goto decline_rdma_unlock; @@ -813,12 +809,8 @@ static void smc_listen_work(struct work_struct *work) } link = &new_smc->conn.lgr->lnk[SMC_SINGLE_LINK]; - rc = smc_sndbuf_create(new_smc); - if (rc) { - reason_code = SMC_CLC_DECL_MEM; - goto decline_rdma; - } - rc = smc_rmb_create(new_smc); + /* create send buffer and rmb */ + rc = smc_buf_create(new_smc); if (rc) { reason_code = SMC_CLC_DECL_MEM; goto decline_rdma; diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 8795c7ed9ce4..ab8cdac5edb3 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -206,16 +206,12 @@ out: return rc; } -static void smc_sndbuf_unuse(struct smc_connection *conn) +static void smc_buf_unuse(struct smc_connection *conn) { if (conn->sndbuf_desc) { conn->sndbuf_desc->used = 0; conn->sndbuf_size = 0; } -} - -static void smc_rmb_unuse(struct smc_connection *conn) -{ if (conn->rmb_desc) { conn->rmb_desc->reused = true; conn->rmb_desc->used = 0; @@ -232,8 +228,7 @@ void smc_conn_free(struct smc_connection *conn) return; smc_cdc_tx_dismiss_slots(conn); smc_lgr_unregister_conn(conn); - smc_rmb_unuse(conn); - smc_sndbuf_unuse(conn); + smc_buf_unuse(conn); } static void smc_link_clear(struct smc_link *lnk) @@ -246,51 +241,57 @@ static void smc_link_clear(struct smc_link *lnk) smc_wr_free_link_mem(lnk); } -static void smc_lgr_free_sndbufs(struct smc_link_group *lgr) +static void smc_buf_free(struct smc_buf_desc *buf_desc, struct smc_link *lnk, + bool is_rmb) { - struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK]; - struct smc_buf_desc *sndbuf_desc, *bf_desc; - int i; - - for (i = 0; i < SMC_RMBE_SIZES; i++) { - list_for_each_entry_safe(sndbuf_desc, bf_desc, &lgr->sndbufs[i], - list) { - list_del(&sndbuf_desc->list); - smc_ib_buf_unmap_sg(lnk->smcibdev, sndbuf_desc, - DMA_TO_DEVICE); - sg_free_table(&sndbuf_desc->sgt[SMC_SINGLE_LINK]); - free_pages((unsigned long)sndbuf_desc->cpu_addr, - sndbuf_desc->order); - kfree(sndbuf_desc); - } + if (is_rmb) { + if (buf_desc->mr_rx[SMC_SINGLE_LINK]) + smc_ib_put_memory_region( + buf_desc->mr_rx[SMC_SINGLE_LINK]); + smc_ib_buf_unmap_sg(lnk->smcibdev, buf_desc, + DMA_FROM_DEVICE); + } else { + smc_ib_buf_unmap_sg(lnk->smcibdev, buf_desc, + DMA_TO_DEVICE); } + sg_free_table(&buf_desc->sgt[SMC_SINGLE_LINK]); + if (buf_desc->cpu_addr) + free_pages((unsigned long)buf_desc->cpu_addr, buf_desc->order); + kfree(buf_desc); } -static void smc_lgr_free_rmbs(struct smc_link_group *lgr) +static void __smc_lgr_free_bufs(struct smc_link_group *lgr, bool is_rmb) { struct smc_link *lnk = &lgr->lnk[SMC_SINGLE_LINK]; - struct smc_buf_desc *rmb_desc, *bf_desc; + struct smc_buf_desc *buf_desc, *bf_desc; + struct list_head *buf_list; int i; for (i = 0; i < SMC_RMBE_SIZES; i++) { - list_for_each_entry_safe(rmb_desc, bf_desc, &lgr->rmbs[i], + if (is_rmb) + buf_list = &lgr->rmbs[i]; + else + buf_list = &lgr->sndbufs[i]; + list_for_each_entry_safe(buf_desc, bf_desc, buf_list, list) { - list_del(&rmb_desc->list); - smc_ib_put_memory_region( - rmb_desc->mr_rx[SMC_SINGLE_LINK]); - smc_ib_buf_unmap_sg(lnk->smcibdev, rmb_desc, - DMA_FROM_DEVICE); - kfree(rmb_desc->cpu_addr); - kfree(rmb_desc); + list_del(&buf_desc->list); + smc_buf_free(buf_desc, lnk, is_rmb); } } } +static void smc_lgr_free_bufs(struct smc_link_group *lgr) +{ + /* free send buffers */ + __smc_lgr_free_bufs(lgr, false); + /* free rmbs */ + __smc_lgr_free_bufs(lgr, true); +} + /* remove a link group */ void smc_lgr_free(struct smc_link_group *lgr) { - smc_lgr_free_rmbs(lgr); - smc_lgr_free_sndbufs(lgr); + smc_lgr_free_bufs(lgr); smc_link_clear(&lgr->lnk[SMC_SINGLE_LINK]); kfree(lgr); } @@ -455,45 +456,25 @@ out: return rc ? rc : local_contact; } -/* try to reuse a sndbuf description slot of the sndbufs list for a certain - * buf_size; if not available, return NULL +/* try to reuse a sndbuf or rmb description slot for a certain + * buffer size; if not available, return NULL */ static inline -struct smc_buf_desc *smc_sndbuf_get_slot(struct smc_link_group *lgr, - int compressed_bufsize) +struct smc_buf_desc *smc_buf_get_slot(struct smc_link_group *lgr, + int compressed_bufsize, + rwlock_t *lock, + struct list_head *buf_list) { - struct smc_buf_desc *sndbuf_slot; - - read_lock_bh(&lgr->sndbufs_lock); - list_for_each_entry(sndbuf_slot, &lgr->sndbufs[compressed_bufsize], - list) { - if (cmpxchg(&sndbuf_slot->used, 0, 1) == 0) { - read_unlock_bh(&lgr->sndbufs_lock); - return sndbuf_slot; - } - } - read_unlock_bh(&lgr->sndbufs_lock); - return NULL; -} + struct smc_buf_desc *buf_slot; -/* try to reuse an rmb description slot of the rmbs list for a certain - * rmbe_size; if not available, return NULL - */ -static inline -struct smc_buf_desc *smc_rmb_get_slot(struct smc_link_group *lgr, - int compressed_bufsize) -{ - struct smc_buf_desc *rmb_slot; - - read_lock_bh(&lgr->rmbs_lock); - list_for_each_entry(rmb_slot, &lgr->rmbs[compressed_bufsize], - list) { - if (cmpxchg(&rmb_slot->used, 0, 1) == 0) { - read_unlock_bh(&lgr->rmbs_lock); - return rmb_slot; + read_lock_bh(lock); + list_for_each_entry(buf_slot, buf_list, list) { + if (cmpxchg(&buf_slot->used, 0, 1) == 0) { + read_unlock_bh(lock); + return buf_slot; } } - read_unlock_bh(&lgr->rmbs_lock); + read_unlock_bh(lock); return NULL; } @@ -506,185 +487,142 @@ static inline int smc_rmb_wnd_update_limit(int rmbe_size) return min_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2); } -/* create the tx buffer for an SMC socket */ -int smc_sndbuf_create(struct smc_sock *smc) +static int __smc_buf_create(struct smc_sock *smc, bool is_rmb) { struct smc_connection *conn = &smc->conn; struct smc_link_group *lgr = conn->lgr; - struct smc_buf_desc *sndbuf_desc; + struct smc_buf_desc *buf_desc = NULL; + struct list_head *buf_list; int bufsize, bufsize_short; + struct smc_link *lnk; + int sk_buf_size; + rwlock_t *lock; int rc; - /* use socket send buffer size (w/o overhead) as start value */ + lnk = &lgr->lnk[SMC_SINGLE_LINK]; + if (is_rmb) + /* use socket recv buffer size (w/o overhead) as start value */ + sk_buf_size = smc->sk.sk_rcvbuf / 2; + else + /* use socket send buffer size (w/o overhead) as start value */ + sk_buf_size = smc->sk.sk_sndbuf / 2; + for (bufsize_short = smc_compress_bufsize(smc->sk.sk_sndbuf / 2); bufsize_short >= 0; bufsize_short--) { - bufsize = smc_uncompress_bufsize(bufsize_short); - if ((1 << get_order(bufsize)) > SG_MAX_SINGLE_ALLOC) - continue; - - /* check for reusable sndbuf_slot in the link group */ - sndbuf_desc = smc_sndbuf_get_slot(lgr, bufsize_short); - if (sndbuf_desc) { - memset(sndbuf_desc->cpu_addr, 0, bufsize); - break; /* found reusable slot */ - } - /* try to alloc a new send buffer */ - sndbuf_desc = kzalloc(sizeof(*sndbuf_desc), GFP_KERNEL); - if (!sndbuf_desc) - break; /* give up with -ENOMEM */ - sndbuf_desc->cpu_addr = - (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | - __GFP_NOMEMALLOC | - __GFP_NORETRY | __GFP_ZERO, - get_order(bufsize)); - if (!sndbuf_desc->cpu_addr) { - kfree(sndbuf_desc); - sndbuf_desc = NULL; - /* if send buffer allocation has failed, - * try a smaller one - */ - continue; - } - sndbuf_desc->order = get_order(bufsize); - - rc = sg_alloc_table(&sndbuf_desc->sgt[SMC_SINGLE_LINK], 1, - GFP_KERNEL); - if (rc) { - free_pages((unsigned long)sndbuf_desc->cpu_addr, - sndbuf_desc->order); - kfree(sndbuf_desc); - sndbuf_desc = NULL; - continue; + if (is_rmb) { + lock = &lgr->rmbs_lock; + buf_list = &lgr->rmbs[bufsize_short]; + } else { + lock = &lgr->sndbufs_lock; + buf_list = &lgr->sndbufs[bufsize_short]; } - sg_set_buf(sndbuf_desc->sgt[SMC_SINGLE_LINK].sgl, - sndbuf_desc->cpu_addr, bufsize); - - rc = smc_ib_buf_map_sg(lgr->lnk[SMC_SINGLE_LINK].smcibdev, - sndbuf_desc, DMA_TO_DEVICE); - if (rc != 1) { - sg_free_table(&sndbuf_desc->sgt[SMC_SINGLE_LINK]); - free_pages((unsigned long)sndbuf_desc->cpu_addr, - sndbuf_desc->order); - kfree(sndbuf_desc); - sndbuf_desc = NULL; - continue; /* if mapping failed, try smaller one */ - } - - sndbuf_desc->used = 1; - write_lock_bh(&lgr->sndbufs_lock); - list_add(&sndbuf_desc->list, &lgr->sndbufs[bufsize_short]); - write_unlock_bh(&lgr->sndbufs_lock); - break; - } - if (sndbuf_desc && sndbuf_desc->cpu_addr) { - conn->sndbuf_desc = sndbuf_desc; - conn->sndbuf_size = bufsize; - smc->sk.sk_sndbuf = bufsize * 2; - atomic_set(&conn->sndbuf_space, bufsize); - return 0; - } else { - return -ENOMEM; - } -} - -/* create the RMB for an SMC socket (even though the SMC protocol - * allows more than one RMB-element per RMB, the Linux implementation - * uses just one RMB-element per RMB, i.e. uses an extra RMB for every - * connection in a link group - */ -int smc_rmb_create(struct smc_sock *smc) -{ - struct smc_connection *conn = &smc->conn; - struct smc_link_group *lgr = conn->lgr; - struct smc_buf_desc *rmb_desc; - int bufsize, bufsize_short; - int rc; - - /* use socket recv buffer size (w/o overhead) as start value */ - for (bufsize_short = smc_compress_bufsize(smc->sk.sk_rcvbuf / 2); - bufsize_short >= 0; bufsize_short--) { bufsize = smc_uncompress_bufsize(bufsize_short); if ((1 << get_order(bufsize)) > SG_MAX_SINGLE_ALLOC) continue; - /* check for reusable rmb_slot in the link group */ - rmb_desc = smc_rmb_get_slot(lgr, bufsize_short); - if (rmb_desc) { - memset(rmb_desc->cpu_addr, 0, bufsize); + /* check for reusable slot in the link group */ + buf_desc = smc_buf_get_slot(lgr, bufsize_short, lock, buf_list); + if (buf_desc) { + memset(buf_desc->cpu_addr, 0, bufsize); break; /* found reusable slot */ } - /* try to alloc a new RMB */ - rmb_desc = kzalloc(sizeof(*rmb_desc), GFP_KERNEL); - if (!rmb_desc) + /* try to allocate the determined number of pages */ + buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL); + if (!buf_desc) break; /* give up with -ENOMEM */ - rmb_desc->cpu_addr = + + buf_desc->cpu_addr = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_NOMEMALLOC | __GFP_NORETRY | __GFP_ZERO, get_order(bufsize)); - if (!rmb_desc->cpu_addr) { - kfree(rmb_desc); - rmb_desc = NULL; + if (!buf_desc->cpu_addr) { + kfree(buf_desc); + buf_desc = NULL; continue; } - rmb_desc->order = get_order(bufsize); - rc = sg_alloc_table(&rmb_desc->sgt[SMC_SINGLE_LINK], 1, + rc = sg_alloc_table(&buf_desc->sgt[SMC_SINGLE_LINK], 1, GFP_KERNEL); if (rc) { - free_pages((unsigned long)rmb_desc->cpu_addr, - rmb_desc->order); - kfree(rmb_desc); - rmb_desc = NULL; + smc_buf_free(buf_desc, lnk, is_rmb); + buf_desc = NULL; continue; } - sg_set_buf(rmb_desc->sgt[SMC_SINGLE_LINK].sgl, - rmb_desc->cpu_addr, bufsize); + sg_set_buf(buf_desc->sgt[SMC_SINGLE_LINK].sgl, + buf_desc->cpu_addr, bufsize); - rc = smc_ib_buf_map_sg(lgr->lnk[SMC_SINGLE_LINK].smcibdev, - rmb_desc, DMA_FROM_DEVICE); + /* map sg table to DMA address */ + rc = smc_ib_buf_map_sg(lnk->smcibdev, buf_desc, is_rmb ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); + /* SMC protocol depends on mapping to one DMA address only */ if (rc != 1) { - sg_free_table(&rmb_desc->sgt[SMC_SINGLE_LINK]); - free_pages((unsigned long)rmb_desc->cpu_addr, - rmb_desc->order); - kfree(rmb_desc); - rmb_desc = NULL; - continue; /* if mapping failed, try smaller one */ + smc_buf_free(buf_desc, lnk, is_rmb); + buf_desc = NULL; + continue; /* if mapping failed, try smaller one */ } - rc = smc_ib_get_memory_region(lgr->lnk[SMC_SINGLE_LINK].roce_pd, - IB_ACCESS_REMOTE_WRITE | - IB_ACCESS_LOCAL_WRITE, - rmb_desc); - if (rc) { - smc_ib_buf_unmap_sg(lgr->lnk[SMC_SINGLE_LINK].smcibdev, - rmb_desc, DMA_FROM_DEVICE); - sg_free_table(&rmb_desc->sgt[SMC_SINGLE_LINK]); - free_pages((unsigned long)rmb_desc->cpu_addr, - rmb_desc->order); - kfree(rmb_desc); - rmb_desc = NULL; - continue; + + /* create a new memory region for the RMB */ + if (is_rmb) { + rc = smc_ib_get_memory_region(lnk->roce_pd, + IB_ACCESS_REMOTE_WRITE | + IB_ACCESS_LOCAL_WRITE, + buf_desc); + if (rc) { + smc_buf_free(buf_desc, lnk, is_rmb); + buf_desc = NULL; + continue; + } } - rmb_desc->used = 1; - write_lock_bh(&lgr->rmbs_lock); - list_add(&rmb_desc->list, &lgr->rmbs[bufsize_short]); - write_unlock_bh(&lgr->rmbs_lock); - break; + buf_desc->used = 1; + write_lock_bh(lock); + list_add(&buf_desc->list, buf_list); + write_unlock_bh(lock); + break; /* found */ } - if (rmb_desc && rmb_desc->cpu_addr) { - conn->rmb_desc = rmb_desc; + + if (!buf_desc || !buf_desc->cpu_addr) + return -ENOMEM; + + if (is_rmb) { + conn->rmb_desc = buf_desc; conn->rmbe_size = bufsize; conn->rmbe_size_short = bufsize_short; smc->sk.sk_rcvbuf = bufsize * 2; atomic_set(&conn->bytes_to_rcv, 0); conn->rmbe_update_limit = smc_rmb_wnd_update_limit(bufsize); - return 0; } else { - return -ENOMEM; + conn->sndbuf_desc = buf_desc; + conn->sndbuf_size = bufsize; + smc->sk.sk_sndbuf = bufsize * 2; + atomic_set(&conn->sndbuf_space, bufsize); } + return 0; +} + +/* create the send and receive buffer for an SMC socket; + * receive buffers are called RMBs; + * (even though the SMC protocol allows more than one RMB-element per RMB, + * the Linux implementation uses just one RMB-element per RMB, i.e. uses an + * extra RMB for every connection in a link group + */ +int smc_buf_create(struct smc_sock *smc) +{ + int rc; + + /* create send buffer */ + rc = __smc_buf_create(smc, false); + if (rc) + return rc; + /* create rmb */ + rc = __smc_buf_create(smc, true); + if (rc) + smc_buf_free(smc->conn.sndbuf_desc, + &smc->conn.lgr->lnk[SMC_SINGLE_LINK], false); + return rc; } static inline int smc_rmb_reserve_rtoken_idx(struct smc_link_group *lgr) diff --git a/net/smc/smc_core.h b/net/smc/smc_core.h index 72c25cb3eb89..1d713e8e067c 100644 --- a/net/smc/smc_core.h +++ b/net/smc/smc_core.h @@ -186,8 +186,7 @@ struct smc_clc_msg_accept_confirm; void smc_lgr_free(struct smc_link_group *lgr); void smc_lgr_terminate(struct smc_link_group *lgr); -int smc_sndbuf_create(struct smc_sock *smc); -int smc_rmb_create(struct smc_sock *smc); +int smc_buf_create(struct smc_sock *smc); int smc_rmb_rtoken_handling(struct smc_connection *conn, struct smc_clc_msg_accept_confirm *clc); #endif -- cgit v1.2.3 From b33982c3a6838d13024ae5a32e1f21897767b04b Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:21 +0200 Subject: net/smc: cleanup function __smc_buf_create() Split function __smc_buf_create() for better readability. Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/smc_core.c | 114 +++++++++++++++++++++++++++++------------------------ 1 file changed, 63 insertions(+), 51 deletions(-) (limited to 'net') diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index ab8cdac5edb3..447bd52da0e2 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -487,6 +487,64 @@ static inline int smc_rmb_wnd_update_limit(int rmbe_size) return min_t(int, rmbe_size / 10, SOCK_MIN_SNDBUF / 2); } +static struct smc_buf_desc *smc_new_buf_create(struct smc_link_group *lgr, + bool is_rmb, int bufsize) +{ + struct smc_buf_desc *buf_desc; + struct smc_link *lnk; + int rc; + + /* try to alloc a new buffer */ + buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL); + if (!buf_desc) + return ERR_PTR(-ENOMEM); + + buf_desc->cpu_addr = + (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | + __GFP_NOMEMALLOC | + __GFP_NORETRY | __GFP_ZERO, + get_order(bufsize)); + if (!buf_desc->cpu_addr) { + kfree(buf_desc); + return ERR_PTR(-EAGAIN); + } + buf_desc->order = get_order(bufsize); + + /* build the sg table from the pages */ + lnk = &lgr->lnk[SMC_SINGLE_LINK]; + rc = sg_alloc_table(&buf_desc->sgt[SMC_SINGLE_LINK], 1, + GFP_KERNEL); + if (rc) { + smc_buf_free(buf_desc, lnk, is_rmb); + return ERR_PTR(rc); + } + sg_set_buf(buf_desc->sgt[SMC_SINGLE_LINK].sgl, + buf_desc->cpu_addr, bufsize); + + /* map sg table to DMA address */ + rc = smc_ib_buf_map_sg(lnk->smcibdev, buf_desc, + is_rmb ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + /* SMC protocol depends on mapping to one DMA address only */ + if (rc != 1) { + smc_buf_free(buf_desc, lnk, is_rmb); + return ERR_PTR(-EAGAIN); + } + + /* create a new memory region for the RMB */ + if (is_rmb) { + rc = smc_ib_get_memory_region(lnk->roce_pd, + IB_ACCESS_REMOTE_WRITE | + IB_ACCESS_LOCAL_WRITE, + buf_desc); + if (rc) { + smc_buf_free(buf_desc, lnk, is_rmb); + return ERR_PTR(rc); + } + } + + return buf_desc; +} + static int __smc_buf_create(struct smc_sock *smc, bool is_rmb) { struct smc_connection *conn = &smc->conn; @@ -494,12 +552,9 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_rmb) struct smc_buf_desc *buf_desc = NULL; struct list_head *buf_list; int bufsize, bufsize_short; - struct smc_link *lnk; int sk_buf_size; rwlock_t *lock; - int rc; - lnk = &lgr->lnk[SMC_SINGLE_LINK]; if (is_rmb) /* use socket recv buffer size (w/o overhead) as start value */ sk_buf_size = smc->sk.sk_rcvbuf / 2; @@ -528,54 +583,11 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_rmb) break; /* found reusable slot */ } - /* try to allocate the determined number of pages */ - buf_desc = kzalloc(sizeof(*buf_desc), GFP_KERNEL); - if (!buf_desc) - break; /* give up with -ENOMEM */ - - buf_desc->cpu_addr = - (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | - __GFP_NOMEMALLOC | - __GFP_NORETRY | __GFP_ZERO, - get_order(bufsize)); - if (!buf_desc->cpu_addr) { - kfree(buf_desc); - buf_desc = NULL; - continue; - } - - rc = sg_alloc_table(&buf_desc->sgt[SMC_SINGLE_LINK], 1, - GFP_KERNEL); - if (rc) { - smc_buf_free(buf_desc, lnk, is_rmb); - buf_desc = NULL; + buf_desc = smc_new_buf_create(lgr, is_rmb, bufsize); + if (PTR_ERR(buf_desc) == -ENOMEM) + break; + if (IS_ERR(buf_desc)) continue; - } - sg_set_buf(buf_desc->sgt[SMC_SINGLE_LINK].sgl, - buf_desc->cpu_addr, bufsize); - - /* map sg table to DMA address */ - rc = smc_ib_buf_map_sg(lnk->smcibdev, buf_desc, is_rmb ? - DMA_FROM_DEVICE : DMA_TO_DEVICE); - /* SMC protocol depends on mapping to one DMA address only */ - if (rc != 1) { - smc_buf_free(buf_desc, lnk, is_rmb); - buf_desc = NULL; - continue; /* if mapping failed, try smaller one */ - } - - /* create a new memory region for the RMB */ - if (is_rmb) { - rc = smc_ib_get_memory_region(lnk->roce_pd, - IB_ACCESS_REMOTE_WRITE | - IB_ACCESS_LOCAL_WRITE, - buf_desc); - if (rc) { - smc_buf_free(buf_desc, lnk, is_rmb); - buf_desc = NULL; - continue; - } - } buf_desc->used = 1; write_lock_bh(lock); @@ -584,7 +596,7 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_rmb) break; /* found */ } - if (!buf_desc || !buf_desc->cpu_addr) + if (IS_ERR(buf_desc)) return -ENOMEM; if (is_rmb) { -- cgit v1.2.3 From 10428dd8354cc1c74ee806df45c2227c1f9d7b0c Mon Sep 17 00:00:00 2001 From: Ursula Braun Date: Fri, 28 Jul 2017 13:56:22 +0200 Subject: net/smc: synchronize buffer usage with device Usage of send buffer "sndbuf" is synced (a) before filling sndbuf for cpu access (b) after filling sndbuf for device access Usage of receive buffer "RMB" is synced (a) before reading RMB content for cpu access (b) after reading RMB content for device access Signed-off-by: Ursula Braun Signed-off-by: David S. Miller --- net/smc/af_smc.c | 2 ++ net/smc/smc_core.c | 32 ++++++++++++++++++++++++++++++++ net/smc/smc_core.h | 4 ++++ net/smc/smc_ib.c | 41 +++++++++++++++++++++++++++++++++++++++++ net/smc/smc_ib.h | 6 ++++++ net/smc/smc_rx.c | 3 +++ net/smc/smc_tx.c | 3 +++ 7 files changed, 91 insertions(+) (limited to 'net') diff --git a/net/smc/af_smc.c b/net/smc/af_smc.c index 75518879b68a..8c6d24b2995d 100644 --- a/net/smc/af_smc.c +++ b/net/smc/af_smc.c @@ -474,6 +474,7 @@ static int smc_connect_rdma(struct smc_sock *smc) } } } + smc_rmb_sync_sg_for_device(&smc->conn); rc = smc_clc_send_confirm(smc); if (rc) @@ -832,6 +833,7 @@ static void smc_listen_work(struct work_struct *work) } } } + smc_rmb_sync_sg_for_device(&new_smc->conn); rc = smc_clc_send_accept(new_smc, local_contact); if (rc) diff --git a/net/smc/smc_core.c b/net/smc/smc_core.c index 447bd52da0e2..1a16d51e2330 100644 --- a/net/smc/smc_core.c +++ b/net/smc/smc_core.c @@ -615,6 +615,38 @@ static int __smc_buf_create(struct smc_sock *smc, bool is_rmb) return 0; } +void smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn) +{ + struct smc_link_group *lgr = conn->lgr; + + smc_ib_sync_sg_for_cpu(lgr->lnk[SMC_SINGLE_LINK].smcibdev, + conn->sndbuf_desc, DMA_TO_DEVICE); +} + +void smc_sndbuf_sync_sg_for_device(struct smc_connection *conn) +{ + struct smc_link_group *lgr = conn->lgr; + + smc_ib_sync_sg_for_device(lgr->lnk[SMC_SINGLE_LINK].smcibdev, + conn->sndbuf_desc, DMA_TO_DEVICE); +} + +void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn) +{ + struct smc_link_group *lgr = conn->lgr; + + smc_ib_sync_sg_for_cpu(lgr->lnk[SMC_SINGLE_LINK].smcibdev, + conn->rmb_desc, DMA_FROM_DEVICE); +} + +void smc_rmb_sync_sg_for_device(struct smc_connection *conn) +{ + struct smc_link_group *lgr = conn->lgr; + + smc_ib_sync_sg_for_device(lgr->lnk[SMC_SINGLE_LINK].smcibdev, + conn->rmb_desc, DMA_FROM_DEVICE); +} + /* create the send and receive buffer for an SMC socket; * receive buffers are called RMBs; * (even though the SMC protocol allows more than one RMB-element per RMB, diff --git a/net/smc/smc_core.h b/net/smc/smc_core.h index 1d713e8e067c..19c44bf4e391 100644 --- a/net/smc/smc_core.h +++ b/net/smc/smc_core.h @@ -189,4 +189,8 @@ void smc_lgr_terminate(struct smc_link_group *lgr); int smc_buf_create(struct smc_sock *smc); int smc_rmb_rtoken_handling(struct smc_connection *conn, struct smc_clc_msg_accept_confirm *clc); +void smc_sndbuf_sync_sg_for_cpu(struct smc_connection *conn); +void smc_sndbuf_sync_sg_for_device(struct smc_connection *conn); +void smc_rmb_sync_sg_for_cpu(struct smc_connection *conn); +void smc_rmb_sync_sg_for_device(struct smc_connection *conn); #endif diff --git a/net/smc/smc_ib.c b/net/smc/smc_ib.c index 021f061609f5..547e0e113b17 100644 --- a/net/smc/smc_ib.c +++ b/net/smc/smc_ib.c @@ -13,6 +13,7 @@ #include #include +#include #include #include "smc_pnet.h" @@ -295,6 +296,46 @@ int smc_ib_get_memory_region(struct ib_pd *pd, int access_flags, return 0; } +/* synchronize buffer usage for cpu access */ +void smc_ib_sync_sg_for_cpu(struct smc_ib_device *smcibdev, + struct smc_buf_desc *buf_slot, + enum dma_data_direction data_direction) +{ + struct scatterlist *sg; + unsigned int i; + + /* for now there is just one DMA address */ + for_each_sg(buf_slot->sgt[SMC_SINGLE_LINK].sgl, sg, + buf_slot->sgt[SMC_SINGLE_LINK].nents, i) { + if (!sg_dma_len(sg)) + break; + ib_dma_sync_single_for_cpu(smcibdev->ibdev, + sg_dma_address(sg), + sg_dma_len(sg), + data_direction); + } +} + +/* synchronize buffer usage for device access */ +void smc_ib_sync_sg_for_device(struct smc_ib_device *smcibdev, + struct smc_buf_desc *buf_slot, + enum dma_data_direction data_direction) +{ + struct scatterlist *sg; + unsigned int i; + + /* for now there is just one DMA address */ + for_each_sg(buf_slot->sgt[SMC_SINGLE_LINK].sgl, sg, + buf_slot->sgt[SMC_SINGLE_LINK].nents, i) { + if (!sg_dma_len(sg)) + break; + ib_dma_sync_single_for_device(smcibdev->ibdev, + sg_dma_address(sg), + sg_dma_len(sg), + data_direction); + } +} + /* Map a new TX or RX buffer SG-table to DMA */ int smc_ib_buf_map_sg(struct smc_ib_device *smcibdev, struct smc_buf_desc *buf_slot, diff --git a/net/smc/smc_ib.h b/net/smc/smc_ib.h index 72acb19ffc67..9b927a33d5e6 100644 --- a/net/smc/smc_ib.h +++ b/net/smc/smc_ib.h @@ -68,4 +68,10 @@ long smc_ib_setup_per_ibdev(struct smc_ib_device *smcibdev); int smc_ib_get_memory_region(struct ib_pd *pd, int access_flags, struct smc_buf_desc *buf_slot); void smc_ib_put_memory_region(struct ib_mr *mr); +void smc_ib_sync_sg_for_cpu(struct smc_ib_device *smcibdev, + struct smc_buf_desc *buf_slot, + enum dma_data_direction data_direction); +void smc_ib_sync_sg_for_device(struct smc_ib_device *smcibdev, + struct smc_buf_desc *buf_slot, + enum dma_data_direction data_direction); #endif diff --git a/net/smc/smc_rx.c b/net/smc/smc_rx.c index f0c8b089f770..b17a333e9bb0 100644 --- a/net/smc/smc_rx.c +++ b/net/smc/smc_rx.c @@ -170,6 +170,7 @@ copy: copylen, conn->rmbe_size - cons.count); chunk_len_sum = chunk_len; chunk_off = cons.count; + smc_rmb_sync_sg_for_cpu(conn); for (chunk = 0; chunk < 2; chunk++) { if (!(flags & MSG_TRUNC)) { rc = memcpy_to_msg(msg, rcvbuf_base + chunk_off, @@ -177,6 +178,7 @@ copy: if (rc) { if (!read_done) read_done = -EFAULT; + smc_rmb_sync_sg_for_device(conn); goto out; } } @@ -190,6 +192,7 @@ copy: chunk_len_sum += chunk_len; chunk_off = 0; /* modulo offset in recv ring buffer */ } + smc_rmb_sync_sg_for_device(conn); /* update cursors */ if (!(flags & MSG_PEEK)) { diff --git a/net/smc/smc_tx.c b/net/smc/smc_tx.c index f4d58e2dd559..3c656beb8820 100644 --- a/net/smc/smc_tx.c +++ b/net/smc/smc_tx.c @@ -174,10 +174,12 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len) copylen, conn->sndbuf_size - tx_cnt_prep); chunk_len_sum = chunk_len; chunk_off = tx_cnt_prep; + smc_sndbuf_sync_sg_for_cpu(conn); for (chunk = 0; chunk < 2; chunk++) { rc = memcpy_from_msg(sndbuf_base + chunk_off, msg, chunk_len); if (rc) { + smc_sndbuf_sync_sg_for_device(conn); if (send_done) return send_done; goto out_err; @@ -192,6 +194,7 @@ int smc_tx_sendmsg(struct smc_sock *smc, struct msghdr *msg, size_t len) chunk_len_sum += chunk_len; chunk_off = 0; /* modulo offset in send ring buffer */ } + smc_sndbuf_sync_sg_for_device(conn); /* update cursors */ smc_curs_add(conn->sndbuf_size, &prep, copylen); smc_curs_write(&conn->tx_curs_prep, -- cgit v1.2.3 From 1a5f3da20bd966220931239fbd31e6ac6ff42251 Mon Sep 17 00:00:00 2001 From: Vidya Sagar Ravipati Date: Thu, 27 Jul 2017 16:47:26 -0700 Subject: net: ethtool: add support for forward error correction modes Forward Error Correction (FEC) modes i.e Base-R and Reed-Solomon modes are introduced in 25G/40G/100G standards for providing good BER at high speeds. Various networking devices which support 25G/40G/100G provides ability to manage supported FEC modes and the lack of FEC encoding control and reporting today is a source for interoperability issues for many vendors. FEC capability as well as specific FEC mode i.e. Base-R or RS modes can be requested or advertised through bits D44:47 of base link codeword. This patch set intends to provide option under ethtool to manage and report FEC encoding settings for networking devices as per IEEE 802.3 bj, bm and by specs. set-fec/show-fec option(s) are designed to provide control and report the FEC encoding on the link. SET FEC option: root@tor: ethtool --set-fec swp1 encoding [off | RS | BaseR | auto] Encoding: Types of encoding Off : Turning off any encoding RS : enforcing RS-FEC encoding on supported speeds BaseR : enforcing Base R encoding on supported speeds Auto : IEEE defaults for the speed/medium combination Here are a few examples of what we would expect if encoding=auto: - if autoneg is on, we are expecting FEC to be negotiated as on or off as long as protocol supports it - if the hardware is capable of detecting the FEC encoding on it's receiver it will reconfigure its encoder to match - in absence of the above, the configuration would be set to IEEE defaults. >From our understanding , this is essentially what most hardware/driver combinations are doing today in the absence of a way for users to control the behavior. SHOW FEC option: root@tor: ethtool --show-fec swp1 FEC parameters for swp1: Active FEC encodings: RS Configured FEC encodings: RS | BaseR ETHTOOL DEVNAME output modification: ethtool devname output: root@tor:~# ethtool swp1 Settings for swp1: root@hpe-7712-03:~# ethtool swp18 Settings for swp18: Supported ports: [ FIBRE ] Supported link modes: 40000baseCR4/Full 40000baseSR4/Full 40000baseLR4/Full 100000baseSR4/Full 100000baseCR4/Full 100000baseLR4_ER4/Full Supported pause frame use: No Supports auto-negotiation: Yes Supported FEC modes: [RS | BaseR | None | Not reported] Advertised link modes: Not reported Advertised pause frame use: No Advertised auto-negotiation: No Advertised FEC modes: [RS | BaseR | None | Not reported] <<<< One or more FEC modes Speed: 100000Mb/s Duplex: Full Port: FIBRE PHYAD: 106 Transceiver: internal Auto-negotiation: off Link detected: yes This patch includes following changes a) New ETHTOOL_SFECPARAM/SFECPARAM API, handled by the new get_fecparam/set_fecparam callbacks, provides support for configuration of forward error correction modes. b) Link mode bits for FEC modes i.e. None (No FEC mode), RS, BaseR/FC are defined so that users can configure these fec modes for supported and advertising fields as part of link autonegotiation. Signed-off-by: Vidya Sagar Ravipati Signed-off-by: Dustin Byford Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller --- include/linux/ethtool.h | 4 ++++ include/uapi/linux/ethtool.h | 48 +++++++++++++++++++++++++++++++++++++++++++- net/core/ethtool.c | 34 +++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h index 83cc9863444b..afdbb701fdb4 100644 --- a/include/linux/ethtool.h +++ b/include/linux/ethtool.h @@ -374,5 +374,9 @@ struct ethtool_ops { struct ethtool_link_ksettings *); int (*set_link_ksettings)(struct net_device *, const struct ethtool_link_ksettings *); + int (*get_fecparam)(struct net_device *, + struct ethtool_fecparam *); + int (*set_fecparam)(struct net_device *, + struct ethtool_fecparam *); }; #endif /* _LINUX_ETHTOOL_H */ diff --git a/include/uapi/linux/ethtool.h b/include/uapi/linux/ethtool.h index 7d4a594d5d58..9c041dae8e2c 100644 --- a/include/uapi/linux/ethtool.h +++ b/include/uapi/linux/ethtool.h @@ -1238,6 +1238,47 @@ struct ethtool_per_queue_op { char data[]; }; +/** + * struct ethtool_fecparam - Ethernet forward error correction(fec) parameters + * @cmd: Command number = %ETHTOOL_GFECPARAM or %ETHTOOL_SFECPARAM + * @active_fec: FEC mode which is active on porte + * @fec: Bitmask of supported/configured FEC modes + * @rsvd: Reserved for future extensions. i.e FEC bypass feature. + * + * Drivers should reject a non-zero setting of @autoneg when + * autoneogotiation is disabled (or not supported) for the link. + * + */ +struct ethtool_fecparam { + __u32 cmd; + /* bitmask of FEC modes */ + __u32 active_fec; + __u32 fec; + __u32 reserved; +}; + +/** + * enum ethtool_fec_config_bits - flags definition of ethtool_fec_configuration + * @ETHTOOL_FEC_NONE: FEC mode configuration is not supported + * @ETHTOOL_FEC_AUTO: Default/Best FEC mode provided by driver + * @ETHTOOL_FEC_OFF: No FEC Mode + * @ETHTOOL_FEC_RS: Reed-Solomon Forward Error Detection mode + * @ETHTOOL_FEC_BASER: Base-R/Reed-Solomon Forward Error Detection mode + */ +enum ethtool_fec_config_bits { + ETHTOOL_FEC_NONE_BIT, + ETHTOOL_FEC_AUTO_BIT, + ETHTOOL_FEC_OFF_BIT, + ETHTOOL_FEC_RS_BIT, + ETHTOOL_FEC_BASER_BIT, +}; + +#define ETHTOOL_FEC_NONE (1 << ETHTOOL_FEC_NONE_BIT) +#define ETHTOOL_FEC_AUTO (1 << ETHTOOL_FEC_AUTO_BIT) +#define ETHTOOL_FEC_OFF (1 << ETHTOOL_FEC_OFF_BIT) +#define ETHTOOL_FEC_RS (1 << ETHTOOL_FEC_RS_BIT) +#define ETHTOOL_FEC_BASER (1 << ETHTOOL_FEC_BASER_BIT) + /* CMDs currently supported */ #define ETHTOOL_GSET 0x00000001 /* DEPRECATED, Get settings. * Please use ETHTOOL_GLINKSETTINGS @@ -1330,6 +1371,8 @@ struct ethtool_per_queue_op { #define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */ #define ETHTOOL_PHY_GTUNABLE 0x0000004e /* Get PHY tunable configuration */ #define ETHTOOL_PHY_STUNABLE 0x0000004f /* Set PHY tunable configuration */ +#define ETHTOOL_GFECPARAM 0x00000050 /* Get FEC settings */ +#define ETHTOOL_SFECPARAM 0x00000051 /* Set FEC settings */ /* compatibility with older code */ #define SPARC_ETH_GSET ETHTOOL_GSET @@ -1387,6 +1430,9 @@ enum ethtool_link_mode_bit_indices { ETHTOOL_LINK_MODE_2500baseT_Full_BIT = 47, ETHTOOL_LINK_MODE_5000baseT_Full_BIT = 48, + ETHTOOL_LINK_MODE_FEC_NONE_BIT = 49, + ETHTOOL_LINK_MODE_FEC_RS_BIT = 50, + ETHTOOL_LINK_MODE_FEC_BASER_BIT = 51, /* Last allowed bit for __ETHTOOL_LINK_MODE_LEGACY_MASK is bit * 31. Please do NOT define any SUPPORTED_* or ADVERTISED_* @@ -1395,7 +1441,7 @@ enum ethtool_link_mode_bit_indices { */ __ETHTOOL_LINK_MODE_LAST - = ETHTOOL_LINK_MODE_5000baseT_Full_BIT, + = ETHTOOL_LINK_MODE_FEC_BASER_BIT, }; #define __ETHTOOL_LINK_MODE_LEGACY_MASK(base_name) \ diff --git a/net/core/ethtool.c b/net/core/ethtool.c index b987bc475fc8..6a582ae4c5d9 100644 --- a/net/core/ethtool.c +++ b/net/core/ethtool.c @@ -2512,6 +2512,33 @@ static int set_phy_tunable(struct net_device *dev, void __user *useraddr) return ret; } +static int ethtool_get_fecparam(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_fecparam fecparam = { ETHTOOL_GFECPARAM }; + + if (!dev->ethtool_ops->get_fecparam) + return -EOPNOTSUPP; + + dev->ethtool_ops->get_fecparam(dev, &fecparam); + + if (copy_to_user(useraddr, &fecparam, sizeof(fecparam))) + return -EFAULT; + return 0; +} + +static int ethtool_set_fecparam(struct net_device *dev, void __user *useraddr) +{ + struct ethtool_fecparam fecparam; + + if (!dev->ethtool_ops->set_fecparam) + return -EOPNOTSUPP; + + if (copy_from_user(&fecparam, useraddr, sizeof(fecparam))) + return -EFAULT; + + return dev->ethtool_ops->set_fecparam(dev, &fecparam); +} + /* The main entry point in this file. Called from net/core/dev_ioctl.c */ int dev_ethtool(struct net *net, struct ifreq *ifr) @@ -2570,6 +2597,7 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_GTUNABLE: case ETHTOOL_PHY_GTUNABLE: case ETHTOOL_GLINKSETTINGS: + case ETHTOOL_GFECPARAM: break; default: if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) @@ -2779,6 +2807,12 @@ int dev_ethtool(struct net *net, struct ifreq *ifr) case ETHTOOL_PHY_STUNABLE: rc = set_phy_tunable(dev, useraddr); break; + case ETHTOOL_GFECPARAM: + rc = ethtool_get_fecparam(dev, useraddr); + break; + case ETHTOOL_SFECPARAM: + rc = ethtool_set_fecparam(dev, useraddr); + break; default: rc = -EOPNOTSUPP; } -- cgit v1.2.3 From df823b02970172f3e4003063699e333295b9b32d Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 30 Jul 2017 13:24:50 -0400 Subject: net sched actions: Use proper root attribute table for actions Bug fix for an issue which has been around for about a decade. We got away with it because the enumeration was larger than needed. Fixes: 7ba699c604ab ("[NET_SCHED]: Convert actions from rtnetlink to new netlink API") Suggested-by: Jiri Pirko Reviewed-by: Simon Horman Signed-off-by: Jamal Hadi Salim Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/act_api.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sched/act_api.c b/net/sched/act_api.c index f2e9ed34a963..848370e2fcca 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1072,7 +1072,7 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); - struct nlattr *tca[TCA_ACT_MAX + 1]; + struct nlattr *tca[TCAA_MAX + 1]; u32 portid = skb ? NETLINK_CB(skb).portid : 0; int ret = 0, ovr = 0; @@ -1080,7 +1080,7 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, !netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; - ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ACT_MAX, NULL, + ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCAA_MAX, NULL, extack); if (ret < 0) return ret; -- cgit v1.2.3 From 90825b23a887f06f6c05bdde77b200c5fe9b6217 Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 30 Jul 2017 13:24:51 -0400 Subject: net sched actions: dump more than TCA_ACT_MAX_PRIO actions per batch When you dump hundreds of thousands of actions, getting only 32 per dump batch even when the socket buffer and memory allocations allow is inefficient. With this change, the user will get as many as possibly fitting within the given constraints available to the kernel. The top level action TLV space is extended. An attribute TCA_ROOT_FLAGS is used to carry flags; flag TCA_FLAG_LARGE_DUMP_ON is set by the user indicating the user is capable of processing these large dumps. Older user space which doesnt set this flag doesnt get the large (than 32) batches. The kernel uses the TCA_ROOT_COUNT attribute to tell the user how many actions are put in a single batch. As such user space app knows how long to iterate (independent of the type of action being dumped) instead of hardcoded maximum of 32 thus maintaining backward compat. Some results dumping 1.5M actions below: first an unpatched tc which doesnt understand these features... prompt$ time -p tc actions ls action gact | grep index | wc -l 1500000 real 1388.43 user 2.07 sys 1386.79 Now lets see a patched tc which sets the correct flags when requesting a dump: prompt$ time -p updatedtc actions ls action gact | grep index | wc -l 1500000 real 178.13 user 2.02 sys 176.96 That is about 8x performance improvement for tc app which sets its receive buffer to about 32K. Signed-off-by: Jamal Hadi Salim Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/uapi/linux/rtnetlink.h | 22 +++++++++++++++++-- net/sched/act_api.c | 50 +++++++++++++++++++++++++++++++++--------- 2 files changed, 60 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index d148505010a7..bfa80a6164d9 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -683,10 +683,28 @@ struct tcamsg { unsigned char tca__pad1; unsigned short tca__pad2; }; + +enum { + TCA_ROOT_UNSPEC, + TCA_ROOT_TAB, +#define TCA_ACT_TAB TCA_ROOT_TAB +#define TCAA_MAX TCA_ROOT_TAB + TCA_ROOT_FLAGS, + TCA_ROOT_COUNT, + __TCA_ROOT_MAX, +#define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1) +}; + #define TA_RTA(r) ((struct rtattr*)(((char*)(r)) + NLMSG_ALIGN(sizeof(struct tcamsg)))) #define TA_PAYLOAD(n) NLMSG_PAYLOAD(n,sizeof(struct tcamsg)) -#define TCA_ACT_TAB 1 /* attr type must be >=1 */ -#define TCAA_MAX 1 +/* tcamsg flags stored in attribute TCA_ROOT_FLAGS + * + * TCA_FLAG_LARGE_DUMP_ON user->kernel to request for larger than TCA_ACT_MAX_PRIO + * actions in a dump. All dump responses will contain the number of actions + * being dumped stored in for user app's consumption in TCA_ROOT_COUNT + * + */ +#define TCA_FLAG_LARGE_DUMP_ON (1 << 0) /* New extended info filters for IFLA_EXT_MASK */ #define RTEXT_FILTER_VF (1 << 0) diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 848370e2fcca..d53653a73c4f 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -110,6 +110,7 @@ static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, struct netlink_callback *cb) { int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; + u32 act_flags = cb->args[2]; struct nlattr *nest; spin_lock_bh(&hinfo->lock); @@ -138,14 +139,18 @@ static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, } nla_nest_end(skb, nest); n_i++; - if (n_i >= TCA_ACT_MAX_PRIO) + if (!(act_flags & TCA_FLAG_LARGE_DUMP_ON) && + n_i >= TCA_ACT_MAX_PRIO) goto done; } } done: spin_unlock_bh(&hinfo->lock); - if (n_i) + if (n_i) { cb->args[0] += n_i; + if (act_flags & TCA_FLAG_LARGE_DUMP_ON) + cb->args[1] = n_i; + } return n_i; nla_put_failure: @@ -1068,11 +1073,17 @@ static int tcf_action_add(struct net *net, struct nlattr *nla, return tcf_add_notify(net, n, &actions, portid); } +static u32 tcaa_root_flags_allowed = TCA_FLAG_LARGE_DUMP_ON; +static const struct nla_policy tcaa_policy[TCA_ROOT_MAX + 1] = { + [TCA_ROOT_FLAGS] = { .type = NLA_BITFIELD32, + .validation_data = &tcaa_root_flags_allowed }, +}; + static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); - struct nlattr *tca[TCAA_MAX + 1]; + struct nlattr *tca[TCA_ROOT_MAX + 1]; u32 portid = skb ? NETLINK_CB(skb).portid : 0; int ret = 0, ovr = 0; @@ -1080,7 +1091,7 @@ static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, !netlink_capable(skb, CAP_NET_ADMIN)) return -EPERM; - ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCAA_MAX, NULL, + ret = nlmsg_parse(n, sizeof(struct tcamsg), tca, TCA_ROOT_MAX, NULL, extack); if (ret < 0) return ret; @@ -1121,16 +1132,12 @@ replay: return ret; } -static struct nlattr *find_dump_kind(const struct nlmsghdr *n) +static struct nlattr *find_dump_kind(struct nlattr **nla) { struct nlattr *tb1, *tb2[TCA_ACT_MAX + 1]; struct nlattr *tb[TCA_ACT_MAX_PRIO + 1]; - struct nlattr *nla[TCAA_MAX + 1]; struct nlattr *kind; - if (nlmsg_parse(n, sizeof(struct tcamsg), nla, TCAA_MAX, - NULL, NULL) < 0) - return NULL; tb1 = nla[TCA_ACT_TAB]; if (tb1 == NULL) return NULL; @@ -1157,8 +1164,18 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) struct tc_action_ops *a_o; int ret = 0; struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh); - struct nlattr *kind = find_dump_kind(cb->nlh); + struct nlattr *tb[TCA_ROOT_MAX + 1]; + struct nlattr *count_attr = NULL; + struct nlattr *kind = NULL; + struct nla_bitfield32 bf; + u32 act_count = 0; + + ret = nlmsg_parse(cb->nlh, sizeof(struct tcamsg), tb, TCA_ROOT_MAX, + tcaa_policy, NULL); + if (ret < 0) + return ret; + kind = find_dump_kind(tb); if (kind == NULL) { pr_info("tc_dump_action: action bad kind\n"); return 0; @@ -1168,14 +1185,24 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) if (a_o == NULL) return 0; + cb->args[2] = 0; + if (tb[TCA_ROOT_FLAGS]) { + bf = nla_get_bitfield32(tb[TCA_ROOT_FLAGS]); + cb->args[2] = bf.value; + } + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cb->nlh->nlmsg_type, sizeof(*t), 0); if (!nlh) goto out_module_put; + t = nlmsg_data(nlh); t->tca_family = AF_UNSPEC; t->tca__pad1 = 0; t->tca__pad2 = 0; + count_attr = nla_reserve(skb, TCA_ROOT_COUNT, sizeof(u32)); + if (!count_attr) + goto out_module_put; nest = nla_nest_start(skb, TCA_ACT_TAB); if (nest == NULL) @@ -1188,6 +1215,9 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) if (ret > 0) { nla_nest_end(skb, nest); ret = skb->len; + act_count = cb->args[1]; + memcpy(nla_data(count_attr), &act_count, sizeof(u32)); + cb->args[1] = 0; } else nlmsg_trim(skb, b); -- cgit v1.2.3 From e62e484df04964ac947c679ef4f00c54ae5395aa Mon Sep 17 00:00:00 2001 From: Jamal Hadi Salim Date: Sun, 30 Jul 2017 13:24:52 -0400 Subject: net sched actions: add time filter for action dumping This patch adds support for filtering based on time since last used. When we are dumping a large number of actions it is useful to have the option of filtering based on when the action was last used to reduce the amount of data crossing to user space. With this patch the user space app sets the TCA_ROOT_TIME_DELTA attribute with the value in milliseconds with "time of interest since now". The kernel converts this to jiffies and does the filtering comparison matching entries that have seen activity since then and returns them to user space. Old kernels and old tc continue to work in legacy mode since they dont specify this attribute. Some example (we have 400 actions bound to 400 filters); at installation time. Using updated when tc setting the time of interest to 120 seconds earlier (we see 400 actions): prompt$ hackedtc actions ls action gact since 120000| grep index | wc -l 400 go get some coffee and wait for > 120 seconds and try again: prompt$ hackedtc actions ls action gact since 120000 | grep index | wc -l 0 Lets see a filter bound to one of these actions: .... filter pref 10 u32 filter pref 10 u32 fh 800: ht divisor 1 filter pref 10 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:10 (rule hit 2 success 1) match 7f000002/ffffffff at 12 (success 1 ) action order 1: gact action pass random type none pass val 0 index 23 ref 2 bind 1 installed 1145 sec used 802 sec Action statistics: Sent 84 bytes 1 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 .... that coffee took long, no? It was good. Now lets ping -c 1 127.0.0.2, then run the actions again: prompt$ hackedtc actions ls action gact since 120 | grep index | wc -l 1 More details please: prompt$ hackedtc -s actions ls action gact since 120000 action order 0: gact action pass random type none pass val 0 index 23 ref 2 bind 1 installed 1270 sec used 30 sec Action statistics: Sent 168 bytes 2 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 And the filter? filter pref 10 u32 filter pref 10 u32 fh 800: ht divisor 1 filter pref 10 u32 fh 800::800 order 2048 key ht 800 bkt 0 flowid 1:10 (rule hit 4 success 2) match 7f000002/ffffffff at 12 (success 2 ) action order 1: gact action pass random type none pass val 0 index 23 ref 2 bind 1 installed 1324 sec used 84 sec Action statistics: Sent 168 bytes 2 pkt (dropped 0, overlimits 0 requeues 0) backlog 0b 0p requeues 0 Signed-off-by: Jamal Hadi Salim Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/uapi/linux/rtnetlink.h | 1 + net/sched/act_api.c | 21 ++++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/uapi/linux/rtnetlink.h b/include/uapi/linux/rtnetlink.h index bfa80a6164d9..dab7dad9e01a 100644 --- a/include/uapi/linux/rtnetlink.h +++ b/include/uapi/linux/rtnetlink.h @@ -691,6 +691,7 @@ enum { #define TCAA_MAX TCA_ROOT_TAB TCA_ROOT_FLAGS, TCA_ROOT_COUNT, + TCA_ROOT_TIME_DELTA, /* in msecs */ __TCA_ROOT_MAX, #define TCA_ROOT_MAX (__TCA_ROOT_MAX - 1) }; diff --git a/net/sched/act_api.c b/net/sched/act_api.c index d53653a73c4f..f19b118df414 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -111,6 +111,7 @@ static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, { int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; u32 act_flags = cb->args[2]; + unsigned long jiffy_since = cb->args[3]; struct nlattr *nest; spin_lock_bh(&hinfo->lock); @@ -128,6 +129,11 @@ static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, if (index < s_i) continue; + if (jiffy_since && + time_after(jiffy_since, + (unsigned long)p->tcfa_tm.lastuse)) + continue; + nest = nla_nest_start(skb, n_i); if (nest == NULL) goto nla_put_failure; @@ -145,9 +151,11 @@ static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, } } done: + if (index >= 0) + cb->args[0] = index + 1; + spin_unlock_bh(&hinfo->lock); if (n_i) { - cb->args[0] += n_i; if (act_flags & TCA_FLAG_LARGE_DUMP_ON) cb->args[1] = n_i; } @@ -1077,6 +1085,7 @@ static u32 tcaa_root_flags_allowed = TCA_FLAG_LARGE_DUMP_ON; static const struct nla_policy tcaa_policy[TCA_ROOT_MAX + 1] = { [TCA_ROOT_FLAGS] = { .type = NLA_BITFIELD32, .validation_data = &tcaa_root_flags_allowed }, + [TCA_ROOT_TIME_DELTA] = { .type = NLA_U32 }, }; static int tc_ctl_action(struct sk_buff *skb, struct nlmsghdr *n, @@ -1166,8 +1175,10 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) struct tcamsg *t = (struct tcamsg *) nlmsg_data(cb->nlh); struct nlattr *tb[TCA_ROOT_MAX + 1]; struct nlattr *count_attr = NULL; + unsigned long jiffy_since = 0; struct nlattr *kind = NULL; struct nla_bitfield32 bf; + u32 msecs_since = 0; u32 act_count = 0; ret = nlmsg_parse(cb->nlh, sizeof(struct tcamsg), tb, TCA_ROOT_MAX, @@ -1191,15 +1202,23 @@ static int tc_dump_action(struct sk_buff *skb, struct netlink_callback *cb) cb->args[2] = bf.value; } + if (tb[TCA_ROOT_TIME_DELTA]) { + msecs_since = nla_get_u32(tb[TCA_ROOT_TIME_DELTA]); + } + nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).portid, cb->nlh->nlmsg_seq, cb->nlh->nlmsg_type, sizeof(*t), 0); if (!nlh) goto out_module_put; + if (msecs_since) + jiffy_since = jiffies - msecs_to_jiffies(msecs_since); + t = nlmsg_data(nlh); t->tca_family = AF_UNSPEC; t->tca__pad1 = 0; t->tca__pad2 = 0; + cb->args[3] = jiffy_since; count_attr = nla_reserve(skb, TCA_ROOT_COUNT, sizeof(u32)); if (!count_attr) goto out_module_put; -- cgit v1.2.3 From f347ec852c7a83e1803192d2c1fce4e42e0715a5 Mon Sep 17 00:00:00 2001 From: "Pablo M. Bermudo Garay" Date: Fri, 21 Jul 2017 01:54:37 +0200 Subject: netfilter: nf_tables: fib: use skb_header_pointer This is a preparatory patch for adding fib support to the netdev family. The netdev family receives the packets from ingress hook. At this point we have no guarantee that the ip header is linear. So this patch replaces ip_hdr with skb_header_pointer in order to address that possible situation. Signed-off-by: Pablo M. Bermudo Garay Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nft_fib_ipv4.c | 20 ++++++++++++++++---- net/ipv6/netfilter/nft_fib_ipv6.c | 29 +++++++++++++++++++++++------ 2 files changed, 39 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/nft_fib_ipv4.c b/net/ipv4/netfilter/nft_fib_ipv4.c index de3681df2ce7..e50976e3c213 100644 --- a/net/ipv4/netfilter/nft_fib_ipv4.c +++ b/net/ipv4/netfilter/nft_fib_ipv4.c @@ -32,9 +32,10 @@ void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { const struct nft_fib *priv = nft_expr_priv(expr); + int noff = skb_network_offset(pkt->skb); u32 *dst = ®s->data[priv->dreg]; const struct net_device *dev = NULL; - const struct iphdr *iph; + struct iphdr *iph, _iph; __be32 addr; if (priv->flags & NFTA_FIB_F_IIF) @@ -42,7 +43,12 @@ void nft_fib4_eval_type(const struct nft_expr *expr, struct nft_regs *regs, else if (priv->flags & NFTA_FIB_F_OIF) dev = nft_out(pkt); - iph = ip_hdr(pkt->skb); + iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph); + if (!iph) { + regs->verdict.code = NFT_BREAK; + return; + } + if (priv->flags & NFTA_FIB_F_DADDR) addr = iph->daddr; else @@ -61,8 +67,9 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { const struct nft_fib *priv = nft_expr_priv(expr); + int noff = skb_network_offset(pkt->skb); u32 *dest = ®s->data[priv->dreg]; - const struct iphdr *iph; + struct iphdr *iph, _iph; struct fib_result res; struct flowi4 fl4 = { .flowi4_scope = RT_SCOPE_UNIVERSE, @@ -95,7 +102,12 @@ void nft_fib4_eval(const struct nft_expr *expr, struct nft_regs *regs, return; } - iph = ip_hdr(pkt->skb); + iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph); + if (!iph) { + regs->verdict.code = NFT_BREAK; + return; + } + if (ipv4_is_zeronet(iph->saddr)) { if (ipv4_is_lbcast(iph->daddr) || ipv4_is_local_multicast(iph->daddr)) { diff --git a/net/ipv6/netfilter/nft_fib_ipv6.c b/net/ipv6/netfilter/nft_fib_ipv6.c index 43f91d9b086c..54b5899543ef 100644 --- a/net/ipv6/netfilter/nft_fib_ipv6.c +++ b/net/ipv6/netfilter/nft_fib_ipv6.c @@ -25,9 +25,9 @@ static int get_ifindex(const struct net_device *dev) static int nft_fib6_flowi_init(struct flowi6 *fl6, const struct nft_fib *priv, const struct nft_pktinfo *pkt, - const struct net_device *dev) + const struct net_device *dev, + struct ipv6hdr *iph) { - const struct ipv6hdr *iph = ipv6_hdr(pkt->skb); int lookup_flags = 0; if (priv->flags & NFTA_FIB_F_DADDR) { @@ -55,7 +55,8 @@ static int nft_fib6_flowi_init(struct flowi6 *fl6, const struct nft_fib *priv, } static u32 __nft_fib6_eval_type(const struct nft_fib *priv, - const struct nft_pktinfo *pkt) + const struct nft_pktinfo *pkt, + struct ipv6hdr *iph) { const struct net_device *dev = NULL; const struct nf_ipv6_ops *v6ops; @@ -77,7 +78,7 @@ static u32 __nft_fib6_eval_type(const struct nft_fib *priv, else if (priv->flags & NFTA_FIB_F_OIF) dev = nft_out(pkt); - nft_fib6_flowi_init(&fl6, priv, pkt, dev); + nft_fib6_flowi_init(&fl6, priv, pkt, dev, iph); v6ops = nf_get_ipv6_ops(); if (dev && v6ops && v6ops->chk_addr(nft_net(pkt), &fl6.daddr, dev, true)) @@ -131,9 +132,17 @@ void nft_fib6_eval_type(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { const struct nft_fib *priv = nft_expr_priv(expr); + int noff = skb_network_offset(pkt->skb); u32 *dest = ®s->data[priv->dreg]; + struct ipv6hdr *iph, _iph; - *dest = __nft_fib6_eval_type(priv, pkt); + iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph); + if (!iph) { + regs->verdict.code = NFT_BREAK; + return; + } + + *dest = __nft_fib6_eval_type(priv, pkt, iph); } EXPORT_SYMBOL_GPL(nft_fib6_eval_type); @@ -141,8 +150,10 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) { const struct nft_fib *priv = nft_expr_priv(expr); + int noff = skb_network_offset(pkt->skb); const struct net_device *oif = NULL; u32 *dest = ®s->data[priv->dreg]; + struct ipv6hdr *iph, _iph; struct flowi6 fl6 = { .flowi6_iif = LOOPBACK_IFINDEX, .flowi6_proto = pkt->tprot, @@ -155,7 +166,13 @@ void nft_fib6_eval(const struct nft_expr *expr, struct nft_regs *regs, else if (priv->flags & NFTA_FIB_F_OIF) oif = nft_out(pkt); - lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif); + iph = skb_header_pointer(pkt->skb, noff, sizeof(_iph), &_iph); + if (!iph) { + regs->verdict.code = NFT_BREAK; + return; + } + + lookup_flags = nft_fib6_flowi_init(&fl6, priv, pkt, oif, iph); if (nft_hook(pkt) == NF_INET_PRE_ROUTING && nft_fib_is_loopback(pkt->skb, nft_in(pkt))) { -- cgit v1.2.3 From 6392c226037c2b90d3062126c65fc354e47156f7 Mon Sep 17 00:00:00 2001 From: "Pablo M. Bermudo Garay" Date: Fri, 21 Jul 2017 01:54:38 +0200 Subject: netfilter: nf_tables: add fib expression to the netdev family Add fib expression support for netdev family. Like inet family, netdev delegates the actual decision to the corresponding backend, either ipv4 or ipv6. This allows to perform very early reverse path filtering, among other things. You can find more information about fib expression in the f6d0cbcf09c5 ("") commit message. Signed-off-by: Pablo M. Bermudo Garay Signed-off-by: Pablo Neira Ayuso --- net/netfilter/Kconfig | 9 +++++ net/netfilter/Makefile | 1 + net/netfilter/nft_fib_netdev.c | 87 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 97 insertions(+) create mode 100644 net/netfilter/nft_fib_netdev.c (limited to 'net') diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig index 9b28864cc36a..e4a13cc8a2e7 100644 --- a/net/netfilter/Kconfig +++ b/net/netfilter/Kconfig @@ -636,6 +636,15 @@ config NFT_FWD_NETDEV help This option enables packet forwarding for the "netdev" family. +config NFT_FIB_NETDEV + depends on NFT_FIB_IPV4 + depends on NFT_FIB_IPV6 + tristate "Netfilter nf_tables netdev fib lookups support" + help + This option allows using the FIB expression from the netdev table. + The lookup will be delegated to the IPv4 or IPv6 FIB depending + on the protocol of the packet. + endif # NF_TABLES_NETDEV endif # NF_TABLES diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile index 913380919301..d3891c93edd6 100644 --- a/net/netfilter/Makefile +++ b/net/netfilter/Makefile @@ -100,6 +100,7 @@ obj-$(CONFIG_NFT_REDIR) += nft_redir.o obj-$(CONFIG_NFT_HASH) += nft_hash.o obj-$(CONFIG_NFT_FIB) += nft_fib.o obj-$(CONFIG_NFT_FIB_INET) += nft_fib_inet.o +obj-$(CONFIG_NFT_FIB_NETDEV) += nft_fib_netdev.o # nf_tables netdev obj-$(CONFIG_NFT_DUP_NETDEV) += nft_dup_netdev.o diff --git a/net/netfilter/nft_fib_netdev.c b/net/netfilter/nft_fib_netdev.c new file mode 100644 index 000000000000..3997ee36cfbd --- /dev/null +++ b/net/netfilter/nft_fib_netdev.c @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2017 Pablo M. Bermudo Garay + * + * 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 code is based on net/netfilter/nft_fib_inet.c, written by + * Florian Westphal . + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static void nft_fib_netdev_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + const struct nft_fib *priv = nft_expr_priv(expr); + + switch (ntohs(pkt->skb->protocol)) { + case ETH_P_IP: + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + case NFT_FIB_RESULT_OIFNAME: + return nft_fib4_eval(expr, regs, pkt); + case NFT_FIB_RESULT_ADDRTYPE: + return nft_fib4_eval_type(expr, regs, pkt); + } + break; + case ETH_P_IPV6: + switch (priv->result) { + case NFT_FIB_RESULT_OIF: + case NFT_FIB_RESULT_OIFNAME: + return nft_fib6_eval(expr, regs, pkt); + case NFT_FIB_RESULT_ADDRTYPE: + return nft_fib6_eval_type(expr, regs, pkt); + } + break; + } + + regs->verdict.code = NFT_BREAK; +} + +static struct nft_expr_type nft_fib_netdev_type; +static const struct nft_expr_ops nft_fib_netdev_ops = { + .type = &nft_fib_netdev_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_fib)), + .eval = nft_fib_netdev_eval, + .init = nft_fib_init, + .dump = nft_fib_dump, + .validate = nft_fib_validate, +}; + +static struct nft_expr_type nft_fib_netdev_type __read_mostly = { + .family = NFPROTO_NETDEV, + .name = "fib", + .ops = &nft_fib_netdev_ops, + .policy = nft_fib_policy, + .maxattr = NFTA_FIB_MAX, + .owner = THIS_MODULE, +}; + +static int __init nft_fib_netdev_module_init(void) +{ + return nft_register_expr(&nft_fib_netdev_type); +} + +static void __exit nft_fib_netdev_module_exit(void) +{ + nft_unregister_expr(&nft_fib_netdev_type); +} + +module_init(nft_fib_netdev_module_init); +module_exit(nft_fib_netdev_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pablo M. Bermudo Garay "); +MODULE_ALIAS_NFT_AF_EXPR(5, "fib"); -- cgit v1.2.3 From a232cd0e0cf1ede77b047bc0c98142cd51f318e3 Mon Sep 17 00:00:00 2001 From: "subashab@codeaurora.org" Date: Thu, 20 Jul 2017 19:42:19 -0600 Subject: netfilter: conntrack: Change to deferable work queue Delayed workqueue causes wakeups to idle CPUs. This was causing a power impact for devices. Use deferable work queue instead so that gc_worker runs when CPU is active only. Signed-off-by: Subash Abhinov Kasiviswanathan Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 69746928cc0a..c6f1cf0bff56 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1081,7 +1081,7 @@ static void gc_worker(struct work_struct *work) static void conntrack_gc_work_init(struct conntrack_gc_work *gc_work) { - INIT_DELAYED_WORK(&gc_work->dwork, gc_worker); + INIT_DEFERRABLE_WORK(&gc_work->dwork, gc_worker); gc_work->next_gc_run = HZ; gc_work->exiting = false; } -- cgit v1.2.3 From ac7b848390036dadd4351899d2a23748075916bd Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 26 Jul 2017 00:02:31 +0200 Subject: netfilter: expect: add and use nf_ct_expect_iterate helpers We have several spots that open-code a expect walk, add a helper that is similar to nf_ct_iterate_destroy/nf_ct_iterate_cleanup. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_expect.h | 5 +++ net/netfilter/nf_conntrack_expect.c | 54 +++++++++++++++++++++++++ net/netfilter/nf_conntrack_helper.c | 34 +++++++--------- net/netfilter/nf_conntrack_netlink.c | 63 ++++++++++------------------- 4 files changed, 95 insertions(+), 61 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_expect.h b/include/net/netfilter/nf_conntrack_expect.h index 2ba54feaccd8..818def011110 100644 --- a/include/net/netfilter/nf_conntrack_expect.h +++ b/include/net/netfilter/nf_conntrack_expect.h @@ -107,6 +107,11 @@ void nf_ct_remove_expectations(struct nf_conn *ct); void nf_ct_unexpect_related(struct nf_conntrack_expect *exp); bool nf_ct_remove_expect(struct nf_conntrack_expect *exp); +void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data), void *data); +void nf_ct_expect_iterate_net(struct net *net, + bool (*iter)(struct nf_conntrack_expect *e, void *data), + void *data, u32 portid, int report); + /* Allocate space for an expectation: this is mandatory before calling nf_ct_expect_related. You will have to call put afterwards. */ struct nf_conntrack_expect *nf_ct_expect_alloc(struct nf_conn *me); diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index 2c63808bea96..dad2c0c22ad5 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -474,6 +474,60 @@ out: } EXPORT_SYMBOL_GPL(nf_ct_expect_related_report); +void nf_ct_expect_iterate_destroy(bool (*iter)(struct nf_conntrack_expect *e, void *data), + void *data) +{ + struct nf_conntrack_expect *exp; + const struct hlist_node *next; + unsigned int i; + + spin_lock_bh(&nf_conntrack_expect_lock); + + for (i = 0; i < nf_ct_expect_hsize; i++) { + hlist_for_each_entry_safe(exp, next, + &nf_ct_expect_hash[i], + hnode) { + if (iter(exp, data) && del_timer(&exp->timeout)) { + nf_ct_unlink_expect(exp); + nf_ct_expect_put(exp); + } + } + } + + spin_unlock_bh(&nf_conntrack_expect_lock); +} +EXPORT_SYMBOL_GPL(nf_ct_expect_iterate_destroy); + +void nf_ct_expect_iterate_net(struct net *net, + bool (*iter)(struct nf_conntrack_expect *e, void *data), + void *data, + u32 portid, int report) +{ + struct nf_conntrack_expect *exp; + const struct hlist_node *next; + unsigned int i; + + spin_lock_bh(&nf_conntrack_expect_lock); + + for (i = 0; i < nf_ct_expect_hsize; i++) { + hlist_for_each_entry_safe(exp, next, + &nf_ct_expect_hash[i], + hnode) { + + if (!net_eq(nf_ct_exp_net(exp), net)) + continue; + + if (iter(exp, data) && del_timer(&exp->timeout)) { + nf_ct_unlink_expect_report(exp, portid, report); + nf_ct_expect_put(exp); + } + } + } + + spin_unlock_bh(&nf_conntrack_expect_lock); +} +EXPORT_SYMBOL_GPL(nf_ct_expect_iterate_net); + #ifdef CONFIG_NF_CONNTRACK_PROCFS struct ct_expect_iter_state { struct seq_net_private p; diff --git a/net/netfilter/nf_conntrack_helper.c b/net/netfilter/nf_conntrack_helper.c index 9129bb3b5153..551a1eddf0fa 100644 --- a/net/netfilter/nf_conntrack_helper.c +++ b/net/netfilter/nf_conntrack_helper.c @@ -437,12 +437,22 @@ out: } EXPORT_SYMBOL_GPL(nf_conntrack_helper_register); -void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) +static bool expect_iter_me(struct nf_conntrack_expect *exp, void *data) { - struct nf_conntrack_expect *exp; - const struct hlist_node *next; - unsigned int i; + struct nf_conn_help *help = nfct_help(exp->master); + const struct nf_conntrack_helper *me = data; + const struct nf_conntrack_helper *this; + + if (exp->helper == me) + return true; + this = rcu_dereference_protected(help->helper, + lockdep_is_held(&nf_conntrack_expect_lock)); + return this == me; +} + +void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) +{ mutex_lock(&nf_ct_helper_mutex); hlist_del_rcu(&me->hnode); nf_ct_helper_count--; @@ -453,21 +463,7 @@ void nf_conntrack_helper_unregister(struct nf_conntrack_helper *me) */ synchronize_rcu(); - /* Get rid of expectations */ - spin_lock_bh(&nf_conntrack_expect_lock); - for (i = 0; i < nf_ct_expect_hsize; i++) { - hlist_for_each_entry_safe(exp, next, - &nf_ct_expect_hash[i], hnode) { - struct nf_conn_help *help = nfct_help(exp->master); - if ((rcu_dereference_protected( - help->helper, - lockdep_is_held(&nf_conntrack_expect_lock) - ) == me || exp->helper == me)) - nf_ct_remove_expect(exp); - } - } - spin_unlock_bh(&nf_conntrack_expect_lock); - + nf_ct_expect_iterate_destroy(expect_iter_me, NULL); nf_ct_iterate_destroy(unhelp, me); } EXPORT_SYMBOL_GPL(nf_conntrack_helper_unregister); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 4dba71de4de7..4922c8aefb2a 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -2898,6 +2898,21 @@ out: return err == -EAGAIN ? -ENOBUFS : err; } +static bool expect_iter_name(struct nf_conntrack_expect *exp, void *data) +{ + const struct nf_conn_help *m_help; + const char *name = data; + + m_help = nfct_help(exp->master); + + return strcmp(m_help->helper->name, name) == 0; +} + +static bool expect_iter_all(struct nf_conntrack_expect *exp, void *data) +{ + return true; +} + static int ctnetlink_del_expect(struct net *net, struct sock *ctnl, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const cda[], @@ -2906,10 +2921,8 @@ static int ctnetlink_del_expect(struct net *net, struct sock *ctnl, struct nf_conntrack_expect *exp; struct nf_conntrack_tuple tuple; struct nfgenmsg *nfmsg = nlmsg_data(nlh); - struct hlist_node *next; u_int8_t u3 = nfmsg->nfgen_family; struct nf_conntrack_zone zone; - unsigned int i; int err; if (cda[CTA_EXPECT_TUPLE]) { @@ -2949,49 +2962,15 @@ static int ctnetlink_del_expect(struct net *net, struct sock *ctnl, nf_ct_expect_put(exp); } else if (cda[CTA_EXPECT_HELP_NAME]) { char *name = nla_data(cda[CTA_EXPECT_HELP_NAME]); - struct nf_conn_help *m_help; - /* delete all expectations for this helper */ - spin_lock_bh(&nf_conntrack_expect_lock); - for (i = 0; i < nf_ct_expect_hsize; i++) { - hlist_for_each_entry_safe(exp, next, - &nf_ct_expect_hash[i], - hnode) { - - if (!net_eq(nf_ct_exp_net(exp), net)) - continue; - - m_help = nfct_help(exp->master); - if (!strcmp(m_help->helper->name, name) && - del_timer(&exp->timeout)) { - nf_ct_unlink_expect_report(exp, - NETLINK_CB(skb).portid, - nlmsg_report(nlh)); - nf_ct_expect_put(exp); - } - } - } - spin_unlock_bh(&nf_conntrack_expect_lock); + nf_ct_expect_iterate_net(net, expect_iter_name, name, + NETLINK_CB(skb).portid, + nlmsg_report(nlh)); } else { /* This basically means we have to flush everything*/ - spin_lock_bh(&nf_conntrack_expect_lock); - for (i = 0; i < nf_ct_expect_hsize; i++) { - hlist_for_each_entry_safe(exp, next, - &nf_ct_expect_hash[i], - hnode) { - - if (!net_eq(nf_ct_exp_net(exp), net)) - continue; - - if (del_timer(&exp->timeout)) { - nf_ct_unlink_expect_report(exp, - NETLINK_CB(skb).portid, - nlmsg_report(nlh)); - nf_ct_expect_put(exp); - } - } - } - spin_unlock_bh(&nf_conntrack_expect_lock); + nf_ct_expect_iterate_net(net, expect_iter_all, NULL, + NETLINK_CB(skb).portid, + nlmsg_report(nlh)); } return 0; -- cgit v1.2.3 From 84657984c26fd0b64743a397f3a1a587fa4b575a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 26 Jul 2017 00:02:32 +0200 Subject: netfilter: add and use nf_ct_unconfirmed_destroy This also removes __nf_ct_unconfirmed_destroy() call from nf_ct_iterate_cleanup_net, so that function can be used only when missing conntracks from unconfirmed list isn't a problem. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack.h | 3 +++ net/netfilter/nf_conntrack_core.c | 15 +++++++++++---- net/netfilter/nfnetlink_cttimeout.c | 1 + 3 files changed, 15 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 48407569585d..6e6f678aaac7 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -224,6 +224,9 @@ extern s32 (*nf_ct_nat_offset)(const struct nf_conn *ct, enum ip_conntrack_dir dir, u32 seq); +/* Set all unconfirmed conntrack as dying */ +void nf_ct_unconfirmed_destroy(struct net *); + /* Iterate over all conntracks: if iter returns true, it's deleted. */ void nf_ct_iterate_cleanup_net(struct net *net, int (*iter)(struct nf_conn *i, void *data), diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index c6f1cf0bff56..80ab4e937765 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1686,6 +1686,17 @@ __nf_ct_unconfirmed_destroy(struct net *net) } } +void nf_ct_unconfirmed_destroy(struct net *net) +{ + might_sleep(); + + if (atomic_read(&net->ct.count) > 0) { + __nf_ct_unconfirmed_destroy(net); + synchronize_net(); + } +} +EXPORT_SYMBOL_GPL(nf_ct_unconfirmed_destroy); + void nf_ct_iterate_cleanup_net(struct net *net, int (*iter)(struct nf_conn *i, void *data), void *data, u32 portid, int report) @@ -1697,14 +1708,10 @@ void nf_ct_iterate_cleanup_net(struct net *net, if (atomic_read(&net->ct.count) == 0) return; - __nf_ct_unconfirmed_destroy(net); - d.iter = iter; d.data = data; d.net = net; - synchronize_net(); - nf_ct_iterate_cleanup(iter_net_only, &d, portid, report); } EXPORT_SYMBOL_GPL(nf_ct_iterate_cleanup_net); diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index 7ce9e86d374c..f4fb6d4dd0b9 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -570,6 +570,7 @@ static void __net_exit cttimeout_net_exit(struct net *net) { struct ctnl_timeout *cur, *tmp; + nf_ct_unconfirmed_destroy(net); ctnl_untimeout(net, NULL); list_for_each_entry_safe(cur, tmp, &net->nfct_timeout_list, head) { -- cgit v1.2.3 From e2a750070aeec7af3818065b39d61cb38627ce64 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 26 Jul 2017 00:02:33 +0200 Subject: netfilter: conntrack: destroy functions need to free queued packets queued skbs might be using conntrack extensions that are being removed, such as timeout. This happens for skbs that have a skb->nfct in unconfirmed state (i.e., not in hash table yet). This is destructive, but there are only two use cases: - module removal (rare) - netns cleanup (most likely no conntracks exist, and if they do, they are removed anyway later on). Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_core.c | 4 ++++ net/netfilter/nf_queue.c | 1 + 2 files changed, 5 insertions(+) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 80ab4e937765..2bc499186186 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -56,6 +56,8 @@ #include #include +#include "nf_internals.h" + #define NF_CONNTRACK_VERSION "0.5.0" int (*nfnetlink_parse_nat_setup_hook)(struct nf_conn *ct, @@ -1692,6 +1694,7 @@ void nf_ct_unconfirmed_destroy(struct net *net) if (atomic_read(&net->ct.count) > 0) { __nf_ct_unconfirmed_destroy(net); + nf_queue_nf_hook_drop(net); synchronize_net(); } } @@ -1737,6 +1740,7 @@ nf_ct_iterate_destroy(int (*iter)(struct nf_conn *i, void *data), void *data) if (atomic_read(&net->ct.count) == 0) continue; __nf_ct_unconfirmed_destroy(net); + nf_queue_nf_hook_drop(net); } rtnl_unlock(); diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 043850c9d154..4f4d80a58fb5 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -109,6 +109,7 @@ unsigned int nf_queue_nf_hook_drop(struct net *net) return count; } +EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop); static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, struct nf_hook_entry *hook_entry, unsigned int queuenum) -- cgit v1.2.3 From 5da773a3e81e6093c4346ee8cd356fc214d7c76c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 26 Jul 2017 00:02:34 +0200 Subject: netfilter: nfnetlink_queue: don't queue dying conntracks to userspace When skb is queued to userspace it leaves softirq/rcu protection. skb->nfct (via conntrack extensions such as helper) could then reference modules that no longer exist if the conntrack was not yet confirmed. nf_ct_iterate_destroy() will set the DYING bit for unconfirmed conntracks, we therefore solve this race as follows: 1. take the queue spinlock. 2. check if the conntrack is unconfirmed and has dying bit set. In this case, we must discard skb while we're still inside rcu read-side section. 3. If nf_ct_iterate_destroy() is called right after the packet is queued to userspace, it will be removed from the queue via nf_ct_iterate_destroy -> nf_queue_nf_hook_drop. When userspace sends the verdict (nfnetlink takes rcu read lock), there are two cases to consider: 1. nf_ct_iterate_destroy() was called while packet was out. In this case, skb will have been removed from the queue already and no reinject takes place as we won't find a matching entry for the packet id. 2. nf_ct_iterate_destroy() gets called right after verdict callback found and removed the skb from queue list. In this case, skb->nfct is marked as dying but it is still valid. The skb will be dropped either in nf_conntrack_confirm (we don't insert DYING conntracks into hash table) or when we try to queue the skb again, but either events don't occur before the rcu read lock is dropped. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nfnetlink_queue.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'net') diff --git a/net/netfilter/nfnetlink_queue.c b/net/netfilter/nfnetlink_queue.c index 7c543bfbf624..c9796629858f 100644 --- a/net/netfilter/nfnetlink_queue.c +++ b/net/netfilter/nfnetlink_queue.c @@ -41,6 +41,10 @@ #include "../bridge/br_private.h" #endif +#if IS_ENABLED(CONFIG_NF_CONNTRACK) +#include +#endif + #define NFQNL_QMAX_DEFAULT 1024 /* We're using struct nlattr which has 16bit nla_len. Note that nla_len @@ -612,6 +616,18 @@ nlmsg_failure: return NULL; } +static bool nf_ct_drop_unconfirmed(const struct nf_queue_entry *entry) +{ +#if IS_ENABLED(CONFIG_NF_CONNTRACK) + static const unsigned long flags = IPS_CONFIRMED | IPS_DYING; + const struct nf_conn *ct = (void *)skb_nfct(entry->skb); + + if (ct && ((ct->status & flags) == IPS_DYING)) + return true; +#endif + return false; +} + static int __nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue, struct nf_queue_entry *entry) @@ -628,6 +644,9 @@ __nfqnl_enqueue_packet(struct net *net, struct nfqnl_instance *queue, } spin_lock_bh(&queue->lock); + if (nf_ct_drop_unconfirmed(entry)) + goto err_out_free_nskb; + if (queue->queue_total >= queue->queue_maxlen) { if (queue->flags & NFQA_CFG_F_FAIL_OPEN) { failopen = 1; -- cgit v1.2.3 From 591bb2789bc2a93f379b13d277f441f1b427102d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 26 Jul 2017 11:40:52 +0200 Subject: netfilter: nf_hook_ops structs can be const We no longer place these on a list so they can be const. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- drivers/net/ipvlan/ipvlan_main.c | 2 +- net/bridge/br_netfilter_hooks.c | 2 +- net/bridge/netfilter/ebtable_filter.c | 2 +- net/bridge/netfilter/ebtable_nat.c | 2 +- net/decnet/netfilter/dn_rtmsg.c | 2 +- net/ipv4/netfilter/ipt_CLUSTERIP.c | 2 +- net/ipv4/netfilter/ipt_SYNPROXY.c | 2 +- net/ipv4/netfilter/iptable_nat.c | 2 +- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 2 +- net/ipv4/netfilter/nf_defrag_ipv4.c | 2 +- net/ipv6/ila/ila_xlat.c | 2 +- net/ipv6/netfilter/ip6t_SYNPROXY.c | 2 +- net/ipv6/netfilter/ip6table_nat.c | 2 +- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 2 +- net/ipv6/netfilter/nf_defrag_ipv6_hooks.c | 2 +- net/netfilter/ipvs/ip_vs_core.c | 2 +- security/selinux/hooks.c | 2 +- security/smack/smack_netfilter.c | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/drivers/net/ipvlan/ipvlan_main.c b/drivers/net/ipvlan/ipvlan_main.c index fdde20735416..943e6907dc19 100644 --- a/drivers/net/ipvlan/ipvlan_main.c +++ b/drivers/net/ipvlan/ipvlan_main.c @@ -15,7 +15,7 @@ struct ipvlan_netns { unsigned int ipvl_nf_hook_refcnt; }; -static struct nf_hook_ops ipvl_nfops[] __read_mostly = { +static const struct nf_hook_ops ipvl_nfops[] = { { .hook = ipvlan_nf_input, .pf = NFPROTO_IPV4, diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 2261e5194c82..626f4b2cef16 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -887,7 +887,7 @@ EXPORT_SYMBOL_GPL(br_netfilter_enable); /* For br_nf_post_routing, we need (prio = NF_BR_PRI_LAST), because * br_dev_queue_push_xmit is called afterwards */ -static struct nf_hook_ops br_nf_ops[] __read_mostly = { +static const struct nf_hook_ops br_nf_ops[] = { { .hook = br_nf_pre_routing, .pf = NFPROTO_BRIDGE, diff --git a/net/bridge/netfilter/ebtable_filter.c b/net/bridge/netfilter/ebtable_filter.c index f22ef7c21913..45a00dbdbcad 100644 --- a/net/bridge/netfilter/ebtable_filter.c +++ b/net/bridge/netfilter/ebtable_filter.c @@ -70,7 +70,7 @@ ebt_out_hook(void *priv, struct sk_buff *skb, return ebt_do_table(skb, state, state->net->xt.frame_filter); } -static struct nf_hook_ops ebt_ops_filter[] __read_mostly = { +static const struct nf_hook_ops ebt_ops_filter[] = { { .hook = ebt_in_hook, .pf = NFPROTO_BRIDGE, diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 2f7a4f314406..4ecf50662b7d 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -70,7 +70,7 @@ ebt_nat_out(void *priv, struct sk_buff *skb, return ebt_do_table(skb, state, state->net->xt.frame_nat); } -static struct nf_hook_ops ebt_ops_nat[] __read_mostly = { +static const struct nf_hook_ops ebt_ops_nat[] = { { .hook = ebt_nat_out, .pf = NFPROTO_BRIDGE, diff --git a/net/decnet/netfilter/dn_rtmsg.c b/net/decnet/netfilter/dn_rtmsg.c index aa8ffecc46a4..ab395e55cd78 100644 --- a/net/decnet/netfilter/dn_rtmsg.c +++ b/net/decnet/netfilter/dn_rtmsg.c @@ -115,7 +115,7 @@ static inline void dnrmg_receive_user_skb(struct sk_buff *skb) RCV_SKB_FAIL(-EINVAL); } -static struct nf_hook_ops dnrmg_ops __read_mostly = { +static const struct nf_hook_ops dnrmg_ops = { .hook = dnrmg_hook, .pf = NFPROTO_DECNET, .hooknum = NF_DN_ROUTE, diff --git a/net/ipv4/netfilter/ipt_CLUSTERIP.c b/net/ipv4/netfilter/ipt_CLUSTERIP.c index 7d72decb80f9..6637e8b37ee2 100644 --- a/net/ipv4/netfilter/ipt_CLUSTERIP.c +++ b/net/ipv4/netfilter/ipt_CLUSTERIP.c @@ -624,7 +624,7 @@ arp_mangle(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops cip_arp_ops __read_mostly = { +static const struct nf_hook_ops cip_arp_ops = { .hook = arp_mangle, .pf = NFPROTO_ARP, .hooknum = NF_ARP_OUT, diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index f1528f7175a8..811689e523c3 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -416,7 +416,7 @@ static unsigned int ipv4_synproxy_hook(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops ipv4_synproxy_ops[] __read_mostly = { +static const struct nf_hook_ops ipv4_synproxy_ops[] = { { .hook = ipv4_synproxy_hook, .pf = NFPROTO_IPV4, diff --git a/net/ipv4/netfilter/iptable_nat.c b/net/ipv4/netfilter/iptable_nat.c index 138a24bc76ad..a1a07b338ccf 100644 --- a/net/ipv4/netfilter/iptable_nat.c +++ b/net/ipv4/netfilter/iptable_nat.c @@ -67,7 +67,7 @@ static unsigned int iptable_nat_ipv4_local_fn(void *priv, return nf_nat_ipv4_local_fn(priv, skb, state, iptable_nat_do_chain); } -static struct nf_hook_ops nf_nat_ipv4_ops[] __read_mostly = { +static const struct nf_hook_ops nf_nat_ipv4_ops[] = { /* Before packet filtering, change destination */ { .hook = iptable_nat_ipv4_in, diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 2e14ed11a35c..63e4ea0e01f8 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -174,7 +174,7 @@ static unsigned int ipv4_conntrack_local(void *priv, /* Connection tracking may drop packets, but never alters them, so make it the first hook. */ -static struct nf_hook_ops ipv4_conntrack_ops[] __read_mostly = { +static const struct nf_hook_ops ipv4_conntrack_ops[] = { { .hook = ipv4_conntrack_in, .pf = NFPROTO_IPV4, diff --git a/net/ipv4/netfilter/nf_defrag_ipv4.c b/net/ipv4/netfilter/nf_defrag_ipv4.c index 346bf7ccac08..37fe1616ca0b 100644 --- a/net/ipv4/netfilter/nf_defrag_ipv4.c +++ b/net/ipv4/netfilter/nf_defrag_ipv4.c @@ -90,7 +90,7 @@ static unsigned int ipv4_conntrack_defrag(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops ipv4_defrag_ops[] = { +static const struct nf_hook_ops ipv4_defrag_ops[] = { { .hook = ipv4_conntrack_defrag, .pf = NFPROTO_IPV4, diff --git a/net/ipv6/ila/ila_xlat.c b/net/ipv6/ila/ila_xlat.c index 77f7f8c7d93d..5bd419c1abc8 100644 --- a/net/ipv6/ila/ila_xlat.c +++ b/net/ipv6/ila/ila_xlat.c @@ -208,7 +208,7 @@ ila_nf_input(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops ila_nf_hook_ops[] __read_mostly = { +static const struct nf_hook_ops ila_nf_hook_ops[] = { { .hook = ila_nf_input, .pf = NFPROTO_IPV6, diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index ce203dd729e0..a5cd43d75393 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -438,7 +438,7 @@ static unsigned int ipv6_synproxy_hook(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops ipv6_synproxy_ops[] __read_mostly = { +static const struct nf_hook_ops ipv6_synproxy_ops[] = { { .hook = ipv6_synproxy_hook, .pf = NFPROTO_IPV6, diff --git a/net/ipv6/netfilter/ip6table_nat.c b/net/ipv6/netfilter/ip6table_nat.c index 7d2bd940291f..991512576c8c 100644 --- a/net/ipv6/netfilter/ip6table_nat.c +++ b/net/ipv6/netfilter/ip6table_nat.c @@ -69,7 +69,7 @@ static unsigned int ip6table_nat_local_fn(void *priv, return nf_nat_ipv6_local_fn(priv, skb, state, ip6table_nat_do_chain); } -static struct nf_hook_ops nf_nat_ipv6_ops[] __read_mostly = { +static const struct nf_hook_ops nf_nat_ipv6_ops[] = { /* Before packet filtering, change destination */ { .hook = ip6table_nat_in, diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 4e3402486833..f2d2f4a9294b 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -191,7 +191,7 @@ static unsigned int ipv6_conntrack_local(void *priv, return nf_conntrack_in(state->net, PF_INET6, state->hook, skb); } -static struct nf_hook_ops ipv6_conntrack_ops[] __read_mostly = { +static const struct nf_hook_ops ipv6_conntrack_ops[] = { { .hook = ipv6_conntrack_in, .pf = NFPROTO_IPV6, diff --git a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c index ada60d1a991b..b326da59257f 100644 --- a/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c +++ b/net/ipv6/netfilter/nf_defrag_ipv6_hooks.c @@ -74,7 +74,7 @@ static unsigned int ipv6_defrag(void *priv, return err == 0 ? NF_ACCEPT : NF_DROP; } -static struct nf_hook_ops ipv6_defrag_ops[] = { +static const struct nf_hook_ops ipv6_defrag_ops[] = { { .hook = ipv6_defrag, .pf = NFPROTO_IPV6, diff --git a/net/netfilter/ipvs/ip_vs_core.c b/net/netfilter/ipvs/ip_vs_core.c index 2ff9d9070c95..5cb7cac9177d 100644 --- a/net/netfilter/ipvs/ip_vs_core.c +++ b/net/netfilter/ipvs/ip_vs_core.c @@ -2101,7 +2101,7 @@ ip_vs_forward_icmp_v6(void *priv, struct sk_buff *skb, #endif -static struct nf_hook_ops ip_vs_ops[] __read_mostly = { +static const struct nf_hook_ops ip_vs_ops[] = { /* After packet filtering, change source only for VS/NAT */ { .hook = ip_vs_reply4, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 33fd061305c4..2f2e1338cd3d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -6530,7 +6530,7 @@ security_initcall(selinux_init); #if defined(CONFIG_NETFILTER) -static struct nf_hook_ops selinux_nf_ops[] = { +static const struct nf_hook_ops selinux_nf_ops[] = { { .hook = selinux_ipv4_postroute, .pf = NFPROTO_IPV4, diff --git a/security/smack/smack_netfilter.c b/security/smack/smack_netfilter.c index cdeb0f3243dd..e36d17835d4f 100644 --- a/security/smack/smack_netfilter.c +++ b/security/smack/smack_netfilter.c @@ -58,7 +58,7 @@ static unsigned int smack_ipv4_output(void *priv, return NF_ACCEPT; } -static struct nf_hook_ops smack_nf_ops[] = { +static const struct nf_hook_ops smack_nf_ops[] = { { .hook = smack_ipv4_output, .pf = NFPROTO_IPV4, -- cgit v1.2.3 From 6e692678d74289d6129bbd4bb20bb9fe01278faa Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 27 Jul 2017 16:56:39 +0200 Subject: netfilter: nf_tables: No need to check chain existence when tracing nft_trace_notify() is called only from __nft_trace_packet(), which assigns its parameter 'chain' to info->chain. __nft_trace_packet() in turn later dereferences 'chain' unconditionally, which indicates that it's never NULL. Same does nft_do_chain(), the only user of the tracing infrastructure. Hence it is safe to assume the check removed here is not needed. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_trace.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c index e1b15e7a5793..0c3a0049e4aa 100644 --- a/net/netfilter/nf_tables_trace.c +++ b/net/netfilter/nf_tables_trace.c @@ -217,14 +217,11 @@ void nft_trace_notify(struct nft_traceinfo *info) if (trace_fill_id(skb, pkt->skb)) goto nla_put_failure; - if (info->chain) { - if (nla_put_string(skb, NFTA_TRACE_CHAIN, - info->chain->name)) - goto nla_put_failure; - if (nla_put_string(skb, NFTA_TRACE_TABLE, - info->chain->table->name)) - goto nla_put_failure; - } + if (nla_put_string(skb, NFTA_TRACE_CHAIN, info->chain->name)) + goto nla_put_failure; + + if (nla_put_string(skb, NFTA_TRACE_TABLE, info->chain->table->name)) + goto nla_put_failure; if (nf_trace_fill_rule_info(skb, info)) goto nla_put_failure; -- cgit v1.2.3 From e46abbcc05aa8a16b0e7f5c94e86d11af9aa2770 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 27 Jul 2017 16:56:41 +0200 Subject: netfilter: nf_tables: Allow table names of up to 255 chars Allocate all table names dynamically to allow for arbitrary lengths but introduce NFT_NAME_MAXLEN as an upper sanity boundary. It's value was chosen to allow using a domain name as per RFC 1035. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 +- include/uapi/linux/netfilter/nf_tables.h | 3 +- net/netfilter/nf_tables_api.c | 49 +++++++++++++++++++++++--------- net/netfilter/nf_tables_trace.c | 2 +- 4 files changed, 40 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index bd5be0d691d5..05ecf78ec078 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -957,7 +957,7 @@ struct nft_table { u32 use; u16 flags:14, genmask:2; - char name[NFT_TABLE_MAXNAMELEN]; + char *name; }; enum nft_af_flags { diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 6f0a950e21c3..0b94e572ef16 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1,7 +1,8 @@ #ifndef _LINUX_NF_TABLES_H #define _LINUX_NF_TABLES_H -#define NFT_TABLE_MAXNAMELEN 32 +#define NFT_NAME_MAXLEN 256 +#define NFT_TABLE_MAXNAMELEN NFT_NAME_MAXLEN #define NFT_CHAIN_MAXNAMELEN 32 #define NFT_SET_MAXNAMELEN 32 #define NFT_OBJ_MAXNAMELEN 32 diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index b77ad0813564..c2e392d5e512 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -726,7 +726,10 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk, if (table == NULL) goto err2; - nla_strlcpy(table->name, name, NFT_TABLE_MAXNAMELEN); + table->name = nla_strdup(name, GFP_KERNEL); + if (table->name == NULL) + goto err3; + INIT_LIST_HEAD(&table->chains); INIT_LIST_HEAD(&table->sets); INIT_LIST_HEAD(&table->objects); @@ -735,10 +738,12 @@ static int nf_tables_newtable(struct net *net, struct sock *nlsk, nft_ctx_init(&ctx, net, skb, nlh, afi, table, NULL, nla); err = nft_trans_table_add(&ctx, NFT_MSG_NEWTABLE); if (err < 0) - goto err3; + goto err4; list_add_tail_rcu(&table->list, &afi->tables); return 0; +err4: + kfree(table->name); err3: kfree(table); err2: @@ -865,6 +870,7 @@ static void nf_tables_table_destroy(struct nft_ctx *ctx) { BUG_ON(ctx->table->use > 0); + kfree(ctx->table->name); kfree(ctx->table); module_put(ctx->afi->owner); } @@ -1972,7 +1978,7 @@ err: } struct nft_rule_dump_ctx { - char table[NFT_TABLE_MAXNAMELEN]; + char *table; char chain[NFT_CHAIN_MAXNAMELEN]; }; @@ -1997,7 +2003,7 @@ static int nf_tables_dump_rules(struct sk_buff *skb, continue; list_for_each_entry_rcu(table, &afi->tables, list) { - if (ctx && ctx->table[0] && + if (ctx && ctx->table && strcmp(ctx->table, table->name) != 0) continue; @@ -2037,7 +2043,12 @@ done: static int nf_tables_dump_rules_done(struct netlink_callback *cb) { - kfree(cb->data); + struct nft_rule_dump_ctx *ctx = cb->data; + + if (ctx) { + kfree(ctx->table); + kfree(ctx); + } return 0; } @@ -2069,9 +2080,14 @@ static int nf_tables_getrule(struct net *net, struct sock *nlsk, if (!ctx) return -ENOMEM; - if (nla[NFTA_RULE_TABLE]) - nla_strlcpy(ctx->table, nla[NFTA_RULE_TABLE], - sizeof(ctx->table)); + if (nla[NFTA_RULE_TABLE]) { + ctx->table = nla_strdup(nla[NFTA_RULE_TABLE], + GFP_KERNEL); + if (!ctx->table) { + kfree(ctx); + return -ENOMEM; + } + } if (nla[NFTA_RULE_CHAIN]) nla_strlcpy(ctx->chain, nla[NFTA_RULE_CHAIN], sizeof(ctx->chain)); @@ -4410,7 +4426,7 @@ nla_put_failure: } struct nft_obj_filter { - char table[NFT_OBJ_MAXNAMELEN]; + char *table; u32 type; }; @@ -4475,7 +4491,10 @@ done: static int nf_tables_dump_obj_done(struct netlink_callback *cb) { - kfree(cb->data); + struct nft_obj_filter *filter = cb->data; + + kfree(filter->table); + kfree(filter); return 0; } @@ -4489,9 +4508,13 @@ nft_obj_filter_alloc(const struct nlattr * const nla[]) if (!filter) return ERR_PTR(-ENOMEM); - if (nla[NFTA_OBJ_TABLE]) - nla_strlcpy(filter->table, nla[NFTA_OBJ_TABLE], - NFT_TABLE_MAXNAMELEN); + if (nla[NFTA_OBJ_TABLE]) { + filter->table = nla_strdup(nla[NFTA_OBJ_TABLE], GFP_KERNEL); + if (!filter->table) { + kfree(filter); + return ERR_PTR(-ENOMEM); + } + } if (nla[NFTA_OBJ_TYPE]) filter->type = ntohl(nla_get_be32(nla[NFTA_OBJ_TYPE])); diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c index 0c3a0049e4aa..62787d985e9d 100644 --- a/net/netfilter/nf_tables_trace.c +++ b/net/netfilter/nf_tables_trace.c @@ -175,7 +175,7 @@ void nft_trace_notify(struct nft_traceinfo *info) return; size = nlmsg_total_size(sizeof(struct nfgenmsg)) + - nla_total_size(NFT_TABLE_MAXNAMELEN) + + nla_total_size(strlen(info->chain->table->name)) + nla_total_size(NFT_CHAIN_MAXNAMELEN) + nla_total_size_64bit(sizeof(__be64)) + /* rule handle */ nla_total_size(sizeof(__be32)) + /* trace type */ -- cgit v1.2.3 From b7263e071aba736cea9e71cdf2e76dfa7aebd039 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 27 Jul 2017 16:56:42 +0200 Subject: netfilter: nf_tables: Allow chain name of up to 255 chars Same conversion as for table names, use NFT_NAME_MAXLEN as upper boundary as well. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 4 ++-- include/uapi/linux/netfilter/nf_tables.h | 2 +- net/netfilter/nf_tables_api.c | 34 ++++++++++++++++++++++++-------- net/netfilter/nf_tables_trace.c | 27 +++++++++++++++++++++++-- 4 files changed, 54 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 05ecf78ec078..be1610162ee0 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -859,7 +859,7 @@ struct nft_chain { u16 level; u8 flags:6, genmask:2; - char name[NFT_CHAIN_MAXNAMELEN]; + char *name; }; enum nft_chain_type { @@ -1272,7 +1272,7 @@ struct nft_trans_set { struct nft_trans_chain { bool update; - char name[NFT_CHAIN_MAXNAMELEN]; + char *name; struct nft_stats __percpu *stats; u8 policy; }; diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 0b94e572ef16..d9c03a8608ee 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -3,7 +3,7 @@ #define NFT_NAME_MAXLEN 256 #define NFT_TABLE_MAXNAMELEN NFT_NAME_MAXLEN -#define NFT_CHAIN_MAXNAMELEN 32 +#define NFT_CHAIN_MAXNAMELEN NFT_NAME_MAXLEN #define NFT_SET_MAXNAMELEN 32 #define NFT_OBJ_MAXNAMELEN 32 #define NFT_USERDATA_MAXLEN 256 diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index c2e392d5e512..747499039709 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1250,8 +1250,10 @@ static void nf_tables_chain_destroy(struct nft_chain *chain) static_branch_dec(&nft_counters_enabled); if (basechain->ops[0].dev != NULL) dev_put(basechain->ops[0].dev); + kfree(chain->name); kfree(basechain); } else { + kfree(chain->name); kfree(chain); } } @@ -1476,8 +1478,13 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, nft_trans_chain_policy(trans) = -1; if (nla[NFTA_CHAIN_HANDLE] && name) { - nla_strlcpy(nft_trans_chain_name(trans), name, - NFT_CHAIN_MAXNAMELEN); + nft_trans_chain_name(trans) = + nla_strdup(name, GFP_KERNEL); + if (!nft_trans_chain_name(trans)) { + kfree(trans); + free_percpu(stats); + return -ENOMEM; + } } list_add_tail(&trans->list, &net->nft.commit_list); return 0; @@ -1544,7 +1551,11 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, INIT_LIST_HEAD(&chain->rules); chain->handle = nf_tables_alloc_handle(table); chain->table = table; - nla_strlcpy(chain->name, name, NFT_CHAIN_MAXNAMELEN); + chain->name = nla_strdup(name, GFP_KERNEL); + if (!chain->name) { + err = -ENOMEM; + goto err1; + } err = nf_tables_register_hooks(net, table, chain, afi->nops); if (err < 0) @@ -1979,7 +1990,7 @@ err: struct nft_rule_dump_ctx { char *table; - char chain[NFT_CHAIN_MAXNAMELEN]; + char *chain; }; static int nf_tables_dump_rules(struct sk_buff *skb, @@ -2047,6 +2058,7 @@ static int nf_tables_dump_rules_done(struct netlink_callback *cb) if (ctx) { kfree(ctx->table); + kfree(ctx->chain); kfree(ctx); } return 0; @@ -2088,9 +2100,15 @@ static int nf_tables_getrule(struct net *net, struct sock *nlsk, return -ENOMEM; } } - if (nla[NFTA_RULE_CHAIN]) - nla_strlcpy(ctx->chain, nla[NFTA_RULE_CHAIN], - sizeof(ctx->chain)); + if (nla[NFTA_RULE_CHAIN]) { + ctx->chain = nla_strdup(nla[NFTA_RULE_CHAIN], + GFP_KERNEL); + if (!ctx->chain) { + kfree(ctx->table); + kfree(ctx); + return -ENOMEM; + } + } c.data = ctx; } @@ -4863,7 +4881,7 @@ static void nft_chain_commit_update(struct nft_trans *trans) { struct nft_base_chain *basechain; - if (nft_trans_chain_name(trans)[0]) + if (nft_trans_chain_name(trans)) strcpy(trans->ctx.chain->name, nft_trans_chain_name(trans)); if (!nft_is_base_chain(trans->ctx.chain)) diff --git a/net/netfilter/nf_tables_trace.c b/net/netfilter/nf_tables_trace.c index 62787d985e9d..e1dc527a493b 100644 --- a/net/netfilter/nf_tables_trace.c +++ b/net/netfilter/nf_tables_trace.c @@ -162,6 +162,27 @@ static int nf_trace_fill_rule_info(struct sk_buff *nlskb, NFTA_TRACE_PAD); } +static bool nft_trace_have_verdict_chain(struct nft_traceinfo *info) +{ + switch (info->type) { + case NFT_TRACETYPE_RETURN: + case NFT_TRACETYPE_RULE: + break; + default: + return false; + } + + switch (info->verdict->code) { + case NFT_JUMP: + case NFT_GOTO: + break; + default: + return false; + } + + return true; +} + void nft_trace_notify(struct nft_traceinfo *info) { const struct nft_pktinfo *pkt = info->pkt; @@ -176,12 +197,11 @@ void nft_trace_notify(struct nft_traceinfo *info) size = nlmsg_total_size(sizeof(struct nfgenmsg)) + nla_total_size(strlen(info->chain->table->name)) + - nla_total_size(NFT_CHAIN_MAXNAMELEN) + + nla_total_size(strlen(info->chain->name)) + nla_total_size_64bit(sizeof(__be64)) + /* rule handle */ nla_total_size(sizeof(__be32)) + /* trace type */ nla_total_size(0) + /* VERDICT, nested */ nla_total_size(sizeof(u32)) + /* verdict code */ - nla_total_size(NFT_CHAIN_MAXNAMELEN) + /* jump target */ nla_total_size(sizeof(u32)) + /* id */ nla_total_size(NFT_TRACETYPE_LL_HSIZE) + nla_total_size(NFT_TRACETYPE_NETWORK_HSIZE) + @@ -194,6 +214,9 @@ void nft_trace_notify(struct nft_traceinfo *info) nla_total_size(sizeof(u32)) + /* nfproto */ nla_total_size(sizeof(u32)); /* policy */ + if (nft_trace_have_verdict_chain(info)) + size += nla_total_size(strlen(info->verdict->chain->name)); /* jump target */ + skb = nlmsg_new(size, GFP_ATOMIC); if (!skb) return; -- cgit v1.2.3 From 387454901bd62022ac1b04e15bd8d4fcc60bbed4 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 27 Jul 2017 16:56:43 +0200 Subject: netfilter: nf_tables: Allow set names of up to 255 chars Same conversion as for table names, use NFT_NAME_MAXLEN as upper boundary as well. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 +- include/uapi/linux/netfilter/nf_tables.h | 2 +- net/netfilter/nf_tables_api.c | 18 ++++++++++++++---- 3 files changed, 16 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index be1610162ee0..66ba62fa7d90 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -396,7 +396,7 @@ void nft_unregister_set(struct nft_set_type *type); struct nft_set { struct list_head list; struct list_head bindings; - char name[NFT_SET_MAXNAMELEN]; + char *name; u32 ktype; u32 dtype; u32 objtype; diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index d9c03a8608ee..b5e73e80b7b6 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -4,7 +4,7 @@ #define NFT_NAME_MAXLEN 256 #define NFT_TABLE_MAXNAMELEN NFT_NAME_MAXLEN #define NFT_CHAIN_MAXNAMELEN NFT_NAME_MAXLEN -#define NFT_SET_MAXNAMELEN 32 +#define NFT_SET_MAXNAMELEN NFT_NAME_MAXLEN #define NFT_OBJ_MAXNAMELEN 32 #define NFT_USERDATA_MAXLEN 256 diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 747499039709..e6a07f27b1a3 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -2650,7 +2650,7 @@ static int nf_tables_set_alloc_name(struct nft_ctx *ctx, struct nft_set *set, unsigned long *inuse; unsigned int n = 0, min = 0; - p = strnchr(name, NFT_SET_MAXNAMELEN, '%'); + p = strchr(name, '%'); if (p != NULL) { if (p[1] != 'd' || strchr(p + 2, '%')) return -EINVAL; @@ -2681,7 +2681,10 @@ cont: free_page((unsigned long)inuse); } - snprintf(set->name, sizeof(set->name), name, min + n); + set->name = kasprintf(GFP_KERNEL, name, min + n); + if (!set->name) + return -ENOMEM; + list_for_each_entry(i, &ctx->table->sets, list) { if (!nft_is_active_next(ctx->net, i)) continue; @@ -2958,7 +2961,7 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, struct nft_table *table; struct nft_set *set; struct nft_ctx ctx; - char name[NFT_SET_MAXNAMELEN]; + char *name; unsigned int size; bool create; u64 timeout; @@ -3104,8 +3107,14 @@ static int nf_tables_newset(struct net *net, struct sock *nlsk, goto err1; } - nla_strlcpy(name, nla[NFTA_SET_NAME], sizeof(set->name)); + name = nla_strdup(nla[NFTA_SET_NAME], GFP_KERNEL); + if (!name) { + err = -ENOMEM; + goto err2; + } + err = nf_tables_set_alloc_name(&ctx, set, name); + kfree(name); if (err < 0) goto err2; @@ -3155,6 +3164,7 @@ static void nft_set_destroy(struct nft_set *set) { set->ops->destroy(set); module_put(set->ops->type->owner); + kfree(set->name); kvfree(set); } -- cgit v1.2.3 From 615095752100748e221028fc96163c2b78185ae4 Mon Sep 17 00:00:00 2001 From: Phil Sutter Date: Thu, 27 Jul 2017 16:56:44 +0200 Subject: netfilter: nf_tables: Allow object names of up to 255 chars Same conversion as for table names, use NFT_NAME_MAXLEN as upper boundary as well. Signed-off-by: Phil Sutter Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 2 +- include/uapi/linux/netfilter/nf_tables.h | 2 +- net/netfilter/nf_tables_api.c | 11 +++++++++-- 3 files changed, 11 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index 66ba62fa7d90..f9795fe394f3 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1016,7 +1016,7 @@ int nft_verdict_dump(struct sk_buff *skb, int type, */ struct nft_object { struct list_head list; - char name[NFT_OBJ_MAXNAMELEN]; + char *name; struct nft_table *table; u32 genmask:2, use:30; diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index b5e73e80b7b6..be25cf69295b 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -5,7 +5,7 @@ #define NFT_TABLE_MAXNAMELEN NFT_NAME_MAXLEN #define NFT_CHAIN_MAXNAMELEN NFT_NAME_MAXLEN #define NFT_SET_MAXNAMELEN NFT_NAME_MAXLEN -#define NFT_OBJ_MAXNAMELEN 32 +#define NFT_OBJ_MAXNAMELEN NFT_NAME_MAXLEN #define NFT_USERDATA_MAXLEN 256 /** diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index e6a07f27b1a3..149785ff1c7b 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4402,15 +4402,21 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk, goto err1; } obj->table = table; - nla_strlcpy(obj->name, nla[NFTA_OBJ_NAME], NFT_OBJ_MAXNAMELEN); + obj->name = nla_strdup(nla[NFTA_OBJ_NAME], GFP_KERNEL); + if (!obj->name) { + err = -ENOMEM; + goto err2; + } err = nft_trans_obj_add(&ctx, NFT_MSG_NEWOBJ, obj); if (err < 0) - goto err2; + goto err3; list_add_tail_rcu(&obj->list, &table->objects); table->use++; return 0; +err3: + kfree(obj->name); err2: if (obj->type->destroy) obj->type->destroy(obj); @@ -4626,6 +4632,7 @@ static void nft_obj_destroy(struct nft_object *obj) obj->type->destroy(obj); module_put(obj->type->owner); + kfree(obj->name); kfree(obj); } -- cgit v1.2.3 From 9b7e26aee7cf27ffb37bb2f17229cecc89a833bd Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 28 Jul 2017 10:34:42 +0200 Subject: netfilter: nft_set_rbtree: use seqcount to avoid lock in most cases switch to lockless lockup. write side now also increments sequence counter. On lookup, sample counter value and only take the lock if we did not find a match and the counter has changed. This avoids need to write to private area in normal (lookup) cases. In case we detect a writer (seqretry is true) we fall back to taking the readlock. The readlock is also used during dumps to ensure we get a consistent tree walk. Similar technique (rbtree+seqlock) was used by David Howells in rxrpc. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_set_rbtree.c | 49 +++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/netfilter/nft_set_rbtree.c b/net/netfilter/nft_set_rbtree.c index bce5382f1d49..d83a4ec5900d 100644 --- a/net/netfilter/nft_set_rbtree.c +++ b/net/netfilter/nft_set_rbtree.c @@ -19,8 +19,9 @@ #include struct nft_rbtree { - rwlock_t lock; struct rb_root root; + rwlock_t lock; + seqcount_t count; }; struct nft_rbtree_elem { @@ -40,8 +41,9 @@ static bool nft_rbtree_equal(const struct nft_set *set, const void *this, return memcmp(this, nft_set_ext_key(&interval->ext), set->klen) == 0; } -static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set, - const u32 *key, const struct nft_set_ext **ext) +static bool __nft_rbtree_lookup(const struct net *net, const struct nft_set *set, + const u32 *key, const struct nft_set_ext **ext, + unsigned int seq) { struct nft_rbtree *priv = nft_set_priv(set); const struct nft_rbtree_elem *rbe, *interval = NULL; @@ -50,15 +52,17 @@ static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set, const void *this; int d; - read_lock_bh(&priv->lock); - parent = priv->root.rb_node; + parent = rcu_dereference_raw(priv->root.rb_node); while (parent != NULL) { + if (read_seqcount_retry(&priv->count, seq)) + return false; + rbe = rb_entry(parent, struct nft_rbtree_elem, node); this = nft_set_ext_key(&rbe->ext); d = memcmp(this, key, set->klen); if (d < 0) { - parent = parent->rb_left; + parent = rcu_dereference_raw(parent->rb_left); if (interval && nft_rbtree_equal(set, this, interval) && nft_rbtree_interval_end(this) && @@ -66,15 +70,14 @@ static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set, continue; interval = rbe; } else if (d > 0) - parent = parent->rb_right; + parent = rcu_dereference_raw(parent->rb_right); else { if (!nft_set_elem_active(&rbe->ext, genmask)) { - parent = parent->rb_left; + parent = rcu_dereference_raw(parent->rb_left); continue; } if (nft_rbtree_interval_end(rbe)) goto out; - read_unlock_bh(&priv->lock); *ext = &rbe->ext; return true; @@ -84,15 +87,32 @@ static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set, if (set->flags & NFT_SET_INTERVAL && interval != NULL && nft_set_elem_active(&interval->ext, genmask) && !nft_rbtree_interval_end(interval)) { - read_unlock_bh(&priv->lock); *ext = &interval->ext; return true; } out: - read_unlock_bh(&priv->lock); return false; } +static bool nft_rbtree_lookup(const struct net *net, const struct nft_set *set, + const u32 *key, const struct nft_set_ext **ext) +{ + struct nft_rbtree *priv = nft_set_priv(set); + unsigned int seq = read_seqcount_begin(&priv->count); + bool ret; + + ret = __nft_rbtree_lookup(net, set, key, ext, seq); + if (ret || !read_seqcount_retry(&priv->count, seq)) + return ret; + + read_lock_bh(&priv->lock); + seq = read_seqcount_begin(&priv->count); + ret = __nft_rbtree_lookup(net, set, key, ext, seq); + read_unlock_bh(&priv->lock); + + return ret; +} + static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, struct nft_rbtree_elem *new, struct nft_set_ext **ext) @@ -130,7 +150,7 @@ static int __nft_rbtree_insert(const struct net *net, const struct nft_set *set, } } } - rb_link_node(&new->node, parent, p); + rb_link_node_rcu(&new->node, parent, p); rb_insert_color(&new->node, &priv->root); return 0; } @@ -144,7 +164,9 @@ static int nft_rbtree_insert(const struct net *net, const struct nft_set *set, int err; write_lock_bh(&priv->lock); + write_seqcount_begin(&priv->count); err = __nft_rbtree_insert(net, set, rbe, ext); + write_seqcount_end(&priv->count); write_unlock_bh(&priv->lock); return err; @@ -158,7 +180,9 @@ static void nft_rbtree_remove(const struct net *net, struct nft_rbtree_elem *rbe = elem->priv; write_lock_bh(&priv->lock); + write_seqcount_begin(&priv->count); rb_erase(&rbe->node, &priv->root); + write_seqcount_end(&priv->count); write_unlock_bh(&priv->lock); } @@ -264,6 +288,7 @@ static int nft_rbtree_init(const struct nft_set *set, struct nft_rbtree *priv = nft_set_priv(set); rwlock_init(&priv->lock); + seqcount_init(&priv->count); priv->root = RB_ROOT; return 0; } -- cgit v1.2.3 From 4d3a57f23dec59f0a2362e63540b2d01b37afe0a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 28 Jul 2017 11:22:04 +0200 Subject: netfilter: conntrack: do not enable connection tracking unless needed Discussion during NFWS 2017 in Faro has shown that the current conntrack behaviour is unreasonable. Even if conntrack module is loaded on behalf of a single net namespace, its turned on for all namespaces, which is expensive. Commit 481fa373476 ("netfilter: conntrack: add nf_conntrack_default_on sysctl") attempted to provide an alternative to the 'default on' behaviour by adding a sysctl to change it. However, as Eric points out, the sysctl only becomes available once the module is loaded, and then its too late. So we either have to move the sysctl to the core, or, alternatively, change conntrack to become active only once the rule set requires this. This does the latter, conntrack is only enabled when a rule needs it. Reported-by: Eric Dumazet Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- Documentation/networking/nf_conntrack-sysctl.txt | 11 --------- include/net/netfilter/nf_conntrack_l3proto.h | 15 ------------ net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 16 ++----------- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 17 ++------------ net/netfilter/nf_conntrack_proto.c | 29 ------------------------ net/netfilter/nf_conntrack_standalone.c | 10 -------- 6 files changed, 4 insertions(+), 94 deletions(-) (limited to 'net') diff --git a/Documentation/networking/nf_conntrack-sysctl.txt b/Documentation/networking/nf_conntrack-sysctl.txt index 497d668288f9..433b6724797a 100644 --- a/Documentation/networking/nf_conntrack-sysctl.txt +++ b/Documentation/networking/nf_conntrack-sysctl.txt @@ -96,17 +96,6 @@ nf_conntrack_max - INTEGER Size of connection tracking table. Default value is nf_conntrack_buckets value * 4. -nf_conntrack_default_on - BOOLEAN - 0 - don't register conntrack in new net namespaces - 1 - register conntrack in new net namespaces (default) - - This controls wheter newly created network namespaces have connection - tracking enabled by default. It will be enabled automatically - regardless of this setting if the new net namespace requires - connection tracking, e.g. when NAT rules are created. - This setting is only visible in initial user namespace, it has no - effect on existing namespaces. - nf_conntrack_tcp_be_liberal - BOOLEAN 0 - disabled (default) not 0 - enabled diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index 6d14b36e3a49..1b8de164d744 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -73,21 +73,6 @@ struct nf_conntrack_l3proto { extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[NFPROTO_NUMPROTO]; -#ifdef CONFIG_SYSCTL -/* Protocol pernet registration. */ -int nf_ct_l3proto_pernet_register(struct net *net, - struct nf_conntrack_l3proto *proto); -#else -static inline int nf_ct_l3proto_pernet_register(struct net *n, - struct nf_conntrack_l3proto *p) -{ - return 0; -} -#endif - -void nf_ct_l3proto_pernet_unregister(struct net *net, - struct nf_conntrack_l3proto *proto); - /* Protocol global registration. */ int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto); void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto); diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 63e4ea0e01f8..de5f0e6ddd1b 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -398,24 +398,12 @@ static struct nf_conntrack_l4proto *builtin_l4proto4[] = { static int ipv4_net_init(struct net *net) { - int ret = 0; - - ret = nf_ct_l4proto_pernet_register(net, builtin_l4proto4, - ARRAY_SIZE(builtin_l4proto4)); - if (ret < 0) - return ret; - ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv4); - if (ret < 0) { - pr_err("nf_conntrack_ipv4: pernet registration failed\n"); - nf_ct_l4proto_pernet_unregister(net, builtin_l4proto4, - ARRAY_SIZE(builtin_l4proto4)); - } - return ret; + return nf_ct_l4proto_pernet_register(net, builtin_l4proto4, + ARRAY_SIZE(builtin_l4proto4)); } static void ipv4_net_exit(struct net *net) { - nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv4); nf_ct_l4proto_pernet_unregister(net, builtin_l4proto4, ARRAY_SIZE(builtin_l4proto4)); } diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index f2d2f4a9294b..ddef5ee9e0a8 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -398,25 +398,12 @@ static struct nf_conntrack_l4proto *builtin_l4proto6[] = { static int ipv6_net_init(struct net *net) { - int ret = 0; - - ret = nf_ct_l4proto_pernet_register(net, builtin_l4proto6, - ARRAY_SIZE(builtin_l4proto6)); - if (ret < 0) - return ret; - - ret = nf_ct_l3proto_pernet_register(net, &nf_conntrack_l3proto_ipv6); - if (ret < 0) { - pr_err("nf_conntrack_ipv6: pernet registration failed.\n"); - nf_ct_l4proto_pernet_unregister(net, builtin_l4proto6, - ARRAY_SIZE(builtin_l4proto6)); - } - return ret; + return nf_ct_l4proto_pernet_register(net, builtin_l4proto6, + ARRAY_SIZE(builtin_l4proto6)); } static void ipv6_net_exit(struct net *net) { - nf_ct_l3proto_pernet_unregister(net, &nf_conntrack_l3proto_ipv6); nf_ct_l4proto_pernet_unregister(net, builtin_l4proto6, ARRAY_SIZE(builtin_l4proto6)); } diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 1dcad229c3cc..7c89dade6fd3 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -238,20 +238,6 @@ out_unlock: } EXPORT_SYMBOL_GPL(nf_ct_l3proto_register); -#ifdef CONFIG_SYSCTL -extern unsigned int nf_conntrack_default_on; - -int nf_ct_l3proto_pernet_register(struct net *net, - struct nf_conntrack_l3proto *proto) -{ - if (nf_conntrack_default_on == 0) - return 0; - - return proto->net_ns_get ? proto->net_ns_get(net) : 0; -} -EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_register); -#endif - void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto) { BUG_ON(proto->l3proto >= NFPROTO_NUMPROTO); @@ -270,21 +256,6 @@ void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto) } EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister); -void nf_ct_l3proto_pernet_unregister(struct net *net, - struct nf_conntrack_l3proto *proto) -{ - /* - * nf_conntrack_default_on *might* have registered hooks. - * ->net_ns_put must cope with more puts() than get(), i.e. - * if nf_conntrack_default_on was 0 at time of - * nf_ct_l3proto_pernet_register invocation this net_ns_put() - * should be a noop. - */ - if (proto->net_ns_put) - proto->net_ns_put(net); -} -EXPORT_SYMBOL_GPL(nf_ct_l3proto_pernet_unregister); - static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, struct nf_conntrack_l4proto *l4proto) { diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index ccb5cb9043e0..5b6c675d55b1 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -452,9 +452,6 @@ static int log_invalid_proto_max __read_mostly = 255; /* size the user *wants to set */ static unsigned int nf_conntrack_htable_size_user __read_mostly; -extern unsigned int nf_conntrack_default_on; -unsigned int nf_conntrack_default_on __read_mostly = 1; - static int nf_conntrack_hash_sysctl(struct ctl_table *table, int write, void __user *buffer, size_t *lenp, loff_t *ppos) @@ -520,13 +517,6 @@ static struct ctl_table nf_ct_sysctl_table[] = { .mode = 0644, .proc_handler = proc_dointvec, }, - { - .procname = "nf_conntrack_default_on", - .data = &nf_conntrack_default_on, - .maxlen = sizeof(unsigned int), - .mode = 0644, - .proc_handler = proc_dointvec, - }, { } }; -- cgit v1.2.3 From e7942d0633c47c791ece6afa038be9cf977226de Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 30 Jul 2017 03:57:18 +0200 Subject: tcp: remove prequeue support prequeue is a tcp receive optimization that moves part of rx processing from bh to process context. This only works if the socket being processed belongs to a process that is blocked in recv on that socket. In practice, this doesn't happen anymore that often because nowadays servers tend to use an event driven (epoll) model. Even normal client applications (web browsers) commonly use many tcp connections in parallel. This has measureable impact only in netperf (which uses plain recv and thus allows prequeue use) from host to locally running vm (~4%), however, there were no changes when using netperf between two physical hosts with ixgbe interfaces. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/tcp.h | 9 ---- include/net/tcp.h | 11 ----- net/ipv4/tcp.c | 105 ----------------------------------------------- net/ipv4/tcp_input.c | 62 ---------------------------- net/ipv4/tcp_ipv4.c | 61 +-------------------------- net/ipv4/tcp_minisocks.c | 1 - net/ipv4/tcp_timer.c | 12 ------ net/ipv6/tcp_ipv6.c | 3 +- 8 files changed, 2 insertions(+), 262 deletions(-) (limited to 'net') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 542ca1ae02c4..32fb37cfb0d1 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -192,15 +192,6 @@ struct tcp_sock { struct list_head tsq_node; /* anchor in tsq_tasklet.head list */ - /* Data for direct copy to user */ - struct { - struct sk_buff_head prequeue; - struct task_struct *task; - struct msghdr *msg; - int memory; - int len; - } ucopy; - u32 snd_wl1; /* Sequence for window update */ u32 snd_wnd; /* The window we expect to receive */ u32 max_window; /* Maximal window ever seen from peer */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 12d68335acd4..93f115cfc8f8 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1244,17 +1244,6 @@ static inline bool tcp_checksum_complete(struct sk_buff *skb) __tcp_checksum_complete(skb); } -/* Prequeue for VJ style copy to user, combined with checksumming. */ - -static inline void tcp_prequeue_init(struct tcp_sock *tp) -{ - tp->ucopy.task = NULL; - tp->ucopy.len = 0; - tp->ucopy.memory = 0; - skb_queue_head_init(&tp->ucopy.prequeue); -} - -bool tcp_prequeue(struct sock *sk, struct sk_buff *skb); bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb); int tcp_filter(struct sock *sk, struct sk_buff *skb); diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 71ce33decd97..62018ea6f45f 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -400,7 +400,6 @@ void tcp_init_sock(struct sock *sk) tp->out_of_order_queue = RB_ROOT; tcp_init_xmit_timers(sk); - tcp_prequeue_init(tp); INIT_LIST_HEAD(&tp->tsq_node); icsk->icsk_rto = TCP_TIMEOUT_INIT; @@ -1525,20 +1524,6 @@ static void tcp_cleanup_rbuf(struct sock *sk, int copied) tcp_send_ack(sk); } -static void tcp_prequeue_process(struct sock *sk) -{ - struct sk_buff *skb; - struct tcp_sock *tp = tcp_sk(sk); - - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPREQUEUED); - - while ((skb = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) - sk_backlog_rcv(sk, skb); - - /* Clear memory counter. */ - tp->ucopy.memory = 0; -} - static struct sk_buff *tcp_recv_skb(struct sock *sk, u32 seq, u32 *off) { struct sk_buff *skb; @@ -1671,7 +1656,6 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, int err; int target; /* Read at least this many bytes */ long timeo; - struct task_struct *user_recv = NULL; struct sk_buff *skb, *last; u32 urg_hole = 0; @@ -1806,51 +1790,6 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, tcp_cleanup_rbuf(sk, copied); - if (!sysctl_tcp_low_latency && tp->ucopy.task == user_recv) { - /* Install new reader */ - if (!user_recv && !(flags & (MSG_TRUNC | MSG_PEEK))) { - user_recv = current; - tp->ucopy.task = user_recv; - tp->ucopy.msg = msg; - } - - tp->ucopy.len = len; - - WARN_ON(tp->copied_seq != tp->rcv_nxt && - !(flags & (MSG_PEEK | MSG_TRUNC))); - - /* Ugly... If prequeue is not empty, we have to - * process it before releasing socket, otherwise - * order will be broken at second iteration. - * More elegant solution is required!!! - * - * Look: we have the following (pseudo)queues: - * - * 1. packets in flight - * 2. backlog - * 3. prequeue - * 4. receive_queue - * - * Each queue can be processed only if the next ones - * are empty. At this point we have empty receive_queue. - * But prequeue _can_ be not empty after 2nd iteration, - * when we jumped to start of loop because backlog - * processing added something to receive_queue. - * We cannot release_sock(), because backlog contains - * packets arrived _after_ prequeued ones. - * - * Shortly, algorithm is clear --- to process all - * the queues in order. We could make it more directly, - * requeueing packets from backlog to prequeue, if - * is not empty. It is more elegant, but eats cycles, - * unfortunately. - */ - if (!skb_queue_empty(&tp->ucopy.prequeue)) - goto do_prequeue; - - /* __ Set realtime policy in scheduler __ */ - } - if (copied >= target) { /* Do not sleep, just process backlog. */ release_sock(sk); @@ -1859,31 +1798,6 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, sk_wait_data(sk, &timeo, last); } - if (user_recv) { - int chunk; - - /* __ Restore normal policy in scheduler __ */ - - chunk = len - tp->ucopy.len; - if (chunk != 0) { - NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, chunk); - len -= chunk; - copied += chunk; - } - - if (tp->rcv_nxt == tp->copied_seq && - !skb_queue_empty(&tp->ucopy.prequeue)) { -do_prequeue: - tcp_prequeue_process(sk); - - chunk = len - tp->ucopy.len; - if (chunk != 0) { - NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk); - len -= chunk; - copied += chunk; - } - } - } if ((flags & MSG_PEEK) && (peek_seq - copied - urg_hole != tp->copied_seq)) { net_dbg_ratelimited("TCP(%s:%d): Application bug, race in MSG_PEEK\n", @@ -1955,25 +1869,6 @@ skip_copy: break; } while (len > 0); - if (user_recv) { - if (!skb_queue_empty(&tp->ucopy.prequeue)) { - int chunk; - - tp->ucopy.len = copied > 0 ? len : 0; - - tcp_prequeue_process(sk); - - if (copied > 0 && (chunk = len - tp->ucopy.len) != 0) { - NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, chunk); - len -= chunk; - copied += chunk; - } - } - - tp->ucopy.task = NULL; - tp->ucopy.len = 0; - } - /* According to UNIX98, msg_name/msg_namelen are ignored * on connected socket. I was just happy when found this 8) --ANK */ diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index adc3f3e9468c..770ce6cb3eca 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4611,22 +4611,6 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) goto out_of_window; /* Ok. In sequence. In window. */ - if (tp->ucopy.task == current && - tp->copied_seq == tp->rcv_nxt && tp->ucopy.len && - sock_owned_by_user(sk) && !tp->urg_data) { - int chunk = min_t(unsigned int, skb->len, - tp->ucopy.len); - - __set_current_state(TASK_RUNNING); - - if (!skb_copy_datagram_msg(skb, 0, tp->ucopy.msg, chunk)) { - tp->ucopy.len -= chunk; - tp->copied_seq += chunk; - eaten = (chunk == skb->len); - tcp_rcv_space_adjust(sk); - } - } - if (eaten <= 0) { queue_and_out: if (eaten < 0) { @@ -5186,26 +5170,6 @@ static void tcp_urg(struct sock *sk, struct sk_buff *skb, const struct tcphdr *t } } -static int tcp_copy_to_iovec(struct sock *sk, struct sk_buff *skb, int hlen) -{ - struct tcp_sock *tp = tcp_sk(sk); - int chunk = skb->len - hlen; - int err; - - if (skb_csum_unnecessary(skb)) - err = skb_copy_datagram_msg(skb, hlen, tp->ucopy.msg, chunk); - else - err = skb_copy_and_csum_datagram_msg(skb, hlen, tp->ucopy.msg); - - if (!err) { - tp->ucopy.len -= chunk; - tp->copied_seq += chunk; - tcp_rcv_space_adjust(sk); - } - - return err; -} - /* Accept RST for rcv_nxt - 1 after a FIN. * When tcp connections are abruptly terminated from Mac OSX (via ^C), a * FIN is sent followed by a RST packet. The RST is sent with the same @@ -5446,32 +5410,6 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, int eaten = 0; bool fragstolen = false; - if (tp->ucopy.task == current && - tp->copied_seq == tp->rcv_nxt && - len - tcp_header_len <= tp->ucopy.len && - sock_owned_by_user(sk)) { - __set_current_state(TASK_RUNNING); - - if (!tcp_copy_to_iovec(sk, skb, tcp_header_len)) { - /* Predicted packet is in window by definition. - * seq == rcv_nxt and rcv_wup <= rcv_nxt. - * Hence, check seq<=rcv_wup reduces to: - */ - if (tcp_header_len == - (sizeof(struct tcphdr) + - TCPOLEN_TSTAMP_ALIGNED) && - tp->rcv_nxt == tp->rcv_wup) - tcp_store_ts_recent(tp); - - tcp_rcv_rtt_measure_ts(sk, skb); - - __skb_pull(skb, tcp_header_len); - tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq); - NET_INC_STATS(sock_net(sk), - LINUX_MIB_TCPHPHITSTOUSER); - eaten = 1; - } - } if (!eaten) { if (tcp_checksum_complete(skb)) goto csum_error; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 3a19ea28339f..a68eb4577d36 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1541,61 +1541,6 @@ void tcp_v4_early_demux(struct sk_buff *skb) } } -/* Packet is added to VJ-style prequeue for processing in process - * context, if a reader task is waiting. Apparently, this exciting - * idea (VJ's mail "Re: query about TCP header on tcp-ip" of 07 Sep 93) - * failed somewhere. Latency? Burstiness? Well, at least now we will - * see, why it failed. 8)8) --ANK - * - */ -bool tcp_prequeue(struct sock *sk, struct sk_buff *skb) -{ - struct tcp_sock *tp = tcp_sk(sk); - - if (sysctl_tcp_low_latency || !tp->ucopy.task) - return false; - - if (skb->len <= tcp_hdrlen(skb) && - skb_queue_len(&tp->ucopy.prequeue) == 0) - return false; - - /* Before escaping RCU protected region, we need to take care of skb - * dst. Prequeue is only enabled for established sockets. - * For such sockets, we might need the skb dst only to set sk->sk_rx_dst - * Instead of doing full sk_rx_dst validity here, let's perform - * an optimistic check. - */ - if (likely(sk->sk_rx_dst)) - skb_dst_drop(skb); - else - skb_dst_force_safe(skb); - - __skb_queue_tail(&tp->ucopy.prequeue, skb); - tp->ucopy.memory += skb->truesize; - if (skb_queue_len(&tp->ucopy.prequeue) >= 32 || - tp->ucopy.memory + atomic_read(&sk->sk_rmem_alloc) > sk->sk_rcvbuf) { - struct sk_buff *skb1; - - BUG_ON(sock_owned_by_user(sk)); - __NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPPREQUEUEDROPPED, - skb_queue_len(&tp->ucopy.prequeue)); - - while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) - sk_backlog_rcv(sk, skb1); - - tp->ucopy.memory = 0; - } else if (skb_queue_len(&tp->ucopy.prequeue) == 1) { - wake_up_interruptible_sync_poll(sk_sleep(sk), - POLLIN | POLLRDNORM | POLLRDBAND); - if (!inet_csk_ack_scheduled(sk)) - inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, - (3 * tcp_rto_min(sk)) / 4, - TCP_RTO_MAX); - } - return true; -} -EXPORT_SYMBOL(tcp_prequeue); - bool tcp_add_backlog(struct sock *sk, struct sk_buff *skb) { u32 limit = sk->sk_rcvbuf + sk->sk_sndbuf; @@ -1770,8 +1715,7 @@ process: tcp_segs_in(tcp_sk(sk), skb); ret = 0; if (!sock_owned_by_user(sk)) { - if (!tcp_prequeue(sk, skb)) - ret = tcp_v4_do_rcv(sk, skb); + ret = tcp_v4_do_rcv(sk, skb); } else if (tcp_add_backlog(sk, skb)) { goto discard_and_relse; } @@ -1936,9 +1880,6 @@ void tcp_v4_destroy_sock(struct sock *sk) } #endif - /* Clean prequeue, it must be empty really */ - __skb_queue_purge(&tp->ucopy.prequeue); - /* Clean up a referenced TCP bind bucket. */ if (inet_csk(sk)->icsk_bind_hash) inet_put_port(sk); diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 0ff83c1637d8..188a6f31356d 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -445,7 +445,6 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, newtp->snd_sml = newtp->snd_una = newtp->snd_nxt = newtp->snd_up = treq->snt_isn + 1; - tcp_prequeue_init(newtp); INIT_LIST_HEAD(&newtp->tsq_node); tcp_init_wl(newtp, treq->rcv_isn); diff --git a/net/ipv4/tcp_timer.c b/net/ipv4/tcp_timer.c index c0feeeef962a..f753f9d2fee3 100644 --- a/net/ipv4/tcp_timer.c +++ b/net/ipv4/tcp_timer.c @@ -239,7 +239,6 @@ static int tcp_write_timeout(struct sock *sk) /* Called with BH disabled */ void tcp_delack_timer_handler(struct sock *sk) { - struct tcp_sock *tp = tcp_sk(sk); struct inet_connection_sock *icsk = inet_csk(sk); sk_mem_reclaim_partial(sk); @@ -254,17 +253,6 @@ void tcp_delack_timer_handler(struct sock *sk) } icsk->icsk_ack.pending &= ~ICSK_ACK_TIMER; - if (!skb_queue_empty(&tp->ucopy.prequeue)) { - struct sk_buff *skb; - - __NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPSCHEDULERFAILED); - - while ((skb = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) - sk_backlog_rcv(sk, skb); - - tp->ucopy.memory = 0; - } - if (inet_csk_ack_scheduled(sk)) { if (!icsk->icsk_ack.pingpong) { /* Delayed ACK missed: inflate ATO. */ diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 2968a33cca7d..39ee8e7fc4bd 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1505,8 +1505,7 @@ process: tcp_segs_in(tcp_sk(sk), skb); ret = 0; if (!sock_owned_by_user(sk)) { - if (!tcp_prequeue(sk, skb)) - ret = tcp_v6_do_rcv(sk, skb); + ret = tcp_v6_do_rcv(sk, skb); } else if (tcp_add_backlog(sk, skb)) { goto discard_and_relse; } -- cgit v1.2.3 From c13ee2a4f03ff6e15102b7731258681913e551a5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 30 Jul 2017 03:57:19 +0200 Subject: tcp: reindent two spots after prequeue removal These two branches are now always true, remove the conditional. objdiff shows no changes. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 50 +++++++++++++++++++++++--------------------------- 1 file changed, 23 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 770ce6cb3eca..87efde9f5a90 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4611,16 +4611,14 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) goto out_of_window; /* Ok. In sequence. In window. */ - if (eaten <= 0) { queue_and_out: - if (eaten < 0) { - if (skb_queue_len(&sk->sk_receive_queue) == 0) - sk_forced_mem_schedule(sk, skb->truesize); - else if (tcp_try_rmem_schedule(sk, skb, skb->truesize)) - goto drop; - } - eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen); + if (eaten < 0) { + if (skb_queue_len(&sk->sk_receive_queue) == 0) + sk_forced_mem_schedule(sk, skb->truesize); + else if (tcp_try_rmem_schedule(sk, skb, skb->truesize)) + goto drop; } + eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen); tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq); if (skb->len) tcp_event_data_recv(sk, skb); @@ -5410,30 +5408,28 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, int eaten = 0; bool fragstolen = false; - if (!eaten) { - if (tcp_checksum_complete(skb)) - goto csum_error; + if (tcp_checksum_complete(skb)) + goto csum_error; - if ((int)skb->truesize > sk->sk_forward_alloc) - goto step5; + if ((int)skb->truesize > sk->sk_forward_alloc) + goto step5; - /* Predicted packet is in window by definition. - * seq == rcv_nxt and rcv_wup <= rcv_nxt. - * Hence, check seq<=rcv_wup reduces to: - */ - if (tcp_header_len == - (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) && - tp->rcv_nxt == tp->rcv_wup) - tcp_store_ts_recent(tp); + /* Predicted packet is in window by definition. + * seq == rcv_nxt and rcv_wup <= rcv_nxt. + * Hence, check seq<=rcv_wup reduces to: + */ + if (tcp_header_len == + (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) && + tp->rcv_nxt == tp->rcv_wup) + tcp_store_ts_recent(tp); - tcp_rcv_rtt_measure_ts(sk, skb); + tcp_rcv_rtt_measure_ts(sk, skb); - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHPHITS); + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHPHITS); - /* Bulk data transfer: receiver */ - eaten = tcp_queue_rcv(sk, skb, tcp_header_len, - &fragstolen); - } + /* Bulk data transfer: receiver */ + eaten = tcp_queue_rcv(sk, skb, tcp_header_len, + &fragstolen); tcp_event_data_recv(sk, skb); -- cgit v1.2.3 From b6690b14386698ce2c19309abad3f17656bdfaea Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 30 Jul 2017 03:57:20 +0200 Subject: tcp: remove low_latency sysctl Was only checked by the removed prequeue code. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 7 +------ include/net/tcp.h | 1 - net/ipv4/sysctl_net_ipv4.c | 3 +++ net/ipv4/tcp_ipv4.c | 2 -- 4 files changed, 4 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index f485d553e65c..84c9b8cee780 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -353,12 +353,7 @@ tcp_l3mdev_accept - BOOLEAN compiled with CONFIG_NET_L3_MASTER_DEV. tcp_low_latency - BOOLEAN - If set, the TCP stack makes decisions that prefer lower - latency as opposed to higher throughput. By default, this - option is not set meaning that higher throughput is preferred. - An example of an application where this default should be - changed would be a Beowulf compute cluster. - Default: 0 + This is a legacy option, it has no effect anymore. tcp_max_orphans - INTEGER Maximal number of TCP sockets not attached to any user file handle, diff --git a/include/net/tcp.h b/include/net/tcp.h index 93f115cfc8f8..8507c81fb0e9 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -256,7 +256,6 @@ extern int sysctl_tcp_rmem[3]; extern int sysctl_tcp_app_win; extern int sysctl_tcp_adv_win_scale; extern int sysctl_tcp_frto; -extern int sysctl_tcp_low_latency; extern int sysctl_tcp_nometrics_save; extern int sysctl_tcp_moderate_rcvbuf; extern int sysctl_tcp_tso_win_divisor; diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 9bf809726066..0d3c038d7b04 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -45,6 +45,9 @@ static int tcp_syn_retries_max = MAX_TCP_SYNCNT; static int ip_ping_group_range_min[] = { 0, 0 }; static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX }; +/* obsolete */ +static int sysctl_tcp_low_latency __read_mostly; + /* Update system visible IP port range */ static void set_local_port_range(struct net *net, int range[2]) { diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index a68eb4577d36..9b51663cd5a4 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -85,8 +85,6 @@ #include #include -int sysctl_tcp_low_latency __read_mostly; - #ifdef CONFIG_TCP_MD5SIG static int tcp_v4_md5_hash_hdr(char *md5_hash, const struct tcp_md5sig_key *key, __be32 daddr, __be32 saddr, const struct tcphdr *th); -- cgit v1.2.3 From 45f119bf936b1f9f546a0b139c5b56f9bb2bdc78 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 30 Jul 2017 03:57:21 +0200 Subject: tcp: remove header prediction Like prequeue, I am not sure this is overly useful nowadays. If we receive a train of packets, GRO will aggregate them if the headers are the same (HP predates GRO by several years) so we don't get a per-packet benefit, only a per-aggregated-packet one. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/tcp.h | 6 -- include/net/tcp.h | 23 ------ net/ipv4/tcp.c | 4 +- net/ipv4/tcp_input.c | 192 +++-------------------------------------------- net/ipv4/tcp_minisocks.c | 2 - net/ipv4/tcp_output.c | 2 - 6 files changed, 10 insertions(+), 219 deletions(-) (limited to 'net') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 32fb37cfb0d1..d7389ea36e10 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -147,12 +147,6 @@ struct tcp_sock { u16 tcp_header_len; /* Bytes of tcp header to send */ u16 gso_segs; /* Max number of segs per GSO packet */ -/* - * Header prediction flags - * 0x5?10 << 16 + snd_wnd in net byte order - */ - __be32 pred_flags; - /* * RFC793 variables by their proper names. This means you can * read the code and the spec side by side (and laugh ...) diff --git a/include/net/tcp.h b/include/net/tcp.h index 8507c81fb0e9..8f11b82b5b5a 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -631,29 +631,6 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp) return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us); } -static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) -{ - tp->pred_flags = htonl((tp->tcp_header_len << 26) | - ntohl(TCP_FLAG_ACK) | - snd_wnd); -} - -static inline void tcp_fast_path_on(struct tcp_sock *tp) -{ - __tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale); -} - -static inline void tcp_fast_path_check(struct sock *sk) -{ - struct tcp_sock *tp = tcp_sk(sk); - - if (RB_EMPTY_ROOT(&tp->out_of_order_queue) && - tp->rcv_wnd && - atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && - !tp->urg_data) - tcp_fast_path_on(tp); -} - /* Compute the actual rto_min value */ static inline u32 tcp_rto_min(struct sock *sk) { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 62018ea6f45f..e022874d509f 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1848,10 +1848,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, tcp_rcv_space_adjust(sk); skip_copy: - if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) { + if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) tp->urg_data = 0; - tcp_fast_path_check(sk); - } if (used + offset < skb->len) continue; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 87efde9f5a90..bfde9d7d210e 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -103,7 +103,6 @@ int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; #define FLAG_DATA_SACKED 0x20 /* New SACK. */ #define FLAG_ECE 0x40 /* ECE in this ACK */ #define FLAG_LOST_RETRANS 0x80 /* This ACK marks some retransmission lost */ -#define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ #define FLAG_ORIG_SACK_ACKED 0x200 /* Never retransmitted data are (s)acked */ #define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ #define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ @@ -3367,12 +3366,6 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 if (tp->snd_wnd != nwin) { tp->snd_wnd = nwin; - /* Note, it is the only place, where - * fast path is recovered for sending TCP. - */ - tp->pred_flags = 0; - tcp_fast_path_check(sk); - if (tcp_send_head(sk)) tcp_slow_start_after_idle_check(sk); @@ -3597,19 +3590,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (flag & FLAG_UPDATE_TS_RECENT) tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq); - if (!(flag & FLAG_SLOWPATH) && after(ack, prior_snd_una)) { - /* Window is constant, pure forward advance. - * No more checks are required. - * Note, we use the fact that SND.UNA>=SND.WL2. - */ - tcp_update_wl(tp, ack_seq); - tcp_snd_una_update(tp, ack); - flag |= FLAG_WIN_UPDATE; - - tcp_in_ack_event(sk, CA_ACK_WIN_UPDATE); - - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHPACKS); - } else { + { u32 ack_ev_flags = CA_ACK_SLOWPATH; if (ack_seq != TCP_SKB_CB(skb)->end_seq) @@ -4398,8 +4379,6 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) return; } - /* Disable header prediction. */ - tp->pred_flags = 0; inet_csk_schedule_ack(sk); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOQUEUE); @@ -4638,8 +4617,6 @@ queue_and_out: if (tp->rx_opt.num_sacks) tcp_sack_remove(tp); - tcp_fast_path_check(sk); - if (eaten > 0) kfree_skb_partial(skb, fragstolen); if (!sock_flag(sk, SOCK_DEAD)) @@ -4965,7 +4942,6 @@ static int tcp_prune_queue(struct sock *sk) NET_INC_STATS(sock_net(sk), LINUX_MIB_RCVPRUNED); /* Massive buffer overcommit. */ - tp->pred_flags = 0; return -1; } @@ -5137,9 +5113,6 @@ static void tcp_check_urg(struct sock *sk, const struct tcphdr *th) tp->urg_data = TCP_URG_NOTYET; tp->urg_seq = ptr; - - /* Disable header prediction. */ - tp->pred_flags = 0; } /* This is the 'fast' part of urgent handling. */ @@ -5298,26 +5271,6 @@ discard: /* * TCP receive function for the ESTABLISHED state. - * - * It is split into a fast path and a slow path. The fast path is - * disabled when: - * - A zero window was announced from us - zero window probing - * is only handled properly in the slow path. - * - Out of order segments arrived. - * - Urgent data is expected. - * - There is no buffer space left - * - Unexpected TCP flags/window values/header lengths are received - * (detected by checking the TCP header against pred_flags) - * - Data is sent in both directions. Fast path only supports pure senders - * or pure receivers (this means either the sequence number or the ack - * value must stay constant) - * - Unexpected TCP option. - * - * When these conditions are not satisfied it drops into a standard - * receive procedure patterned after RFC793 to handle all cases. - * The first three cases are guaranteed by proper pred_flags setting, - * the rest is checked inline. Fast processing is turned on in - * tcp_data_queue when everything is OK. */ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th) @@ -5328,144 +5281,19 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, tcp_mstamp_refresh(tp); if (unlikely(!sk->sk_rx_dst)) inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb); - /* - * Header prediction. - * The code loosely follows the one in the famous - * "30 instruction TCP receive" Van Jacobson mail. - * - * Van's trick is to deposit buffers into socket queue - * on a device interrupt, to call tcp_recv function - * on the receive process context and checksum and copy - * the buffer to user space. smart... - * - * Our current scheme is not silly either but we take the - * extra cost of the net_bh soft interrupt processing... - * We do checksum and copy also but from device to kernel. - */ tp->rx_opt.saw_tstamp = 0; - /* pred_flags is 0xS?10 << 16 + snd_wnd - * if header_prediction is to be made - * 'S' will always be tp->tcp_header_len >> 2 - * '?' will be 0 for the fast path, otherwise pred_flags is 0 to - * turn it off (when there are holes in the receive - * space for instance) - * PSH flag is ignored. - */ - - if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags && - TCP_SKB_CB(skb)->seq == tp->rcv_nxt && - !after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt)) { - int tcp_header_len = tp->tcp_header_len; - - /* Timestamp header prediction: tcp_header_len - * is automatically equal to th->doff*4 due to pred_flags - * match. - */ - - /* Check timestamp */ - if (tcp_header_len == sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) { - /* No? Slow path! */ - if (!tcp_parse_aligned_timestamp(tp, th)) - goto slow_path; - - /* If PAWS failed, check it more carefully in slow path */ - if ((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) < 0) - goto slow_path; - - /* DO NOT update ts_recent here, if checksum fails - * and timestamp was corrupted part, it will result - * in a hung connection since we will drop all - * future packets due to the PAWS test. - */ - } - - if (len <= tcp_header_len) { - /* Bulk data transfer: sender */ - if (len == tcp_header_len) { - /* Predicted packet is in window by definition. - * seq == rcv_nxt and rcv_wup <= rcv_nxt. - * Hence, check seq<=rcv_wup reduces to: - */ - if (tcp_header_len == - (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) && - tp->rcv_nxt == tp->rcv_wup) - tcp_store_ts_recent(tp); - - /* We know that such packets are checksummed - * on entry. - */ - tcp_ack(sk, skb, 0); - __kfree_skb(skb); - tcp_data_snd_check(sk); - return; - } else { /* Header too small */ - TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS); - goto discard; - } - } else { - int eaten = 0; - bool fragstolen = false; - - if (tcp_checksum_complete(skb)) - goto csum_error; - - if ((int)skb->truesize > sk->sk_forward_alloc) - goto step5; - - /* Predicted packet is in window by definition. - * seq == rcv_nxt and rcv_wup <= rcv_nxt. - * Hence, check seq<=rcv_wup reduces to: - */ - if (tcp_header_len == - (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) && - tp->rcv_nxt == tp->rcv_wup) - tcp_store_ts_recent(tp); - - tcp_rcv_rtt_measure_ts(sk, skb); - - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHPHITS); - - /* Bulk data transfer: receiver */ - eaten = tcp_queue_rcv(sk, skb, tcp_header_len, - &fragstolen); - - tcp_event_data_recv(sk, skb); - - if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) { - /* Well, only one small jumplet in fast path... */ - tcp_ack(sk, skb, FLAG_DATA); - tcp_data_snd_check(sk); - if (!inet_csk_ack_scheduled(sk)) - goto no_ack; - } - - __tcp_ack_snd_check(sk, 0); -no_ack: - if (eaten) - kfree_skb_partial(skb, fragstolen); - sk->sk_data_ready(sk); - return; - } - } - -slow_path: if (len < (th->doff << 2) || tcp_checksum_complete(skb)) goto csum_error; if (!th->ack && !th->rst && !th->syn) goto discard; - /* - * Standard slow path. - */ - if (!tcp_validate_incoming(sk, skb, th, 1)) return; -step5: - if (tcp_ack(sk, skb, FLAG_SLOWPATH | FLAG_UPDATE_TS_RECENT) < 0) + if (tcp_ack(sk, skb, FLAG_UPDATE_TS_RECENT) < 0) goto discard; tcp_rcv_rtt_measure_ts(sk, skb); @@ -5519,11 +5347,10 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) if (sock_flag(sk, SOCK_KEEPOPEN)) inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tp)); - if (!tp->rx_opt.snd_wscale) - __tcp_fast_path_on(tp, tp->snd_wnd); - else - tp->pred_flags = 0; - + if (!sock_flag(sk, SOCK_DEAD)) { + sk->sk_state_change(sk); + sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT); + } } static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, @@ -5652,7 +5479,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, tcp_ecn_rcv_synack(tp, th); tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); - tcp_ack(sk, skb, FLAG_SLOWPATH); + tcp_ack(sk, skb, 0); /* Ok.. it's good. Set up sequence numbers and * move to established. @@ -5888,8 +5715,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) return 0; /* step 5: check the ACK field */ - acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH | - FLAG_UPDATE_TS_RECENT | + + acceptable = tcp_ack(sk, skb, FLAG_UPDATE_TS_RECENT | FLAG_NO_CHALLENGE_ACK) > 0; if (!acceptable) { @@ -5957,7 +5784,6 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) tp->lsndtime = tcp_jiffies32; tcp_initialize_rcv_mss(sk); - tcp_fast_path_on(tp); break; case TCP_FIN_WAIT1: { diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 188a6f31356d..1537b87c657f 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -436,8 +436,6 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, struct tcp_sock *newtp = tcp_sk(newsk); /* Now setup tcp_sock */ - newtp->pred_flags = 0; - newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1; newtp->segs_in = 1; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 886d874775df..8380464aead1 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -295,9 +295,7 @@ static u16 tcp_select_window(struct sock *sk) /* RFC1323 scaling applied */ new_win >>= tp->rx_opt.rcv_wscale; - /* If we advertise zero window, disable fast path. */ if (new_win == 0) { - tp->pred_flags = 0; if (old_win) NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTOZEROWINDOWADV); -- cgit v1.2.3 From 573aeb0492be3d0e5be9796a0c91abde794c1e36 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 30 Jul 2017 03:57:22 +0200 Subject: tcp: remove CA_ACK_SLOWPATH re-indent tcp_ack, and remove CA_ACK_SLOWPATH; it is always set now. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/tcp.h | 5 ++--- net/ipv4/tcp_input.c | 35 ++++++++++++++++------------------- net/ipv4/tcp_westwood.c | 31 ++++--------------------------- 3 files changed, 22 insertions(+), 49 deletions(-) (limited to 'net') diff --git a/include/net/tcp.h b/include/net/tcp.h index 8f11b82b5b5a..3ecb62811004 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -880,9 +880,8 @@ enum tcp_ca_event { /* Information about inbound ACK, passed to cong_ops->in_ack_event() */ enum tcp_ca_ack_event_flags { - CA_ACK_SLOWPATH = (1 << 0), /* In slow path processing */ - CA_ACK_WIN_UPDATE = (1 << 1), /* ACK updated window */ - CA_ACK_ECE = (1 << 2), /* ECE bit is set on ack */ + CA_ACK_WIN_UPDATE = (1 << 0), /* ACK updated window */ + CA_ACK_ECE = (1 << 1), /* ECE bit is set on ack */ }; /* diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index bfde9d7d210e..af0a98d54b62 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3547,6 +3547,7 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) u32 lost = tp->lost; int acked = 0; /* Number of packets newly acked */ int rexmit = REXMIT_NONE; /* Flag to (re)transmit to recover losses */ + u32 ack_ev_flags = 0; sack_state.first_sackt = 0; sack_state.rate = &rs; @@ -3590,30 +3591,26 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (flag & FLAG_UPDATE_TS_RECENT) tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq); - { - u32 ack_ev_flags = CA_ACK_SLOWPATH; - - if (ack_seq != TCP_SKB_CB(skb)->end_seq) - flag |= FLAG_DATA; - else - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPUREACKS); + if (ack_seq != TCP_SKB_CB(skb)->end_seq) + flag |= FLAG_DATA; + else + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPUREACKS); - flag |= tcp_ack_update_window(sk, skb, ack, ack_seq); + flag |= tcp_ack_update_window(sk, skb, ack, ack_seq); - if (TCP_SKB_CB(skb)->sacked) - flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una, - &sack_state); + if (TCP_SKB_CB(skb)->sacked) + flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una, + &sack_state); - if (tcp_ecn_rcv_ecn_echo(tp, tcp_hdr(skb))) { - flag |= FLAG_ECE; - ack_ev_flags |= CA_ACK_ECE; - } + if (tcp_ecn_rcv_ecn_echo(tp, tcp_hdr(skb))) { + flag |= FLAG_ECE; + ack_ev_flags = CA_ACK_ECE; + } - if (flag & FLAG_WIN_UPDATE) - ack_ev_flags |= CA_ACK_WIN_UPDATE; + if (flag & FLAG_WIN_UPDATE) + ack_ev_flags |= CA_ACK_WIN_UPDATE; - tcp_in_ack_event(sk, ack_ev_flags); - } + tcp_in_ack_event(sk, ack_ev_flags); /* We passed data and got it acked, remove any soft error * log. Something worked... diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c index bec9cafbe3f9..e5de84310949 100644 --- a/net/ipv4/tcp_westwood.c +++ b/net/ipv4/tcp_westwood.c @@ -153,24 +153,6 @@ static inline void update_rtt_min(struct westwood *w) w->rtt_min = min(w->rtt, w->rtt_min); } -/* - * @westwood_fast_bw - * It is called when we are in fast path. In particular it is called when - * header prediction is successful. In such case in fact update is - * straight forward and doesn't need any particular care. - */ -static inline void westwood_fast_bw(struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - struct westwood *w = inet_csk_ca(sk); - - westwood_update_window(sk); - - w->bk += tp->snd_una - w->snd_una; - w->snd_una = tp->snd_una; - update_rtt_min(w); -} - /* * @westwood_acked_count * This function evaluates cumul_ack for evaluating bk in case of @@ -223,17 +205,12 @@ static u32 tcp_westwood_bw_rttmin(const struct sock *sk) static void tcp_westwood_ack(struct sock *sk, u32 ack_flags) { - if (ack_flags & CA_ACK_SLOWPATH) { - struct westwood *w = inet_csk_ca(sk); - - westwood_update_window(sk); - w->bk += westwood_acked_count(sk); + struct westwood *w = inet_csk_ca(sk); - update_rtt_min(w); - return; - } + westwood_update_window(sk); + w->bk += westwood_acked_count(sk); - westwood_fast_bw(sk); + update_rtt_min(w); } static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event) -- cgit v1.2.3 From 3282e65558b3651e230ee985c174c35cb2fedaf1 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 30 Jul 2017 03:57:23 +0200 Subject: tcp: remove unused mib counters was used by tcp prequeue and header prediction. TCPFORWARDRETRANS use was removed in january. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/uapi/linux/snmp.h | 9 --------- net/ipv4/proc.c | 9 --------- 2 files changed, 18 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index d85693295798..b3f346fb9fe3 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -184,14 +184,7 @@ enum LINUX_MIB_DELAYEDACKLOST, /* DelayedACKLost */ LINUX_MIB_LISTENOVERFLOWS, /* ListenOverflows */ LINUX_MIB_LISTENDROPS, /* ListenDrops */ - LINUX_MIB_TCPPREQUEUED, /* TCPPrequeued */ - LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG, /* TCPDirectCopyFromBacklog */ - LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE, /* TCPDirectCopyFromPrequeue */ - LINUX_MIB_TCPPREQUEUEDROPPED, /* TCPPrequeueDropped */ - LINUX_MIB_TCPHPHITS, /* TCPHPHits */ - LINUX_MIB_TCPHPHITSTOUSER, /* TCPHPHitsToUser */ LINUX_MIB_TCPPUREACKS, /* TCPPureAcks */ - LINUX_MIB_TCPHPACKS, /* TCPHPAcks */ LINUX_MIB_TCPRENORECOVERY, /* TCPRenoRecovery */ LINUX_MIB_TCPSACKRECOVERY, /* TCPSackRecovery */ LINUX_MIB_TCPSACKRENEGING, /* TCPSACKReneging */ @@ -208,14 +201,12 @@ enum LINUX_MIB_TCPSACKFAILURES, /* TCPSackFailures */ LINUX_MIB_TCPLOSSFAILURES, /* TCPLossFailures */ LINUX_MIB_TCPFASTRETRANS, /* TCPFastRetrans */ - LINUX_MIB_TCPFORWARDRETRANS, /* TCPForwardRetrans */ LINUX_MIB_TCPSLOWSTARTRETRANS, /* TCPSlowStartRetrans */ LINUX_MIB_TCPTIMEOUTS, /* TCPTimeouts */ LINUX_MIB_TCPLOSSPROBES, /* TCPLossProbes */ LINUX_MIB_TCPLOSSPROBERECOVERY, /* TCPLossProbeRecovery */ LINUX_MIB_TCPRENORECOVERYFAIL, /* TCPRenoRecoveryFail */ LINUX_MIB_TCPSACKRECOVERYFAIL, /* TCPSackRecoveryFail */ - LINUX_MIB_TCPSCHEDULERFAILED, /* TCPSchedulerFailed */ LINUX_MIB_TCPRCVCOLLAPSED, /* TCPRcvCollapsed */ LINUX_MIB_TCPDSACKOLDSENT, /* TCPDSACKOldSent */ LINUX_MIB_TCPDSACKOFOSENT, /* TCPDSACKOfoSent */ diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index 43eb6567b3a0..b6d3fe03feb3 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -206,14 +206,7 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("DelayedACKLost", LINUX_MIB_DELAYEDACKLOST), SNMP_MIB_ITEM("ListenOverflows", LINUX_MIB_LISTENOVERFLOWS), SNMP_MIB_ITEM("ListenDrops", LINUX_MIB_LISTENDROPS), - SNMP_MIB_ITEM("TCPPrequeued", LINUX_MIB_TCPPREQUEUED), - SNMP_MIB_ITEM("TCPDirectCopyFromBacklog", LINUX_MIB_TCPDIRECTCOPYFROMBACKLOG), - SNMP_MIB_ITEM("TCPDirectCopyFromPrequeue", LINUX_MIB_TCPDIRECTCOPYFROMPREQUEUE), - SNMP_MIB_ITEM("TCPPrequeueDropped", LINUX_MIB_TCPPREQUEUEDROPPED), - SNMP_MIB_ITEM("TCPHPHits", LINUX_MIB_TCPHPHITS), - SNMP_MIB_ITEM("TCPHPHitsToUser", LINUX_MIB_TCPHPHITSTOUSER), SNMP_MIB_ITEM("TCPPureAcks", LINUX_MIB_TCPPUREACKS), - SNMP_MIB_ITEM("TCPHPAcks", LINUX_MIB_TCPHPACKS), SNMP_MIB_ITEM("TCPRenoRecovery", LINUX_MIB_TCPRENORECOVERY), SNMP_MIB_ITEM("TCPSackRecovery", LINUX_MIB_TCPSACKRECOVERY), SNMP_MIB_ITEM("TCPSACKReneging", LINUX_MIB_TCPSACKRENEGING), @@ -230,14 +223,12 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("TCPSackFailures", LINUX_MIB_TCPSACKFAILURES), SNMP_MIB_ITEM("TCPLossFailures", LINUX_MIB_TCPLOSSFAILURES), SNMP_MIB_ITEM("TCPFastRetrans", LINUX_MIB_TCPFASTRETRANS), - SNMP_MIB_ITEM("TCPForwardRetrans", LINUX_MIB_TCPFORWARDRETRANS), SNMP_MIB_ITEM("TCPSlowStartRetrans", LINUX_MIB_TCPSLOWSTARTRETRANS), SNMP_MIB_ITEM("TCPTimeouts", LINUX_MIB_TCPTIMEOUTS), SNMP_MIB_ITEM("TCPLossProbes", LINUX_MIB_TCPLOSSPROBES), SNMP_MIB_ITEM("TCPLossProbeRecovery", LINUX_MIB_TCPLOSSPROBERECOVERY), SNMP_MIB_ITEM("TCPRenoRecoveryFail", LINUX_MIB_TCPRENORECOVERYFAIL), SNMP_MIB_ITEM("TCPSackRecoveryFail", LINUX_MIB_TCPSACKRECOVERYFAIL), - SNMP_MIB_ITEM("TCPSchedulerFailed", LINUX_MIB_TCPSCHEDULERFAILED), SNMP_MIB_ITEM("TCPRcvCollapsed", LINUX_MIB_TCPRCVCOLLAPSED), SNMP_MIB_ITEM("TCPDSACKOldSent", LINUX_MIB_TCPDSACKOLDSENT), SNMP_MIB_ITEM("TCPDSACKOfoSent", LINUX_MIB_TCPDSACKOFOSENT), -- cgit v1.2.3 From 0263598c774250f72b275f7f44f93dfd85b88f2b Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 28 Jul 2017 10:28:20 -0700 Subject: tcp: extract the function to compute delivery rate Refactor the code to extract the function to compute delivery rate. This function will be used in later commit. Signed-off-by: Wei Wang Acked-by: Yuchung Cheng Acked-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index e022874d509f..acee7acdcba6 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -388,6 +388,19 @@ static int retrans_to_secs(u8 retrans, int timeout, int rto_max) return period; } +static u64 tcp_compute_delivery_rate(const struct tcp_sock *tp) +{ + u32 rate = READ_ONCE(tp->rate_delivered); + u32 intv = READ_ONCE(tp->rate_interval_us); + u64 rate64 = 0; + + if (rate && intv) { + rate64 = (u64)rate * tp->mss_cache * USEC_PER_SEC; + do_div(rate64, intv); + } + return rate64; +} + /* Address-family independent initialization for a tcp_sock. * * NOTE: A lot of things set to zero explicitly by call to @@ -2716,7 +2729,7 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) { const struct tcp_sock *tp = tcp_sk(sk); /* iff sk_type == SOCK_STREAM */ const struct inet_connection_sock *icsk = inet_csk(sk); - u32 now, intv; + u32 now; u64 rate64; bool slow; u32 rate; @@ -2815,13 +2828,9 @@ void tcp_get_info(struct sock *sk, struct tcp_info *info) info->tcpi_data_segs_out = tp->data_segs_out; info->tcpi_delivery_rate_app_limited = tp->rate_app_limited ? 1 : 0; - rate = READ_ONCE(tp->rate_delivered); - intv = READ_ONCE(tp->rate_interval_us); - if (rate && intv) { - rate64 = (u64)rate * tp->mss_cache * USEC_PER_SEC; - do_div(rate64, intv); + rate64 = tcp_compute_delivery_rate(tp); + if (rate64) info->tcpi_delivery_rate = rate64; - } unlock_sock_fast(sk, slow); } EXPORT_SYMBOL_GPL(tcp_get_info); -- cgit v1.2.3 From bb7c19f96012720b895111300b9d9f3f858c3a69 Mon Sep 17 00:00:00 2001 From: Wei Wang Date: Fri, 28 Jul 2017 10:28:21 -0700 Subject: tcp: add related fields into SCM_TIMESTAMPING_OPT_STATS Add the following stats into SCM_TIMESTAMPING_OPT_STATS control msg: TCP_NLA_PACING_RATE TCP_NLA_DELIVERY_RATE TCP_NLA_SND_CWND TCP_NLA_REORDERING TCP_NLA_MIN_RTT TCP_NLA_RECUR_RETRANS TCP_NLA_DELIVERY_RATE_APP_LMT Signed-off-by: Wei Wang Acked-by: Yuchung Cheng Acked-by: Soheil Hassas Yeganeh Signed-off-by: David S. Miller --- include/uapi/linux/tcp.h | 8 ++++++++ net/ipv4/tcp.c | 20 +++++++++++++++++++- 2 files changed, 27 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index a5507c977497..030e594bab45 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -231,6 +231,14 @@ enum { TCP_NLA_SNDBUF_LIMITED, /* Time (usec) limited by send buffer */ TCP_NLA_DATA_SEGS_OUT, /* Data pkts sent including retransmission */ TCP_NLA_TOTAL_RETRANS, /* Data pkts retransmitted */ + TCP_NLA_PACING_RATE, /* Pacing rate in bytes per second */ + TCP_NLA_DELIVERY_RATE, /* Delivery rate in bytes per second */ + TCP_NLA_SND_CWND, /* Sending congestion window */ + TCP_NLA_REORDERING, /* Reordering metric */ + TCP_NLA_MIN_RTT, /* minimum RTT */ + TCP_NLA_RECUR_RETRANS, /* Recurring retransmits for the current pkt */ + TCP_NLA_DELIVERY_RATE_APP_LMT, /* delivery rate application limited ? */ + }; /* for TCP_MD5SIG socket option */ diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index acee7acdcba6..5326b50a3450 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -2840,8 +2840,12 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) const struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *stats; struct tcp_info info; + u64 rate64; + u32 rate; - stats = alloc_skb(5 * nla_total_size_64bit(sizeof(u64)), GFP_ATOMIC); + stats = alloc_skb(7 * nla_total_size_64bit(sizeof(u64)) + + 3 * nla_total_size(sizeof(u32)) + + 2 * nla_total_size(sizeof(u8)), GFP_ATOMIC); if (!stats) return NULL; @@ -2856,6 +2860,20 @@ struct sk_buff *tcp_get_timestamping_opt_stats(const struct sock *sk) tp->data_segs_out, TCP_NLA_PAD); nla_put_u64_64bit(stats, TCP_NLA_TOTAL_RETRANS, tp->total_retrans, TCP_NLA_PAD); + + rate = READ_ONCE(sk->sk_pacing_rate); + rate64 = rate != ~0U ? rate : ~0ULL; + nla_put_u64_64bit(stats, TCP_NLA_PACING_RATE, rate64, TCP_NLA_PAD); + + rate64 = tcp_compute_delivery_rate(tp); + nla_put_u64_64bit(stats, TCP_NLA_DELIVERY_RATE, rate64, TCP_NLA_PAD); + + nla_put_u32(stats, TCP_NLA_SND_CWND, tp->snd_cwnd); + nla_put_u32(stats, TCP_NLA_REORDERING, tp->reordering); + nla_put_u32(stats, TCP_NLA_MIN_RTT, tcp_min_rtt(tp)); + + nla_put_u8(stats, TCP_NLA_RECUR_RETRANS, inet_csk(sk)->icsk_retransmits); + nla_put_u8(stats, TCP_NLA_DELIVERY_RATE_APP_LMT, !!tp->rate_app_limited); return stats; } -- cgit v1.2.3 From 1f139ed9ec40521b4497aa34e107d38bb082b0e0 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Mon, 31 Jul 2017 10:09:41 +0200 Subject: ipv6: Avoid going through ->sk_net to access the netns There is no need to go through sk->sk_net to access the net namespace and its sysctl variables because we allocate the sock and initialize sk_net just a few lines earlier in the same routine. Signed-off-by: Jakub Sitnicki Signed-off-by: David S. Miller --- net/ipv6/af_inet6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index a88b5b5b7955..0a7c74049a0c 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -210,7 +210,7 @@ lookup_protocol: np->mcast_hops = IPV6_DEFAULT_MCASTHOPS; np->mc_loop = 1; np->pmtudisc = IPV6_PMTUDISC_WANT; - np->autoflowlabel = ip6_default_np_autolabel(sock_net(sk)); + np->autoflowlabel = ip6_default_np_autolabel(net); sk->sk_ipv6only = net->ipv6.sysctl.bindv6only; /* Init the ipv4 part of the socket since we can have sockets -- cgit v1.2.3 From 39294c3df2a8dc5ec64513633446511f7e842acd Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 1 Aug 2017 18:27:28 +0200 Subject: Revert "ipv6: constify inet6_protocol structures" This reverts commit 3a3a4e3054137c5ff5d4d306ec834f6d25d7f95b. inet6_add_protocol and inet6_del_protocol include casts that remove the effect of the const annotation on their parameter, leading to possible runtime crashes. Reported-by: Eric Dumazet Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- net/ipv6/ip6_gre.c | 2 +- net/ipv6/tcp_ipv6.c | 2 +- net/ipv6/udp.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index 33865d67bcb4..67ff2aaf5dcb 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -1080,7 +1080,7 @@ static void ip6gre_fb_tunnel_init(struct net_device *dev) } -static const struct inet6_protocol ip6gre_protocol = { +static struct inet6_protocol ip6gre_protocol __read_mostly = { .handler = gre_rcv, .err_handler = ip6gre_err, .flags = INET6_PROTO_NOPOLICY|INET6_PROTO_FINAL, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 39ee8e7fc4bd..ced5dcf37465 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1944,7 +1944,7 @@ struct proto tcpv6_prot = { .diag_destroy = tcp_abort, }; -static const struct inet6_protocol tcpv6_protocol = { +static struct inet6_protocol tcpv6_protocol = { .early_demux = tcp_v6_early_demux, .early_demux_handler = tcp_v6_early_demux, .handler = tcp_v6_rcv, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 5f8b8d766c63..4a3e65626e8b 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1448,7 +1448,7 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, } #endif -static const struct inet6_protocol udpv6_protocol = { +static struct inet6_protocol udpv6_protocol = { .early_demux = udp_v6_early_demux, .early_demux_handler = udp_v6_early_demux, .handler = udpv6_rcv, -- cgit v1.2.3 From e6eeb287e3b534ce1bd6477894b9df8e394b4fbc Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 1 Aug 2017 18:27:29 +0200 Subject: Revert "l2tp: constify inet6_protocol structures" This reverts commit d04916a48ad4a3db892b664fa9c3a2a693c378ad. inet6_add_protocol and inet6_del_protocol include casts that remove the effect of the const annotation on their parameter, leading to possible runtime crashes. Reported-by: Eric Dumazet Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- net/l2tp/l2tp_ip6.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/l2tp/l2tp_ip6.c b/net/l2tp/l2tp_ip6.c index d2efcd93e1e2..88b397c30d86 100644 --- a/net/l2tp/l2tp_ip6.c +++ b/net/l2tp/l2tp_ip6.c @@ -788,7 +788,7 @@ static struct inet_protosw l2tp_ip6_protosw = { .ops = &l2tp_ip6_ops, }; -static const struct inet6_protocol l2tp_ip6_protocol = { +static struct inet6_protocol l2tp_ip6_protocol __read_mostly = { .handler = l2tp_ip6_recv, }; -- cgit v1.2.3 From 306b13eb3cf9515a8214bbf5d69d811371d05792 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 28 Jul 2017 16:22:41 -0700 Subject: proto_ops: Add locked held versions of sendmsg and sendpage Add new proto_ops sendmsg_locked and sendpage_locked that can be called when the socket lock is already held. Correspondingly, add kernel_sendmsg_locked and kernel_sendpage_locked as front end functions. These functions will be used in zero proxy so that we can take the socket lock in a ULP sendmsg/sendpage and then directly call the backend transport proto_ops functions. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/linux/net.h | 12 ++++++++++++ include/net/sock.h | 3 +++ include/net/tcp.h | 3 +++ net/core/sock.c | 22 ++++++++++++++++++++++ net/ipv4/af_inet.c | 2 ++ net/ipv4/tcp.c | 39 ++++++++++++++++++++++++++------------- net/socket.c | 27 +++++++++++++++++++++++++++ 7 files changed, 95 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/include/linux/net.h b/include/linux/net.h index dda2cc939a53..b5c15b31709b 100644 --- a/include/linux/net.h +++ b/include/linux/net.h @@ -190,8 +190,16 @@ struct proto_ops { struct pipe_inode_info *pipe, size_t len, unsigned int flags); int (*set_peek_off)(struct sock *sk, int val); int (*peek_len)(struct socket *sock); + + /* The following functions are called internally by kernel with + * sock lock already held. + */ int (*read_sock)(struct sock *sk, read_descriptor_t *desc, sk_read_actor_t recv_actor); + int (*sendpage_locked)(struct sock *sk, struct page *page, + int offset, size_t size, int flags); + int (*sendmsg_locked)(struct sock *sk, struct msghdr *msg, + size_t size); }; #define DECLARE_SOCKADDR(type, dst, src) \ @@ -279,6 +287,8 @@ do { \ int kernel_sendmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, size_t num, size_t len); +int kernel_sendmsg_locked(struct sock *sk, struct msghdr *msg, + struct kvec *vec, size_t num, size_t len); int kernel_recvmsg(struct socket *sock, struct msghdr *msg, struct kvec *vec, size_t num, size_t len, int flags); @@ -297,6 +307,8 @@ int kernel_setsockopt(struct socket *sock, int level, int optname, char *optval, unsigned int optlen); int kernel_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags); +int kernel_sendpage_locked(struct sock *sk, struct page *page, int offset, + size_t size, int flags); int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg); int kernel_sock_shutdown(struct socket *sock, enum sock_shutdown_cmd how); diff --git a/include/net/sock.h b/include/net/sock.h index 7c0632c7e870..393c38e9f6aa 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1582,11 +1582,14 @@ int sock_no_shutdown(struct socket *, int); int sock_no_getsockopt(struct socket *, int , int, char __user *, int __user *); int sock_no_setsockopt(struct socket *, int, int, char __user *, unsigned int); int sock_no_sendmsg(struct socket *, struct msghdr *, size_t); +int sock_no_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t len); int sock_no_recvmsg(struct socket *, struct msghdr *, size_t, int); int sock_no_mmap(struct file *file, struct socket *sock, struct vm_area_struct *vma); ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset, size_t size, int flags); +ssize_t sock_no_sendpage_locked(struct sock *sk, struct page *page, + int offset, size_t size, int flags); /* * Functions to fill in entries in struct proto_ops when a protocol diff --git a/include/net/tcp.h b/include/net/tcp.h index 3ecb62811004..bb1881b4ce48 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -350,8 +350,11 @@ int tcp_v4_rcv(struct sk_buff *skb); int tcp_v4_tw_remember_stamp(struct inet_timewait_sock *tw); int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size); +int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size); int tcp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, int flags); +int tcp_sendpage_locked(struct sock *sk, struct page *page, int offset, + size_t size, int flags); ssize_t do_tcp_sendpages(struct sock *sk, struct page *page, int offset, size_t size, int flags); void tcp_release_cb(struct sock *sk); diff --git a/net/core/sock.c b/net/core/sock.c index ac2a404c73eb..742f68c9c84a 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2500,6 +2500,12 @@ int sock_no_sendmsg(struct socket *sock, struct msghdr *m, size_t len) } EXPORT_SYMBOL(sock_no_sendmsg); +int sock_no_sendmsg_locked(struct sock *sk, struct msghdr *m, size_t len) +{ + return -EOPNOTSUPP; +} +EXPORT_SYMBOL(sock_no_sendmsg_locked); + int sock_no_recvmsg(struct socket *sock, struct msghdr *m, size_t len, int flags) { @@ -2528,6 +2534,22 @@ ssize_t sock_no_sendpage(struct socket *sock, struct page *page, int offset, siz } EXPORT_SYMBOL(sock_no_sendpage); +ssize_t sock_no_sendpage_locked(struct sock *sk, struct page *page, + int offset, size_t size, int flags) +{ + ssize_t res; + struct msghdr msg = {.msg_flags = flags}; + struct kvec iov; + char *kaddr = kmap(page); + + iov.iov_base = kaddr + offset; + iov.iov_len = size; + res = kernel_sendmsg_locked(sk, &msg, &iov, 1, size); + kunmap(page); + return res; +} +EXPORT_SYMBOL(sock_no_sendpage_locked); + /* * Default Socket Callbacks */ diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 5ce44fb7d498..f0103ffe1cdb 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -944,6 +944,8 @@ const struct proto_ops inet_stream_ops = { .sendpage = inet_sendpage, .splice_read = tcp_splice_read, .read_sock = tcp_read_sock, + .sendmsg_locked = tcp_sendmsg_locked, + .sendpage_locked = tcp_sendpage_locked, .peek_len = tcp_peek_len, #ifdef CONFIG_COMPAT .compat_setsockopt = compat_sock_common_setsockopt, diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 5326b50a3450..9dd6f4dba9b1 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1046,23 +1046,29 @@ out_err: } EXPORT_SYMBOL_GPL(do_tcp_sendpages); -int tcp_sendpage(struct sock *sk, struct page *page, int offset, - size_t size, int flags) +int tcp_sendpage_locked(struct sock *sk, struct page *page, int offset, + size_t size, int flags) { - ssize_t res; - if (!(sk->sk_route_caps & NETIF_F_SG) || !sk_check_csum_caps(sk)) return sock_no_sendpage(sk->sk_socket, page, offset, size, flags); - lock_sock(sk); - tcp_rate_check_app_limited(sk); /* is sending application-limited? */ - res = do_tcp_sendpages(sk, page, offset, size, flags); + return do_tcp_sendpages(sk, page, offset, size, flags); +} + +int tcp_sendpage(struct sock *sk, struct page *page, int offset, + size_t size, int flags) +{ + int ret; + + lock_sock(sk); + ret = tcp_sendpage_locked(sk, page, offset, size, flags); release_sock(sk); - return res; + + return ret; } EXPORT_SYMBOL(tcp_sendpage); @@ -1156,7 +1162,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, return err; } -int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) +int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size) { struct tcp_sock *tp = tcp_sk(sk); struct sk_buff *skb; @@ -1167,8 +1173,6 @@ int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) bool sg; long timeo; - lock_sock(sk); - flags = msg->msg_flags; if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect)) { err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size); @@ -1377,7 +1381,6 @@ out: tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); } out_nopush: - release_sock(sk); return copied + copied_syn; do_fault: @@ -1401,9 +1404,19 @@ out_err: sk->sk_write_space(sk); tcp_chrono_stop(sk, TCP_CHRONO_SNDBUF_LIMITED); } - release_sock(sk); return err; } + +int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) +{ + int ret; + + lock_sock(sk); + ret = tcp_sendmsg_locked(sk, msg, size); + release_sock(sk); + + return ret; +} EXPORT_SYMBOL(tcp_sendmsg); /* diff --git a/net/socket.c b/net/socket.c index cb0fdf799f40..b332d1e8e4e4 100644 --- a/net/socket.c +++ b/net/socket.c @@ -652,6 +652,20 @@ int kernel_sendmsg(struct socket *sock, struct msghdr *msg, } EXPORT_SYMBOL(kernel_sendmsg); +int kernel_sendmsg_locked(struct sock *sk, struct msghdr *msg, + struct kvec *vec, size_t num, size_t size) +{ + struct socket *sock = sk->sk_socket; + + if (!sock->ops->sendmsg_locked) + sock_no_sendmsg_locked(sk, msg, size); + + iov_iter_kvec(&msg->msg_iter, WRITE | ITER_KVEC, vec, num, size); + + return sock->ops->sendmsg_locked(sk, msg, msg_data_left(msg)); +} +EXPORT_SYMBOL(kernel_sendmsg_locked); + static bool skb_is_err_queue(const struct sk_buff *skb) { /* pkt_type of skbs enqueued on the error queue are set to @@ -3376,6 +3390,19 @@ int kernel_sendpage(struct socket *sock, struct page *page, int offset, } EXPORT_SYMBOL(kernel_sendpage); +int kernel_sendpage_locked(struct sock *sk, struct page *page, int offset, + size_t size, int flags) +{ + struct socket *sock = sk->sk_socket; + + if (sock->ops->sendpage_locked) + return sock->ops->sendpage_locked(sk, page, offset, size, + flags); + + return sock_no_sendpage_locked(sk, page, offset, size, flags); +} +EXPORT_SYMBOL(kernel_sendpage_locked); + int kernel_sock_ioctl(struct socket *sock, int cmd, unsigned long arg) { mm_segment_t oldfs = get_fs(); -- cgit v1.2.3 From 20bf50de3028cb15fa81e1d1e63ab6e0c85257fc Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 28 Jul 2017 16:22:42 -0700 Subject: skbuff: Function to send an skbuf on a socket Add skb_send_sock to send an skbuff on a socket within the kernel. Arguments include an offset so that an skbuf might be sent in mulitple calls (e.g. send buffer limit is hit). Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/linux/skbuff.h | 3 ++ net/core/skbuff.c | 101 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) (limited to 'net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 4093552be1de..18e76bf9574e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -3113,6 +3113,9 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, u8 *to, int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset, struct pipe_inode_info *pipe, unsigned int len, unsigned int flags); +int skb_send_sock_locked(struct sock *sk, struct sk_buff *skb, int offset, + int len); +int skb_send_sock(struct sock *sk, struct sk_buff *skb, int offset, int len); void skb_copy_and_csum_dev(const struct sk_buff *skb, u8 *to); unsigned int skb_zerocopy_headlen(const struct sk_buff *from); int skb_zerocopy(struct sk_buff *to, struct sk_buff *from, diff --git a/net/core/skbuff.c b/net/core/skbuff.c index c27da51d14e4..9c0e015ff3fe 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1982,6 +1982,107 @@ int skb_splice_bits(struct sk_buff *skb, struct sock *sk, unsigned int offset, } EXPORT_SYMBOL_GPL(skb_splice_bits); +/* Send skb data on a socket. Socket must be locked. */ +int skb_send_sock_locked(struct sock *sk, struct sk_buff *skb, int offset, + int len) +{ + unsigned int orig_len = len; + struct sk_buff *head = skb; + unsigned short fragidx; + int slen, ret; + +do_frag_list: + + /* Deal with head data */ + while (offset < skb_headlen(skb) && len) { + struct kvec kv; + struct msghdr msg; + + slen = min_t(int, len, skb_headlen(skb) - offset); + kv.iov_base = skb->data + offset; + kv.iov_len = len; + memset(&msg, 0, sizeof(msg)); + + ret = kernel_sendmsg_locked(sk, &msg, &kv, 1, slen); + if (ret <= 0) + goto error; + + offset += ret; + len -= ret; + } + + /* All the data was skb head? */ + if (!len) + goto out; + + /* Make offset relative to start of frags */ + offset -= skb_headlen(skb); + + /* Find where we are in frag list */ + for (fragidx = 0; fragidx < skb_shinfo(skb)->nr_frags; fragidx++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[fragidx]; + + if (offset < frag->size) + break; + + offset -= frag->size; + } + + for (; len && fragidx < skb_shinfo(skb)->nr_frags; fragidx++) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[fragidx]; + + slen = min_t(size_t, len, frag->size - offset); + + while (slen) { + ret = kernel_sendpage_locked(sk, frag->page.p, + frag->page_offset + offset, + slen, MSG_DONTWAIT); + if (ret <= 0) + goto error; + + len -= ret; + offset += ret; + slen -= ret; + } + + offset = 0; + } + + if (len) { + /* Process any frag lists */ + + if (skb == head) { + if (skb_has_frag_list(skb)) { + skb = skb_shinfo(skb)->frag_list; + goto do_frag_list; + } + } else if (skb->next) { + skb = skb->next; + goto do_frag_list; + } + } + +out: + return orig_len - len; + +error: + return orig_len == len ? ret : orig_len - len; +} +EXPORT_SYMBOL_GPL(skb_send_sock_locked); + +/* Send skb data on a socket. */ +int skb_send_sock(struct sock *sk, struct sk_buff *skb, int offset, int len) +{ + int ret = 0; + + lock_sock(sk); + ret = skb_send_sock_locked(sk, skb, offset, len); + release_sock(sk); + + return ret; +} +EXPORT_SYMBOL_GPL(skb_send_sock); + /** * skb_store_bits - store bits from kernel buffer to skb * @skb: destination buffer -- cgit v1.2.3 From bbb03029a899679d73e62d7e6ae80348cc5d0054 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 28 Jul 2017 16:22:43 -0700 Subject: strparser: Generalize strparser Generalize strparser from more than just being used in conjunction with read_sock. strparser will also be used in the send path with zero proxy. The primary change is to create strp_process function that performs the critical processing on skbs. The documentation is also updated to reflect the new uses. Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- Documentation/networking/strparser.txt | 207 +++++++++++++++------- include/net/strparser.h | 119 +++++++------ net/kcm/kcmproc.c | 34 ++-- net/kcm/kcmsock.c | 38 ++-- net/strparser/strparser.c | 313 ++++++++++++++++++++------------- 5 files changed, 424 insertions(+), 287 deletions(-) (limited to 'net') diff --git a/Documentation/networking/strparser.txt b/Documentation/networking/strparser.txt index a0bf573dfa61..fe01302471ae 100644 --- a/Documentation/networking/strparser.txt +++ b/Documentation/networking/strparser.txt @@ -1,45 +1,107 @@ -Stream Parser -------------- +Stream Parser (strparser) + +Introduction +============ The stream parser (strparser) is a utility that parses messages of an -application layer protocol running over a TCP connection. The stream +application layer protocol running over a data stream. The stream parser works in conjunction with an upper layer in the kernel to provide kernel support for application layer messages. For instance, Kernel Connection Multiplexor (KCM) uses the Stream Parser to parse messages using a BPF program. +The strparser works in one of two modes: receive callback or general +mode. + +In receive callback mode, the strparser is called from the data_ready +callback of a TCP socket. Messages are parsed and delivered as they are +received on the socket. + +In general mode, a sequence of skbs are fed to strparser from an +outside source. Message are parsed and delivered as the sequence is +processed. This modes allows strparser to be applied to arbitrary +streams of data. + Interface ---------- +========= The API includes a context structure, a set of callbacks, utility -functions, and a data_ready function. The callbacks include -a parse_msg function that is called to perform parsing (e.g. -BPF parsing in case of KCM), and a rcv_msg function that is called -when a full message has been completed. +functions, and a data_ready function for receive callback mode. The +callbacks include a parse_msg function that is called to perform +parsing (e.g. BPF parsing in case of KCM), and a rcv_msg function +that is called when a full message has been completed. -A stream parser can be instantiated for a TCP connection. This is done -by: +Functions +========= -strp_init(struct strparser *strp, struct sock *csk, +strp_init(struct strparser *strp, struct sock *sk, struct strp_callbacks *cb) -strp is a struct of type strparser that is allocated by the upper layer. -csk is the TCP socket associated with the stream parser. Callbacks are -called by the stream parser. + Called to initialize a stream parser. strp is a struct of type + strparser that is allocated by the upper layer. sk is the TCP + socket associated with the stream parser for use with receive + callback mode; in general mode this is set to NULL. Callbacks + are called by the stream parser (the callbacks are listed below). + +void strp_pause(struct strparser *strp) + + Temporarily pause a stream parser. Message parsing is suspended + and no new messages are delivered to the upper layer. + +void strp_pause(struct strparser *strp) + + Unpause a paused stream parser. + +void strp_stop(struct strparser *strp); + + strp_stop is called to completely stop stream parser operations. + This is called internally when the stream parser encounters an + error, and it is called from the upper layer to stop parsing + operations. + +void strp_done(struct strparser *strp); + + strp_done is called to release any resources held by the stream + parser instance. This must be called after the stream processor + has been stopped. + +int strp_process(struct strparser *strp, struct sk_buff *orig_skb, + unsigned int orig_offset, size_t orig_len, + size_t max_msg_size, long timeo) + + strp_process is called in general mode for a stream parser to + parse an sk_buff. The number of bytes processed or a negative + error number is returned. Note that strp_process does not + consume the sk_buff. max_msg_size is maximum size the stream + parser will parse. timeo is timeout for completing a message. + +void strp_data_ready(struct strparser *strp); + + The upper layer calls strp_tcp_data_ready when data is ready on + the lower socket for strparser to process. This should be called + from a data_ready callback that is set on the socket. Note that + maximum messages size is the limit of the receive socket + buffer and message timeout is the receive timeout for the socket. + +void strp_check_rcv(struct strparser *strp); + + strp_check_rcv is called to check for new messages on the socket. + This is normally called at initialization of a stream parser + instance or after strp_unpause. Callbacks ---------- +========= -There are four callbacks: +There are six callbacks: int (*parse_msg)(struct strparser *strp, struct sk_buff *skb); parse_msg is called to determine the length of the next message in the stream. The upper layer must implement this function. It should parse the sk_buff as containing the headers for the - next application layer messages in the stream. + next application layer message in the stream. - The skb->cb in the input skb is a struct strp_rx_msg. Only + The skb->cb in the input skb is a struct strp_msg. Only the offset field is relevant in parse_msg and gives the offset where the message starts in the skb. @@ -50,26 +112,41 @@ int (*parse_msg)(struct strparser *strp, struct sk_buff *skb); -ESTRPIPE : current message should not be processed by the kernel, return control of the socket to userspace which can proceed to read the messages itself - other < 0 : Error is parsing, give control back to userspace + other < 0 : Error in parsing, give control back to userspace assuming that synchronization is lost and the stream is unrecoverable (application expected to close TCP socket) In the case that an error is returned (return value is less than - zero) the stream parser will set the error on TCP socket and wake - it up. If parse_msg returned -ESTRPIPE and the stream parser had - previously read some bytes for the current message, then the error - set on the attached socket is ENODATA since the stream is - unrecoverable in that case. + zero) and the parser is in receive callback mode, then it will set + the error on TCP socket and wake it up. If parse_msg returned + -ESTRPIPE and the stream parser had previously read some bytes for + the current message, then the error set on the attached socket is + ENODATA since the stream is unrecoverable in that case. + +void (*lock)(struct strparser *strp) + + The lock callback is called to lock the strp structure when + the strparser is performing an asynchronous operation (such as + processing a timeout). In receive callback mode the default + function is to lock_sock for the associated socket. In general + mode the callback must be set appropriately. + +void (*unlock)(struct strparser *strp) + + The unlock callback is called to release the lock obtained + by the lock callback. In receive callback mode the default + function is release_sock for the associated socket. In general + mode the callback must be set appropriately. void (*rcv_msg)(struct strparser *strp, struct sk_buff *skb); rcv_msg is called when a full message has been received and is queued. The callee must consume the sk_buff; it can call strp_pause to prevent any further messages from being - received in rcv_msg (see strp_pause below). This callback + received in rcv_msg (see strp_pause above). This callback must be set. - The skb->cb in the input skb is a struct strp_rx_msg. This + The skb->cb in the input skb is a struct strp_msg. This struct contains two fields: offset and full_len. Offset is where the message starts in the skb, and full_len is the the length of the message. skb->len - offset may be greater @@ -78,59 +155,53 @@ void (*rcv_msg)(struct strparser *strp, struct sk_buff *skb); int (*read_sock_done)(struct strparser *strp, int err); read_sock_done is called when the stream parser is done reading - the TCP socket. The stream parser may read multiple messages - in a loop and this function allows cleanup to occur when existing - the loop. If the callback is not set (NULL in strp_init) a - default function is used. + the TCP socket in receive callback mode. The stream parser may + read multiple messages in a loop and this function allows cleanup + to occur when exiting the loop. If the callback is not set (NULL + in strp_init) a default function is used. void (*abort_parser)(struct strparser *strp, int err); This function is called when stream parser encounters an error - in parsing. The default function stops the stream parser for the - TCP socket and sets the error in the socket. The default function - can be changed by setting the callback to non-NULL in strp_init. + in parsing. The default function stops the stream parser and + sets the error in the socket if the parser is in receive callback + mode. The default function can be changed by setting the callback + to non-NULL in strp_init. -Functions ---------- +Statistics +========== -The upper layer calls strp_tcp_data_ready when data is ready on the lower -socket for strparser to process. This should be called from a data_ready -callback that is set on the socket. +Various counters are kept for each stream parser instance. These are in +the strp_stats structure. strp_aggr_stats is a convenience structure for +accumulating statistics for multiple stream parser instances. +save_strp_stats and aggregate_strp_stats are helper functions to save +and aggregate statistics. -strp_stop is called to completely stop stream parser operations. This -is called internally when the stream parser encounters an error, and -it is called from the upper layer when unattaching a TCP socket. +Message assembly limits +======================= -strp_done is called to unattach the stream parser from the TCP socket. -This must be called after the stream processor has be stopped. +The stream parser provide mechanisms to limit the resources consumed by +message assembly. -strp_check_rcv is called to check for new messages on the socket. This -is normally called at initialization of the a stream parser instance -of after strp_unpause. +A timer is set when assembly starts for a new message. In receive +callback mode the message timeout is taken from rcvtime for the +associated TCP socket. In general mode, the timeout is passed as an +argument in strp_process. If the timer fires before assembly completes +the stream parser is aborted and the ETIMEDOUT error is set on the TCP +socket if in receive callback mode. -Statistics ----------- +In receive callback mode, message length is limited to the receive +buffer size of the associated TCP socket. If the length returned by +parse_msg is greater than the socket buffer size then the stream parser +is aborted with EMSGSIZE error set on the TCP socket. Note that this +makes the maximum size of receive skbuffs for a socket with a stream +parser to be 2*sk_rcvbuf of the TCP socket. -Various counters are kept for each stream parser for a TCP socket. -These are in the strp_stats structure. strp_aggr_stats is a convenience -structure for accumulating statistics for multiple stream parser -instances. save_strp_stats and aggregate_strp_stats are helper functions -to save and aggregate statistics. +In general mode the message length limit is passed in as an argument +to strp_process. -Message assembly limits ------------------------ +Author +====== -The stream parser provide mechanisms to limit the resources consumed by -message assembly. +Tom Herbert (tom@quantonium.net) -A timer is set when assembly starts for a new message. The message -timeout is taken from rcvtime for the associated TCP socket. If the -timer fires before assembly completes the stream parser is aborted -and the ETIMEDOUT error is set on the TCP socket. - -Message length is limited to the receive buffer size of the associated -TCP socket. If the length returned by parse_msg is greater than -the socket buffer size then the stream parser is aborted with -EMSGSIZE error set on the TCP socket. Note that this makes the -maximum size of receive skbuffs for a socket with a stream parser -to be 2*sk_rcvbuf of the TCP socket. diff --git a/include/net/strparser.h b/include/net/strparser.h index 0c28ad97c52f..4fe966a0ad92 100644 --- a/include/net/strparser.h +++ b/include/net/strparser.h @@ -18,26 +18,26 @@ #define STRP_STATS_INCR(stat) ((stat)++) struct strp_stats { - unsigned long long rx_msgs; - unsigned long long rx_bytes; - unsigned int rx_mem_fail; - unsigned int rx_need_more_hdr; - unsigned int rx_msg_too_big; - unsigned int rx_msg_timeouts; - unsigned int rx_bad_hdr_len; + unsigned long long msgs; + unsigned long long bytes; + unsigned int mem_fail; + unsigned int need_more_hdr; + unsigned int msg_too_big; + unsigned int msg_timeouts; + unsigned int bad_hdr_len; }; struct strp_aggr_stats { - unsigned long long rx_msgs; - unsigned long long rx_bytes; - unsigned int rx_mem_fail; - unsigned int rx_need_more_hdr; - unsigned int rx_msg_too_big; - unsigned int rx_msg_timeouts; - unsigned int rx_bad_hdr_len; - unsigned int rx_aborts; - unsigned int rx_interrupted; - unsigned int rx_unrecov_intr; + unsigned long long msgs; + unsigned long long bytes; + unsigned int mem_fail; + unsigned int need_more_hdr; + unsigned int msg_too_big; + unsigned int msg_timeouts; + unsigned int bad_hdr_len; + unsigned int aborts; + unsigned int interrupted; + unsigned int unrecov_intr; }; struct strparser; @@ -48,16 +48,18 @@ struct strp_callbacks { void (*rcv_msg)(struct strparser *strp, struct sk_buff *skb); int (*read_sock_done)(struct strparser *strp, int err); void (*abort_parser)(struct strparser *strp, int err); + void (*lock)(struct strparser *strp); + void (*unlock)(struct strparser *strp); }; -struct strp_rx_msg { +struct strp_msg { int full_len; int offset; }; -static inline struct strp_rx_msg *strp_rx_msg(struct sk_buff *skb) +static inline struct strp_msg *strp_msg(struct sk_buff *skb) { - return (struct strp_rx_msg *)((void *)skb->cb + + return (struct strp_msg *)((void *)skb->cb + offsetof(struct qdisc_skb_cb, data)); } @@ -65,18 +67,18 @@ static inline struct strp_rx_msg *strp_rx_msg(struct sk_buff *skb) struct strparser { struct sock *sk; - u32 rx_stopped : 1; - u32 rx_paused : 1; - u32 rx_aborted : 1; - u32 rx_interrupted : 1; - u32 rx_unrecov_intr : 1; - - struct sk_buff **rx_skb_nextp; - struct timer_list rx_msg_timer; - struct sk_buff *rx_skb_head; - unsigned int rx_need_bytes; - struct delayed_work rx_delayed_work; - struct work_struct rx_work; + u32 stopped : 1; + u32 paused : 1; + u32 aborted : 1; + u32 interrupted : 1; + u32 unrecov_intr : 1; + + struct sk_buff **skb_nextp; + struct timer_list msg_timer; + struct sk_buff *skb_head; + unsigned int need_bytes; + struct delayed_work delayed_work; + struct work_struct work; struct strp_stats stats; struct strp_callbacks cb; }; @@ -84,7 +86,7 @@ struct strparser { /* Must be called with lock held for attached socket */ static inline void strp_pause(struct strparser *strp) { - strp->rx_paused = 1; + strp->paused = 1; } /* May be called without holding lock for attached socket */ @@ -97,37 +99,37 @@ static inline void save_strp_stats(struct strparser *strp, #define SAVE_PSOCK_STATS(_stat) (agg_stats->_stat += \ strp->stats._stat) - SAVE_PSOCK_STATS(rx_msgs); - SAVE_PSOCK_STATS(rx_bytes); - SAVE_PSOCK_STATS(rx_mem_fail); - SAVE_PSOCK_STATS(rx_need_more_hdr); - SAVE_PSOCK_STATS(rx_msg_too_big); - SAVE_PSOCK_STATS(rx_msg_timeouts); - SAVE_PSOCK_STATS(rx_bad_hdr_len); + SAVE_PSOCK_STATS(msgs); + SAVE_PSOCK_STATS(bytes); + SAVE_PSOCK_STATS(mem_fail); + SAVE_PSOCK_STATS(need_more_hdr); + SAVE_PSOCK_STATS(msg_too_big); + SAVE_PSOCK_STATS(msg_timeouts); + SAVE_PSOCK_STATS(bad_hdr_len); #undef SAVE_PSOCK_STATS - if (strp->rx_aborted) - agg_stats->rx_aborts++; - if (strp->rx_interrupted) - agg_stats->rx_interrupted++; - if (strp->rx_unrecov_intr) - agg_stats->rx_unrecov_intr++; + if (strp->aborted) + agg_stats->aborts++; + if (strp->interrupted) + agg_stats->interrupted++; + if (strp->unrecov_intr) + agg_stats->unrecov_intr++; } static inline void aggregate_strp_stats(struct strp_aggr_stats *stats, struct strp_aggr_stats *agg_stats) { #define SAVE_PSOCK_STATS(_stat) (agg_stats->_stat += stats->_stat) - SAVE_PSOCK_STATS(rx_msgs); - SAVE_PSOCK_STATS(rx_bytes); - SAVE_PSOCK_STATS(rx_mem_fail); - SAVE_PSOCK_STATS(rx_need_more_hdr); - SAVE_PSOCK_STATS(rx_msg_too_big); - SAVE_PSOCK_STATS(rx_msg_timeouts); - SAVE_PSOCK_STATS(rx_bad_hdr_len); - SAVE_PSOCK_STATS(rx_aborts); - SAVE_PSOCK_STATS(rx_interrupted); - SAVE_PSOCK_STATS(rx_unrecov_intr); + SAVE_PSOCK_STATS(msgs); + SAVE_PSOCK_STATS(bytes); + SAVE_PSOCK_STATS(mem_fail); + SAVE_PSOCK_STATS(need_more_hdr); + SAVE_PSOCK_STATS(msg_too_big); + SAVE_PSOCK_STATS(msg_timeouts); + SAVE_PSOCK_STATS(bad_hdr_len); + SAVE_PSOCK_STATS(aborts); + SAVE_PSOCK_STATS(interrupted); + SAVE_PSOCK_STATS(unrecov_intr); #undef SAVE_PSOCK_STATS } @@ -135,8 +137,11 @@ static inline void aggregate_strp_stats(struct strp_aggr_stats *stats, void strp_done(struct strparser *strp); void strp_stop(struct strparser *strp); void strp_check_rcv(struct strparser *strp); -int strp_init(struct strparser *strp, struct sock *csk, +int strp_init(struct strparser *strp, struct sock *sk, struct strp_callbacks *cb); void strp_data_ready(struct strparser *strp); +int strp_process(struct strparser *strp, struct sk_buff *orig_skb, + unsigned int orig_offset, size_t orig_len, + size_t max_msg_size, long timeo); #endif /* __NET_STRPARSER_H_ */ diff --git a/net/kcm/kcmproc.c b/net/kcm/kcmproc.c index c343ac60bf50..c748e8a6a72c 100644 --- a/net/kcm/kcmproc.c +++ b/net/kcm/kcmproc.c @@ -155,8 +155,8 @@ static void kcm_format_psock(struct kcm_psock *psock, struct seq_file *seq, seq_printf(seq, " psock-%-5u %-10llu %-16llu %-10llu %-16llu %-8d %-8d %-8d %-8d ", psock->index, - psock->strp.stats.rx_msgs, - psock->strp.stats.rx_bytes, + psock->strp.stats.msgs, + psock->strp.stats.bytes, psock->stats.tx_msgs, psock->stats.tx_bytes, psock->sk->sk_receive_queue.qlen, @@ -170,22 +170,22 @@ static void kcm_format_psock(struct kcm_psock *psock, struct seq_file *seq, if (psock->tx_stopped) seq_puts(seq, "TxStop "); - if (psock->strp.rx_stopped) + if (psock->strp.stopped) seq_puts(seq, "RxStop "); if (psock->tx_kcm) seq_printf(seq, "Rsvd-%d ", psock->tx_kcm->index); - if (!psock->strp.rx_paused && !psock->ready_rx_msg) { + if (!psock->strp.paused && !psock->ready_rx_msg) { if (psock->sk->sk_receive_queue.qlen) { - if (psock->strp.rx_need_bytes) + if (psock->strp.need_bytes) seq_printf(seq, "RxWait=%u ", - psock->strp.rx_need_bytes); + psock->strp.need_bytes); else seq_printf(seq, "RxWait "); } } else { - if (psock->strp.rx_paused) + if (psock->strp.paused) seq_puts(seq, "RxPause "); if (psock->ready_rx_msg) @@ -371,20 +371,20 @@ static int kcm_stats_seq_show(struct seq_file *seq, void *v) seq_printf(seq, "%-8s %-10llu %-16llu %-10llu %-16llu %-10llu %-10llu %-10u %-10u %-10u %-10u %-10u %-10u %-10u %-10u %-10u\n", "", - strp_stats.rx_msgs, - strp_stats.rx_bytes, + strp_stats.msgs, + strp_stats.bytes, psock_stats.tx_msgs, psock_stats.tx_bytes, psock_stats.reserved, psock_stats.unreserved, - strp_stats.rx_aborts, - strp_stats.rx_interrupted, - strp_stats.rx_unrecov_intr, - strp_stats.rx_mem_fail, - strp_stats.rx_need_more_hdr, - strp_stats.rx_bad_hdr_len, - strp_stats.rx_msg_too_big, - strp_stats.rx_msg_timeouts, + strp_stats.aborts, + strp_stats.interrupted, + strp_stats.unrecov_intr, + strp_stats.mem_fail, + strp_stats.need_more_hdr, + strp_stats.bad_hdr_len, + strp_stats.msg_too_big, + strp_stats.msg_timeouts, psock_stats.tx_aborts); return 0; diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index da49191f7ad0..88ce73288247 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -96,12 +96,12 @@ static void kcm_update_rx_mux_stats(struct kcm_mux *mux, struct kcm_psock *psock) { STRP_STATS_ADD(mux->stats.rx_bytes, - psock->strp.stats.rx_bytes - + psock->strp.stats.bytes - psock->saved_rx_bytes); mux->stats.rx_msgs += - psock->strp.stats.rx_msgs - psock->saved_rx_msgs; - psock->saved_rx_msgs = psock->strp.stats.rx_msgs; - psock->saved_rx_bytes = psock->strp.stats.rx_bytes; + psock->strp.stats.msgs - psock->saved_rx_msgs; + psock->saved_rx_msgs = psock->strp.stats.msgs; + psock->saved_rx_bytes = psock->strp.stats.bytes; } static void kcm_update_tx_mux_stats(struct kcm_mux *mux, @@ -1118,7 +1118,7 @@ static int kcm_recvmsg(struct socket *sock, struct msghdr *msg, struct kcm_sock *kcm = kcm_sk(sk); int err = 0; long timeo; - struct strp_rx_msg *rxm; + struct strp_msg *stm; int copied = 0; struct sk_buff *skb; @@ -1132,26 +1132,26 @@ static int kcm_recvmsg(struct socket *sock, struct msghdr *msg, /* Okay, have a message on the receive queue */ - rxm = strp_rx_msg(skb); + stm = strp_msg(skb); - if (len > rxm->full_len) - len = rxm->full_len; + if (len > stm->full_len) + len = stm->full_len; - err = skb_copy_datagram_msg(skb, rxm->offset, msg, len); + err = skb_copy_datagram_msg(skb, stm->offset, msg, len); if (err < 0) goto out; copied = len; if (likely(!(flags & MSG_PEEK))) { KCM_STATS_ADD(kcm->stats.rx_bytes, copied); - if (copied < rxm->full_len) { + if (copied < stm->full_len) { if (sock->type == SOCK_DGRAM) { /* Truncated message */ msg->msg_flags |= MSG_TRUNC; goto msg_finished; } - rxm->offset += copied; - rxm->full_len -= copied; + stm->offset += copied; + stm->full_len -= copied; } else { msg_finished: /* Finished with message */ @@ -1175,7 +1175,7 @@ static ssize_t kcm_splice_read(struct socket *sock, loff_t *ppos, struct sock *sk = sock->sk; struct kcm_sock *kcm = kcm_sk(sk); long timeo; - struct strp_rx_msg *rxm; + struct strp_msg *stm; int err = 0; ssize_t copied; struct sk_buff *skb; @@ -1192,12 +1192,12 @@ static ssize_t kcm_splice_read(struct socket *sock, loff_t *ppos, /* Okay, have a message on the receive queue */ - rxm = strp_rx_msg(skb); + stm = strp_msg(skb); - if (len > rxm->full_len) - len = rxm->full_len; + if (len > stm->full_len) + len = stm->full_len; - copied = skb_splice_bits(skb, sk, rxm->offset, pipe, len, flags); + copied = skb_splice_bits(skb, sk, stm->offset, pipe, len, flags); if (copied < 0) { err = copied; goto err_out; @@ -1205,8 +1205,8 @@ static ssize_t kcm_splice_read(struct socket *sock, loff_t *ppos, KCM_STATS_ADD(kcm->stats.rx_bytes, copied); - rxm->offset += copied; - rxm->full_len -= copied; + stm->offset += copied; + stm->full_len -= copied; /* We have no way to return MSG_EOR. If all the bytes have been * read we still leave the message in the receive socket buffer. diff --git a/net/strparser/strparser.c b/net/strparser/strparser.c index b5c279b22680..0d18fbc6f870 100644 --- a/net/strparser/strparser.c +++ b/net/strparser/strparser.c @@ -29,44 +29,46 @@ static struct workqueue_struct *strp_wq; -struct _strp_rx_msg { - /* Internal cb structure. struct strp_rx_msg must be first for passing +struct _strp_msg { + /* Internal cb structure. struct strp_msg must be first for passing * to upper layer. */ - struct strp_rx_msg strp; + struct strp_msg strp; int accum_len; int early_eaten; }; -static inline struct _strp_rx_msg *_strp_rx_msg(struct sk_buff *skb) +static inline struct _strp_msg *_strp_msg(struct sk_buff *skb) { - return (struct _strp_rx_msg *)((void *)skb->cb + + return (struct _strp_msg *)((void *)skb->cb + offsetof(struct qdisc_skb_cb, data)); } /* Lower lock held */ -static void strp_abort_rx_strp(struct strparser *strp, int err) +static void strp_abort_strp(struct strparser *strp, int err) { - struct sock *csk = strp->sk; - /* Unrecoverable error in receive */ - del_timer(&strp->rx_msg_timer); + del_timer(&strp->msg_timer); - if (strp->rx_stopped) + if (strp->stopped) return; - strp->rx_stopped = 1; + strp->stopped = 1; + + if (strp->sk) { + struct sock *sk = strp->sk; - /* Report an error on the lower socket */ - csk->sk_err = err; - csk->sk_error_report(csk); + /* Report an error on the lower socket */ + sk->sk_err = err; + sk->sk_error_report(sk); + } } -static void strp_start_rx_timer(struct strparser *strp) +static void strp_start_timer(struct strparser *strp, long timeo) { - if (strp->sk->sk_rcvtimeo) - mod_timer(&strp->rx_msg_timer, strp->sk->sk_rcvtimeo); + if (timeo) + mod_timer(&strp->msg_timer, timeo); } /* Lower lock held */ @@ -74,46 +76,55 @@ static void strp_parser_err(struct strparser *strp, int err, read_descriptor_t *desc) { desc->error = err; - kfree_skb(strp->rx_skb_head); - strp->rx_skb_head = NULL; + kfree_skb(strp->skb_head); + strp->skb_head = NULL; strp->cb.abort_parser(strp, err); } static inline int strp_peek_len(struct strparser *strp) { - struct socket *sock = strp->sk->sk_socket; + if (strp->sk) { + struct socket *sock = strp->sk->sk_socket; + + return sock->ops->peek_len(sock); + } + + /* If we don't have an associated socket there's nothing to peek. + * Return int max to avoid stopping the strparser. + */ - return sock->ops->peek_len(sock); + return INT_MAX; } /* Lower socket lock held */ -static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, - unsigned int orig_offset, size_t orig_len) +static int __strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, + unsigned int orig_offset, size_t orig_len, + size_t max_msg_size, long timeo) { struct strparser *strp = (struct strparser *)desc->arg.data; - struct _strp_rx_msg *rxm; + struct _strp_msg *stm; struct sk_buff *head, *skb; size_t eaten = 0, cand_len; ssize_t extra; int err; bool cloned_orig = false; - if (strp->rx_paused) + if (strp->paused) return 0; - head = strp->rx_skb_head; + head = strp->skb_head; if (head) { /* Message already in progress */ - rxm = _strp_rx_msg(head); - if (unlikely(rxm->early_eaten)) { + stm = _strp_msg(head); + if (unlikely(stm->early_eaten)) { /* Already some number of bytes on the receive sock - * data saved in rx_skb_head, just indicate they + * data saved in skb_head, just indicate they * are consumed. */ - eaten = orig_len <= rxm->early_eaten ? - orig_len : rxm->early_eaten; - rxm->early_eaten -= eaten; + eaten = orig_len <= stm->early_eaten ? + orig_len : stm->early_eaten; + stm->early_eaten -= eaten; return eaten; } @@ -126,12 +137,12 @@ static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, */ orig_skb = skb_clone(orig_skb, GFP_ATOMIC); if (!orig_skb) { - STRP_STATS_INCR(strp->stats.rx_mem_fail); + STRP_STATS_INCR(strp->stats.mem_fail); desc->error = -ENOMEM; return 0; } if (!pskb_pull(orig_skb, orig_offset)) { - STRP_STATS_INCR(strp->stats.rx_mem_fail); + STRP_STATS_INCR(strp->stats.mem_fail); kfree_skb(orig_skb); desc->error = -ENOMEM; return 0; @@ -140,13 +151,13 @@ static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, orig_offset = 0; } - if (!strp->rx_skb_nextp) { + if (!strp->skb_nextp) { /* We are going to append to the frags_list of head. * Need to unshare the frag_list. */ err = skb_unclone(head, GFP_ATOMIC); if (err) { - STRP_STATS_INCR(strp->stats.rx_mem_fail); + STRP_STATS_INCR(strp->stats.mem_fail); desc->error = err; return 0; } @@ -165,20 +176,20 @@ static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, skb = alloc_skb(0, GFP_ATOMIC); if (!skb) { - STRP_STATS_INCR(strp->stats.rx_mem_fail); + STRP_STATS_INCR(strp->stats.mem_fail); desc->error = -ENOMEM; return 0; } skb->len = head->len; skb->data_len = head->len; skb->truesize = head->truesize; - *_strp_rx_msg(skb) = *_strp_rx_msg(head); - strp->rx_skb_nextp = &head->next; + *_strp_msg(skb) = *_strp_msg(head); + strp->skb_nextp = &head->next; skb_shinfo(skb)->frag_list = head; - strp->rx_skb_head = skb; + strp->skb_head = skb; head = skb; } else { - strp->rx_skb_nextp = + strp->skb_nextp = &skb_shinfo(head)->frag_list; } } @@ -188,112 +199,112 @@ static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, /* Always clone since we will consume something */ skb = skb_clone(orig_skb, GFP_ATOMIC); if (!skb) { - STRP_STATS_INCR(strp->stats.rx_mem_fail); + STRP_STATS_INCR(strp->stats.mem_fail); desc->error = -ENOMEM; break; } cand_len = orig_len - eaten; - head = strp->rx_skb_head; + head = strp->skb_head; if (!head) { head = skb; - strp->rx_skb_head = head; - /* Will set rx_skb_nextp on next packet if needed */ - strp->rx_skb_nextp = NULL; - rxm = _strp_rx_msg(head); - memset(rxm, 0, sizeof(*rxm)); - rxm->strp.offset = orig_offset + eaten; + strp->skb_head = head; + /* Will set skb_nextp on next packet if needed */ + strp->skb_nextp = NULL; + stm = _strp_msg(head); + memset(stm, 0, sizeof(*stm)); + stm->strp.offset = orig_offset + eaten; } else { /* Unclone since we may be appending to an skb that we * already share a frag_list with. */ err = skb_unclone(skb, GFP_ATOMIC); if (err) { - STRP_STATS_INCR(strp->stats.rx_mem_fail); + STRP_STATS_INCR(strp->stats.mem_fail); desc->error = err; break; } - rxm = _strp_rx_msg(head); - *strp->rx_skb_nextp = skb; - strp->rx_skb_nextp = &skb->next; + stm = _strp_msg(head); + *strp->skb_nextp = skb; + strp->skb_nextp = &skb->next; head->data_len += skb->len; head->len += skb->len; head->truesize += skb->truesize; } - if (!rxm->strp.full_len) { + if (!stm->strp.full_len) { ssize_t len; len = (*strp->cb.parse_msg)(strp, head); if (!len) { /* Need more header to determine length */ - if (!rxm->accum_len) { + if (!stm->accum_len) { /* Start RX timer for new message */ - strp_start_rx_timer(strp); + strp_start_timer(strp, timeo); } - rxm->accum_len += cand_len; + stm->accum_len += cand_len; eaten += cand_len; - STRP_STATS_INCR(strp->stats.rx_need_more_hdr); + STRP_STATS_INCR(strp->stats.need_more_hdr); WARN_ON(eaten != orig_len); break; } else if (len < 0) { - if (len == -ESTRPIPE && rxm->accum_len) { + if (len == -ESTRPIPE && stm->accum_len) { len = -ENODATA; - strp->rx_unrecov_intr = 1; + strp->unrecov_intr = 1; } else { - strp->rx_interrupted = 1; + strp->interrupted = 1; } strp_parser_err(strp, len, desc); break; - } else if (len > strp->sk->sk_rcvbuf) { + } else if (len > max_msg_size) { /* Message length exceeds maximum allowed */ - STRP_STATS_INCR(strp->stats.rx_msg_too_big); + STRP_STATS_INCR(strp->stats.msg_too_big); strp_parser_err(strp, -EMSGSIZE, desc); break; } else if (len <= (ssize_t)head->len - - skb->len - rxm->strp.offset) { + skb->len - stm->strp.offset) { /* Length must be into new skb (and also * greater than zero) */ - STRP_STATS_INCR(strp->stats.rx_bad_hdr_len); + STRP_STATS_INCR(strp->stats.bad_hdr_len); strp_parser_err(strp, -EPROTO, desc); break; } - rxm->strp.full_len = len; + stm->strp.full_len = len; } - extra = (ssize_t)(rxm->accum_len + cand_len) - - rxm->strp.full_len; + extra = (ssize_t)(stm->accum_len + cand_len) - + stm->strp.full_len; if (extra < 0) { /* Message not complete yet. */ - if (rxm->strp.full_len - rxm->accum_len > + if (stm->strp.full_len - stm->accum_len > strp_peek_len(strp)) { - /* Don't have the whole messages in the socket - * buffer. Set strp->rx_need_bytes to wait for + /* Don't have the whole message in the socket + * buffer. Set strp->need_bytes to wait for * the rest of the message. Also, set "early * eaten" since we've already buffered the skb * but don't consume yet per strp_read_sock. */ - if (!rxm->accum_len) { + if (!stm->accum_len) { /* Start RX timer for new message */ - strp_start_rx_timer(strp); + strp_start_timer(strp, timeo); } - strp->rx_need_bytes = rxm->strp.full_len - - rxm->accum_len; - rxm->accum_len += cand_len; - rxm->early_eaten = cand_len; - STRP_STATS_ADD(strp->stats.rx_bytes, cand_len); + strp->need_bytes = stm->strp.full_len - + stm->accum_len; + stm->accum_len += cand_len; + stm->early_eaten = cand_len; + STRP_STATS_ADD(strp->stats.bytes, cand_len); desc->count = 0; /* Stop reading socket */ break; } - rxm->accum_len += cand_len; + stm->accum_len += cand_len; eaten += cand_len; WARN_ON(eaten != orig_len); break; @@ -308,14 +319,14 @@ static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, eaten += (cand_len - extra); /* Hurray, we have a new message! */ - del_timer(&strp->rx_msg_timer); - strp->rx_skb_head = NULL; - STRP_STATS_INCR(strp->stats.rx_msgs); + del_timer(&strp->msg_timer); + strp->skb_head = NULL; + STRP_STATS_INCR(strp->stats.msgs); /* Give skb to upper layer */ strp->cb.rcv_msg(strp, head); - if (unlikely(strp->rx_paused)) { + if (unlikely(strp->paused)) { /* Upper layer paused strp */ break; } @@ -324,11 +335,33 @@ static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, if (cloned_orig) kfree_skb(orig_skb); - STRP_STATS_ADD(strp->stats.rx_bytes, eaten); + STRP_STATS_ADD(strp->stats.bytes, eaten); return eaten; } +int strp_process(struct strparser *strp, struct sk_buff *orig_skb, + unsigned int orig_offset, size_t orig_len, + size_t max_msg_size, long timeo) +{ + read_descriptor_t desc; /* Dummy arg to strp_recv */ + + desc.arg.data = strp; + + return __strp_recv(&desc, orig_skb, orig_offset, orig_len, + max_msg_size, timeo); +} +EXPORT_SYMBOL_GPL(strp_process); + +static int strp_recv(read_descriptor_t *desc, struct sk_buff *orig_skb, + unsigned int orig_offset, size_t orig_len) +{ + struct strparser *strp = (struct strparser *)desc->arg.data; + + return __strp_recv(desc, orig_skb, orig_offset, orig_len, + strp->sk->sk_rcvbuf, strp->sk->sk_rcvtimeo); +} + static int default_read_sock_done(struct strparser *strp, int err) { return err; @@ -355,101 +388,129 @@ static int strp_read_sock(struct strparser *strp) /* Lower sock lock held */ void strp_data_ready(struct strparser *strp) { - if (unlikely(strp->rx_stopped)) + if (unlikely(strp->stopped)) return; - /* This check is needed to synchronize with do_strp_rx_work. - * do_strp_rx_work acquires a process lock (lock_sock) whereas + /* This check is needed to synchronize with do_strp_work. + * do_strp_work acquires a process lock (lock_sock) whereas * the lock held here is bh_lock_sock. The two locks can be * held by different threads at the same time, but bh_lock_sock * allows a thread in BH context to safely check if the process * lock is held. In this case, if the lock is held, queue work. */ if (sock_owned_by_user(strp->sk)) { - queue_work(strp_wq, &strp->rx_work); + queue_work(strp_wq, &strp->work); return; } - if (strp->rx_paused) + if (strp->paused) return; - if (strp->rx_need_bytes) { - if (strp_peek_len(strp) >= strp->rx_need_bytes) - strp->rx_need_bytes = 0; + if (strp->need_bytes) { + if (strp_peek_len(strp) >= strp->need_bytes) + strp->need_bytes = 0; else return; } if (strp_read_sock(strp) == -ENOMEM) - queue_work(strp_wq, &strp->rx_work); + queue_work(strp_wq, &strp->work); } EXPORT_SYMBOL_GPL(strp_data_ready); -static void do_strp_rx_work(struct strparser *strp) +static void do_strp_work(struct strparser *strp) { read_descriptor_t rd_desc; - struct sock *csk = strp->sk; /* We need the read lock to synchronize with strp_data_ready. We * need the socket lock for calling strp_read_sock. */ - lock_sock(csk); + strp->cb.lock(strp); - if (unlikely(strp->rx_stopped)) + if (unlikely(strp->stopped)) goto out; - if (strp->rx_paused) + if (strp->paused) goto out; rd_desc.arg.data = strp; if (strp_read_sock(strp) == -ENOMEM) - queue_work(strp_wq, &strp->rx_work); + queue_work(strp_wq, &strp->work); out: - release_sock(csk); + strp->cb.unlock(strp); } -static void strp_rx_work(struct work_struct *w) +static void strp_work(struct work_struct *w) { - do_strp_rx_work(container_of(w, struct strparser, rx_work)); + do_strp_work(container_of(w, struct strparser, work)); } -static void strp_rx_msg_timeout(unsigned long arg) +static void strp_msg_timeout(unsigned long arg) { struct strparser *strp = (struct strparser *)arg; /* Message assembly timed out */ - STRP_STATS_INCR(strp->stats.rx_msg_timeouts); - lock_sock(strp->sk); + STRP_STATS_INCR(strp->stats.msg_timeouts); + strp->cb.lock(strp); strp->cb.abort_parser(strp, ETIMEDOUT); + strp->cb.unlock(strp); +} + +static void strp_sock_lock(struct strparser *strp) +{ + lock_sock(strp->sk); +} + +static void strp_sock_unlock(struct strparser *strp) +{ release_sock(strp->sk); } -int strp_init(struct strparser *strp, struct sock *csk, +int strp_init(struct strparser *strp, struct sock *sk, struct strp_callbacks *cb) { - struct socket *sock = csk->sk_socket; if (!cb || !cb->rcv_msg || !cb->parse_msg) return -EINVAL; - if (!sock->ops->read_sock || !sock->ops->peek_len) - return -EAFNOSUPPORT; + /* The sk (sock) arg determines the mode of the stream parser. + * + * If the sock is set then the strparser is in receive callback mode. + * The upper layer calls strp_data_ready to kick receive processing + * and strparser calls the read_sock function on the socket to + * get packets. + * + * If the sock is not set then the strparser is in general mode. + * The upper layer calls strp_process for each skb to be parsed. + */ - memset(strp, 0, sizeof(*strp)); + if (sk) { + struct socket *sock = sk->sk_socket; - strp->sk = csk; + if (!sock->ops->read_sock || !sock->ops->peek_len) + return -EAFNOSUPPORT; + } else { + if (!cb->lock || !cb->unlock) + return -EINVAL; + } - setup_timer(&strp->rx_msg_timer, strp_rx_msg_timeout, - (unsigned long)strp); + memset(strp, 0, sizeof(*strp)); - INIT_WORK(&strp->rx_work, strp_rx_work); + strp->sk = sk; + strp->cb.lock = cb->lock ? : strp_sock_lock; + strp->cb.unlock = cb->unlock ? : strp_sock_unlock; strp->cb.rcv_msg = cb->rcv_msg; strp->cb.parse_msg = cb->parse_msg; strp->cb.read_sock_done = cb->read_sock_done ? : default_read_sock_done; - strp->cb.abort_parser = cb->abort_parser ? : strp_abort_rx_strp; + strp->cb.abort_parser = cb->abort_parser ? : strp_abort_strp; + + setup_timer(&strp->msg_timer, strp_msg_timeout, + (unsigned long)strp); + + INIT_WORK(&strp->work, strp_work); return 0; } @@ -457,12 +518,12 @@ EXPORT_SYMBOL_GPL(strp_init); void strp_unpause(struct strparser *strp) { - strp->rx_paused = 0; + strp->paused = 0; - /* Sync setting rx_paused with RX work */ + /* Sync setting paused with RX work */ smp_mb(); - queue_work(strp_wq, &strp->rx_work); + queue_work(strp_wq, &strp->work); } EXPORT_SYMBOL_GPL(strp_unpause); @@ -471,27 +532,27 @@ EXPORT_SYMBOL_GPL(strp_unpause); */ void strp_done(struct strparser *strp) { - WARN_ON(!strp->rx_stopped); + WARN_ON(!strp->stopped); - del_timer_sync(&strp->rx_msg_timer); - cancel_work_sync(&strp->rx_work); + del_timer_sync(&strp->msg_timer); + cancel_work_sync(&strp->work); - if (strp->rx_skb_head) { - kfree_skb(strp->rx_skb_head); - strp->rx_skb_head = NULL; + if (strp->skb_head) { + kfree_skb(strp->skb_head); + strp->skb_head = NULL; } } EXPORT_SYMBOL_GPL(strp_done); void strp_stop(struct strparser *strp) { - strp->rx_stopped = 1; + strp->stopped = 1; } EXPORT_SYMBOL_GPL(strp_stop); void strp_check_rcv(struct strparser *strp) { - queue_work(strp_wq, &strp->rx_work); + queue_work(strp_wq, &strp->work); } EXPORT_SYMBOL_GPL(strp_check_rcv); -- cgit v1.2.3 From c613c209c3f351d47158f728271d0c73b6dd24c6 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Mon, 31 Jul 2017 08:15:47 -0400 Subject: net: add skb_frag_foreach_page and use with kmap_atomic Skb frags may contain compound pages. Various operations map frags temporarily using kmap_atomic, but this function works on single pages, not whole compound pages. The distinction is only relevant for high mem pages that require temporary mappings. Introduce a looping mechanism that for compound highmem pages maps one page at a time, does not change behavior on other pages. Use the loop in the kmap_atomic callers in net/core/skbuff.c. Verified by triggering skb_copy_bits with tcpdump -n -c 100 -i ${DEV} -w /dev/null & netperf -t TCP_STREAM -H ${HOST} and by triggering __skb_checksum with ethtool -K ${DEV} tx off repeated the tests with looping on a non-highmem platform (x86_64) by making skb_frag_must_loop always return true. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/linux/skbuff.h | 36 +++++++++++++++++++++ net/core/skbuff.c | 88 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 95 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 18e76bf9574e..6f9f1b2715ec 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -345,6 +345,42 @@ static inline void skb_frag_size_sub(skb_frag_t *frag, int delta) frag->size -= delta; } +static inline bool skb_frag_must_loop(struct page *p) +{ +#if defined(CONFIG_HIGHMEM) + if (PageHighMem(p)) + return true; +#endif + return false; +} + +/** + * skb_frag_foreach_page - loop over pages in a fragment + * + * @f: skb frag to operate on + * @f_off: offset from start of f->page.p + * @f_len: length from f_off to loop over + * @p: (temp var) current page + * @p_off: (temp var) offset from start of current page, + * non-zero only on first page. + * @p_len: (temp var) length in current page, + * < PAGE_SIZE only on first and last page. + * @copied: (temp var) length so far, excluding current p_len. + * + * A fragment can hold a compound page, in which case per-page + * operations, notably kmap_atomic, must be called for each + * regular page. + */ +#define skb_frag_foreach_page(f, f_off, f_len, p, p_off, p_len, copied) \ + for (p = skb_frag_page(f) + ((f_off) >> PAGE_SHIFT), \ + p_off = (f_off) & (PAGE_SIZE - 1), \ + p_len = skb_frag_must_loop(p) ? \ + min_t(u32, f_len, PAGE_SIZE - p_off) : f_len, \ + copied = 0; \ + copied < f_len; \ + copied += p_len, p++, p_off = 0, \ + p_len = min_t(u32, f_len - copied, PAGE_SIZE)) \ + #define HAVE_HW_TIME_STAMP /** diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 9c0e015ff3fe..0f0933b338d7 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -938,8 +938,10 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg; for (i = 0; i < num_frags; i++) { - u8 *vaddr; skb_frag_t *f = &skb_shinfo(skb)->frags[i]; + u32 p_off, p_len, copied; + struct page *p; + u8 *vaddr; page = alloc_page(gfp_mask); if (!page) { @@ -950,10 +952,15 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) } return -ENOMEM; } - vaddr = kmap_atomic(skb_frag_page(f)); - memcpy(page_address(page), - vaddr + f->page_offset, skb_frag_size(f)); - kunmap_atomic(vaddr); + + skb_frag_foreach_page(f, f->page_offset, skb_frag_size(f), + p, p_off, p_len, copied) { + vaddr = kmap_atomic(p); + memcpy(page_address(page) + copied, vaddr + p_off, + p_len); + kunmap_atomic(vaddr); + } + set_page_private(page, (unsigned long)head); head = page; } @@ -1753,16 +1760,20 @@ int skb_copy_bits(const struct sk_buff *skb, int offset, void *to, int len) end = start + skb_frag_size(f); if ((copy = end - offset) > 0) { + u32 p_off, p_len, copied; + struct page *p; u8 *vaddr; if (copy > len) copy = len; - vaddr = kmap_atomic(skb_frag_page(f)); - memcpy(to, - vaddr + f->page_offset + offset - start, - copy); - kunmap_atomic(vaddr); + skb_frag_foreach_page(f, + f->page_offset + offset - start, + copy, p, p_off, p_len, copied) { + vaddr = kmap_atomic(p); + memcpy(to + copied, vaddr + p_off, p_len); + kunmap_atomic(vaddr); + } if ((len -= copy) == 0) return 0; @@ -2122,15 +2133,20 @@ int skb_store_bits(struct sk_buff *skb, int offset, const void *from, int len) end = start + skb_frag_size(frag); if ((copy = end - offset) > 0) { + u32 p_off, p_len, copied; + struct page *p; u8 *vaddr; if (copy > len) copy = len; - vaddr = kmap_atomic(skb_frag_page(frag)); - memcpy(vaddr + frag->page_offset + offset - start, - from, copy); - kunmap_atomic(vaddr); + skb_frag_foreach_page(frag, + frag->page_offset + offset - start, + copy, p, p_off, p_len, copied) { + vaddr = kmap_atomic(p); + memcpy(vaddr + p_off, from + copied, p_len); + kunmap_atomic(vaddr); + } if ((len -= copy) == 0) return 0; @@ -2195,20 +2211,27 @@ __wsum __skb_checksum(const struct sk_buff *skb, int offset, int len, end = start + skb_frag_size(frag); if ((copy = end - offset) > 0) { + u32 p_off, p_len, copied; + struct page *p; __wsum csum2; u8 *vaddr; if (copy > len) copy = len; - vaddr = kmap_atomic(skb_frag_page(frag)); - csum2 = ops->update(vaddr + frag->page_offset + - offset - start, copy, 0); - kunmap_atomic(vaddr); - csum = ops->combine(csum, csum2, pos, copy); + + skb_frag_foreach_page(frag, + frag->page_offset + offset - start, + copy, p, p_off, p_len, copied) { + vaddr = kmap_atomic(p); + csum2 = ops->update(vaddr + p_off, p_len, 0); + kunmap_atomic(vaddr); + csum = ops->combine(csum, csum2, pos, p_len); + pos += p_len; + } + if (!(len -= copy)) return csum; offset += copy; - pos += copy; } start = end; } @@ -2281,24 +2304,31 @@ __wsum skb_copy_and_csum_bits(const struct sk_buff *skb, int offset, end = start + skb_frag_size(&skb_shinfo(skb)->frags[i]); if ((copy = end - offset) > 0) { + skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; + u32 p_off, p_len, copied; + struct page *p; __wsum csum2; u8 *vaddr; - skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; if (copy > len) copy = len; - vaddr = kmap_atomic(skb_frag_page(frag)); - csum2 = csum_partial_copy_nocheck(vaddr + - frag->page_offset + - offset - start, to, - copy, 0); - kunmap_atomic(vaddr); - csum = csum_block_add(csum, csum2, pos); + + skb_frag_foreach_page(frag, + frag->page_offset + offset - start, + copy, p, p_off, p_len, copied) { + vaddr = kmap_atomic(p); + csum2 = csum_partial_copy_nocheck(vaddr + p_off, + to + copied, + p_len, 0); + kunmap_atomic(vaddr); + csum = csum_block_add(csum, csum2, pos); + pos += p_len; + } + if (!(len -= copy)) return csum; offset += copy; to += copy; - pos += copy; } start = end; } -- cgit v1.2.3 From 7b9cc73843e89ea1ac215511c13c259a70d1b4f7 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:31 -0400 Subject: net: dsa: PHY device is mandatory for EEE The port's PHY and MAC are both implied in EEE. The current code does not call the PHY operations if the related device is NULL. Change that by returning -ENODEV if there's no PHY device attached to the interface. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/slave.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 9507bd38cf04..7df55d597740 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -648,6 +648,10 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e) struct dsa_switch *ds = p->dp->ds; int ret; + /* Port's PHY and MAC both need to be EEE capable */ + if (!p->phy) + return -ENODEV; + if (!ds->ops->set_eee) return -EOPNOTSUPP; @@ -655,10 +659,7 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e) if (ret) return ret; - if (p->phy) - ret = phy_ethtool_set_eee(p->phy, e); - - return ret; + return phy_ethtool_set_eee(p->phy, e); } static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e) @@ -667,6 +668,10 @@ static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e) struct dsa_switch *ds = p->dp->ds; int ret; + /* Port's PHY and MAC both need to be EEE capable */ + if (!p->phy) + return -ENODEV; + if (!ds->ops->get_eee) return -EOPNOTSUPP; @@ -674,10 +679,7 @@ static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e) if (ret) return ret; - if (p->phy) - ret = phy_ethtool_get_eee(p->phy, e); - - return ret; + return phy_ethtool_get_eee(p->phy, e); } #ifdef CONFIG_NET_POLL_CONTROLLER -- cgit v1.2.3 From c48f7eb3021c993cfb15ee4963df2fd6683c2f0d Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:38 -0400 Subject: net: dsa: call phy_init_eee in DSA layer All DSA drivers are calling phy_init_eee if eee_enabled is true. Move up this statement in the DSA layer to simplify the DSA drivers. qca8k does not require to cache the ethtool_eee structures from now on. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 9 +-------- drivers/net/dsa/mv88e6xxx/chip.c | 6 ------ drivers/net/dsa/qca8k.c | 31 ++----------------------------- drivers/net/dsa/qca8k.h | 1 - net/dsa/slave.c | 6 ++++++ 5 files changed, 9 insertions(+), 44 deletions(-) (limited to 'net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index aef475f1ce06..9d10aac8f241 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -360,14 +360,7 @@ static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, struct ethtool_eee *p = &priv->port_sts[port].eee; p->eee_enabled = e->eee_enabled; - - if (!p->eee_enabled) { - bcm_sf2_eee_enable_set(ds, port, false); - } else { - p->eee_enabled = bcm_sf2_eee_init(ds, port, phydev); - if (!p->eee_enabled) - return -EOPNOTSUPP; - } + bcm_sf2_eee_enable_set(ds, port, e->eee_enabled); return 0; } diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index b531d4a3bab5..647d5d45c1d6 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -855,12 +855,6 @@ static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, struct mv88e6xxx_chip *chip = ds->priv; int err; - if (e->eee_enabled) { - err = phy_init_eee(phydev, 0); - if (err) - return err; - } - mutex_lock(&chip->reg_lock); err = mv88e6xxx_energy_detect_write(chip, port, e); mutex_unlock(&chip->reg_lock); diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 8cd4634c6985..bfe0172ae6cc 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -654,41 +654,14 @@ qca8k_eee_enable_set(struct dsa_switch *ds, int port, bool enable) mutex_unlock(&priv->reg_mutex); } -static int -qca8k_eee_init(struct dsa_switch *ds, int port, - struct phy_device *phy) -{ - int ret; - - ret = phy_init_eee(phy, 0); - if (ret) - return 0; - - qca8k_eee_enable_set(ds, port, true); - - return 1; -} - static int qca8k_set_eee(struct dsa_switch *ds, int port, struct phy_device *phydev, struct ethtool_eee *e) { - struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - struct ethtool_eee *p = &priv->port_sts[port].eee; - int ret = 0; + qca8k_eee_enable_set(ds, port, e->eee_enabled); - p->eee_enabled = e->eee_enabled; - - if (!p->eee_enabled) { - qca8k_eee_enable_set(ds, port, false); - } else { - p->eee_enabled = qca8k_eee_init(ds, port, phydev); - if (!p->eee_enabled) - ret = -EOPNOTSUPP; - } - - return ret; + return 0; } static int diff --git a/drivers/net/dsa/qca8k.h b/drivers/net/dsa/qca8k.h index 1ed4fac6cd6d..1cf8a920d4ff 100644 --- a/drivers/net/dsa/qca8k.h +++ b/drivers/net/dsa/qca8k.h @@ -156,7 +156,6 @@ enum qca8k_fdb_cmd { }; struct ar8xxx_port_status { - struct ethtool_eee eee; int enabled; }; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 7df55d597740..ad5caaf384d7 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -659,6 +659,12 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e) if (ret) return ret; + if (e->eee_enabled) { + ret = phy_init_eee(p->phy, 0); + if (ret) + return ret; + } + return phy_ethtool_set_eee(p->phy, e); } -- cgit v1.2.3 From 46587e4a312780a63132483bc8678c397eca6aff Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:39 -0400 Subject: net: dsa: remove PHY device argument from .set_eee The DSA switch operations for EEE are only meant to configure a port's MAC EEE settings. The port's PHY EEE settings are accessed by the DSA layer and must be made available via a proper PHY driver. In order to reduce this confusion, remove the phy_device argument from the .set_eee operation. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 1 - drivers/net/dsa/mv88e6xxx/chip.c | 2 +- drivers/net/dsa/qca8k.c | 14 +++----------- include/net/dsa.h | 1 - net/dsa/slave.c | 2 +- 5 files changed, 5 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 9d10aac8f241..ce886345d8d2 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -353,7 +353,6 @@ static int bcm_sf2_sw_get_eee(struct dsa_switch *ds, int port, } static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, - struct phy_device *phydev, struct ethtool_eee *e) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 647d5d45c1d6..aaa96487f21f 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -850,7 +850,7 @@ static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, } static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, - struct phy_device *phydev, struct ethtool_eee *e) + struct ethtool_eee *e) { struct mv88e6xxx_chip *chip = ds->priv; int err; diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index bfe0172ae6cc..e209e229ed4c 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -637,8 +637,8 @@ qca8k_get_sset_count(struct dsa_switch *ds) return ARRAY_SIZE(ar8327_mib); } -static void -qca8k_eee_enable_set(struct dsa_switch *ds, int port, bool enable) +static int +qca8k_set_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); @@ -646,20 +646,12 @@ qca8k_eee_enable_set(struct dsa_switch *ds, int port, bool enable) mutex_lock(&priv->reg_mutex); reg = qca8k_read(priv, QCA8K_REG_EEE_CTRL); - if (enable) + if (eee->eee_enabled) reg |= lpi_en; else reg &= ~lpi_en; qca8k_write(priv, QCA8K_REG_EEE_CTRL, reg); mutex_unlock(&priv->reg_mutex); -} - -static int -qca8k_set_eee(struct dsa_switch *ds, int port, - struct phy_device *phydev, - struct ethtool_eee *e) -{ - qca8k_eee_enable_set(ds, port, e->eee_enabled); return 0; } diff --git a/include/net/dsa.h b/include/net/dsa.h index 88da272d20d0..ce46db323394 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -335,7 +335,6 @@ struct dsa_switch_ops { * EEE setttings */ int (*set_eee)(struct dsa_switch *ds, int port, - struct phy_device *phydev, struct ethtool_eee *e); int (*get_eee)(struct dsa_switch *ds, int port, struct ethtool_eee *e); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index ad5caaf384d7..9ddc584e70b0 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -655,7 +655,7 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e) if (!ds->ops->set_eee) return -EOPNOTSUPP; - ret = ds->ops->set_eee(ds, p->dp->index, p->phy, e); + ret = ds->ops->set_eee(ds, p->dp->index, e); if (ret) return ret; -- cgit v1.2.3 From 08f500610f39809c107f206cba1f799c98c38054 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Tue, 1 Aug 2017 16:32:41 -0400 Subject: net: dsa: rename switch EEE ops To avoid confusion with the PHY EEE settings, rename the .set_eee and .get_eee ops to respectively .set_mac_eee and .get_mac_eee. Signed-off-by: Vivien Didelot Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- drivers/net/dsa/bcm_sf2.c | 12 ++++++------ drivers/net/dsa/mv88e6xxx/chip.c | 12 ++++++------ drivers/net/dsa/qca8k.c | 9 ++++----- include/net/dsa.h | 10 +++++----- net/dsa/slave.c | 8 ++++---- 5 files changed, 25 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index ce886345d8d2..6bbfa6ea1efb 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -338,8 +338,8 @@ static int bcm_sf2_eee_init(struct dsa_switch *ds, int port, return 1; } -static int bcm_sf2_sw_get_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +static int bcm_sf2_sw_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_eee *p = &priv->port_sts[port].eee; @@ -352,8 +352,8 @@ static int bcm_sf2_sw_get_eee(struct dsa_switch *ds, int port, return 0; } -static int bcm_sf2_sw_set_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +static int bcm_sf2_sw_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { struct bcm_sf2_priv *priv = bcm_sf2_to_priv(ds); struct ethtool_eee *p = &priv->port_sts[port].eee; @@ -1011,8 +1011,8 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .set_wol = bcm_sf2_sw_set_wol, .port_enable = bcm_sf2_port_setup, .port_disable = bcm_sf2_port_disable, - .get_eee = bcm_sf2_sw_get_eee, - .set_eee = bcm_sf2_sw_set_eee, + .get_mac_eee = bcm_sf2_sw_get_mac_eee, + .set_mac_eee = bcm_sf2_sw_set_mac_eee, .port_bridge_join = b53_br_join, .port_bridge_leave = b53_br_leave, .port_stp_state_set = b53_br_set_stp_state, diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index aa0c5493fb9d..521738c4cd17 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -810,15 +810,15 @@ static void mv88e6xxx_get_regs(struct dsa_switch *ds, int port, mutex_unlock(&chip->reg_lock); } -static int mv88e6xxx_get_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +static int mv88e6xxx_get_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { /* Nothing to do on the port's MAC */ return 0; } -static int mv88e6xxx_set_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +static int mv88e6xxx_set_mac_eee(struct dsa_switch *ds, int port, + struct ethtool_eee *e) { /* Nothing to do on the port's MAC */ return 0; @@ -3890,8 +3890,8 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .get_sset_count = mv88e6xxx_get_sset_count, .port_enable = mv88e6xxx_port_enable, .port_disable = mv88e6xxx_port_disable, - .set_eee = mv88e6xxx_set_eee, - .get_eee = mv88e6xxx_get_eee, + .get_mac_eee = mv88e6xxx_get_mac_eee, + .set_mac_eee = mv88e6xxx_set_mac_eee, .get_eeprom_len = mv88e6xxx_get_eeprom_len, .get_eeprom = mv88e6xxx_get_eeprom, .set_eeprom = mv88e6xxx_set_eeprom, diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index e209e229ed4c..36c169b0c705 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -638,7 +638,7 @@ qca8k_get_sset_count(struct dsa_switch *ds) } static int -qca8k_set_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) +qca8k_set_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u32 lpi_en = QCA8K_REG_EEE_CTRL_LPI_EN(port); @@ -657,8 +657,7 @@ qca8k_set_eee(struct dsa_switch *ds, int port, struct ethtool_eee *eee) } static int -qca8k_get_eee(struct dsa_switch *ds, int port, - struct ethtool_eee *e) +qca8k_get_mac_eee(struct dsa_switch *ds, int port, struct ethtool_eee *e) { /* Nothing to do on the port's MAC */ return 0; @@ -863,8 +862,8 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .phy_write = qca8k_phy_write, .get_ethtool_stats = qca8k_get_ethtool_stats, .get_sset_count = qca8k_get_sset_count, - .get_eee = qca8k_get_eee, - .set_eee = qca8k_set_eee, + .get_mac_eee = qca8k_get_mac_eee, + .set_mac_eee = qca8k_set_mac_eee, .port_enable = qca8k_port_enable, .port_disable = qca8k_port_disable, .port_stp_state_set = qca8k_port_stp_state_set, diff --git a/include/net/dsa.h b/include/net/dsa.h index ce46db323394..0b1a0622b33c 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -332,12 +332,12 @@ struct dsa_switch_ops { struct phy_device *phy); /* - * EEE setttings + * Port's MAC EEE settings */ - int (*set_eee)(struct dsa_switch *ds, int port, - struct ethtool_eee *e); - int (*get_eee)(struct dsa_switch *ds, int port, - struct ethtool_eee *e); + int (*set_mac_eee)(struct dsa_switch *ds, int port, + struct ethtool_eee *e); + int (*get_mac_eee)(struct dsa_switch *ds, int port, + struct ethtool_eee *e); /* EEPROM access */ int (*get_eeprom_len)(struct dsa_switch *ds); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 9ddc584e70b0..cc4bad3dadb4 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -652,10 +652,10 @@ static int dsa_slave_set_eee(struct net_device *dev, struct ethtool_eee *e) if (!p->phy) return -ENODEV; - if (!ds->ops->set_eee) + if (!ds->ops->set_mac_eee) return -EOPNOTSUPP; - ret = ds->ops->set_eee(ds, p->dp->index, e); + ret = ds->ops->set_mac_eee(ds, p->dp->index, e); if (ret) return ret; @@ -678,10 +678,10 @@ static int dsa_slave_get_eee(struct net_device *dev, struct ethtool_eee *e) if (!p->phy) return -ENODEV; - if (!ds->ops->get_eee) + if (!ds->ops->get_mac_eee) return -EOPNOTSUPP; - ret = ds->ops->get_eee(ds, p->dp->index, e); + ret = ds->ops->get_mac_eee(ds, p->dp->index, e); if (ret) return ret; -- cgit v1.2.3 From ec9567a9e008d1248e4d88f7ff1026ba68133621 Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Tue, 1 Aug 2017 12:49:04 +0300 Subject: esp4: Support RX checksum with crypto offload Keep the device's reported ip_summed indication in case crypto was offloaded by the device. Subtract the csum values of the stripped parts (esp header+iv, esp trailer+auth_data) to keep value correct. Note: CHECKSUM_COMPLETE should be indicated only if skb->csum has the post-decryption offload csum value. Signed-off-by: Ariel Levkovich Signed-off-by: Ilan Tayari Signed-off-by: Steffen Klassert --- net/ipv4/esp4.c | 14 +++++++++++--- net/ipv4/esp4_offload.c | 4 +++- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 0cbee0a666ff..741acd7b9646 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -510,7 +510,8 @@ int esp_input_done2(struct sk_buff *skb, int err) int elen = skb->len - hlen; int ihl; u8 nexthdr[2]; - int padlen; + int padlen, trimlen; + __wsum csumdiff; if (!xo || (xo && !(xo->flags & CRYPTO_DONE))) kfree(ESP_SKB_CB(skb)->tmp); @@ -568,8 +569,15 @@ int esp_input_done2(struct sk_buff *skb, int err) skb->ip_summed = CHECKSUM_UNNECESSARY; } - pskb_trim(skb, skb->len - alen - padlen - 2); - __skb_pull(skb, hlen); + trimlen = alen + padlen + 2; + if (skb->ip_summed == CHECKSUM_COMPLETE) { + csumdiff = skb_checksum(skb, skb->len - trimlen, trimlen, 0); + skb->csum = csum_block_sub(skb->csum, csumdiff, + skb->len - trimlen); + } + pskb_trim(skb, skb->len - trimlen); + + skb_pull_rcsum(skb, hlen); if (x->props.mode == XFRM_MODE_TUNNEL) skb_reset_transport_header(skb); else diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index e0666016a764..05831dea00f4 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -182,11 +182,13 @@ out: static int esp_input_tail(struct xfrm_state *x, struct sk_buff *skb) { struct crypto_aead *aead = x->data; + struct xfrm_offload *xo = xfrm_offload(skb); if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead))) return -EINVAL; - skb->ip_summed = CHECKSUM_NONE; + if (!(xo->flags & CRYPTO_DONE)) + skb->ip_summed = CHECKSUM_NONE; return esp_input_done2(skb, 0); } -- cgit v1.2.3 From e51a64727079f46fc3a99f380de384d5ab01fffa Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Tue, 1 Aug 2017 12:49:05 +0300 Subject: esp6: Support RX checksum with crypto offload Keep the device's reported ip_summed indication in case crypto was offloaded by the device. Subtract the csum values of the stripped parts (esp header+iv, esp trailer+auth_data) to keep value correct. Note: CHECKSUM_COMPLETE should be indicated only if skb->csum has the post-decryption offload csum value. Signed-off-by: Ariel Levkovich Signed-off-by: Ilan Tayari Signed-off-by: Steffen Klassert --- net/ipv6/esp6.c | 14 +++++++++++--- net/ipv6/esp6_offload.c | 4 +++- 2 files changed, 14 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 9ed35473dcb5..0ca1db62e381 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -470,7 +470,8 @@ int esp6_input_done2(struct sk_buff *skb, int err) int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); int elen = skb->len - hlen; int hdr_len = skb_network_header_len(skb); - int padlen; + int padlen, trimlen; + __wsum csumdiff; u8 nexthdr[2]; if (!xo || (xo && !(xo->flags & CRYPTO_DONE))) @@ -492,8 +493,15 @@ int esp6_input_done2(struct sk_buff *skb, int err) /* ... check padding bits here. Silly. :-) */ - pskb_trim(skb, skb->len - alen - padlen - 2); - __skb_pull(skb, hlen); + trimlen = alen + padlen + 2; + if (skb->ip_summed == CHECKSUM_COMPLETE) { + csumdiff = skb_checksum(skb, skb->len - trimlen, trimlen, 0); + skb->csum = csum_block_sub(skb->csum, csumdiff, + skb->len - trimlen); + } + pskb_trim(skb, skb->len - trimlen); + + skb_pull_rcsum(skb, hlen); if (x->props.mode == XFRM_MODE_TUNNEL) skb_reset_transport_header(skb); else diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index f02f131f6435..eec3add177fe 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -209,11 +209,13 @@ out: static int esp6_input_tail(struct xfrm_state *x, struct sk_buff *skb) { struct crypto_aead *aead = x->data; + struct xfrm_offload *xo = xfrm_offload(skb); if (!pskb_may_pull(skb, sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead))) return -EINVAL; - skb->ip_summed = CHECKSUM_NONE; + if (!(xo->flags & CRYPTO_DONE)) + skb->ip_summed = CHECKSUM_NONE; return esp6_input_done2(skb, 0); } -- cgit v1.2.3 From e9cba69448df7686e1c35e74be48fc715d41ac45 Mon Sep 17 00:00:00 2001 From: Yossi Kuperman Date: Tue, 1 Aug 2017 12:49:06 +0300 Subject: xfrm6: Fix CHECKSUM_COMPLETE after IPv6 header push xfrm6_transport_finish rebuilds the IPv6 header based on the original one and pushes it back without fixing skb->csum. Therefore, CHECKSUM_COMPLETE is no longer valid and the packet gets dropped. Fix skb->csum by calling skb_postpush_rcsum. Note: A valid IPv4 header has checksum 0, unlike IPv6. Thus, the change is not needed in the sibling xfrm4_transport_finish function. Signed-off-by: Yossi Kuperman Signed-off-by: Ilan Tayari Signed-off-by: Steffen Klassert --- net/ipv6/xfrm6_input.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/xfrm6_input.c b/net/ipv6/xfrm6_input.c index 3ef5d913e7a3..f95943a13abc 100644 --- a/net/ipv6/xfrm6_input.c +++ b/net/ipv6/xfrm6_input.c @@ -34,6 +34,7 @@ EXPORT_SYMBOL(xfrm6_rcv_spi); int xfrm6_transport_finish(struct sk_buff *skb, int async) { struct xfrm_offload *xo = xfrm_offload(skb); + int nhlen = skb->data - skb_network_header(skb); skb_network_header(skb)[IP6CB(skb)->nhoff] = XFRM_MODE_SKB_CB(skb)->protocol; @@ -43,8 +44,9 @@ int xfrm6_transport_finish(struct sk_buff *skb, int async) return 1; #endif - __skb_push(skb, skb->data - skb_network_header(skb)); + __skb_push(skb, nhlen); ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + skb_postpush_rcsum(skb, skb_network_header(skb), nhlen); if (xo && (xo->flags & XFRM_GRO)) { skb_mac_header_rebuild(skb); -- cgit v1.2.3 From a9b28c2bf05d9d9998d5d3c6453fd75bc4cf8a6d Mon Sep 17 00:00:00 2001 From: Yossi Kuperman Date: Tue, 1 Aug 2017 12:49:07 +0300 Subject: esp6: Fix RX checksum after header pull Both ip6_input_finish (non-GRO) and esp6_gro_receive (GRO) strip the IPv6 header without adjusting skb->csum accordingly. As a result CHECKSUM_COMPLETE breaks and "hw csum failure" is written to the kernel log by netdev_rx_csum_fault (dev.c). Fix skb->csum by substracting the checksum value of the pulled IPv6 header using a call to skb_postpull_rcsum. This affects both transport and tunnel modes. Note that the fix occurs far from the place that the header was pulled. This is based on existing code, see: ipv6_srh_rcv() in exthdrs.c and rawv6_rcv() in raw.c Signed-off-by: Yossi Kuperman Signed-off-by: Ilan Tayari Signed-off-by: Steffen Klassert --- net/ipv6/esp6.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 0ca1db62e381..74bde202eb9a 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -495,6 +495,8 @@ int esp6_input_done2(struct sk_buff *skb, int err) trimlen = alen + padlen + 2; if (skb->ip_summed == CHECKSUM_COMPLETE) { + skb_postpull_rcsum(skb, skb_network_header(skb), + skb_network_header_len(skb)); csumdiff = skb_checksum(skb, skb->len - trimlen, trimlen, 0); skb->csum = csum_block_sub(skb->csum, csumdiff, skb->len - trimlen); -- cgit v1.2.3 From ffdb5211da1c20354f1b40c204b6cf6c29c68161 Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Tue, 1 Aug 2017 12:49:08 +0300 Subject: xfrm: Auto-load xfrm offload modules IPSec crypto offload depends on the protocol-specific offload module (such as esp_offload.ko). When the user installs an SA with crypto-offload, load the offload module automatically, in the same way that the protocol module is loaded (such as esp.ko) Signed-off-by: Ilan Tayari Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 4 +++- net/ipv4/esp4_offload.c | 1 + net/ipv6/esp6_offload.c | 1 + net/xfrm/xfrm_device.c | 2 +- net/xfrm/xfrm_state.c | 16 ++++++++++++---- net/xfrm/xfrm_user.c | 2 +- 6 files changed, 19 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index afb4929d7232..5a360100136c 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -43,6 +43,8 @@ MODULE_ALIAS("xfrm-mode-" __stringify(family) "-" __stringify(encap)) #define MODULE_ALIAS_XFRM_TYPE(family, proto) \ MODULE_ALIAS("xfrm-type-" __stringify(family) "-" __stringify(proto)) +#define MODULE_ALIAS_XFRM_OFFLOAD_TYPE(family, proto) \ + MODULE_ALIAS("xfrm-offload-" __stringify(family) "-" __stringify(proto)) #ifdef CONFIG_XFRM_STATISTICS #define XFRM_INC_STATS(net, field) SNMP_INC_STATS((net)->mib.xfrm_statistics, field) @@ -1558,7 +1560,7 @@ void xfrm_spd_getinfo(struct net *net, struct xfrmk_spdinfo *si); u32 xfrm_replay_seqhi(struct xfrm_state *x, __be32 net_seq); int xfrm_init_replay(struct xfrm_state *x); int xfrm_state_mtu(struct xfrm_state *x, int mtu); -int __xfrm_init_state(struct xfrm_state *x, bool init_replay); +int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload); int xfrm_init_state(struct xfrm_state *x); int xfrm_prepare_input(struct xfrm_state *x, struct sk_buff *skb); int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type); diff --git a/net/ipv4/esp4_offload.c b/net/ipv4/esp4_offload.c index 05831dea00f4..aca1c85f0795 100644 --- a/net/ipv4/esp4_offload.c +++ b/net/ipv4/esp4_offload.c @@ -305,3 +305,4 @@ module_init(esp4_offload_init); module_exit(esp4_offload_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Steffen Klassert "); +MODULE_ALIAS_XFRM_OFFLOAD_TYPE(AF_INET, XFRM_PROTO_ESP); diff --git a/net/ipv6/esp6_offload.c b/net/ipv6/esp6_offload.c index eec3add177fe..8d4e2ba9163d 100644 --- a/net/ipv6/esp6_offload.c +++ b/net/ipv6/esp6_offload.c @@ -334,3 +334,4 @@ module_init(esp6_offload_init); module_exit(esp6_offload_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Steffen Klassert "); +MODULE_ALIAS_XFRM_OFFLOAD_TYPE(AF_INET6, XFRM_PROTO_ESP); diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 5cd7a244e88d..1904127f5fb8 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -63,7 +63,7 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, xfrm_address_t *daddr; if (!x->type_offload) - return 0; + return -EINVAL; /* We don't yet support UDP encapsulation, TFC padding and ESN. */ if (x->encap || x->tfcpad || (x->props.flags & XFRM_STATE_ESN)) diff --git a/net/xfrm/xfrm_state.c b/net/xfrm/xfrm_state.c index 82cbbce69b79..a41e2ef789c0 100644 --- a/net/xfrm/xfrm_state.c +++ b/net/xfrm/xfrm_state.c @@ -296,12 +296,14 @@ int xfrm_unregister_type_offload(const struct xfrm_type_offload *type, } EXPORT_SYMBOL(xfrm_unregister_type_offload); -static const struct xfrm_type_offload *xfrm_get_type_offload(u8 proto, unsigned short family) +static const struct xfrm_type_offload * +xfrm_get_type_offload(u8 proto, unsigned short family, bool try_load) { struct xfrm_state_afinfo *afinfo; const struct xfrm_type_offload **typemap; const struct xfrm_type_offload *type; +retry: afinfo = xfrm_state_get_afinfo(family); if (unlikely(afinfo == NULL)) return NULL; @@ -311,6 +313,12 @@ static const struct xfrm_type_offload *xfrm_get_type_offload(u8 proto, unsigned if ((type && !try_module_get(type->owner))) type = NULL; + if (!type && try_load) { + request_module("xfrm-offload-%d-%d", family, proto); + try_load = 0; + goto retry; + } + rcu_read_unlock(); return type; } @@ -2165,7 +2173,7 @@ int xfrm_state_mtu(struct xfrm_state *x, int mtu) return mtu - x->props.header_len; } -int __xfrm_init_state(struct xfrm_state *x, bool init_replay) +int __xfrm_init_state(struct xfrm_state *x, bool init_replay, bool offload) { struct xfrm_state_afinfo *afinfo; struct xfrm_mode *inner_mode; @@ -2230,7 +2238,7 @@ int __xfrm_init_state(struct xfrm_state *x, bool init_replay) if (x->type == NULL) goto error; - x->type_offload = xfrm_get_type_offload(x->id.proto, family); + x->type_offload = xfrm_get_type_offload(x->id.proto, family, offload); err = x->type->init_state(x); if (err) @@ -2258,7 +2266,7 @@ EXPORT_SYMBOL(__xfrm_init_state); int xfrm_init_state(struct xfrm_state *x) { - return __xfrm_init_state(x, true); + return __xfrm_init_state(x, true, false); } EXPORT_SYMBOL(xfrm_init_state); diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index 1b539b7dcfab..ffe8d5ef09eb 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -584,7 +584,7 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, xfrm_mark_get(attrs, &x->mark); - err = __xfrm_init_state(x, false); + err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]); if (err) goto error; -- cgit v1.2.3 From 7e9e9202bccc3a8224ae10ad5d69cac8627f9c7b Mon Sep 17 00:00:00 2001 From: Ilan Tayari Date: Tue, 1 Aug 2017 12:49:09 +0300 Subject: xfrm: Clear RX SKB secpath xfrm_offload If an incoming packet undergoes XFRM crypto-offload, its secpath is filled with xfrm_offload struct denoting offload information. If the SKB is then forwarded to a device which supports crypto- offload, the stack wrongfully attempts to offload it (even though the output SA may not exist on the device) due to the leftover secpath xo. Clear the ingress xo by zeroizing secpath->olen just before delivering the decapsulated packet to the network stack. Fixes: d77e38e612a0 ("xfrm: Add an IPsec hardware offloading API") Signed-off-by: Ilan Tayari Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_input.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index 923205e279f7..f07eec59dcae 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -424,6 +424,7 @@ resume: nf_reset(skb); if (decaps) { + skb->sp->olen = 0; skb_dst_drop(skb); gro_cells_receive(&gro_cells, skb); return 0; @@ -434,6 +435,7 @@ resume: err = x->inner_mode->afinfo->transport_finish(skb, xfrm_gro || async); if (xfrm_gro) { + skb->sp->olen = 0; skb_dst_drop(skb); gro_cells_receive(&gro_cells, skb); return err; -- cgit v1.2.3 From f70f250a77313b542531e1ff7a449cd0ccd83ec0 Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Tue, 1 Aug 2017 12:49:10 +0300 Subject: net: Allow IPsec GSO for local sockets This patch allows local sockets to make use of XFRM GSO code path. Signed-off-by: Steffen Klassert Signed-off-by: Ilan Tayari --- include/net/xfrm.h | 19 +++++++++++++++++++ net/core/sock.c | 2 +- 2 files changed, 20 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 5a360100136c..18d7de34a5c3 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1858,6 +1858,20 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, struct xfrm_user_offload *xuo); bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x); +static inline bool xfrm_dst_offload_ok(struct dst_entry *dst) +{ + struct xfrm_state *x = dst->xfrm; + + if (!x || !x->type_offload) + return false; + + if (x->xso.offload_handle && (x->xso.dev == dst->path->dev) && + !dst->child->xfrm) + return true; + + return false; +} + static inline void xfrm_dev_state_delete(struct xfrm_state *x) { struct xfrm_state_offload *xso = &x->xso; @@ -1900,6 +1914,11 @@ static inline bool xfrm_dev_offload_ok(struct sk_buff *skb, struct xfrm_state *x { return false; } + +static inline bool xfrm_dst_offload_ok(struct dst_entry *dst) +{ + return false; +} #endif static inline int xfrm_mark_get(struct nlattr **attrs, struct xfrm_mark *m) diff --git a/net/core/sock.c b/net/core/sock.c index 742f68c9c84a..564f835f408a 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1757,7 +1757,7 @@ void sk_setup_caps(struct sock *sk, struct dst_entry *dst) sk->sk_route_caps |= NETIF_F_GSO_SOFTWARE; sk->sk_route_caps &= ~sk->sk_route_nocaps; if (sk_can_gso(sk)) { - if (dst->header_len) { + if (dst->header_len && !xfrm_dst_offload_ok(dst)) { sk->sk_route_caps &= ~NETIF_F_GSO_MASK; } else { sk->sk_route_caps |= NETIF_F_SG | NETIF_F_HW_CSUM; -- cgit v1.2.3 From 5b9ccdcb98429b7e5c814772e3d9448c76441d87 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Sat, 29 Jul 2017 20:31:00 +0900 Subject: netfilter: xtables: Remove unused variable in compat_copy_entry_from_user() The target variable is not used in the compat_copy_entry_from_user(). So It can be removed. Signed-off-by: Taehee Yoo Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/arp_tables.c | 2 -- net/ipv4/netfilter/ip_tables.c | 2 -- 2 files changed, 4 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/arp_tables.c b/net/ipv4/netfilter/arp_tables.c index 0bc3c3d73e61..cf520d30cb94 100644 --- a/net/ipv4/netfilter/arp_tables.c +++ b/net/ipv4/netfilter/arp_tables.c @@ -1117,7 +1117,6 @@ compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr, struct xt_table_info *newinfo, unsigned char *base) { struct xt_entry_target *t; - struct xt_target *target; struct arpt_entry *de; unsigned int origsize; int h; @@ -1132,7 +1131,6 @@ compat_copy_entry_from_user(struct compat_arpt_entry *e, void **dstptr, de->target_offset = e->target_offset - (origsize - *size); t = compat_arpt_get_target(e); - target = t->u.kernel.target; xt_compat_target_from_user(t, dstptr, size); de->next_offset = e->next_offset - (origsize - *size); diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index 2a55a40211cb..f47e8dad5e95 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -1355,7 +1355,6 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr, struct xt_table_info *newinfo, unsigned char *base) { struct xt_entry_target *t; - struct xt_target *target; struct ipt_entry *de; unsigned int origsize; int h; @@ -1374,7 +1373,6 @@ compat_copy_entry_from_user(struct compat_ipt_entry *e, void **dstptr, de->target_offset = e->target_offset - (origsize - *size); t = compat_ipt_get_target(e); - target = t->u.kernel.target; xt_compat_target_from_user(t, dstptr, size); de->next_offset = e->next_offset - (origsize - *size); -- cgit v1.2.3 From 2a04aabf5c96c9e25df488949b21223bcc623815 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 1 Aug 2017 12:25:01 +0200 Subject: netfilter: constify nf_conntrack_l3/4proto parameters When a nf_conntrack_l3/4proto parameter is not on the left hand side of an assignment, its address is not taken, and it is not passed to a function that may modify its fields, then it can be declared as const. This change is useful from a documentation point of view, and can possibly facilitate making some nf_conntrack_l3/4proto structures const subsequently. Done with the help of Coccinelle. Signed-off-by: Julia Lawall Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 14 +++++++------- include/net/netfilter/nf_conntrack_timeout.h | 2 +- net/netfilter/nf_conntrack_core.c | 8 ++++---- net/netfilter/nf_conntrack_netlink.c | 6 +++--- net/netfilter/nf_conntrack_proto.c | 24 ++++++++++++------------ net/netfilter/nfnetlink_cttimeout.c | 5 +++-- 6 files changed, 30 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 7032e044bbe2..b6e27cafb1d9 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -125,23 +125,23 @@ struct nf_conntrack_l4proto *__nf_ct_l4proto_find(u_int16_t l3proto, struct nf_conntrack_l4proto *nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto); -void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p); +void nf_ct_l4proto_put(const struct nf_conntrack_l4proto *p); /* Protocol pernet registration. */ int nf_ct_l4proto_pernet_register_one(struct net *net, - struct nf_conntrack_l4proto *proto); + const struct nf_conntrack_l4proto *proto); void nf_ct_l4proto_pernet_unregister_one(struct net *net, - struct nf_conntrack_l4proto *proto); + const struct nf_conntrack_l4proto *proto); int nf_ct_l4proto_pernet_register(struct net *net, - struct nf_conntrack_l4proto *proto[], + struct nf_conntrack_l4proto *const proto[], unsigned int num_proto); void nf_ct_l4proto_pernet_unregister(struct net *net, - struct nf_conntrack_l4proto *proto[], - unsigned int num_proto); + struct nf_conntrack_l4proto *const proto[], + unsigned int num_proto); /* Protocol global registration. */ int nf_ct_l4proto_register_one(struct nf_conntrack_l4proto *proto); -void nf_ct_l4proto_unregister_one(struct nf_conntrack_l4proto *proto); +void nf_ct_l4proto_unregister_one(const struct nf_conntrack_l4proto *proto); int nf_ct_l4proto_register(struct nf_conntrack_l4proto *proto[], unsigned int num_proto); void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *proto[], diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index d40b89355fdd..b222957062b5 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -68,7 +68,7 @@ struct nf_conn_timeout *nf_ct_timeout_ext_add(struct nf_conn *ct, static inline unsigned int * nf_ct_timeout_lookup(struct net *net, struct nf_conn *ct, - struct nf_conntrack_l4proto *l4proto) + const struct nf_conntrack_l4proto *l4proto) { #ifdef CONFIG_NF_CONNTRACK_TIMEOUT struct nf_conn_timeout *timeout_ext; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 2bc499186186..f2f00eaf217d 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1176,8 +1176,8 @@ EXPORT_SYMBOL_GPL(nf_conntrack_free); static noinline struct nf_conntrack_tuple_hash * init_conntrack(struct net *net, struct nf_conn *tmpl, const struct nf_conntrack_tuple *tuple, - struct nf_conntrack_l3proto *l3proto, - struct nf_conntrack_l4proto *l4proto, + const struct nf_conntrack_l3proto *l3proto, + const struct nf_conntrack_l4proto *l4proto, struct sk_buff *skb, unsigned int dataoff, u32 hash) { @@ -1288,8 +1288,8 @@ resolve_normal_ct(struct net *net, struct nf_conn *tmpl, unsigned int dataoff, u_int16_t l3num, u_int8_t protonum, - struct nf_conntrack_l3proto *l3proto, - struct nf_conntrack_l4proto *l4proto) + const struct nf_conntrack_l3proto *l3proto, + const struct nf_conntrack_l4proto *l4proto) { const struct nf_conntrack_zone *zone; struct nf_conntrack_tuple tuple; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index 4922c8aefb2a..f4ca48817f66 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -61,8 +61,8 @@ MODULE_LICENSE("GPL"); static char __initdata version[] = "0.93"; static int ctnetlink_dump_tuples_proto(struct sk_buff *skb, - const struct nf_conntrack_tuple *tuple, - struct nf_conntrack_l4proto *l4proto) + const struct nf_conntrack_tuple *tuple, + const struct nf_conntrack_l4proto *l4proto) { int ret = 0; struct nlattr *nest_parms; @@ -86,7 +86,7 @@ nla_put_failure: static int ctnetlink_dump_tuples_ip(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple, - struct nf_conntrack_l3proto *l3proto) + const struct nf_conntrack_l3proto *l3proto) { int ret = 0; struct nlattr *nest_parms; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 7c89dade6fd3..27810cf816a6 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -188,7 +188,7 @@ nf_ct_l4proto_find_get(u_int16_t l3num, u_int8_t l4num) } EXPORT_SYMBOL_GPL(nf_ct_l4proto_find_get); -void nf_ct_l4proto_put(struct nf_conntrack_l4proto *p) +void nf_ct_l4proto_put(const struct nf_conntrack_l4proto *p) { module_put(p->me); } @@ -257,7 +257,7 @@ void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto) EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister); static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, - struct nf_conntrack_l4proto *l4proto) + const struct nf_conntrack_l4proto *l4proto) { if (l4proto->get_net_proto) { /* statically built-in protocols use static per-net */ @@ -272,7 +272,7 @@ static struct nf_proto_net *nf_ct_l4proto_net(struct net *net, static int nf_ct_l4proto_register_sysctl(struct net *net, struct nf_proto_net *pn, - struct nf_conntrack_l4proto *l4proto) + const struct nf_conntrack_l4proto *l4proto) { int err = 0; @@ -295,8 +295,8 @@ int nf_ct_l4proto_register_sysctl(struct net *net, static void nf_ct_l4proto_unregister_sysctl(struct net *net, - struct nf_proto_net *pn, - struct nf_conntrack_l4proto *l4proto) + struct nf_proto_net *pn, + const struct nf_conntrack_l4proto *l4proto) { #ifdef CONFIG_SYSCTL if (pn->ctl_table_header != NULL) @@ -366,7 +366,7 @@ out_unlock: EXPORT_SYMBOL_GPL(nf_ct_l4proto_register_one); int nf_ct_l4proto_pernet_register_one(struct net *net, - struct nf_conntrack_l4proto *l4proto) + const struct nf_conntrack_l4proto *l4proto) { int ret = 0; struct nf_proto_net *pn = NULL; @@ -391,7 +391,7 @@ out: } EXPORT_SYMBOL_GPL(nf_ct_l4proto_pernet_register_one); -static void __nf_ct_l4proto_unregister_one(struct nf_conntrack_l4proto *l4proto) +static void __nf_ct_l4proto_unregister_one(const struct nf_conntrack_l4proto *l4proto) { BUG_ON(l4proto->l3proto >= ARRAY_SIZE(nf_ct_protos)); @@ -404,7 +404,7 @@ static void __nf_ct_l4proto_unregister_one(struct nf_conntrack_l4proto *l4proto) &nf_conntrack_l4proto_generic); } -void nf_ct_l4proto_unregister_one(struct nf_conntrack_l4proto *l4proto) +void nf_ct_l4proto_unregister_one(const struct nf_conntrack_l4proto *l4proto) { mutex_lock(&nf_ct_proto_mutex); __nf_ct_l4proto_unregister_one(l4proto); @@ -415,7 +415,7 @@ void nf_ct_l4proto_unregister_one(struct nf_conntrack_l4proto *l4proto) EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister_one); void nf_ct_l4proto_pernet_unregister_one(struct net *net, - struct nf_conntrack_l4proto *l4proto) + const struct nf_conntrack_l4proto *l4proto) { struct nf_proto_net *pn = nf_ct_l4proto_net(net, l4proto); @@ -449,7 +449,7 @@ int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto[], EXPORT_SYMBOL_GPL(nf_ct_l4proto_register); int nf_ct_l4proto_pernet_register(struct net *net, - struct nf_conntrack_l4proto *l4proto[], + struct nf_conntrack_l4proto *const l4proto[], unsigned int num_proto) { int ret = -EINVAL; @@ -485,8 +485,8 @@ void nf_ct_l4proto_unregister(struct nf_conntrack_l4proto *l4proto[], EXPORT_SYMBOL_GPL(nf_ct_l4proto_unregister); void nf_ct_l4proto_pernet_unregister(struct net *net, - struct nf_conntrack_l4proto *l4proto[], - unsigned int num_proto) + struct nf_conntrack_l4proto *const l4proto[], + unsigned int num_proto) { while (num_proto-- != 0) nf_ct_l4proto_pernet_unregister_one(net, l4proto[num_proto]); diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index f4fb6d4dd0b9..fcabccc99f0d 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -47,7 +47,8 @@ static const struct nla_policy cttimeout_nla_policy[CTA_TIMEOUT_MAX+1] = { }; static int -ctnl_timeout_parse_policy(void *timeouts, struct nf_conntrack_l4proto *l4proto, +ctnl_timeout_parse_policy(void *timeouts, + const struct nf_conntrack_l4proto *l4proto, struct net *net, const struct nlattr *attr) { int ret = 0; @@ -401,7 +402,7 @@ err: static int cttimeout_default_fill_info(struct net *net, struct sk_buff *skb, u32 portid, u32 seq, u32 type, int event, - struct nf_conntrack_l4proto *l4proto) + const struct nf_conntrack_l4proto *l4proto) { struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; -- cgit v1.2.3 From 549d2d41c1a448380872858302ee91be5a3ed499 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 1 Aug 2017 12:48:03 +0200 Subject: netfilter: constify nf_loginfo structures The nf_loginfo structures are only passed as the seventh argument to nf_log_trace, which is declared as const or stored in a local const variable. Thus the nf_loginfo structures themselves can be const. Done with the help of Coccinelle. // @r disable optional_qualifier@ identifier i; position p; @@ static struct nf_loginfo i@p = { ... }; @ok1@ identifier r.i; expression list[6] es; position p; @@ nf_log_trace(es,&i@p,...) @ok2@ identifier r.i; const struct nf_loginfo *e; position p; @@ e = &i@p @bad@ position p != {r.p,ok1.p,ok2.p}; identifier r.i; struct nf_loginfo e; @@ e@i@p @depends on !bad disable optional_qualifier@ identifier r.i; @@ static +const struct nf_loginfo i = { ... }; // Signed-off-by: Julia Lawall Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/ip_tables.c | 2 +- net/ipv4/netfilter/nf_log_arp.c | 2 +- net/ipv4/netfilter/nf_log_ipv4.c | 2 +- net/ipv6/netfilter/ip6_tables.c | 2 +- net/ipv6/netfilter/nf_log_ipv6.c | 2 +- net/netfilter/nf_tables_core.c | 2 +- net/netfilter/nfnetlink_log.c | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index f47e8dad5e95..2aea896f5708 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -151,7 +151,7 @@ static const char *const comments[] = { [NF_IP_TRACE_COMMENT_POLICY] = "policy", }; -static struct nf_loginfo trace_loginfo = { +static const struct nf_loginfo trace_loginfo = { .type = NF_LOG_TYPE_LOG, .u = { .log = { diff --git a/net/ipv4/netfilter/nf_log_arp.c b/net/ipv4/netfilter/nf_log_arp.c index 2f3895ddc275..df5c2a2061a4 100644 --- a/net/ipv4/netfilter/nf_log_arp.c +++ b/net/ipv4/netfilter/nf_log_arp.c @@ -25,7 +25,7 @@ #include #include -static struct nf_loginfo default_loginfo = { +static const struct nf_loginfo default_loginfo = { .type = NF_LOG_TYPE_LOG, .u = { .log = { diff --git a/net/ipv4/netfilter/nf_log_ipv4.c b/net/ipv4/netfilter/nf_log_ipv4.c index c83a9963269b..4388de0e5380 100644 --- a/net/ipv4/netfilter/nf_log_ipv4.c +++ b/net/ipv4/netfilter/nf_log_ipv4.c @@ -24,7 +24,7 @@ #include #include -static struct nf_loginfo default_loginfo = { +static const struct nf_loginfo default_loginfo = { .type = NF_LOG_TYPE_LOG, .u = { .log = { diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 1f90644056ac..9f6644958e5e 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -176,7 +176,7 @@ static const char *const comments[] = { [NF_IP6_TRACE_COMMENT_POLICY] = "policy", }; -static struct nf_loginfo trace_loginfo = { +static const struct nf_loginfo trace_loginfo = { .type = NF_LOG_TYPE_LOG, .u = { .log = { diff --git a/net/ipv6/netfilter/nf_log_ipv6.c b/net/ipv6/netfilter/nf_log_ipv6.c index 97c724224da7..b397a8fe88b9 100644 --- a/net/ipv6/netfilter/nf_log_ipv6.c +++ b/net/ipv6/netfilter/nf_log_ipv6.c @@ -25,7 +25,7 @@ #include #include -static struct nf_loginfo default_loginfo = { +static const struct nf_loginfo default_loginfo = { .type = NF_LOG_TYPE_LOG, .u = { .log = { diff --git a/net/netfilter/nf_tables_core.c b/net/netfilter/nf_tables_core.c index c5bab08b0d73..dfd0bf3810d2 100644 --- a/net/netfilter/nf_tables_core.c +++ b/net/netfilter/nf_tables_core.c @@ -29,7 +29,7 @@ static const char *const comments[__NFT_TRACETYPE_MAX] = { [NFT_TRACETYPE_RULE] = "rule", }; -static struct nf_loginfo trace_loginfo = { +static const struct nf_loginfo trace_loginfo = { .type = NF_LOG_TYPE_LOG, .u = { .log = { diff --git a/net/netfilter/nfnetlink_log.c b/net/netfilter/nfnetlink_log.c index c684ba95dbb4..cad6498f10b0 100644 --- a/net/netfilter/nfnetlink_log.c +++ b/net/netfilter/nfnetlink_log.c @@ -606,7 +606,7 @@ nla_put_failure: return -1; } -static struct nf_loginfo default_loginfo = { +static const struct nf_loginfo default_loginfo = { .type = NF_LOG_TYPE_ULOG, .u = { .ulog = { -- cgit v1.2.3 From 5357f0bd4edf0b351f2cd98a57386b0324734f67 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 1 Aug 2017 07:02:44 -0700 Subject: tcp: tcp_data_queue() cleanup Commit c13ee2a4f03f ("tcp: reindent two spots after prequeue removal") removed code in tcp_data_queue(). We can go a little farther, removing an always true test, and removing initializers for fragstolen and eaten variables. Signed-off-by: Eric Dumazet Cc: Florian Westphal Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index af0a98d54b62..df670d7ed98d 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4564,8 +4564,8 @@ err: static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) { struct tcp_sock *tp = tcp_sk(sk); - bool fragstolen = false; - int eaten = -1; + bool fragstolen; + int eaten; if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) { __kfree_skb(skb); @@ -4588,12 +4588,11 @@ static void tcp_data_queue(struct sock *sk, struct sk_buff *skb) /* Ok. In sequence. In window. */ queue_and_out: - if (eaten < 0) { - if (skb_queue_len(&sk->sk_receive_queue) == 0) - sk_forced_mem_schedule(sk, skb->truesize); - else if (tcp_try_rmem_schedule(sk, skb, skb->truesize)) - goto drop; - } + if (skb_queue_len(&sk->sk_receive_queue) == 0) + sk_forced_mem_schedule(sk, skb->truesize); + else if (tcp_try_rmem_schedule(sk, skb, skb->truesize)) + goto drop; + eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen); tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq); if (skb->len) -- cgit v1.2.3 From b2f9d432deebab5096aad5942c2f2b1ec2865f5a Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Tue, 1 Aug 2017 13:18:09 -0700 Subject: flow_dissector: remove unused functions They are introduced by commit f70ea018da06 ("net: Add functions to get skb->hash based on flow structures") but never gets used in tree. Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/linux/skbuff.h | 16 ---------------- net/core/flow_dissector.c | 45 --------------------------------------------- 2 files changed, 61 deletions(-) (limited to 'net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 6f9f1b2715ec..be76082f48aa 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1158,8 +1158,6 @@ static inline __u32 skb_get_hash(struct sk_buff *skb) return skb->hash; } -__u32 __skb_get_hash_flowi6(struct sk_buff *skb, const struct flowi6 *fl6); - static inline __u32 skb_get_hash_flowi6(struct sk_buff *skb, const struct flowi6 *fl6) { if (!skb->l4_hash && !skb->sw_hash) { @@ -1172,20 +1170,6 @@ static inline __u32 skb_get_hash_flowi6(struct sk_buff *skb, const struct flowi6 return skb->hash; } -__u32 __skb_get_hash_flowi4(struct sk_buff *skb, const struct flowi4 *fl); - -static inline __u32 skb_get_hash_flowi4(struct sk_buff *skb, const struct flowi4 *fl4) -{ - if (!skb->l4_hash && !skb->sw_hash) { - struct flow_keys keys; - __u32 hash = __get_hash_from_flowi4(fl4, &keys); - - __skb_set_sw_hash(skb, hash, flow_keys_have_l4(&keys)); - } - - return skb->hash; -} - __u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb); static inline __u32 skb_get_hash_raw(const struct sk_buff *skb) diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index fc5fc4594c90..0cc672aba1f0 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -998,51 +998,6 @@ __u32 skb_get_hash_perturb(const struct sk_buff *skb, u32 perturb) } EXPORT_SYMBOL(skb_get_hash_perturb); -__u32 __skb_get_hash_flowi6(struct sk_buff *skb, const struct flowi6 *fl6) -{ - struct flow_keys keys; - - memset(&keys, 0, sizeof(keys)); - - memcpy(&keys.addrs.v6addrs.src, &fl6->saddr, - sizeof(keys.addrs.v6addrs.src)); - memcpy(&keys.addrs.v6addrs.dst, &fl6->daddr, - sizeof(keys.addrs.v6addrs.dst)); - keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; - keys.ports.src = fl6->fl6_sport; - keys.ports.dst = fl6->fl6_dport; - keys.keyid.keyid = fl6->fl6_gre_key; - keys.tags.flow_label = (__force u32)fl6->flowlabel; - keys.basic.ip_proto = fl6->flowi6_proto; - - __skb_set_sw_hash(skb, flow_hash_from_keys(&keys), - flow_keys_have_l4(&keys)); - - return skb->hash; -} -EXPORT_SYMBOL(__skb_get_hash_flowi6); - -__u32 __skb_get_hash_flowi4(struct sk_buff *skb, const struct flowi4 *fl4) -{ - struct flow_keys keys; - - memset(&keys, 0, sizeof(keys)); - - keys.addrs.v4addrs.src = fl4->saddr; - keys.addrs.v4addrs.dst = fl4->daddr; - keys.control.addr_type = FLOW_DISSECTOR_KEY_IPV4_ADDRS; - keys.ports.src = fl4->fl4_sport; - keys.ports.dst = fl4->fl4_dport; - keys.keyid.keyid = fl4->fl4_gre_key; - keys.basic.ip_proto = fl4->flowi4_proto; - - __skb_set_sw_hash(skb, flow_hash_from_keys(&keys), - flow_keys_have_l4(&keys)); - - return skb->hash; -} -EXPORT_SYMBOL(__skb_get_hash_flowi4); - u32 __skb_get_poff(const struct sk_buff *skb, void *data, const struct flow_keys *keys, int hlen) { -- cgit v1.2.3 From f613ed665bb3ec49edc4907bd8799e3a2de47df5 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 1 Aug 2017 15:00:36 -0700 Subject: net: dsa: Add support for 64-bit statistics DSA slave network devices maintain a pair of bytes and packets counters for each directions, but these are not 64-bit capable. Re-use pcpu_sw_netstats which contains exactly what we need for that purpose and update the code path to report 64-bit capable statistics. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa.c | 8 ++++++-- net/dsa/dsa_priv.h | 2 ++ net/dsa/slave.c | 38 +++++++++++++++++++++++++++++++------- 3 files changed, 39 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index a55e2e4087a4..0ba842c08dd3 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -190,6 +190,7 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, { struct dsa_switch_tree *dst = dev->dsa_ptr; struct sk_buff *nskb = NULL; + struct dsa_slave_priv *p; if (unlikely(dst == NULL)) { kfree_skb(skb); @@ -207,12 +208,15 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, } skb = nskb; + p = netdev_priv(skb->dev); skb_push(skb, ETH_HLEN); skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, skb->dev); - skb->dev->stats.rx_packets++; - skb->dev->stats.rx_bytes += skb->len; + u64_stats_update_begin(&p->stats64.syncp); + p->stats64.rx_packets++; + p->stats64.rx_bytes += skb->len; + u64_stats_update_end(&p->stats64.syncp); netif_receive_skb(skb); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 55982cc39b24..7aa0656296c2 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -77,6 +77,8 @@ struct dsa_slave_priv { struct sk_buff * (*xmit)(struct sk_buff *skb, struct net_device *dev); + struct pcpu_sw_netstats stats64; + /* DSA port data, such as switch, port index, etc. */ struct dsa_port *dp; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index cc4bad3dadb4..e196562035b1 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -354,8 +354,10 @@ static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) struct dsa_slave_priv *p = netdev_priv(dev); struct sk_buff *nskb; - dev->stats.tx_packets++; - dev->stats.tx_bytes += skb->len; + u64_stats_update_begin(&p->stats64.syncp); + p->stats64.tx_packets++; + p->stats64.tx_bytes += skb->len; + u64_stats_update_end(&p->stats64.syncp); /* Transmit function may have to reallocate the original SKB, * in which case it must have freed it. Only free it here on error. @@ -594,11 +596,15 @@ static void dsa_slave_get_ethtool_stats(struct net_device *dev, { struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_switch *ds = p->dp->ds; - - data[0] = dev->stats.tx_packets; - data[1] = dev->stats.tx_bytes; - data[2] = dev->stats.rx_packets; - data[3] = dev->stats.rx_bytes; + unsigned int start; + + do { + start = u64_stats_fetch_begin_irq(&p->stats64.syncp); + data[0] = p->stats64.tx_packets; + data[1] = p->stats64.tx_bytes; + data[2] = p->stats64.rx_packets; + data[3] = p->stats64.rx_bytes; + } while (u64_stats_fetch_retry_irq(&p->stats64.syncp, start)); if (ds->ops->get_ethtool_stats) ds->ops->get_ethtool_stats(ds, p->dp->index, data + 4); } @@ -869,6 +875,22 @@ static int dsa_slave_setup_tc(struct net_device *dev, u32 handle, } } +static void dsa_slave_get_stats64(struct net_device *dev, + struct rtnl_link_stats64 *stats) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + unsigned int start; + + netdev_stats_to_stats64(stats, &dev->stats); + do { + start = u64_stats_fetch_begin_irq(&p->stats64.syncp); + stats->tx_packets = p->stats64.tx_packets; + stats->tx_bytes = p->stats64.tx_bytes; + stats->rx_packets = p->stats64.rx_packets; + stats->rx_bytes = p->stats64.rx_bytes; + } while (u64_stats_fetch_retry_irq(&p->stats64.syncp, start)); +} + void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops) { ops->get_sset_count = dsa_cpu_port_get_sset_count; @@ -944,6 +966,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_bridge_dellink = switchdev_port_bridge_dellink, .ndo_get_phys_port_name = dsa_slave_get_phys_port_name, .ndo_setup_tc = dsa_slave_setup_tc, + .ndo_get_stats64 = dsa_slave_get_stats64, }; static const struct switchdev_ops dsa_slave_switchdev_ops = { @@ -1179,6 +1202,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, slave_dev->vlan_features = master->vlan_features; p = netdev_priv(slave_dev); + u64_stats_init(&p->stats64.syncp); p->dp = &ds->ports[port]; INIT_LIST_HEAD(&p->mall_tc_list); p->xmit = dst->tag_ops->xmit; -- cgit v1.2.3 From 475abbf1ef67fdf35eed0720adcf59ed25ad924e Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Wed, 2 Aug 2017 09:56:01 +0200 Subject: ipv4: fib: Set offload indication according to nexthop flags We're going to have capable drivers indicate route offload using the nexthop flags, but for non-multipath routes these flags aren't dumped to user space. Instead, set the offload indication in the route message flags. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Acked-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/fib_semantics.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index b8d18171cca3..f62dc2463280 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -1342,6 +1342,8 @@ int fib_dump_info(struct sk_buff *skb, u32 portid, u32 seq, int event, IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev)) rtm->rtm_flags |= RTNH_F_DEAD; } + if (fi->fib_nh->nh_flags & RTNH_F_OFFLOAD) + rtm->rtm_flags |= RTNH_F_OFFLOAD; #ifdef CONFIG_IP_ROUTE_CLASSID if (fi->fib_nh[0].nh_tclassid && nla_put_u32(skb, RTA_FLOW, fi->fib_nh[0].nh_tclassid)) -- cgit v1.2.3 From e9e6c2329a518d43a4734b26349bb19d8dfd7e6b Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 2 Aug 2017 11:35:00 +0200 Subject: X25: constify null_x25_address null_x25_address is only used to access the string it contains, so it can be const. Signed-off-by: Julia Lawall Signed-off-by: David S. Miller --- net/x25/af_x25.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/x25/af_x25.c b/net/x25/af_x25.c index 5a1a98df3499..ac095936552d 100644 --- a/net/x25/af_x25.c +++ b/net/x25/af_x25.c @@ -74,7 +74,7 @@ DEFINE_RWLOCK(x25_list_lock); static const struct proto_ops x25_proto_ops; -static struct x25_address null_x25_address = {" "}; +static const struct x25_address null_x25_address = {" "}; #ifdef CONFIG_COMPAT struct compat_x25_subscrip_struct { -- cgit v1.2.3 From eb48d682814c8b0e44111c45d7c554e1989fd10d Mon Sep 17 00:00:00 2001 From: William Tu Date: Wed, 2 Aug 2017 08:43:52 -0700 Subject: bpf: fix the printing of ifindex Save the ifindex before it gets zeroed so the invalid ifindex can be printed out. Signed-off-by: William Tu Acked-by: Daniel Borkmann Acked-by: John Fastabend Signed-off-by: David S. Miller --- net/core/filter.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/core/filter.c b/net/core/filter.c index 7e9708653c6f..78d00933dbe7 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2509,15 +2509,16 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, { struct redirect_info *ri = this_cpu_ptr(&redirect_info); struct net_device *fwd; + u32 index = ri->ifindex; if (ri->map) return xdp_do_redirect_map(dev, xdp, xdp_prog); - fwd = dev_get_by_index_rcu(dev_net(dev), ri->ifindex); + fwd = dev_get_by_index_rcu(dev_net(dev), index); ri->ifindex = 0; ri->map = NULL; if (unlikely(!fwd)) { - bpf_warn_invalid_xdp_redirect(ri->ifindex); + bpf_warn_invalid_xdp_redirect(index); return -EINVAL; } @@ -2531,11 +2532,12 @@ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); unsigned int len; + u32 index = ri->ifindex; - dev = dev_get_by_index_rcu(dev_net(dev), ri->ifindex); + dev = dev_get_by_index_rcu(dev_net(dev), index); ri->ifindex = 0; if (unlikely(!dev)) { - bpf_warn_invalid_xdp_redirect(ri->ifindex); + bpf_warn_invalid_xdp_redirect(index); goto err; } -- cgit v1.2.3 From 93b1b31f87ae7c0795afc7586d28f3ed4f859d20 Mon Sep 17 00:00:00 2001 From: Tonghao Zhang Date: Wed, 2 Aug 2017 09:34:15 -0700 Subject: ipv4: Introduce ipip_offload_init helper function. It's convenient to init ipip offload. We will check the return value, and print KERN_CRIT info on failure. Signed-off-by: Tonghao Zhang Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index f0103ffe1cdb..683ffafb6ff9 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1765,6 +1765,11 @@ static const struct net_offload ipip_offload = { }, }; +static int __init ipip_offload_init(void) +{ + return inet_add_offload(&ipip_offload, IPPROTO_IPIP); +} + static int __init ipv4_offload_init(void) { /* @@ -1774,9 +1779,10 @@ static int __init ipv4_offload_init(void) pr_crit("%s: Cannot add UDP protocol offload\n", __func__); if (tcpv4_offload_init() < 0) pr_crit("%s: Cannot add TCP protocol offload\n", __func__); + if (ipip_offload_init() < 0) + pr_crit("%s: Cannot add IPIP protocol offload\n", __func__); dev_add_offload(&ip_packet_offload); - inet_add_offload(&ipip_offload, IPPROTO_IPIP); return 0; } -- cgit v1.2.3 From 840df162b3eb3ec02e2613411fad1285a0017c13 Mon Sep 17 00:00:00 2001 From: Sowmini Varadhan Date: Wed, 2 Aug 2017 10:34:31 -0700 Subject: rds: reduce memory footprint for RDS when transport is RDMA RDS over IB does not use multipath RDS, so the array of additional rds_conn_path structures is always superfluous in this case. Reduce the memory footprint of the rds module by making this a dynamic allocation predicated on whether the transport is mp_capable. Signed-off-by: Sowmini Varadhan Acked-by: Santosh Shilimkar Tested-by: Efrain Galaviz Signed-off-by: David S. Miller --- net/rds/connection.c | 34 +++++++++++++++++++++++++--------- net/rds/rds.h | 2 +- 2 files changed, 26 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/rds/connection.c b/net/rds/connection.c index 005bca68aa94..7ee2d5d68b78 100644 --- a/net/rds/connection.c +++ b/net/rds/connection.c @@ -151,6 +151,7 @@ static struct rds_connection *__rds_conn_create(struct net *net, struct rds_transport *loop_trans; unsigned long flags; int ret, i; + int npaths = (trans->t_mp_capable ? RDS_MPATH_WORKERS : 1); rcu_read_lock(); conn = rds_conn_lookup(net, head, laddr, faddr, trans); @@ -172,6 +173,12 @@ static struct rds_connection *__rds_conn_create(struct net *net, conn = ERR_PTR(-ENOMEM); goto out; } + conn->c_path = kcalloc(npaths, sizeof(struct rds_conn_path), gfp); + if (!conn->c_path) { + kmem_cache_free(rds_conn_slab, conn); + conn = ERR_PTR(-ENOMEM); + goto out; + } INIT_HLIST_NODE(&conn->c_hash_node); conn->c_laddr = laddr; @@ -181,6 +188,7 @@ static struct rds_connection *__rds_conn_create(struct net *net, ret = rds_cong_get_maps(conn); if (ret) { + kfree(conn->c_path); kmem_cache_free(rds_conn_slab, conn); conn = ERR_PTR(ret); goto out; @@ -207,13 +215,14 @@ static struct rds_connection *__rds_conn_create(struct net *net, conn->c_trans = trans; init_waitqueue_head(&conn->c_hs_waitq); - for (i = 0; i < RDS_MPATH_WORKERS; i++) { + for (i = 0; i < npaths; i++) { __rds_conn_path_init(conn, &conn->c_path[i], is_outgoing); conn->c_path[i].cp_index = i; } ret = trans->conn_alloc(conn, gfp); if (ret) { + kfree(conn->c_path); kmem_cache_free(rds_conn_slab, conn); conn = ERR_PTR(ret); goto out; @@ -236,6 +245,7 @@ static struct rds_connection *__rds_conn_create(struct net *net, /* Creating passive conn */ if (parent->c_passive) { trans->conn_free(conn->c_path[0].cp_transport_data); + kfree(conn->c_path); kmem_cache_free(rds_conn_slab, conn); conn = parent->c_passive; } else { @@ -252,7 +262,7 @@ static struct rds_connection *__rds_conn_create(struct net *net, struct rds_conn_path *cp; int i; - for (i = 0; i < RDS_MPATH_WORKERS; i++) { + for (i = 0; i < npaths; i++) { cp = &conn->c_path[i]; /* The ->conn_alloc invocation may have * allocated resource for all paths, so all @@ -261,6 +271,7 @@ static struct rds_connection *__rds_conn_create(struct net *net, if (cp->cp_transport_data) trans->conn_free(cp->cp_transport_data); } + kfree(conn->c_path); kmem_cache_free(rds_conn_slab, conn); conn = found; } else { @@ -407,6 +418,7 @@ void rds_conn_destroy(struct rds_connection *conn) unsigned long flags; int i; struct rds_conn_path *cp; + int npaths = (conn->c_trans->t_mp_capable ? RDS_MPATH_WORKERS : 1); rdsdebug("freeing conn %p for %pI4 -> " "%pI4\n", conn, &conn->c_laddr, @@ -420,7 +432,7 @@ void rds_conn_destroy(struct rds_connection *conn) synchronize_rcu(); /* shut the connection down */ - for (i = 0; i < RDS_MPATH_WORKERS; i++) { + for (i = 0; i < npaths; i++) { cp = &conn->c_path[i]; rds_conn_path_destroy(cp); BUG_ON(!list_empty(&cp->cp_retrans)); @@ -434,6 +446,7 @@ void rds_conn_destroy(struct rds_connection *conn) rds_cong_remove_conn(conn); put_net(conn->c_net); + kfree(conn->c_path); kmem_cache_free(rds_conn_slab, conn); spin_lock_irqsave(&rds_conn_lock, flags); @@ -464,8 +477,12 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len, i++, head++) { hlist_for_each_entry_rcu(conn, head, c_hash_node) { struct rds_conn_path *cp; + int npaths; + + npaths = (conn->c_trans->t_mp_capable ? + RDS_MPATH_WORKERS : 1); - for (j = 0; j < RDS_MPATH_WORKERS; j++) { + for (j = 0; j < npaths; j++) { cp = &conn->c_path[j]; if (want_send) list = &cp->cp_send_queue; @@ -486,8 +503,6 @@ static void rds_conn_message_info(struct socket *sock, unsigned int len, } spin_unlock_irqrestore(&cp->cp_lock, flags); - if (!conn->c_trans->t_mp_capable) - break; } } } @@ -571,15 +586,16 @@ static void rds_walk_conn_path_info(struct socket *sock, unsigned int len, i++, head++) { hlist_for_each_entry_rcu(conn, head, c_hash_node) { struct rds_conn_path *cp; + int npaths; - for (j = 0; j < RDS_MPATH_WORKERS; j++) { + npaths = (conn->c_trans->t_mp_capable ? + RDS_MPATH_WORKERS : 1); + for (j = 0; j < npaths; j++) { cp = &conn->c_path[j]; /* XXX no cp_lock usage.. */ if (!visitor(cp, buffer)) continue; - if (!conn->c_trans->t_mp_capable) - break; } /* We copy as much as we can fit in the buffer, diff --git a/net/rds/rds.h b/net/rds/rds.h index 3382695bf46c..2e0315b159cb 100644 --- a/net/rds/rds.h +++ b/net/rds/rds.h @@ -154,7 +154,7 @@ struct rds_connection { struct list_head c_map_item; unsigned long c_map_queued; - struct rds_conn_path c_path[RDS_MPATH_WORKERS]; + struct rds_conn_path *c_path; wait_queue_head_t c_hs_waitq; /* handshake waitq */ u32 c_my_gen_num; -- cgit v1.2.3 From d06c3583c2cf6c27a56ee05aa128a950e31b224a Mon Sep 17 00:00:00 2001 From: Neal Cardwell Date: Wed, 2 Aug 2017 15:59:58 -0400 Subject: tcp: remove extra POLL_OUT added for finished active connect() Commit 45f119bf936b ("tcp: remove header prediction") introduced a minor bug: the sk_state_change() and sk_wake_async() notifications for a completed active connection happen twice: once in this new spot inside tcp_finish_connect() and once in the existing code in tcp_rcv_synsent_state_process() immediately after it calls tcp_finish_connect(). This commit remoes the duplicate POLL_OUT notifications. Fixes: 45f119bf936b ("tcp: remove header prediction") Signed-off-by: Neal Cardwell Cc: Florian Westphal Cc: Eric Dumazet Cc: Yuchung Cheng Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index df670d7ed98d..99cdf4ccabb8 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -5342,11 +5342,6 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) if (sock_flag(sk, SOCK_KEEPOPEN)) inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tp)); - - if (!sock_flag(sk, SOCK_DEAD)) { - sk->sk_state_change(sk); - sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT); - } } static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, -- cgit v1.2.3 From e61e4055b165f4c645ce2a85890b313abf841f67 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:09 +0800 Subject: sctp: remove the typedef sctp_shutdownhdr_t This patch is to remove the typedef sctp_shutdownhdr_t, and replace with struct sctp_shutdownhdr in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- net/sctp/sm_make_chunk.c | 4 ++-- net/sctp/sm_statefuns.c | 10 +++++----- 3 files changed, 10 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index ba007163acfd..7a586ba7dcd4 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -416,13 +416,13 @@ struct sctp_abort_chunk { /* For the graceful shutdown we must carry the tag (in common header) * and the highest consecutive acking value. */ -typedef struct sctp_shutdownhdr { +struct sctp_shutdownhdr { __be32 cum_tsn_ack; -} sctp_shutdownhdr_t; +}; struct sctp_shutdown_chunk_t { struct sctp_chunkhdr chunk_hdr; - sctp_shutdownhdr_t shutdown_hdr; + struct sctp_shutdownhdr shutdown_hdr; }; /* RFC 2960. Section 3.3.10 Operation Error (ERROR) (9) */ diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 163004e7047c..8f1c6b639ac1 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -857,15 +857,15 @@ nodata: struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc, const struct sctp_chunk *chunk) { + struct sctp_shutdownhdr shut; struct sctp_chunk *retval; - sctp_shutdownhdr_t shut; __u32 ctsn; ctsn = sctp_tsnmap_get_ctsn(&asoc->peer.tsn_map); shut.cum_tsn_ack = htonl(ctsn); retval = sctp_make_control(asoc, SCTP_CID_SHUTDOWN, 0, - sizeof(sctp_shutdownhdr_t), GFP_ATOMIC); + sizeof(shut), GFP_ATOMIC); if (!retval) goto nodata; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index dc0c2c4188d8..5b95e2d8c227 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2656,8 +2656,8 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; - sctp_shutdownhdr_t *sdh; sctp_disposition_t disposition; + struct sctp_shutdownhdr *sdh; struct sctp_ulpevent *ev; __u32 ctsn; @@ -2671,8 +2671,8 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net, commands); /* Convert the elaborate header. */ - sdh = (sctp_shutdownhdr_t *)chunk->skb->data; - skb_pull(chunk->skb, sizeof(sctp_shutdownhdr_t)); + sdh = (struct sctp_shutdownhdr *)chunk->skb->data; + skb_pull(chunk->skb, sizeof(*sdh)); chunk->subh.shutdown_hdr = sdh; ctsn = ntohl(sdh->cum_tsn_ack); @@ -2746,7 +2746,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; - sctp_shutdownhdr_t *sdh; + struct sctp_shutdownhdr *sdh; __u32 ctsn; if (!sctp_vtag_verify(chunk, asoc)) @@ -2758,7 +2758,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); - sdh = (sctp_shutdownhdr_t *)chunk->skb->data; + sdh = (struct sctp_shutdownhdr *)chunk->skb->data; ctsn = ntohl(sdh->cum_tsn_ack); if (TSN_lt(ctsn, asoc->ctsn_ack_point)) { -- cgit v1.2.3 From ac23e68133f4570f40ec0910286ced08ced2d378 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:10 +0800 Subject: sctp: fix the name of struct sctp_shutdown_chunk_t This patch is to fix the name of struct sctp_shutdown_chunk_t , replace with struct sctp_initack_chunk in the places where it's using it. It is also to fix some indent problem. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 2 +- net/sctp/sm_statefuns.c | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 7a586ba7dcd4..553020cbf6f7 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -420,7 +420,7 @@ struct sctp_shutdownhdr { __be32 cum_tsn_ack; }; -struct sctp_shutdown_chunk_t { +struct sctp_shutdown_chunk { struct sctp_chunkhdr chunk_hdr; struct sctp_shutdownhdr shutdown_hdr; }; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 5b95e2d8c227..d4d8fab52a11 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2665,8 +2665,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net, return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the SHUTDOWN chunk has a valid length. */ - if (!sctp_chunk_length_valid(chunk, - sizeof(struct sctp_shutdown_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_shutdown_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); @@ -2753,8 +2752,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the SHUTDOWN chunk has a valid length. */ - if (!sctp_chunk_length_valid(chunk, - sizeof(struct sctp_shutdown_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_shutdown_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); @@ -5419,12 +5417,14 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack( */ if (chunk) { if (!sctp_vtag_verify(chunk, asoc)) - return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, + commands); /* Make sure that the SHUTDOWN chunk has a valid length. */ - if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_shutdown_chunk_t))) - return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, - commands); + if (!sctp_chunk_length_valid( + chunk, sizeof(struct sctp_shutdown_chunk))) + return sctp_sf_violation_chunklen(net, ep, asoc, type, + arg, commands); } /* If it has no more outstanding DATA chunks, the SHUTDOWN receiver -- cgit v1.2.3 From d8238d9dab8fbea22dd04f4e77639c7f7b83eef7 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:11 +0800 Subject: sctp: remove the typedef sctp_errhdr_t This patch is to remove the typedef sctp_errhdr_t, and replace with struct sctp_errhdr in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- include/net/sctp/sctp.h | 8 ++++---- net/sctp/sm_make_chunk.c | 38 ++++++++++++++++++++------------------ net/sctp/sm_sideeffect.c | 2 +- net/sctp/sm_statefuns.c | 29 +++++++++++++++-------------- net/sctp/ulpevent.c | 10 +++++----- 6 files changed, 48 insertions(+), 45 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 553020cbf6f7..d35bdd30fa0f 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -427,15 +427,15 @@ struct sctp_shutdown_chunk { /* RFC 2960. Section 3.3.10 Operation Error (ERROR) (9) */ -typedef struct sctp_errhdr { +struct sctp_errhdr { __be16 cause; __be16 length; __u8 variable[0]; -} sctp_errhdr_t; +}; typedef struct sctp_operr_chunk { struct sctp_chunkhdr chunk_hdr; - sctp_errhdr_t err_hdr; + struct sctp_errhdr err_hdr; } sctp_operr_chunk_t; /* RFC 2960 3.3.10 - Operation Error diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 45fd4c6056b5..84650fed1e6a 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -479,13 +479,13 @@ for (pos.v = chunk->member;\ _sctp_walk_errors((err), (chunk_hdr), ntohs((chunk_hdr)->length)) #define _sctp_walk_errors(err, chunk_hdr, end)\ -for (err = (sctp_errhdr_t *)((void *)chunk_hdr + \ +for (err = (struct sctp_errhdr *)((void *)chunk_hdr + \ sizeof(struct sctp_chunkhdr));\ - ((void *)err + offsetof(sctp_errhdr_t, length) + sizeof(err->length) <=\ + ((void *)err + offsetof(struct sctp_errhdr, length) + sizeof(err->length) <=\ (void *)chunk_hdr + end) &&\ (void *)err <= (void *)chunk_hdr + end - ntohs(err->length) &&\ - ntohs(err->length) >= sizeof(sctp_errhdr_t); \ - err = (sctp_errhdr_t *)((void *)err + SCTP_PAD4(ntohs(err->length)))) + ntohs(err->length) >= sizeof(struct sctp_errhdr); \ + err = (struct sctp_errhdr *)((void *)err + SCTP_PAD4(ntohs(err->length)))) #define sctp_walk_fwdtsn(pos, chunk)\ _sctp_walk_fwdtsn((pos), (chunk), ntohs((chunk)->chunk_hdr->length) - sizeof(struct sctp_fwdtsn_chunk)) diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 8f1c6b639ac1..0b2298bbb84e 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -135,14 +135,14 @@ static const struct sctp_paramhdr prsctp_param = { void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code, size_t paylen) { - sctp_errhdr_t err; + struct sctp_errhdr err; __u16 len; /* Cause code constants are now defined in network order. */ err.cause = cause_code; - len = sizeof(sctp_errhdr_t) + paylen; + len = sizeof(err) + paylen; err.length = htons(len); - chunk->subh.err_hdr = sctp_addto_chunk(chunk, sizeof(sctp_errhdr_t), &err); + chunk->subh.err_hdr = sctp_addto_chunk(chunk, sizeof(err), &err); } /* A helper to initialize an op error inside a @@ -153,19 +153,19 @@ void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code, static int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code, size_t paylen) { - sctp_errhdr_t err; + struct sctp_errhdr err; __u16 len; /* Cause code constants are now defined in network order. */ err.cause = cause_code; - len = sizeof(sctp_errhdr_t) + paylen; + len = sizeof(err) + paylen; err.length = htons(len); if (skb_tailroom(chunk->skb) < len) return -ENOSPC; - chunk->subh.err_hdr = sctp_addto_chunk_fixed(chunk, - sizeof(sctp_errhdr_t), - &err); + + chunk->subh.err_hdr = sctp_addto_chunk_fixed(chunk, sizeof(err), &err); + return 0; } /* 3.3.2 Initiation (INIT) (1) @@ -979,8 +979,8 @@ struct sctp_chunk *sctp_make_abort_no_data( struct sctp_chunk *retval; __be32 payload; - retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) - + sizeof(tsn)); + retval = sctp_make_abort(asoc, chunk, + sizeof(struct sctp_errhdr) + sizeof(tsn)); if (!retval) goto no_mem; @@ -1015,7 +1015,8 @@ struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, void *payload = NULL; int err; - retval = sctp_make_abort(asoc, NULL, sizeof(sctp_errhdr_t) + paylen); + retval = sctp_make_abort(asoc, NULL, + sizeof(struct sctp_errhdr) + paylen); if (!retval) goto err_chunk; @@ -1080,8 +1081,8 @@ struct sctp_chunk *sctp_make_abort_violation( struct sctp_chunk *retval; struct sctp_paramhdr phdr; - retval = sctp_make_abort(asoc, chunk, sizeof(sctp_errhdr_t) + paylen + - sizeof(phdr)); + retval = sctp_make_abort(asoc, chunk, sizeof(struct sctp_errhdr) + + paylen + sizeof(phdr)); if (!retval) goto end; @@ -1104,7 +1105,7 @@ struct sctp_chunk *sctp_make_violation_paramlen( { struct sctp_chunk *retval; static const char error[] = "The following parameter had invalid length:"; - size_t payload_len = sizeof(error) + sizeof(sctp_errhdr_t) + + size_t payload_len = sizeof(error) + sizeof(struct sctp_errhdr) + sizeof(*param); retval = sctp_make_abort(asoc, chunk, payload_len); @@ -1126,7 +1127,7 @@ struct sctp_chunk *sctp_make_violation_max_retrans( { struct sctp_chunk *retval; static const char error[] = "Association exceeded its max_retans count"; - size_t payload_len = sizeof(error) + sizeof(sctp_errhdr_t); + size_t payload_len = sizeof(error) + sizeof(struct sctp_errhdr); retval = sctp_make_abort(asoc, chunk, payload_len); if (!retval) @@ -1209,7 +1210,8 @@ static struct sctp_chunk *sctp_make_op_error_space( struct sctp_chunk *retval; retval = sctp_make_control(asoc, SCTP_CID_ERROR, 0, - sizeof(sctp_errhdr_t) + size, GFP_ATOMIC); + sizeof(struct sctp_errhdr) + size, + GFP_ATOMIC); if (!retval) goto nodata; @@ -2966,7 +2968,7 @@ static void sctp_add_asconf_response(struct sctp_chunk *chunk, __be32 crr_id, __be16 err_code, sctp_addip_param_t *asconf_param) { sctp_addip_param_t ack_param; - sctp_errhdr_t err_param; + struct sctp_errhdr err_param; int asconf_param_len = 0; int err_param_len = 0; __be16 response_type; @@ -3351,7 +3353,7 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, int no_err) { sctp_addip_param_t *asconf_ack_param; - sctp_errhdr_t *err_param; + struct sctp_errhdr *err_param; int length; int asconf_ack_len; __be16 err_code; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index d6e5e9e0fd6d..5dda8c42b5f6 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -828,7 +828,7 @@ static void sctp_cmd_assoc_update(sctp_cmd_seq_t *cmds, if (!sctp_assoc_update(asoc, new)) return; - abort = sctp_make_abort(asoc, NULL, sizeof(sctp_errhdr_t)); + abort = sctp_make_abort(asoc, NULL, sizeof(struct sctp_errhdr)); if (abort) { sctp_init_cause(abort, SCTP_ERROR_RSRC_LOW, 0); sctp_add_cmd_sf(cmds, SCTP_CMD_REPLY, SCTP_CHUNK(abort)); diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index d4d8fab52a11..7a2ba4c187d0 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1233,7 +1233,7 @@ static int sctp_sf_send_restart_abort(struct net *net, union sctp_addr *ssa, union sctp_addr_param *addrparm; struct sctp_errhdr *errhdr; struct sctp_endpoint *ep; - char buffer[sizeof(struct sctp_errhdr)+sizeof(union sctp_addr_param)]; + char buffer[sizeof(*errhdr) + sizeof(*addrparm)]; struct sctp_af *af = sctp_get_af_specific(ssa->v4.sin_family); /* Build the error on the stack. We are way to malloc crazy @@ -1244,7 +1244,7 @@ static int sctp_sf_send_restart_abort(struct net *net, union sctp_addr *ssa, /* Copy into a parm format. */ len = af->to_addr_param(ssa, addrparm); - len += sizeof(sctp_errhdr_t); + len += sizeof(*errhdr); errhdr->cause = SCTP_ERROR_RESTART; errhdr->length = htons(len); @@ -2270,7 +2270,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(struct net *net, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; - sctp_errhdr_t *err; + struct sctp_errhdr *err; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -2337,7 +2337,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, struct sctp_chunk *chunk = arg, *reply; struct sctp_cookie_preserve_param bht; struct sctp_bind_addr *bp; - sctp_errhdr_t *err; + struct sctp_errhdr *err; u32 stale; if (attempts > asoc->max_init_attempts) { @@ -2348,7 +2348,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, return SCTP_DISPOSITION_DELETE_TCB; } - err = (sctp_errhdr_t *)(chunk->skb->data); + err = (struct sctp_errhdr *)(chunk->skb->data); /* When calculating the time extension, an implementation * SHOULD use the RTT information measured based on the @@ -2364,7 +2364,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, * to give ample time to retransmit the new cookie and thus * yield a higher probability of success on the reattempt. */ - stale = ntohl(*(__be32 *)((u8 *)err + sizeof(sctp_errhdr_t))); + stale = ntohl(*(__be32 *)((u8 *)err + sizeof(*err))); stale = (stale * 2) / 1000; bht.param_hdr.type = SCTP_PARAM_COOKIE_PRESERVATIVE; @@ -2499,13 +2499,14 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) { + struct sctp_errhdr *err; - sctp_errhdr_t *err; sctp_walk_errors(err, chunk->chunk_hdr); if ((void *)err != (void *)chunk->chunk_end) - return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); + return sctp_sf_pdiscard(net, ep, asoc, type, arg, + commands); - error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + error = ((struct sctp_errhdr *)chunk->skb->data)->cause; } sctp_add_cmd_sf(commands, SCTP_CMD_SET_SK_ERR, SCTP_ERROR(ECONNRESET)); @@ -2552,7 +2553,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(struct net *net, /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); if (len >= sizeof(struct sctp_chunkhdr) + sizeof(struct sctp_errhdr)) - error = ((sctp_errhdr_t *)chunk->skb->data)->cause; + error = ((struct sctp_errhdr *)chunk->skb->data)->cause; return sctp_stop_t1_and_abort(net, commands, error, ECONNREFUSED, asoc, chunk->transport); @@ -3310,7 +3311,7 @@ sctp_disposition_t sctp_sf_operr_notify(struct net *net, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; - sctp_errhdr_t *err; + struct sctp_errhdr *err; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -3433,7 +3434,7 @@ sctp_disposition_t sctp_sf_ootb(struct net *net, struct sctp_chunk *chunk = arg; struct sk_buff *skb = chunk->skb; struct sctp_chunkhdr *ch; - sctp_errhdr_t *err; + struct sctp_errhdr *err; __u8 *ch_end; int ootb_shut_ack = 0; int ootb_cookie_ack = 0; @@ -3776,7 +3777,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, if (ADDIP_SERIAL_gte(rcvd_serial, sent_serial + 1) && !(asoc->addip_last_asconf)) { abort = sctp_make_abort(asoc, asconf_ack, - sizeof(sctp_errhdr_t)); + sizeof(struct sctp_errhdr)); if (abort) { sctp_init_cause(abort, SCTP_ERROR_ASCONF_ACK, 0); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, @@ -3812,7 +3813,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, } abort = sctp_make_abort(asoc, asconf_ack, - sizeof(sctp_errhdr_t)); + sizeof(struct sctp_errhdr)); if (abort) { sctp_init_cause(abort, SCTP_ERROR_RSRC_LOW, 0); sctp_add_cmd_sf(commands, SCTP_CMD_REPLY, diff --git a/net/sctp/ulpevent.c b/net/sctp/ulpevent.c index 5f86c5062a98..67abc0194f30 100644 --- a/net/sctp/ulpevent.c +++ b/net/sctp/ulpevent.c @@ -371,19 +371,19 @@ sctp_ulpevent_make_remote_error(const struct sctp_association *asoc, struct sctp_chunk *chunk, __u16 flags, gfp_t gfp) { - struct sctp_ulpevent *event; struct sctp_remote_error *sre; + struct sctp_ulpevent *event; + struct sctp_errhdr *ch; struct sk_buff *skb; - sctp_errhdr_t *ch; __be16 cause; int elen; - ch = (sctp_errhdr_t *)(chunk->skb->data); + ch = (struct sctp_errhdr *)(chunk->skb->data); cause = ch->cause; - elen = SCTP_PAD4(ntohs(ch->length)) - sizeof(sctp_errhdr_t); + elen = SCTP_PAD4(ntohs(ch->length)) - sizeof(*ch); /* Pull off the ERROR header. */ - skb_pull(chunk->skb, sizeof(sctp_errhdr_t)); + skb_pull(chunk->skb, sizeof(*ch)); /* Copy the skb to a new skb with room for us to prepend * notification with. -- cgit v1.2.3 From 87caeba7914979ef6a5765dfa3163b51a30133ab Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:12 +0800 Subject: sctp: remove the typedef sctp_operr_chunk_t This patch is to remove the typedef sctp_operr_chunk_t, and replace with struct sctp_operr_chunk in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_statefuns.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index d35bdd30fa0f..07c12e98fa85 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -433,10 +433,10 @@ struct sctp_errhdr { __u8 variable[0]; }; -typedef struct sctp_operr_chunk { +struct sctp_operr_chunk { struct sctp_chunkhdr chunk_hdr; struct sctp_errhdr err_hdr; -} sctp_operr_chunk_t; +}; /* RFC 2960 3.3.10 - Operation Error * diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 7a2ba4c187d0..3b121d2ca309 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2278,7 +2278,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(struct net *net, /* Make sure that the ERROR chunk has a valid length. * The parameter walking depends on this as well. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_operr_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); @@ -3317,7 +3317,7 @@ sctp_disposition_t sctp_sf_operr_notify(struct net *net, return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the ERROR chunk has a valid length. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_operr_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_operr_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); sctp_walk_errors(err, chunk->chunk_hdr); -- cgit v1.2.3 From 2a4932167772874c5bc4b3dfebf61cfadb5554b9 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:13 +0800 Subject: sctp: remove the typedef sctp_error_t This patch is to remove the typedef sctp_error_t, and replace with enum sctp_error in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_statefuns.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 07c12e98fa85..c74ea93e36eb 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -457,7 +457,7 @@ struct sctp_operr_chunk { * 9 No User Data * 10 Cookie Received While Shutting Down */ -typedef enum { +enum sctp_error { SCTP_ERROR_NO_ERROR = cpu_to_be16(0x00), SCTP_ERROR_INV_STRM = cpu_to_be16(0x01), @@ -512,7 +512,7 @@ typedef enum { * 0x0105 Unsupported HMAC Identifier */ SCTP_ERROR_UNSUP_HMAC = cpu_to_be16(0x0105) -} sctp_error_t; +}; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 3b121d2ca309..1d1249962993 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -528,7 +528,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, (struct sctp_init_chunk *)chunk->chunk_hdr, chunk, &err_chunk)) { - sctp_error_t error = SCTP_ERROR_NO_RESOURCE; + enum sctp_error error = SCTP_ERROR_NO_RESOURCE; /* This chunk contains fatal error. It is to be discarded. * Send an ABORT, with causes. If there are no causes, -- cgit v1.2.3 From 1fb6d83bd37dc7d0949b8a8005f7c24dddc3ee1e Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:14 +0800 Subject: sctp: remove the typedef sctp_ecnehdr_t This patch is to remove the typedef sctp_ecnehdr_t, and replace with struct sctp_ecnehdr in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- net/sctp/sm_make_chunk.c | 4 ++-- net/sctp/sm_statefuns.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index c74ea93e36eb..5ea739b99f8d 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -519,13 +519,13 @@ enum sctp_error { /* RFC 2960. Appendix A. Explicit Congestion Notification. * Explicit Congestion Notification Echo (ECNE) (12) */ -typedef struct sctp_ecnehdr { +struct sctp_ecnehdr { __be32 lowest_tsn; -} sctp_ecnehdr_t; +}; typedef struct sctp_ecne_chunk { struct sctp_chunkhdr chunk_hdr; - sctp_ecnehdr_t ence_hdr; + struct sctp_ecnehdr ence_hdr; } sctp_ecne_chunk_t; /* RFC 2960. Appendix A. Explicit Congestion Notification. diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 0b2298bbb84e..1c7cc6a48bde 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -697,11 +697,11 @@ struct sctp_chunk *sctp_make_ecne(const struct sctp_association *asoc, const __u32 lowest_tsn) { struct sctp_chunk *retval; - sctp_ecnehdr_t ecne; + struct sctp_ecnehdr ecne; ecne.lowest_tsn = htonl(lowest_tsn); retval = sctp_make_control(asoc, SCTP_CID_ECN_ECNE, 0, - sizeof(sctp_ecnehdr_t), GFP_ATOMIC); + sizeof(ecne), GFP_ATOMIC); if (!retval) goto nodata; retval->subh.ecne_hdr = diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 1d1249962993..de3e5bf88484 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2918,8 +2918,8 @@ sctp_disposition_t sctp_sf_do_ecne(struct net *net, void *arg, sctp_cmd_seq_t *commands) { - sctp_ecnehdr_t *ecne; struct sctp_chunk *chunk = arg; + struct sctp_ecnehdr *ecne; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -2928,8 +2928,8 @@ sctp_disposition_t sctp_sf_do_ecne(struct net *net, return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); - ecne = (sctp_ecnehdr_t *) chunk->skb->data; - skb_pull(chunk->skb, sizeof(sctp_ecnehdr_t)); + ecne = (struct sctp_ecnehdr *)chunk->skb->data; + skb_pull(chunk->skb, sizeof(*ecne)); /* If this is a newer ECNE than the last CWR packet we sent out */ sctp_add_cmd_sf(commands, SCTP_CMD_ECN_ECNE, -- cgit v1.2.3 From b515fd27591dea06bc9f0178fba361b43b76ab6b Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:15 +0800 Subject: sctp: remove the typedef sctp_ecne_chunk_t This patch is to remove the typedef sctp_ecne_chunk_t, and replace with struct sctp_ecne_chunk in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/sm_statefuns.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 5ea739b99f8d..026bbdfcaf44 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -523,10 +523,10 @@ struct sctp_ecnehdr { __be32 lowest_tsn; }; -typedef struct sctp_ecne_chunk { +struct sctp_ecne_chunk { struct sctp_chunkhdr chunk_hdr; struct sctp_ecnehdr ence_hdr; -} sctp_ecne_chunk_t; +}; /* RFC 2960. Appendix A. Explicit Congestion Notification. * Congestion Window Reduced (CWR) (13) diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index de3e5bf88484..286dce14c5cc 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2869,7 +2869,7 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_ecne_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_ecne_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); @@ -2924,7 +2924,7 @@ sctp_disposition_t sctp_sf_do_ecne(struct net *net, if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_ecne_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_ecne_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); -- cgit v1.2.3 From 65f77105438a7793836d7ba2d9a05fa585b8caf9 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:16 +0800 Subject: sctp: remove the typedef sctp_cwrhdr_t This patch is to remove the typedef sctp_cwrhdr_t, and replace with struct sctp_cwrhdr in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- net/sctp/sm_make_chunk.c | 4 ++-- net/sctp/sm_statefuns.c | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 026bbdfcaf44..3c8c418425e6 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -531,13 +531,13 @@ struct sctp_ecne_chunk { /* RFC 2960. Appendix A. Explicit Congestion Notification. * Congestion Window Reduced (CWR) (13) */ -typedef struct sctp_cwrhdr { +struct sctp_cwrhdr { __be32 lowest_tsn; -} sctp_cwrhdr_t; +}; typedef struct sctp_cwr_chunk { struct sctp_chunkhdr chunk_hdr; - sctp_cwrhdr_t cwr_hdr; + struct sctp_cwrhdr cwr_hdr; } sctp_cwr_chunk_t; /* PR-SCTP diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 1c7cc6a48bde..e8e506522193 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -663,11 +663,11 @@ struct sctp_chunk *sctp_make_cwr(const struct sctp_association *asoc, const struct sctp_chunk *chunk) { struct sctp_chunk *retval; - sctp_cwrhdr_t cwr; + struct sctp_cwrhdr cwr; cwr.lowest_tsn = htonl(lowest_tsn); retval = sctp_make_control(asoc, SCTP_CID_ECN_CWR, 0, - sizeof(sctp_cwrhdr_t), GFP_ATOMIC); + sizeof(cwr), GFP_ATOMIC); if (!retval) goto nodata; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 286dce14c5cc..e13c83f9a6ee 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2862,8 +2862,8 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net, void *arg, sctp_cmd_seq_t *commands) { - sctp_cwrhdr_t *cwr; struct sctp_chunk *chunk = arg; + struct sctp_cwrhdr *cwr; u32 lowest_tsn; if (!sctp_vtag_verify(chunk, asoc)) @@ -2873,8 +2873,8 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net, return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); - cwr = (sctp_cwrhdr_t *) chunk->skb->data; - skb_pull(chunk->skb, sizeof(sctp_cwrhdr_t)); + cwr = (struct sctp_cwrhdr *)chunk->skb->data; + skb_pull(chunk->skb, sizeof(*cwr)); lowest_tsn = ntohl(cwr->lowest_tsn); -- cgit v1.2.3 From 8b32f2348a0441ef202562618b13d1bb494f3a47 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:18 +0800 Subject: sctp: remove the typedef sctp_addip_param_t This patch is to remove the typedef sctp_addip_param_t, and replace with struct sctp_addip_param in the places where it's using this typedef. It is to use sizeof(variable) instead of sizeof(type), and also fix some indent problems. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 8 ++++---- net/sctp/sm_make_chunk.c | 39 ++++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 23 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 2a97639eb035..5b7d6d9d3fc5 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -629,10 +629,10 @@ struct sctp_fwdtsn_chunk { * The ASCONF Parameter Response is used in the ASCONF-ACK to * report status of ASCONF processing. */ -typedef struct sctp_addip_param { - struct sctp_paramhdr param_hdr; - __be32 crr_id; -} sctp_addip_param_t; +struct sctp_addip_param { + struct sctp_paramhdr param_hdr; + __be32 crr_id; +}; typedef struct sctp_addiphdr { __be32 serial; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index e8e506522193..bab3354ecdc3 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2615,7 +2615,7 @@ do_addr_param: if (!net->sctp.addip_enable) goto fall_through; - addr_param = param.v + sizeof(sctp_addip_param_t); + addr_param = param.v + sizeof(struct sctp_addip_param); af = sctp_get_af_specific(param_type2af(addr_param->p.type)); if (af == NULL) @@ -2810,7 +2810,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, int addrcnt, __be16 flags) { - sctp_addip_param_t param; + struct sctp_addip_param param; struct sctp_chunk *retval; union sctp_addr_param addr_param; union sctp_addr *addr; @@ -2896,7 +2896,7 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, union sctp_addr *addr) { - sctp_addip_param_t param; + struct sctp_addip_param param; struct sctp_chunk *retval; int len = sizeof(param); union sctp_addr_param addrparam; @@ -2965,9 +2965,10 @@ static struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *as /* Add response parameters to an ASCONF_ACK chunk. */ static void sctp_add_asconf_response(struct sctp_chunk *chunk, __be32 crr_id, - __be16 err_code, sctp_addip_param_t *asconf_param) + __be16 err_code, + struct sctp_addip_param *asconf_param) { - sctp_addip_param_t ack_param; + struct sctp_addip_param ack_param; struct sctp_errhdr err_param; int asconf_param_len = 0; int err_param_len = 0; @@ -3006,15 +3007,15 @@ static void sctp_add_asconf_response(struct sctp_chunk *chunk, __be32 crr_id, /* Process a asconf parameter. */ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, - struct sctp_chunk *asconf, - sctp_addip_param_t *asconf_param) + struct sctp_chunk *asconf, + struct sctp_addip_param *asconf_param) { struct sctp_transport *peer; struct sctp_af *af; union sctp_addr addr; union sctp_addr_param *addr_param; - addr_param = (void *)asconf_param + sizeof(sctp_addip_param_t); + addr_param = (void *)asconf_param + sizeof(*asconf_param); if (asconf_param->param_hdr.type != SCTP_PARAM_ADD_IP && asconf_param->param_hdr.type != SCTP_PARAM_DEL_IP && @@ -3174,13 +3175,13 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, if (addr_param_needed && !addr_param_seen) return false; length = ntohs(param.addip->param_hdr.length); - if (length < sizeof(sctp_addip_param_t) + + if (length < sizeof(struct sctp_addip_param) + sizeof(**errp)) return false; break; case SCTP_PARAM_SUCCESS_REPORT: case SCTP_PARAM_ADAPTATION_LAYER_IND: - if (length != sizeof(sctp_addip_param_t)) + if (length != sizeof(struct sctp_addip_param)) return false; break; default: @@ -3289,7 +3290,7 @@ done: /* Process a asconf parameter that is successfully acked. */ static void sctp_asconf_param_success(struct sctp_association *asoc, - sctp_addip_param_t *asconf_param) + struct sctp_addip_param *asconf_param) { struct sctp_af *af; union sctp_addr addr; @@ -3298,7 +3299,7 @@ static void sctp_asconf_param_success(struct sctp_association *asoc, struct sctp_transport *transport; struct sctp_sockaddr_entry *saddr; - addr_param = (void *)asconf_param + sizeof(sctp_addip_param_t); + addr_param = (void *)asconf_param + sizeof(*asconf_param); /* We have checked the packet before, so we do not check again. */ af = sctp_get_af_specific(param_type2af(addr_param->p.type)); @@ -3349,10 +3350,10 @@ static void sctp_asconf_param_success(struct sctp_association *asoc, * specific success indication is present for the parameter. */ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, - sctp_addip_param_t *asconf_param, - int no_err) + struct sctp_addip_param *asconf_param, + int no_err) { - sctp_addip_param_t *asconf_ack_param; + struct sctp_addip_param *asconf_ack_param; struct sctp_errhdr *err_param; int length; int asconf_ack_len; @@ -3370,8 +3371,8 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, * the first asconf_ack parameter. */ length = sizeof(sctp_addiphdr_t); - asconf_ack_param = (sctp_addip_param_t *)(asconf_ack->skb->data + - length); + asconf_ack_param = (struct sctp_addip_param *)(asconf_ack->skb->data + + length); asconf_ack_len -= length; while (asconf_ack_len > 0) { @@ -3380,7 +3381,7 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, case SCTP_PARAM_SUCCESS_REPORT: return SCTP_ERROR_NO_ERROR; case SCTP_PARAM_ERR_CAUSE: - length = sizeof(sctp_addip_param_t); + length = sizeof(*asconf_ack_param); err_param = (void *)asconf_ack_param + length; asconf_ack_len -= length; if (asconf_ack_len > 0) @@ -3407,7 +3408,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, { struct sctp_chunk *asconf = asoc->addip_last_asconf; union sctp_addr_param *addr_param; - sctp_addip_param_t *asconf_param; + struct sctp_addip_param *asconf_param; int length = 0; int asconf_len = asconf->skb->len; int all_param_pass = 0; -- cgit v1.2.3 From 65205cc465e9b37abbdbb3d595c46081b97e35bc Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:19 +0800 Subject: sctp: remove the typedef sctp_addiphdr_t This patch is to remove the typedef sctp_addiphdr_t, and replace with struct sctp_addiphdr in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- net/sctp/sm_make_chunk.c | 14 +++++++------- net/sctp/sm_statefuns.c | 10 +++++----- 3 files changed, 15 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 5b7d6d9d3fc5..83dac9b0b4bd 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -634,14 +634,14 @@ struct sctp_addip_param { __be32 crr_id; }; -typedef struct sctp_addiphdr { +struct sctp_addiphdr { __be32 serial; __u8 params[0]; -} sctp_addiphdr_t; +}; typedef struct sctp_addip_chunk { struct sctp_chunkhdr chunk_hdr; - sctp_addiphdr_t addip_hdr; + struct sctp_addiphdr addip_hdr; } sctp_addip_chunk_t; /* AUTH diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index bab3354ecdc3..0e71e5054ead 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2752,7 +2752,7 @@ static struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, union sctp_addr *addr, int vparam_len) { - sctp_addiphdr_t asconf; + struct sctp_addiphdr asconf; struct sctp_chunk *retval; int length = sizeof(asconf) + vparam_len; union sctp_addr_param addrparam; @@ -2945,7 +2945,7 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, static struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, __u32 serial, int vparam_len) { - sctp_addiphdr_t asconf; + struct sctp_addiphdr asconf; struct sctp_chunk *retval; int length = sizeof(asconf) + vparam_len; @@ -3210,7 +3210,7 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr; bool all_param_pass = true; union sctp_params param; - sctp_addiphdr_t *hdr; + struct sctp_addiphdr *hdr; union sctp_addr_param *addr_param; struct sctp_chunk *asconf_ack; __be16 err_code; @@ -3220,11 +3220,11 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(struct sctp_chunkhdr); - hdr = (sctp_addiphdr_t *)asconf->skb->data; + hdr = (struct sctp_addiphdr *)asconf->skb->data; serial = ntohl(hdr->serial); /* Skip the addiphdr and store a pointer to address parameter. */ - length = sizeof(sctp_addiphdr_t); + length = sizeof(*hdr); addr_param = (union sctp_addr_param *)(asconf->skb->data + length); chunk_len -= length; @@ -3370,7 +3370,7 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, /* Skip the addiphdr from the asconf_ack chunk and store a pointer to * the first asconf_ack parameter. */ - length = sizeof(sctp_addiphdr_t); + length = sizeof(struct sctp_addiphdr); asconf_ack_param = (struct sctp_addip_param *)(asconf_ack->skb->data + length); asconf_ack_len -= length; @@ -3435,7 +3435,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, * failures are indicated, then all request(s) are considered * successful. */ - if (asconf_ack->skb->len == sizeof(sctp_addiphdr_t)) + if (asconf_ack->skb->len == sizeof(struct sctp_addiphdr)) all_param_pass = 1; /* Process the TLVs contained in the last sent ASCONF chunk. */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index e13c83f9a6ee..d722f380b36e 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3613,7 +3613,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, struct sctp_chunk *chunk = arg; struct sctp_chunk *asconf_ack = NULL; struct sctp_paramhdr *err_param = NULL; - sctp_addiphdr_t *hdr; + struct sctp_addiphdr *hdr; __u32 serial; if (!sctp_vtag_verify(chunk, asoc)) { @@ -3636,7 +3636,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); - hdr = (sctp_addiphdr_t *)chunk->skb->data; + hdr = (struct sctp_addiphdr *)chunk->skb->data; serial = ntohl(hdr->serial); /* Verify the ASCONF chunk before processing it. */ @@ -3730,7 +3730,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, struct sctp_chunk *last_asconf = asoc->addip_last_asconf; struct sctp_chunk *abort; struct sctp_paramhdr *err_param = NULL; - sctp_addiphdr_t *addip_hdr; + struct sctp_addiphdr *addip_hdr; __u32 sent_serial, rcvd_serial; if (!sctp_vtag_verify(asconf_ack, asoc)) { @@ -3753,7 +3753,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); - addip_hdr = (sctp_addiphdr_t *)asconf_ack->skb->data; + addip_hdr = (struct sctp_addiphdr *)asconf_ack->skb->data; rcvd_serial = ntohl(addip_hdr->serial); /* Verify the ASCONF-ACK chunk before processing it. */ @@ -3762,7 +3762,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, (void *)err_param, commands); if (last_asconf) { - addip_hdr = (sctp_addiphdr_t *)last_asconf->subh.addip_hdr; + addip_hdr = (struct sctp_addiphdr *)last_asconf->subh.addip_hdr; sent_serial = ntohl(addip_hdr->serial); } else { sent_serial = asoc->addip_serial - 1; -- cgit v1.2.3 From 68d75469468620c37cd58dc352f1dcec8c3896b6 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:20 +0800 Subject: sctp: remove the typedef sctp_addip_chunk_t This patch is to remove the typedef sctp_addip_chunk_t, and replace with struct sctp_addip_chunk in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/input.c | 2 +- net/sctp/sm_make_chunk.c | 8 +++++--- net/sctp/sm_statefuns.c | 11 +++++++---- 4 files changed, 15 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index 83dac9b0b4bd..e7b439ed6371 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -639,10 +639,10 @@ struct sctp_addiphdr { __u8 params[0]; }; -typedef struct sctp_addip_chunk { +struct sctp_addip_chunk { struct sctp_chunkhdr chunk_hdr; struct sctp_addiphdr addip_hdr; -} sctp_addip_chunk_t; +}; /* AUTH * Section 4.1 Authentication Chunk (AUTH) diff --git a/net/sctp/input.c b/net/sctp/input.c index 41eb2ec10460..92a07141fd07 100644 --- a/net/sctp/input.c +++ b/net/sctp/input.c @@ -1111,7 +1111,7 @@ static struct sctp_association *__sctp_rcv_asconf_lookup( __be16 peer_port, struct sctp_transport **transportp) { - sctp_addip_chunk_t *asconf = (struct sctp_addip_chunk *)ch; + struct sctp_addip_chunk *asconf = (struct sctp_addip_chunk *)ch; struct sctp_af *af; union sctp_addr_param *param; union sctp_addr paddr; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 0e71e5054ead..ae54c6ecd26a 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3140,10 +3140,11 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, struct sctp_chunk *chunk, bool addr_param_needed, struct sctp_paramhdr **errp) { - sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) chunk->chunk_hdr; + struct sctp_addip_chunk *addip; union sctp_params param; bool addr_param_seen = false; + addip = (struct sctp_addip_chunk *)chunk->chunk_hdr; sctp_walk_params(param, addip, addip_hdr.params) { size_t length = ntohs(param.p->length); @@ -3207,7 +3208,7 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, struct sctp_chunk *asconf) { - sctp_addip_chunk_t *addip = (sctp_addip_chunk_t *) asconf->chunk_hdr; + struct sctp_addip_chunk *addip; bool all_param_pass = true; union sctp_params param; struct sctp_addiphdr *hdr; @@ -3218,6 +3219,7 @@ struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, int chunk_len; __u32 serial; + addip = (struct sctp_addip_chunk *)asconf->chunk_hdr; chunk_len = ntohs(asconf->chunk_hdr->length) - sizeof(struct sctp_chunkhdr); hdr = (struct sctp_addiphdr *)asconf->skb->data; @@ -3419,7 +3421,7 @@ int sctp_process_asconf_ack(struct sctp_association *asoc, /* Skip the chunkhdr and addiphdr from the last asconf sent and store * a pointer to address parameter. */ - length = sizeof(sctp_addip_chunk_t); + length = sizeof(struct sctp_addip_chunk); addr_param = (union sctp_addr_param *)(asconf->skb->data + length); asconf_len -= length; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index d722f380b36e..9c235bb7aafe 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -3629,10 +3629,11 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, * described in [I-D.ietf-tsvwg-sctp-auth]. */ if (!net->sctp.addip_noauth && !chunk->auth) - return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); + return sctp_sf_discard_chunk(net, ep, asoc, type, arg, + commands); /* Make sure that the ASCONF ADDIP chunk has a valid length. */ - if (!sctp_chunk_length_valid(chunk, sizeof(sctp_addip_chunk_t))) + if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_addip_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); @@ -3746,10 +3747,12 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, * described in [I-D.ietf-tsvwg-sctp-auth]. */ if (!net->sctp.addip_noauth && !asconf_ack->auth) - return sctp_sf_discard_chunk(net, ep, asoc, type, arg, commands); + return sctp_sf_discard_chunk(net, ep, asoc, type, arg, + commands); /* Make sure that the ADDIP chunk has a valid length. */ - if (!sctp_chunk_length_valid(asconf_ack, sizeof(sctp_addip_chunk_t))) + if (!sctp_chunk_length_valid(asconf_ack, + sizeof(struct sctp_addip_chunk))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); -- cgit v1.2.3 From 96f7ef4d58100d7168e0c4d01a59fbd8589f5756 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:21 +0800 Subject: sctp: remove the typedef sctp_authhdr_t This patch is to remove the typedef sctp_authhdr_t, and replace with struct sctp_authhdr in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 6 +++--- net/sctp/sm_make_chunk.c | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index e7b439ed6371..b7603f5efebe 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -693,15 +693,15 @@ struct sctp_addip_chunk { * HMAC: n bytes (unsigned integer) This hold the result of the HMAC * calculation. */ -typedef struct sctp_authhdr { +struct sctp_authhdr { __be16 shkey_id; __be16 hmac_id; __u8 hmac[0]; -} sctp_authhdr_t; +}; typedef struct sctp_auth_chunk { struct sctp_chunkhdr chunk_hdr; - sctp_authhdr_t auth_hdr; + struct sctp_authhdr auth_hdr; } sctp_auth_chunk_t; struct sctp_infox { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index ae54c6ecd26a..d17e8d1f2ed9 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1282,16 +1282,16 @@ struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc) return NULL; retval = sctp_make_control(asoc, SCTP_CID_AUTH, 0, - hmac_desc->hmac_len + sizeof(sctp_authhdr_t), - GFP_ATOMIC); + hmac_desc->hmac_len + sizeof(auth_hdr), + GFP_ATOMIC); if (!retval) return NULL; auth_hdr.hmac_id = htons(hmac_desc->hmac_id); auth_hdr.shkey_id = htons(asoc->active_key_id); - retval->subh.auth_hdr = sctp_addto_chunk(retval, sizeof(sctp_authhdr_t), - &auth_hdr); + retval->subh.auth_hdr = sctp_addto_chunk(retval, sizeof(auth_hdr), + &auth_hdr); hmac = skb_put_zero(retval->skb, hmac_desc->hmac_len); -- cgit v1.2.3 From bb96dec74543bb3ceb4ac5caf39341dadb4cb559 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Thu, 3 Aug 2017 15:42:22 +0800 Subject: sctp: remove the typedef sctp_auth_chunk_t This patch is to remove the typedef sctp_auth_chunk_t, and replace with struct sctp_auth_chunk in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/linux/sctp.h | 4 ++-- net/sctp/chunk.c | 2 +- net/sctp/sm_statefuns.c | 9 +++++---- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/linux/sctp.h b/include/linux/sctp.h index b7603f5efebe..82b171e1aa0b 100644 --- a/include/linux/sctp.h +++ b/include/linux/sctp.h @@ -699,10 +699,10 @@ struct sctp_authhdr { __u8 hmac[0]; }; -typedef struct sctp_auth_chunk { +struct sctp_auth_chunk { struct sctp_chunkhdr chunk_hdr; struct sctp_authhdr auth_hdr; -} sctp_auth_chunk_t; +}; struct sctp_infox { struct sctp_info *sctpinfo; diff --git a/net/sctp/chunk.c b/net/sctp/chunk.c index 681b181e7ae3..3afac275ee82 100644 --- a/net/sctp/chunk.c +++ b/net/sctp/chunk.c @@ -201,7 +201,7 @@ struct sctp_datamsg *sctp_datamsg_from_user(struct sctp_association *asoc, struct sctp_hmac *hmac_desc = sctp_auth_asoc_get_hmac(asoc); if (hmac_desc) - max_data -= SCTP_PAD4(sizeof(sctp_auth_chunk_t) + + max_data -= SCTP_PAD4(sizeof(struct sctp_auth_chunk) + hmac_desc->hmac_len); } diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 9c235bb7aafe..8af90a5f23cd 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -4093,7 +4093,7 @@ static sctp_ierror_t sctp_sf_authenticate(struct net *net, /* Pull in the auth header, so we can do some more verification */ auth_hdr = (struct sctp_authhdr *)chunk->skb->data; chunk->subh.auth_hdr = auth_hdr; - skb_pull(chunk->skb, sizeof(struct sctp_authhdr)); + skb_pull(chunk->skb, sizeof(*auth_hdr)); /* Make sure that we support the HMAC algorithm from the auth * chunk. @@ -4112,7 +4112,8 @@ static sctp_ierror_t sctp_sf_authenticate(struct net *net, /* Make sure that the length of the signature matches what * we expect. */ - sig_len = ntohs(chunk->chunk_hdr->length) - sizeof(sctp_auth_chunk_t); + sig_len = ntohs(chunk->chunk_hdr->length) - + sizeof(struct sctp_auth_chunk); hmac = sctp_auth_get_hmac(ntohs(auth_hdr->hmac_id)); if (sig_len != hmac->hmac_len) return SCTP_IERROR_PROTO_VIOLATION; @@ -4134,8 +4135,8 @@ static sctp_ierror_t sctp_sf_authenticate(struct net *net, memset(digest, 0, sig_len); sctp_auth_calculate_hmac(asoc, chunk->skb, - (struct sctp_auth_chunk *)chunk->chunk_hdr, - GFP_ATOMIC); + (struct sctp_auth_chunk *)chunk->chunk_hdr, + GFP_ATOMIC); /* Discard the packet if the digests do not match */ if (memcmp(save_digest, digest, sig_len)) { -- cgit v1.2.3 From 04b1d4e50e82536c12da00ee04a77510c459c844 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:11 +0200 Subject: net: core: Make the FIB notification chain generic The FIB notification chain is currently soley used by IPv4 code. However, we're going to introduce IPv6 FIB offload support, which requires these notification as well. As explained in commit c3852ef7f2f8 ("ipv4: fib: Replay events when registering FIB notifier"), upon registration to the chain, the callee receives a full dump of the FIB tables and rules by traversing all the net namespaces. The integrity of the dump is ensured by a per-namespace sequence counter that is incremented whenever a change to the tables or rules occurs. In order to allow more address families to use the chain, each family is expected to register its fib_notifier_ops in its pernet init. These operations allow the common code to read the family's sequence counter as well as dump its tables and rules in the given net namespace. Additionally, a 'family' parameter is added to sent notifications, so that listeners could distinguish between the different families. Implement the common code that allows listeners to register to the chain and for address families to register their fib_notifier_ops. Subsequent patches will implement these operations in IPv6. In the future, ipmr and ip6mr will be extended to provide these notifications as well. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_router.c | 1 + drivers/net/ethernet/rocker/rocker_main.c | 1 + include/net/fib_notifier.h | 44 ++++++ include/net/ip_fib.h | 30 +--- include/net/net_namespace.h | 1 + include/net/netns/ipv4.h | 1 + net/core/Makefile | 3 +- net/core/fib_notifier.c | 164 +++++++++++++++++++++ net/ipv4/fib_frontend.c | 17 ++- net/ipv4/fib_notifier.c | 94 +++++------- net/ipv4/fib_rules.c | 5 +- net/ipv4/fib_semantics.c | 9 +- net/ipv4/fib_trie.c | 5 +- 13 files changed, 282 insertions(+), 93 deletions(-) create mode 100644 include/net/fib_notifier.h create mode 100644 net/core/fib_notifier.c (limited to 'net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 2f03c7e71584..b79f9b67f285 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -53,6 +53,7 @@ #include #include #include +#include #include "spectrum.h" #include "core.h" diff --git a/drivers/net/ethernet/rocker/rocker_main.c b/drivers/net/ethernet/rocker/rocker_main.c index b1e5c07099fa..ef38c1a41bdd 100644 --- a/drivers/net/ethernet/rocker/rocker_main.c +++ b/drivers/net/ethernet/rocker/rocker_main.c @@ -34,6 +34,7 @@ #include #include #include +#include #include #include diff --git a/include/net/fib_notifier.h b/include/net/fib_notifier.h new file mode 100644 index 000000000000..241475224f74 --- /dev/null +++ b/include/net/fib_notifier.h @@ -0,0 +1,44 @@ +#ifndef __NET_FIB_NOTIFIER_H +#define __NET_FIB_NOTIFIER_H + +#include +#include +#include + +struct fib_notifier_info { + struct net *net; + int family; +}; + +enum fib_event_type { + FIB_EVENT_ENTRY_REPLACE, + FIB_EVENT_ENTRY_APPEND, + FIB_EVENT_ENTRY_ADD, + FIB_EVENT_ENTRY_DEL, + FIB_EVENT_RULE_ADD, + FIB_EVENT_RULE_DEL, + FIB_EVENT_NH_ADD, + FIB_EVENT_NH_DEL, +}; + +struct fib_notifier_ops { + int family; + struct list_head list; + unsigned int (*fib_seq_read)(struct net *net); + int (*fib_dump)(struct net *net, struct notifier_block *nb); + struct rcu_head rcu; +}; + +int call_fib_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info); +int call_fib_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info); +int register_fib_notifier(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb)); +int unregister_fib_notifier(struct notifier_block *nb); +struct fib_notifier_ops * +fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net); +void fib_notifier_ops_unregister(struct fib_notifier_ops *ops); + +#endif diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index ef8992d49bc3..c0295c3ec5f3 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -188,10 +189,6 @@ __be32 fib_info_update_nh_saddr(struct net *net, struct fib_nh *nh); #define FIB_RES_PREFSRC(net, res) ((res).fi->fib_prefsrc ? : \ FIB_RES_SADDR(net, res)) -struct fib_notifier_info { - struct net *net; -}; - struct fib_entry_notifier_info { struct fib_notifier_info info; /* must be first */ u32 dst; @@ -212,25 +209,14 @@ struct fib_nh_notifier_info { struct fib_nh *fib_nh; }; -enum fib_event_type { - FIB_EVENT_ENTRY_REPLACE, - FIB_EVENT_ENTRY_APPEND, - FIB_EVENT_ENTRY_ADD, - FIB_EVENT_ENTRY_DEL, - FIB_EVENT_RULE_ADD, - FIB_EVENT_RULE_DEL, - FIB_EVENT_NH_ADD, - FIB_EVENT_NH_DEL, -}; - -int register_fib_notifier(struct notifier_block *nb, - void (*cb)(struct notifier_block *nb)); -int unregister_fib_notifier(struct notifier_block *nb); -int call_fib_notifier(struct notifier_block *nb, struct net *net, - enum fib_event_type event_type, - struct fib_notifier_info *info); -int call_fib_notifiers(struct net *net, enum fib_event_type event_type, +int call_fib4_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, struct fib_notifier_info *info); +int call_fib4_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info); + +int __net_init fib4_notifier_init(struct net *net); +void __net_exit fib4_notifier_exit(struct net *net); void fib_notify(struct net *net, struct notifier_block *nb); #ifdef CONFIG_IP_MULTIPLE_TABLES diff --git a/include/net/net_namespace.h b/include/net/net_namespace.h index 1c401bd4c2e0..57faa375eab9 100644 --- a/include/net/net_namespace.h +++ b/include/net/net_namespace.h @@ -88,6 +88,7 @@ struct net { /* core fib_rules */ struct list_head rules_ops; + struct list_head fib_notifier_ops; /* protected by net_mutex */ struct net_device *loopback_dev; /* The loopback */ struct netns_core core; diff --git a/include/net/netns/ipv4.h b/include/net/netns/ipv4.h index 9a14a0850b0e..20d061c805e3 100644 --- a/include/net/netns/ipv4.h +++ b/include/net/netns/ipv4.h @@ -159,6 +159,7 @@ struct netns_ipv4 { int sysctl_fib_multipath_hash_policy; #endif + struct fib_notifier_ops *notifier_ops; unsigned int fib_seq; /* protected by rtnl_mutex */ atomic_t rt_genid; diff --git a/net/core/Makefile b/net/core/Makefile index d501c4278015..56d771a887b6 100644 --- a/net/core/Makefile +++ b/net/core/Makefile @@ -9,7 +9,8 @@ obj-$(CONFIG_SYSCTL) += sysctl_net_core.o obj-y += dev.o ethtool.o dev_addr_lists.o dst.o netevent.o \ neighbour.o rtnetlink.o utils.o link_watch.o filter.o \ - sock_diag.o dev_ioctl.o tso.o sock_reuseport.o + sock_diag.o dev_ioctl.o tso.o sock_reuseport.o \ + fib_notifier.o obj-y += net-sysfs.o obj-$(CONFIG_PROC_FS) += net-procfs.o diff --git a/net/core/fib_notifier.c b/net/core/fib_notifier.c new file mode 100644 index 000000000000..292aab83702f --- /dev/null +++ b/net/core/fib_notifier.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include + +static ATOMIC_NOTIFIER_HEAD(fib_chain); + +int call_fib_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info) +{ + info->net = net; + return nb->notifier_call(nb, event_type, info); +} +EXPORT_SYMBOL(call_fib_notifier); + +int call_fib_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info) +{ + info->net = net; + return atomic_notifier_call_chain(&fib_chain, event_type, info); +} +EXPORT_SYMBOL(call_fib_notifiers); + +static unsigned int fib_seq_sum(void) +{ + struct fib_notifier_ops *ops; + unsigned int fib_seq = 0; + struct net *net; + + rtnl_lock(); + for_each_net(net) { + list_for_each_entry(ops, &net->fib_notifier_ops, list) + fib_seq += ops->fib_seq_read(net); + } + rtnl_unlock(); + + return fib_seq; +} + +static int fib_net_dump(struct net *net, struct notifier_block *nb) +{ + struct fib_notifier_ops *ops; + + list_for_each_entry_rcu(ops, &net->fib_notifier_ops, list) { + int err = ops->fib_dump(net, nb); + + if (err) + return err; + } + + return 0; +} + +static bool fib_dump_is_consistent(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb), + unsigned int fib_seq) +{ + atomic_notifier_chain_register(&fib_chain, nb); + if (fib_seq == fib_seq_sum()) + return true; + atomic_notifier_chain_unregister(&fib_chain, nb); + if (cb) + cb(nb); + return false; +} + +#define FIB_DUMP_MAX_RETRIES 5 +int register_fib_notifier(struct notifier_block *nb, + void (*cb)(struct notifier_block *nb)) +{ + int retries = 0; + int err; + + do { + unsigned int fib_seq = fib_seq_sum(); + struct net *net; + + rcu_read_lock(); + for_each_net_rcu(net) { + err = fib_net_dump(net, nb); + if (err) + goto err_fib_net_dump; + } + rcu_read_unlock(); + + if (fib_dump_is_consistent(nb, cb, fib_seq)) + return 0; + } while (++retries < FIB_DUMP_MAX_RETRIES); + + return -EBUSY; + +err_fib_net_dump: + rcu_read_unlock(); + return err; +} +EXPORT_SYMBOL(register_fib_notifier); + +int unregister_fib_notifier(struct notifier_block *nb) +{ + return atomic_notifier_chain_unregister(&fib_chain, nb); +} +EXPORT_SYMBOL(unregister_fib_notifier); + +static int __fib_notifier_ops_register(struct fib_notifier_ops *ops, + struct net *net) +{ + struct fib_notifier_ops *o; + + list_for_each_entry(o, &net->fib_notifier_ops, list) + if (ops->family == o->family) + return -EEXIST; + list_add_tail_rcu(&ops->list, &net->fib_notifier_ops); + return 0; +} + +struct fib_notifier_ops * +fib_notifier_ops_register(const struct fib_notifier_ops *tmpl, struct net *net) +{ + struct fib_notifier_ops *ops; + int err; + + ops = kmemdup(tmpl, sizeof(*ops), GFP_KERNEL); + if (!ops) + return ERR_PTR(-ENOMEM); + + err = __fib_notifier_ops_register(ops, net); + if (err) + goto err_register; + + return ops; + +err_register: + kfree(ops); + return ERR_PTR(err); +} +EXPORT_SYMBOL(fib_notifier_ops_register); + +void fib_notifier_ops_unregister(struct fib_notifier_ops *ops) +{ + list_del_rcu(&ops->list); + kfree_rcu(ops, rcu); +} +EXPORT_SYMBOL(fib_notifier_ops_unregister); + +static int __net_init fib_notifier_net_init(struct net *net) +{ + INIT_LIST_HEAD(&net->fib_notifier_ops); + return 0; +} + +static struct pernet_operations fib_notifier_net_ops = { + .init = fib_notifier_net_init, +}; + +static int __init fib_notifier_init(void) +{ + return register_pernet_subsys(&fib_notifier_net_ops); +} + +subsys_initcall(fib_notifier_init); diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 044d2a159a3c..2cba559f14df 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1247,22 +1247,28 @@ static int __net_init ip_fib_net_init(struct net *net) int err; size_t size = sizeof(struct hlist_head) * FIB_TABLE_HASHSZ; - net->ipv4.fib_seq = 0; + err = fib4_notifier_init(net); + if (err) + return err; /* Avoid false sharing : Use at least a full cache line */ size = max_t(size_t, size, L1_CACHE_BYTES); net->ipv4.fib_table_hash = kzalloc(size, GFP_KERNEL); - if (!net->ipv4.fib_table_hash) - return -ENOMEM; + if (!net->ipv4.fib_table_hash) { + err = -ENOMEM; + goto err_table_hash_alloc; + } err = fib4_rules_init(net); if (err < 0) - goto fail; + goto err_rules_init; return 0; -fail: +err_rules_init: kfree(net->ipv4.fib_table_hash); +err_table_hash_alloc: + fib4_notifier_exit(net); return err; } @@ -1292,6 +1298,7 @@ static void ip_fib_net_exit(struct net *net) #endif rtnl_unlock(); kfree(net->ipv4.fib_table_hash); + fib4_notifier_exit(net); } static int __net_init fib_net_init(struct net *net) diff --git a/net/ipv4/fib_notifier.c b/net/ipv4/fib_notifier.c index e0714d975947..7cf1954bbadc 100644 --- a/net/ipv4/fib_notifier.c +++ b/net/ipv4/fib_notifier.c @@ -1,86 +1,66 @@ #include #include -#include +#include #include #include +#include #include #include -static ATOMIC_NOTIFIER_HEAD(fib_chain); - -int call_fib_notifier(struct notifier_block *nb, struct net *net, - enum fib_event_type event_type, - struct fib_notifier_info *info) +int call_fib4_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info) { - info->net = net; - return nb->notifier_call(nb, event_type, info); + info->family = AF_INET; + return call_fib_notifier(nb, net, event_type, info); } -int call_fib_notifiers(struct net *net, enum fib_event_type event_type, - struct fib_notifier_info *info) +int call_fib4_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info) { + ASSERT_RTNL(); + + info->family = AF_INET; net->ipv4.fib_seq++; - info->net = net; - return atomic_notifier_call_chain(&fib_chain, event_type, info); + return call_fib_notifiers(net, event_type, info); } -static unsigned int fib_seq_sum(void) +static unsigned int fib4_seq_read(struct net *net) { - unsigned int fib_seq = 0; - struct net *net; + ASSERT_RTNL(); - rtnl_lock(); - for_each_net(net) - fib_seq += net->ipv4.fib_seq; - rtnl_unlock(); - - return fib_seq; + return net->ipv4.fib_seq; } -static bool fib_dump_is_consistent(struct notifier_block *nb, - void (*cb)(struct notifier_block *nb), - unsigned int fib_seq) +static int fib4_dump(struct net *net, struct notifier_block *nb) { - atomic_notifier_chain_register(&fib_chain, nb); - if (fib_seq == fib_seq_sum()) - return true; - atomic_notifier_chain_unregister(&fib_chain, nb); - if (cb) - cb(nb); - return false; + fib_rules_notify(net, nb); + fib_notify(net, nb); + + return 0; } -#define FIB_DUMP_MAX_RETRIES 5 -int register_fib_notifier(struct notifier_block *nb, - void (*cb)(struct notifier_block *nb)) -{ - int retries = 0; +static const struct fib_notifier_ops fib4_notifier_ops_template = { + .family = AF_INET, + .fib_seq_read = fib4_seq_read, + .fib_dump = fib4_dump, +}; - do { - unsigned int fib_seq = fib_seq_sum(); - struct net *net; +int __net_init fib4_notifier_init(struct net *net) +{ + struct fib_notifier_ops *ops; - /* Mutex semantics guarantee that every change done to - * FIB tries before we read the change sequence counter - * is now visible to us. - */ - rcu_read_lock(); - for_each_net_rcu(net) { - fib_rules_notify(net, nb); - fib_notify(net, nb); - } - rcu_read_unlock(); + net->ipv4.fib_seq = 0; - if (fib_dump_is_consistent(nb, cb, fib_seq)) - return 0; - } while (++retries < FIB_DUMP_MAX_RETRIES); + ops = fib_notifier_ops_register(&fib4_notifier_ops_template, net); + if (IS_ERR(ops)) + return PTR_ERR(ops); + net->ipv4.notifier_ops = ops; - return -EBUSY; + return 0; } -EXPORT_SYMBOL(register_fib_notifier); -int unregister_fib_notifier(struct notifier_block *nb) +void __net_exit fib4_notifier_exit(struct net *net) { - return atomic_notifier_chain_unregister(&fib_chain, nb); + fib_notifier_ops_unregister(net->ipv4.notifier_ops); } -EXPORT_SYMBOL(unregister_fib_notifier); diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index 778ecf977eb2..acdbf5a24ac9 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -32,6 +32,7 @@ #include #include #include +#include struct fib4_rule { struct fib_rule common; @@ -193,7 +194,7 @@ static int call_fib_rule_notifier(struct notifier_block *nb, struct net *net, .rule = rule, }; - return call_fib_notifier(nb, net, event_type, &info.info); + return call_fib4_notifier(nb, net, event_type, &info.info); } static int call_fib_rule_notifiers(struct net *net, @@ -204,7 +205,7 @@ static int call_fib_rule_notifiers(struct net *net, .rule = rule, }; - return call_fib_notifiers(net, event_type, &info.info); + return call_fib4_notifiers(net, event_type, &info.info); } /* Called with rcu_read_lock() */ diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index f62dc2463280..632b454ce77c 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -44,6 +44,7 @@ #include #include #include +#include #include "fib_lookup.h" @@ -1451,14 +1452,14 @@ static int call_fib_nh_notifiers(struct fib_nh *fib_nh, if (IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && fib_nh->nh_flags & RTNH_F_LINKDOWN) break; - return call_fib_notifiers(dev_net(fib_nh->nh_dev), event_type, - &info.info); + return call_fib4_notifiers(dev_net(fib_nh->nh_dev), event_type, + &info.info); case FIB_EVENT_NH_DEL: if ((in_dev && IN_DEV_IGNORE_ROUTES_WITH_LINKDOWN(in_dev) && fib_nh->nh_flags & RTNH_F_LINKDOWN) || (fib_nh->nh_flags & RTNH_F_DEAD)) - return call_fib_notifiers(dev_net(fib_nh->nh_dev), - event_type, &info.info); + return call_fib4_notifiers(dev_net(fib_nh->nh_dev), + event_type, &info.info); default: break; } diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 64668c69dda6..1a6ffb0dab9c 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -81,6 +81,7 @@ #include #include #include +#include #include #include "fib_lookup.h" @@ -97,7 +98,7 @@ static int call_fib_entry_notifier(struct notifier_block *nb, struct net *net, .type = type, .tb_id = tb_id, }; - return call_fib_notifier(nb, net, event_type, &info.info); + return call_fib4_notifier(nb, net, event_type, &info.info); } static int call_fib_entry_notifiers(struct net *net, @@ -113,7 +114,7 @@ static int call_fib_entry_notifiers(struct net *net, .type = type, .tb_id = tb_id, }; - return call_fib_notifiers(net, event_type, &info.info); + return call_fib4_notifiers(net, event_type, &info.info); } #define MAX_STAT_DEPTH 32 -- cgit v1.2.3 From 1b2a4440858857f2f93bb2ec5bb3a60f4fcc25be Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:14 +0200 Subject: net: fib_rules: Implement notification logic in core Unlike the routing tables, the FIB rules share a common core, so instead of replicating the same logic for each address family we can simply dump the rules and send notifications from the core itself. To protect the integrity of the dump, a rules-specific sequence counter is added for each address family and incremented whenever a rule is added or deleted (under RTNL). Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/fib_rules.h | 9 +++++++ include/net/ip_fib.h | 24 +++++++++---------- net/core/fib_rules.c | 63 +++++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/fib_notifier.c | 9 +++++-- net/ipv4/fib_rules.c | 45 ++++++++--------------------------- 5 files changed, 101 insertions(+), 49 deletions(-) (limited to 'net') diff --git a/include/net/fib_rules.h b/include/net/fib_rules.h index c487bfa2f479..3d7f1cefc6f5 100644 --- a/include/net/fib_rules.h +++ b/include/net/fib_rules.h @@ -8,6 +8,7 @@ #include #include #include +#include struct fib_kuid_range { kuid_t start; @@ -57,6 +58,7 @@ struct fib_rules_ops { int addr_size; int unresolved_rules; int nr_goto_rules; + unsigned int fib_rules_seq; int (*action)(struct fib_rule *, struct flowi *, int, @@ -89,6 +91,11 @@ struct fib_rules_ops { struct rcu_head rcu; }; +struct fib_rule_notifier_info { + struct fib_notifier_info info; /* must be first */ + struct fib_rule *rule; +}; + #define FRA_GENERIC_POLICY \ [FRA_IIFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, \ [FRA_OIFNAME] = { .type = NLA_STRING, .len = IFNAMSIZ - 1 }, \ @@ -143,6 +150,8 @@ int fib_rules_lookup(struct fib_rules_ops *, struct flowi *, int flags, int fib_default_rule_add(struct fib_rules_ops *, u32 pref, u32 table, u32 flags); bool fib_rule_matchall(const struct fib_rule *rule); +int fib_rules_dump(struct net *net, struct notifier_block *nb, int family); +unsigned int fib_rules_seq_read(struct net *net, int family); int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack); diff --git a/include/net/ip_fib.h b/include/net/ip_fib.h index c0295c3ec5f3..1a7f7e424320 100644 --- a/include/net/ip_fib.h +++ b/include/net/ip_fib.h @@ -199,11 +199,6 @@ struct fib_entry_notifier_info { u32 tb_id; }; -struct fib_rule_notifier_info { - struct fib_notifier_info info; /* must be first */ - struct fib_rule *rule; -}; - struct fib_nh_notifier_info { struct fib_notifier_info info; /* must be first */ struct fib_nh *fib_nh; @@ -219,13 +214,6 @@ int __net_init fib4_notifier_init(struct net *net); void __net_exit fib4_notifier_exit(struct net *net); void fib_notify(struct net *net, struct notifier_block *nb); -#ifdef CONFIG_IP_MULTIPLE_TABLES -void fib_rules_notify(struct net *net, struct notifier_block *nb); -#else -static inline void fib_rules_notify(struct net *net, struct notifier_block *nb) -{ -} -#endif struct fib_table { struct hlist_node tb_hlist; @@ -298,6 +286,16 @@ static inline bool fib4_rule_default(const struct fib_rule *rule) return true; } +static inline int fib4_rules_dump(struct net *net, struct notifier_block *nb) +{ + return 0; +} + +static inline unsigned int fib4_rules_seq_read(struct net *net) +{ + return 0; +} + #else /* CONFIG_IP_MULTIPLE_TABLES */ int __net_init fib4_rules_init(struct net *net); void __net_exit fib4_rules_exit(struct net *net); @@ -343,6 +341,8 @@ out: } bool fib4_rule_default(const struct fib_rule *rule); +int fib4_rules_dump(struct net *net, struct notifier_block *nb); +unsigned int fib4_rules_seq_read(struct net *net); #endif /* CONFIG_IP_MULTIPLE_TABLES */ diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index fdcb1bcd2afa..fc0b65093417 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -299,6 +299,67 @@ out: } EXPORT_SYMBOL_GPL(fib_rules_lookup); +static int call_fib_rule_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_rule *rule, int family) +{ + struct fib_rule_notifier_info info = { + .info.family = family, + .rule = rule, + }; + + return call_fib_notifier(nb, net, event_type, &info.info); +} + +static int call_fib_rule_notifiers(struct net *net, + enum fib_event_type event_type, + struct fib_rule *rule, + struct fib_rules_ops *ops) +{ + struct fib_rule_notifier_info info = { + .info.family = ops->family, + .rule = rule, + }; + + ops->fib_rules_seq++; + return call_fib_notifiers(net, event_type, &info.info); +} + +/* Called with rcu_read_lock() */ +int fib_rules_dump(struct net *net, struct notifier_block *nb, int family) +{ + struct fib_rules_ops *ops; + struct fib_rule *rule; + + ops = lookup_rules_ops(net, family); + if (!ops) + return -EAFNOSUPPORT; + list_for_each_entry_rcu(rule, &ops->rules_list, list) + call_fib_rule_notifier(nb, net, FIB_EVENT_RULE_ADD, rule, + family); + rules_ops_put(ops); + + return 0; +} +EXPORT_SYMBOL_GPL(fib_rules_dump); + +unsigned int fib_rules_seq_read(struct net *net, int family) +{ + unsigned int fib_rules_seq; + struct fib_rules_ops *ops; + + ASSERT_RTNL(); + + ops = lookup_rules_ops(net, family); + if (!ops) + return 0; + fib_rules_seq = ops->fib_rules_seq; + rules_ops_put(ops); + + return fib_rules_seq; +} +EXPORT_SYMBOL_GPL(fib_rules_seq_read); + static int validate_rulemsg(struct fib_rule_hdr *frh, struct nlattr **tb, struct fib_rules_ops *ops) { @@ -548,6 +609,7 @@ int fib_nl_newrule(struct sk_buff *skb, struct nlmsghdr *nlh, if (rule->tun_id) ip_tunnel_need_metadata(); + call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule, ops); notify_rule_change(RTM_NEWRULE, rule, ops, nlh, NETLINK_CB(skb).portid); flush_route_cache(ops); rules_ops_put(ops); @@ -687,6 +749,7 @@ int fib_nl_delrule(struct sk_buff *skb, struct nlmsghdr *nlh, } } + call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule, ops); notify_rule_change(RTM_DELRULE, rule, ops, nlh, NETLINK_CB(skb).portid); fib_rule_put(rule); diff --git a/net/ipv4/fib_notifier.c b/net/ipv4/fib_notifier.c index 7cf1954bbadc..5d7afb145562 100644 --- a/net/ipv4/fib_notifier.c +++ b/net/ipv4/fib_notifier.c @@ -29,12 +29,17 @@ static unsigned int fib4_seq_read(struct net *net) { ASSERT_RTNL(); - return net->ipv4.fib_seq; + return net->ipv4.fib_seq + fib4_rules_seq_read(net); } static int fib4_dump(struct net *net, struct notifier_block *nb) { - fib_rules_notify(net, nb); + int err; + + err = fib4_rules_dump(net, nb); + if (err) + return err; + fib_notify(net, nb); return 0; diff --git a/net/ipv4/fib_rules.c b/net/ipv4/fib_rules.c index acdbf5a24ac9..35d646a62ad4 100644 --- a/net/ipv4/fib_rules.c +++ b/net/ipv4/fib_rules.c @@ -32,7 +32,6 @@ #include #include #include -#include struct fib4_rule { struct fib_rule common; @@ -69,6 +68,16 @@ bool fib4_rule_default(const struct fib_rule *rule) } EXPORT_SYMBOL_GPL(fib4_rule_default); +int fib4_rules_dump(struct net *net, struct notifier_block *nb) +{ + return fib_rules_dump(net, nb, AF_INET); +} + +unsigned int fib4_rules_seq_read(struct net *net) +{ + return fib_rules_seq_read(net, AF_INET); +} + int __fib_lookup(struct net *net, struct flowi4 *flp, struct fib_result *res, unsigned int flags) { @@ -186,38 +195,6 @@ static struct fib_table *fib_empty_table(struct net *net) return NULL; } -static int call_fib_rule_notifier(struct notifier_block *nb, struct net *net, - enum fib_event_type event_type, - struct fib_rule *rule) -{ - struct fib_rule_notifier_info info = { - .rule = rule, - }; - - return call_fib4_notifier(nb, net, event_type, &info.info); -} - -static int call_fib_rule_notifiers(struct net *net, - enum fib_event_type event_type, - struct fib_rule *rule) -{ - struct fib_rule_notifier_info info = { - .rule = rule, - }; - - return call_fib4_notifiers(net, event_type, &info.info); -} - -/* Called with rcu_read_lock() */ -void fib_rules_notify(struct net *net, struct notifier_block *nb) -{ - struct fib_rules_ops *ops = net->ipv4.rules_ops; - struct fib_rule *rule; - - list_for_each_entry_rcu(rule, &ops->rules_list, list) - call_fib_rule_notifier(nb, net, FIB_EVENT_RULE_ADD, rule); -} - static const struct nla_policy fib4_rule_policy[FRA_MAX+1] = { FRA_GENERIC_POLICY, [FRA_FLOW] = { .type = NLA_U32 }, @@ -274,7 +251,6 @@ static int fib4_rule_configure(struct fib_rule *rule, struct sk_buff *skb, rule4->tos = frh->tos; net->ipv4.fib_has_custom_rules = true; - call_fib_rule_notifiers(net, FIB_EVENT_RULE_ADD, rule); err = 0; errout: @@ -296,7 +272,6 @@ static int fib4_rule_delete(struct fib_rule *rule) net->ipv4.fib_num_tclassid_users--; #endif net->ipv4.fib_has_custom_rules = true; - call_fib_rule_notifiers(net, FIB_EVENT_RULE_DEL, rule); errout: return err; } -- cgit v1.2.3 From e3ea973159d53559c5ae9a9dbc824da9aba6cac0 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:15 +0200 Subject: ipv6: fib_rules: Check if rule is a default rule As explained in commit 3c71006d15fd ("ipv4: fib_rules: Check if rule is a default rule"), drivers supporting IPv6 FIB offload need to be able to sanitize the rules they don't support and potentially flush their tables. Add an IPv6 helper to check if a FIB rule is a default rule. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 5 +++++ net/ipv6/fib6_rules.c | 20 ++++++++++++++++++++ 2 files changed, 25 insertions(+) (limited to 'net') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 1a88008cc6f5..6000b0dc51ee 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -295,6 +295,7 @@ int ipv6_route_open(struct inode *inode, struct file *file); #ifdef CONFIG_IPV6_MULTIPLE_TABLES int fib6_rules_init(void); void fib6_rules_cleanup(void); +bool fib6_rule_default(const struct fib_rule *rule); #else static inline int fib6_rules_init(void) { @@ -304,5 +305,9 @@ static inline void fib6_rules_cleanup(void) { return ; } +static inline bool fib6_rule_default(const struct fib_rule *rule) +{ + return true; +} #endif #endif diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index ec849d88a662..ef1fcee6bf16 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -29,6 +29,26 @@ struct fib6_rule { u8 tclass; }; +static bool fib6_rule_matchall(const struct fib_rule *rule) +{ + struct fib6_rule *r = container_of(rule, struct fib6_rule, common); + + if (r->dst.plen || r->src.plen || r->tclass) + return false; + return fib_rule_matchall(rule); +} + +bool fib6_rule_default(const struct fib_rule *rule) +{ + if (!fib6_rule_matchall(rule) || rule->action != FR_ACT_TO_TBL || + rule->l3mdev) + return false; + if (rule->table != RT6_TABLE_LOCAL && rule->table != RT6_TABLE_MAIN) + return false; + return true; +} +EXPORT_SYMBOL_GPL(fib6_rule_default); + struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, int flags, pol_lookup_t lookup) { -- cgit v1.2.3 From 16ab6d7d4d8cc037bb4be12c2b849ac92787e1ff Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:16 +0200 Subject: ipv6: fib: Add FIB notifiers callbacks We're about to add IPv6 FIB offload support, so implement the necessary callbacks in IPv6 code, which will later allow us to add routes and rules notifications. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 11 ++++++++++ include/net/netns/ipv6.h | 1 + net/ipv6/Makefile | 2 +- net/ipv6/fib6_notifier.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++ net/ipv6/ip6_fib.c | 7 ++++++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 net/ipv6/fib6_notifier.c (limited to 'net') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 6000b0dc51ee..be8ddf3253dc 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -16,10 +16,12 @@ #include #include #include +#include #include #include #include #include +#include #ifdef CONFIG_IPV6_MULTIPLE_TABLES #define FIB6_TABLE_HASHSZ 256 @@ -292,6 +294,15 @@ int fib6_init(void); int ipv6_route_open(struct inode *inode, struct file *file); +int call_fib6_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info); +int call_fib6_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info); + +int __net_init fib6_notifier_init(struct net *net); +void __net_exit fib6_notifier_exit(struct net *net); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES int fib6_rules_init(void); void fib6_rules_cleanup(void); diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index de7745e2edcc..abdf3b40303b 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -86,6 +86,7 @@ struct netns_ipv6 { atomic_t dev_addr_genid; atomic_t fib6_sernum; struct seg6_pernet_data *seg6_data; + struct fib_notifier_ops *notifier_ops; }; #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV6) diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index 217e9ff0e24b..f8b24c2e0d77 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -9,7 +9,7 @@ ipv6-objs := af_inet6.o anycast.o ip6_output.o ip6_input.o addrconf.o \ route.o ip6_fib.o ipv6_sockglue.o ndisc.o udp.o udplite.o \ raw.o icmp.o mcast.o reassembly.o tcp_ipv6.o ping.o \ exthdrs.o datagram.o ip6_flowlabel.o inet6_connection_sock.o \ - udp_offload.o seg6.o + udp_offload.o seg6.o fib6_notifier.o ipv6-offload := ip6_offload.o tcpv6_offload.o exthdrs_offload.o diff --git a/net/ipv6/fib6_notifier.c b/net/ipv6/fib6_notifier.c new file mode 100644 index 000000000000..c2bb1ab5b5eb --- /dev/null +++ b/net/ipv6/fib6_notifier.c @@ -0,0 +1,55 @@ +#include +#include +#include +#include +#include +#include +#include + +int call_fib6_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct fib_notifier_info *info) +{ + info->family = AF_INET6; + return call_fib_notifier(nb, net, event_type, info); +} + +int call_fib6_notifiers(struct net *net, enum fib_event_type event_type, + struct fib_notifier_info *info) +{ + info->family = AF_INET6; + return call_fib_notifiers(net, event_type, info); +} + +static unsigned int fib6_seq_read(struct net *net) +{ + return 0; +} + +static int fib6_dump(struct net *net, struct notifier_block *nb) +{ + return 0; +} + +static const struct fib_notifier_ops fib6_notifier_ops_template = { + .family = AF_INET6, + .fib_seq_read = fib6_seq_read, + .fib_dump = fib6_dump, +}; + +int __net_init fib6_notifier_init(struct net *net) +{ + struct fib_notifier_ops *ops; + + ops = fib_notifier_ops_register(&fib6_notifier_ops_template, net); + if (IS_ERR(ops)) + return PTR_ERR(ops); + net->ipv6.notifier_ops = ops; + + return 0; +} + +void __net_exit fib6_notifier_exit(struct net *net) +{ + fib_notifier_ops_unregister(net->ipv6.notifier_ops); +} diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index ebb299cf72b7..f93976e3f65c 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1839,6 +1839,11 @@ static void fib6_gc_timer_cb(unsigned long arg) static int __net_init fib6_net_init(struct net *net) { size_t size = sizeof(struct hlist_head) * FIB6_TABLE_HASHSZ; + int err; + + err = fib6_notifier_init(net); + if (err) + return err; spin_lock_init(&net->ipv6.fib6_gc_lock); rwlock_init(&net->ipv6.fib6_walker_lock); @@ -1891,6 +1896,7 @@ out_fib_table_hash: out_rt6_stats: kfree(net->ipv6.rt6_stats); out_timer: + fib6_notifier_exit(net); return -ENOMEM; } @@ -1907,6 +1913,7 @@ static void fib6_net_exit(struct net *net) kfree(net->ipv6.fib6_main_tbl); kfree(net->ipv6.fib_table_hash); kfree(net->ipv6.rt6_stats); + fib6_notifier_exit(net); } static struct pernet_operations fib6_net_ops = { -- cgit v1.2.3 From df77fe4d9865c6354372876632bcbceeda84f6c8 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:17 +0200 Subject: ipv6: fib: Add in-kernel notifications for route add / delete As with IPv4, allow listeners of the FIB notification chain to receive notifications whenever a route is added, replaced or deleted. This is done by placing calls to the FIB notification chain in the two lowest level functions that end up performing these operations - namely, fib6_add_rt2node() and fib6_del_route(). Unlike IPv4, APPEND notifications aren't sent as the kernel doesn't distinguish between "append" (NLM_F_CREATE|NLM_F_APPEND) and "prepend" (NLM_F_CREATE). If NLM_F_EXCL isn't set, duplicate routes are always added after the existing duplicate routes. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 5 +++++ net/ipv6/ip6_fib.c | 17 +++++++++++++++++ 2 files changed, 22 insertions(+) (limited to 'net') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index be8ddf3253dc..e2b292b79e99 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -258,6 +258,11 @@ typedef struct rt6_info *(*pol_lookup_t)(struct net *, struct fib6_table *, struct flowi6 *, int); +struct fib6_entry_notifier_info { + struct fib_notifier_info info; /* must be first */ + struct rt6_info *rt; +}; + /* * exported functions */ diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index f93976e3f65c..595a57cbbc7b 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -302,6 +303,17 @@ static void __net_init fib6_tables_init(struct net *net) #endif +static int call_fib6_entry_notifiers(struct net *net, + enum fib_event_type event_type, + struct rt6_info *rt) +{ + struct fib6_entry_notifier_info info = { + .rt = rt, + }; + + return call_fib6_notifiers(net, event_type, &info.info); +} + static int fib6_dump_node(struct fib6_walker *w) { int res; @@ -879,6 +891,8 @@ add: *ins = rt; rt->rt6i_node = fn; atomic_inc(&rt->rt6i_ref); + call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_ADD, + rt); if (!info->skip_notify) inet6_rt_notify(RTM_NEWROUTE, rt, info, nlflags); info->nl_net->ipv6.rt6_stats->fib_rt_entries++; @@ -906,6 +920,8 @@ add: rt->rt6i_node = fn; rt->dst.rt6_next = iter->dst.rt6_next; atomic_inc(&rt->rt6i_ref); + call_fib6_entry_notifiers(info->nl_net, FIB_EVENT_ENTRY_REPLACE, + rt); if (!info->skip_notify) inet6_rt_notify(RTM_NEWROUTE, rt, info, NLM_F_REPLACE); if (!(fn->fn_flags & RTN_RTINFO)) { @@ -1459,6 +1475,7 @@ static void fib6_del_route(struct fib6_node *fn, struct rt6_info **rtp, fib6_purge_rt(rt, fn, net); + call_fib6_entry_notifiers(net, FIB_EVENT_ENTRY_DEL, rt); if (!info->skip_notify) inet6_rt_notify(RTM_DELROUTE, rt, info, 0); rt6_release(rt); -- cgit v1.2.3 From dcb18f762f6ac83a6dc9cdc26dd694dcc167beb7 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:18 +0200 Subject: ipv6: fib_rules: Dump rules during registration to FIB chain Allow users of the FIB notification chain to receive a complete view of the IPv6 FIB rules upon registration to the chain. The integrity of the dump is ensured by a per-family sequence counter that is incremented (under RTNL) whenever a rule is added or deleted. All the sequence counters are read (under RTNL) and summed, prior and after the dump. In case the counters differ, then the dump is either restarted or the registration fails. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 10 ++++++++++ net/ipv6/fib6_notifier.c | 4 ++-- net/ipv6/fib6_rules.c | 11 +++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index e2b292b79e99..dbe5537809f5 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -312,6 +312,8 @@ void __net_exit fib6_notifier_exit(struct net *net); int fib6_rules_init(void); void fib6_rules_cleanup(void); bool fib6_rule_default(const struct fib_rule *rule); +int fib6_rules_dump(struct net *net, struct notifier_block *nb); +unsigned int fib6_rules_seq_read(struct net *net); #else static inline int fib6_rules_init(void) { @@ -325,5 +327,13 @@ static inline bool fib6_rule_default(const struct fib_rule *rule) { return true; } +static inline int fib6_rules_dump(struct net *net, struct notifier_block *nb) +{ + return 0; +} +static inline unsigned int fib6_rules_seq_read(struct net *net) +{ + return 0; +} #endif #endif diff --git a/net/ipv6/fib6_notifier.c b/net/ipv6/fib6_notifier.c index c2bb1ab5b5eb..298efc678f3b 100644 --- a/net/ipv6/fib6_notifier.c +++ b/net/ipv6/fib6_notifier.c @@ -23,12 +23,12 @@ int call_fib6_notifiers(struct net *net, enum fib_event_type event_type, static unsigned int fib6_seq_read(struct net *net) { - return 0; + return fib6_rules_seq_read(net); } static int fib6_dump(struct net *net, struct notifier_block *nb) { - return 0; + return fib6_rules_dump(net, nb); } static const struct fib_notifier_ops fib6_notifier_ops_template = { diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index ef1fcee6bf16..2f29e4e33bd3 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -14,6 +14,7 @@ */ #include +#include #include #include @@ -49,6 +50,16 @@ bool fib6_rule_default(const struct fib_rule *rule) } EXPORT_SYMBOL_GPL(fib6_rule_default); +int fib6_rules_dump(struct net *net, struct notifier_block *nb) +{ + return fib_rules_dump(net, nb, AF_INET6); +} + +unsigned int fib6_rules_seq_read(struct net *net) +{ + return fib_rules_seq_read(net, AF_INET6); +} + struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, int flags, pol_lookup_t lookup) { -- cgit v1.2.3 From e1ee0a5ba35d999caef94d659b4cb842e63aeb68 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:19 +0200 Subject: ipv6: fib: Dump tables during registration to FIB chain Dump all the FIB tables in each net namespace upon registration to the FIB notification chain so that the callee will have a complete view of the tables. The integrity of the dump is ensured by a per-table sequence counter that is incremented (under write lock) whenever a route is added or deleted from the table. All the sequence counters are read (under each table's read lock) and summed, prior and after the dump. In case the counters differ, then the dump is either restarted or the registration fails. While it's possible for a table to be modified after its counter has been read, this isn't really a problem. In case it happened before it was read the second time, then the comparison at the end will fail. If it happened afterwards, then we're guaranteed to be notified about the change, as the notification block is registered prior to the second read. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 4 +++ net/ipv6/fib6_notifier.c | 10 ++++-- net/ipv6/ip6_fib.c | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 104 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index dbe5537809f5..0b3052157e6b 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -235,6 +235,7 @@ struct fib6_table { struct fib6_node tb6_root; struct inet_peer_base tb6_peers; unsigned int flags; + unsigned int fib_seq; #define RT6_TABLE_HAS_DFLT_ROUTER BIT(0) }; @@ -308,6 +309,9 @@ int call_fib6_notifiers(struct net *net, enum fib_event_type event_type, int __net_init fib6_notifier_init(struct net *net); void __net_exit fib6_notifier_exit(struct net *net); +unsigned int fib6_tables_seq_read(struct net *net); +int fib6_tables_dump(struct net *net, struct notifier_block *nb); + #ifdef CONFIG_IPV6_MULTIPLE_TABLES int fib6_rules_init(void); void fib6_rules_cleanup(void); diff --git a/net/ipv6/fib6_notifier.c b/net/ipv6/fib6_notifier.c index 298efc678f3b..66a103ef7e86 100644 --- a/net/ipv6/fib6_notifier.c +++ b/net/ipv6/fib6_notifier.c @@ -23,12 +23,18 @@ int call_fib6_notifiers(struct net *net, enum fib_event_type event_type, static unsigned int fib6_seq_read(struct net *net) { - return fib6_rules_seq_read(net); + return fib6_tables_seq_read(net) + fib6_rules_seq_read(net); } static int fib6_dump(struct net *net, struct notifier_block *nb) { - return fib6_rules_dump(net, nb); + int err; + + err = fib6_rules_dump(net, nb); + if (err) + return err; + + return fib6_tables_dump(net, nb); } static const struct fib_notifier_ops fib6_notifier_ops_template = { diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 595a57cbbc7b..719c10480c74 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -303,6 +303,37 @@ static void __net_init fib6_tables_init(struct net *net) #endif +unsigned int fib6_tables_seq_read(struct net *net) +{ + unsigned int h, fib_seq = 0; + + rcu_read_lock(); + for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { + struct hlist_head *head = &net->ipv6.fib_table_hash[h]; + struct fib6_table *tb; + + hlist_for_each_entry_rcu(tb, head, tb6_hlist) { + read_lock_bh(&tb->tb6_lock); + fib_seq += tb->fib_seq; + read_unlock_bh(&tb->tb6_lock); + } + } + rcu_read_unlock(); + + return fib_seq; +} + +static int call_fib6_entry_notifier(struct notifier_block *nb, struct net *net, + enum fib_event_type event_type, + struct rt6_info *rt) +{ + struct fib6_entry_notifier_info info = { + .rt = rt, + }; + + return call_fib6_notifier(nb, net, event_type, &info.info); +} + static int call_fib6_entry_notifiers(struct net *net, enum fib_event_type event_type, struct rt6_info *rt) @@ -311,9 +342,70 @@ static int call_fib6_entry_notifiers(struct net *net, .rt = rt, }; + rt->rt6i_table->fib_seq++; return call_fib6_notifiers(net, event_type, &info.info); } +struct fib6_dump_arg { + struct net *net; + struct notifier_block *nb; +}; + +static void fib6_rt_dump(struct rt6_info *rt, struct fib6_dump_arg *arg) +{ + if (rt == arg->net->ipv6.ip6_null_entry) + return; + call_fib6_entry_notifier(arg->nb, arg->net, FIB_EVENT_ENTRY_ADD, rt); +} + +static int fib6_node_dump(struct fib6_walker *w) +{ + struct rt6_info *rt; + + for (rt = w->leaf; rt; rt = rt->dst.rt6_next) + fib6_rt_dump(rt, w->args); + w->leaf = NULL; + return 0; +} + +static void fib6_table_dump(struct net *net, struct fib6_table *tb, + struct fib6_walker *w) +{ + w->root = &tb->tb6_root; + read_lock_bh(&tb->tb6_lock); + fib6_walk(net, w); + read_unlock_bh(&tb->tb6_lock); +} + +/* Called with rcu_read_lock() */ +int fib6_tables_dump(struct net *net, struct notifier_block *nb) +{ + struct fib6_dump_arg arg; + struct fib6_walker *w; + unsigned int h; + + w = kzalloc(sizeof(*w), GFP_ATOMIC); + if (!w) + return -ENOMEM; + + w->func = fib6_node_dump; + arg.net = net; + arg.nb = nb; + w->args = &arg; + + for (h = 0; h < FIB6_TABLE_HASHSZ; h++) { + struct hlist_head *head = &net->ipv6.fib_table_hash[h]; + struct fib6_table *tb; + + hlist_for_each_entry_rcu(tb, head, tb6_hlist) + fib6_table_dump(net, tb, w); + } + + kfree(w); + + return 0; +} + static int fib6_dump_node(struct fib6_walker *w) { int res; -- cgit v1.2.3 From 61e4d01e16acddadb9723143637a20417fa67ac9 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:20 +0200 Subject: ipv6: fib: Add offload indication to routes Allow user space applications to see which routes are offloaded and which aren't by setting the RTNH_F_OFFLOAD flag when dumping them. To be consistent with IPv4, offload indication is provided on a per-nexthop basis. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/uapi/linux/ipv6_route.h | 1 + net/ipv6/route.c | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'net') diff --git a/include/uapi/linux/ipv6_route.h b/include/uapi/linux/ipv6_route.h index d496c02e14bc..33e2a5732bd1 100644 --- a/include/uapi/linux/ipv6_route.h +++ b/include/uapi/linux/ipv6_route.h @@ -35,6 +35,7 @@ #define RTF_PREF(pref) ((pref) << 27) #define RTF_PREF_MASK 0x18000000 +#define RTF_OFFLOAD 0x20000000 /* offloaded route */ #define RTF_PCPU 0x40000000 /* read-only: can not be set by user */ #define RTF_LOCAL 0x80000000 diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 4d30c96a819d..aba07fce67fb 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1820,6 +1820,11 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, goto out; } + if (cfg->fc_flags & RTF_OFFLOAD) { + NL_SET_ERR_MSG(extack, "Userspace can not set RTF_OFFLOAD"); + goto out; + } + if (cfg->fc_dst_len > 128) { NL_SET_ERR_MSG(extack, "Invalid prefix length"); goto out; @@ -3327,6 +3332,9 @@ static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt, goto nla_put_failure; } + if (rt->rt6i_flags & RTF_OFFLOAD) + *flags |= RTNH_F_OFFLOAD; + /* not needed for multipath encoding b/c it has a rtnexthop struct */ if (!skip_oif && rt->dst.dev && nla_put_u32(skb, RTA_OIF, rt->dst.dev->ifindex)) -- cgit v1.2.3 From c5b12410fa591acb1d48e167b9bd0d2a7a38498d Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:21 +0200 Subject: ipv6: fib: Don't assume only nodes hold a reference on routes The code currently assumes that only FIB nodes can hold a reference on routes. Therefore, after fib6_purge_rt() has run and the route is no longer present in any intermediate nodes, it's assumed that its reference count would be 1 - taken by the node where it's currently stored. However, we're going to allow users other than the FIB to take a reference on a route, so this assumption is no longer valid and the BUG_ON() needs to be removed. Note that purging only takes place if the initial reference count is different than 1. I've left that check intact, as in the majority of systems (where routes are only referenced by the FIB), it does actually mean the route is present in intermediate nodes. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/ipv6/ip6_fib.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 719c10480c74..fa27905de92e 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -837,8 +837,6 @@ static void fib6_purge_rt(struct rt6_info *rt, struct fib6_node *fn, } fn = fn->parent; } - /* No more references are possible at this point. */ - BUG_ON(atomic_read(&rt->rt6i_ref) != 1); } } -- cgit v1.2.3 From 7483cea79957312e9f8e9cf760a1bc5d6c507113 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:22 +0200 Subject: ipv6: fib: Unlink replaced routes from their nodes When a route is deleted its node pointer is set to NULL to indicate it's no longer linked to its node. Do the same for routes that are replaced. This will later allow us to test if a route is still in the FIB by checking its node pointer instead of its reference count. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/ipv6/ip6_fib.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index fa27905de92e..fe193b48ef61 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -1019,6 +1019,7 @@ add: fn->fn_flags |= RTN_RTINFO; } nsiblings = iter->rt6i_nsiblings; + iter->rt6i_node = NULL; fib6_purge_rt(iter, fn, info->nl_net); rt6_release(iter); @@ -1031,6 +1032,7 @@ add: break; if (rt6_qualify_for_ecmp(iter)) { *ins = iter->dst.rt6_next; + iter->rt6i_node = NULL; fib6_purge_rt(iter, fn, info->nl_net); rt6_release(iter); nsiblings--; -- cgit v1.2.3 From 9217d8c2fe743f02a3ce6d430fe3b5d514fd5f1c Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:23 +0200 Subject: ipv6: Regenerate host route according to node pointer upon loopback up When the loopback device is brought back up we need to check if the host route attached to the address is still in the FIB and regenerate one in case it's not. Host routes using the loopback device are always inserted into and removed from the FIB under RTNL (under which this function is called), so we can test their node pointer instead of the reference count in order to check if the route is in the FIB or not. Tested using the following script from Nicolas mentioned in commit a220445f9f43 ("ipv6: correctly add local routes when lo goes up"): $ ip link add dummy1 type dummy $ ip link set dummy1 up $ ip link set lo down ; ip link set lo up The host route is correctly regenerated. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 3c46e9513a31..e8e4f867b994 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3066,7 +3066,7 @@ static void init_loopback(struct net_device *dev) * lo device down, release this obsolete dst and * reallocate a new router for ifa. */ - if (!atomic_read(&sp_ifa->rt->rt6i_ref)) { + if (!sp_ifa->rt->rt6i_node) { ip6_rt_put(sp_ifa->rt); sp_ifa->rt = NULL; } else { -- cgit v1.2.3 From fc882fcff1ee774cb6be9d3c714ae5ab9eec5aa4 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:24 +0200 Subject: ipv6: Regenerate host route according to node pointer upon interface up When an interface is brought back up, the kernel tries to restore the host routes tied to its permanent addresses. However, if the host route was removed from the FIB, then we need to reinsert it. This is done by releasing the current dst and allocating a new, so as to not reuse a dst with obsolete values. Since this function is called under RTNL and using the same explanation from the previous patch, we can test if the route is in the FIB by checking its node pointer instead of its reference count. Tested using the following script and Andrey's reproducer mentioned in commit 8048ced9beb2 ("net: ipv6: regenerate host route if moved to gc list") and linked below: $ ip link set dev lo up $ ip link add dummy1 type dummy $ ip -6 address add cafe::1/64 dev dummy1 $ ip link set dev lo down # cafe::1/128 is removed $ ip link set dev dummy1 up $ ip link set dev lo up The host route is correctly regenerated. Signed-off-by: Ido Schimmel Link: http://lkml.kernel.org/r/CAAeHK+zSe82vc5gCRgr_EoUwiALPnWVdWJBPwJZBpbxYz=kGJw@mail.gmail.com Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index e8e4f867b994..30ee23eef268 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3321,11 +3321,11 @@ static void addrconf_gre_config(struct net_device *dev) static int fixup_permanent_addr(struct inet6_dev *idev, struct inet6_ifaddr *ifp) { - /* rt6i_ref == 0 means the host route was removed from the + /* !rt6i_node means the host route was removed from the * FIB, for example, if 'lo' device is taken down. In that * case regenerate the host route. */ - if (!ifp->rt || !atomic_read(&ifp->rt->rt6i_ref)) { + if (!ifp->rt || !ifp->rt->rt6i_node) { struct rt6_info *rt, *prev; rt = addrconf_dst_alloc(idev, &ifp->addr, false); -- cgit v1.2.3 From a460aa83963b185a32a6377eb486b6e613ac8e38 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Thu, 3 Aug 2017 13:28:25 +0200 Subject: ipv6: fib: Add helpers to hold / drop a reference on rt6_info Similar to commit 1c677b3d2828 ("ipv4: fib: Add fib_info_hold() helper") and commit b423cb10807b ("ipv4: fib: Export free_fib_info()") add an helper to hold a reference on rt6_info and export rt6_release() to drop it and potentially release the route. This is needed so that drivers capable of FIB offload could hold a reference on the route before queueing it for offload and drop it after the route has been programmed to the device's tables. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/ip6_fib.h | 16 ++++++++++++++++ net/ipv6/ip6_fib.c | 12 ++---------- 2 files changed, 18 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 0b3052157e6b..1d790ea40ea7 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -187,6 +187,22 @@ static inline void ip6_rt_put(struct rt6_info *rt) dst_release(&rt->dst); } +void rt6_free_pcpu(struct rt6_info *non_pcpu_rt); + +static inline void rt6_hold(struct rt6_info *rt) +{ + atomic_inc(&rt->rt6i_ref); +} + +static inline void rt6_release(struct rt6_info *rt) +{ + if (atomic_dec_and_test(&rt->rt6i_ref)) { + rt6_free_pcpu(rt); + dst_dev_put(&rt->dst); + dst_release(&rt->dst); + } +} + enum fib6_walk_state { #ifdef CONFIG_IPV6_SUBTREES FWS_S, diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index fe193b48ef61..69ed0043d117 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -154,7 +154,7 @@ static void node_free(struct fib6_node *fn) kmem_cache_free(fib6_node_kmem, fn); } -static void rt6_free_pcpu(struct rt6_info *non_pcpu_rt) +void rt6_free_pcpu(struct rt6_info *non_pcpu_rt) { int cpu; @@ -177,15 +177,7 @@ static void rt6_free_pcpu(struct rt6_info *non_pcpu_rt) free_percpu(non_pcpu_rt->rt6i_pcpu); non_pcpu_rt->rt6i_pcpu = NULL; } - -static void rt6_release(struct rt6_info *rt) -{ - if (atomic_dec_and_test(&rt->rt6i_ref)) { - rt6_free_pcpu(rt); - dst_dev_put(&rt->dst); - dst_release(&rt->dst); - } -} +EXPORT_SYMBOL_GPL(rt6_free_pcpu); static void fib6_link_table(struct net *net, struct fib6_table *tb) { -- cgit v1.2.3 From 98ba0bd5505dcbb90322a4be07bcfe6b8a18c73f Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 3 Aug 2017 16:29:37 -0400 Subject: sock: allocate skbs from optmem Add sock_omalloc and sock_ofree to be able to allocate control skbs, for instance for looping errors onto sk_error_queue. The transmit budget (sk_wmem_alloc) is involved in transmit skb shaping, most notably in TCP Small Queues. Using this budget for control packets would impact transmission. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/net/sock.h | 2 ++ net/core/sock.c | 27 +++++++++++++++++++++++++++ 2 files changed, 29 insertions(+) (limited to 'net') diff --git a/include/net/sock.h b/include/net/sock.h index 393c38e9f6aa..0f778d3c4300 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -1531,6 +1531,8 @@ struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, gfp_t priority); void __sock_wfree(struct sk_buff *skb); void sock_wfree(struct sk_buff *skb); +struct sk_buff *sock_omalloc(struct sock *sk, unsigned long size, + gfp_t priority); void skb_orphan_partial(struct sk_buff *skb); void sock_rfree(struct sk_buff *skb); void sock_efree(struct sk_buff *skb); diff --git a/net/core/sock.c b/net/core/sock.c index 742f68c9c84a..1261880bdcc8 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1923,6 +1923,33 @@ struct sk_buff *sock_wmalloc(struct sock *sk, unsigned long size, int force, } EXPORT_SYMBOL(sock_wmalloc); +static void sock_ofree(struct sk_buff *skb) +{ + struct sock *sk = skb->sk; + + atomic_sub(skb->truesize, &sk->sk_omem_alloc); +} + +struct sk_buff *sock_omalloc(struct sock *sk, unsigned long size, + gfp_t priority) +{ + struct sk_buff *skb; + + /* small safe race: SKB_TRUESIZE may differ from final skb->truesize */ + if (atomic_read(&sk->sk_omem_alloc) + SKB_TRUESIZE(size) > + sysctl_optmem_max) + return NULL; + + skb = alloc_skb(size, priority); + if (!skb) + return NULL; + + atomic_add(skb->truesize, &sk->sk_omem_alloc); + skb->sk = sk; + skb->destructor = sock_ofree; + return skb; +} + /* * Allocate a memory block from the socket's option memory buffer. */ -- cgit v1.2.3 From 3ece782693c4b64d588dd217868558ab9a19bfe7 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 3 Aug 2017 16:29:38 -0400 Subject: sock: skb_copy_ubufs support for compound pages Refine skb_copy_ubufs to support compound pages. With upcoming TCP zerocopy sendmsg, such fragments may appear. The existing code replaces each page one for one. Splitting each compound page into an independent number of regular pages can result in exceeding limit MAX_SKB_FRAGS if data is not exactly page aligned. Instead, fill all destination pages but the last to PAGE_SIZE. Split the existing alloc + copy loop into separate stages: 1. compute bytelength and minimum number of pages to store this. 2. allocate 3. copy, filling each page except the last to PAGE_SIZE bytes 4. update skb frag array Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/linux/skbuff.h | 9 +++++++-- net/core/skbuff.c | 53 ++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 45 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index be76082f48aa..2f64e2bbb592 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1796,13 +1796,18 @@ static inline unsigned int skb_headlen(const struct sk_buff *skb) return skb->len - skb->data_len; } -static inline unsigned int skb_pagelen(const struct sk_buff *skb) +static inline unsigned int __skb_pagelen(const struct sk_buff *skb) { unsigned int i, len = 0; for (i = skb_shinfo(skb)->nr_frags - 1; (int)i >= 0; i--) len += skb_frag_size(&skb_shinfo(skb)->frags[i]); - return len + skb_headlen(skb); + return len; +} + +static inline unsigned int skb_pagelen(const struct sk_buff *skb) +{ + return skb_headlen(skb) + __skb_pagelen(skb); } /** diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 0f0933b338d7..a95877a8ac8b 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -932,17 +932,20 @@ EXPORT_SYMBOL_GPL(skb_morph); */ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) { - int i; + struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg; int num_frags = skb_shinfo(skb)->nr_frags; struct page *page, *head = NULL; - struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg; + int i, new_frags; + u32 d_off; - for (i = 0; i < num_frags; i++) { - skb_frag_t *f = &skb_shinfo(skb)->frags[i]; - u32 p_off, p_len, copied; - struct page *p; - u8 *vaddr; + if (!num_frags) + return 0; + + if (skb_shared(skb) || skb_unclone(skb, gfp_mask)) + return -EINVAL; + new_frags = (__skb_pagelen(skb) + PAGE_SIZE - 1) >> PAGE_SHIFT; + for (i = 0; i < new_frags; i++) { page = alloc_page(gfp_mask); if (!page) { while (head) { @@ -952,17 +955,36 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) } return -ENOMEM; } + set_page_private(page, (unsigned long)head); + head = page; + } + + page = head; + d_off = 0; + for (i = 0; i < num_frags; i++) { + skb_frag_t *f = &skb_shinfo(skb)->frags[i]; + u32 p_off, p_len, copied; + struct page *p; + u8 *vaddr; skb_frag_foreach_page(f, f->page_offset, skb_frag_size(f), p, p_off, p_len, copied) { + u32 copy, done = 0; vaddr = kmap_atomic(p); - memcpy(page_address(page) + copied, vaddr + p_off, - p_len); + + while (done < p_len) { + if (d_off == PAGE_SIZE) { + d_off = 0; + page = (struct page *)page_private(page); + } + copy = min_t(u32, PAGE_SIZE - d_off, p_len - done); + memcpy(page_address(page) + d_off, + vaddr + p_off + done, copy); + done += copy; + d_off += copy; + } kunmap_atomic(vaddr); } - - set_page_private(page, (unsigned long)head); - head = page; } /* skb frags release userspace buffers */ @@ -972,11 +994,12 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) uarg->callback(uarg, false); /* skb frags point to kernel buffers */ - for (i = num_frags - 1; i >= 0; i--) { - __skb_fill_page_desc(skb, i, head, 0, - skb_shinfo(skb)->frags[i].size); + for (i = 0; i < new_frags - 1; i++) { + __skb_fill_page_desc(skb, i, head, 0, PAGE_SIZE); head = (struct page *)page_private(head); } + __skb_fill_page_desc(skb, new_frags - 1, head, 0, d_off); + skb_shinfo(skb)->nr_frags = new_frags; skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; return 0; -- cgit v1.2.3 From 52267790ef52d7513879238ca9fac22c1733e0e3 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 3 Aug 2017 16:29:39 -0400 Subject: sock: add MSG_ZEROCOPY The kernel supports zerocopy sendmsg in virtio and tap. Expand the infrastructure to support other socket types. Introduce a completion notification channel over the socket error queue. Notifications are returned with ee_origin SO_EE_ORIGIN_ZEROCOPY. ee_errno is 0 to avoid blocking the send/recv path on receiving notifications. Add reference counting, to support the skb split, merge, resize and clone operations possible with SOCK_STREAM and other socket types. The patch does not yet modify any datapaths. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/linux/skbuff.h | 60 +++++++++++++++++++ include/linux/socket.h | 1 + include/net/sock.h | 2 + include/uapi/linux/errqueue.h | 3 + net/core/datagram.c | 55 ++++++++++------- net/core/skbuff.c | 133 ++++++++++++++++++++++++++++++++++++++++++ net/core/sock.c | 2 + 7 files changed, 235 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 2f64e2bbb592..59cff7aa494e 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -429,6 +429,7 @@ enum { SKBTX_SCHED_TSTAMP = 1 << 6, }; +#define SKBTX_ZEROCOPY_FRAG (SKBTX_DEV_ZEROCOPY | SKBTX_SHARED_FRAG) #define SKBTX_ANY_SW_TSTAMP (SKBTX_SW_TSTAMP | \ SKBTX_SCHED_TSTAMP) #define SKBTX_ANY_TSTAMP (SKBTX_HW_TSTAMP | SKBTX_ANY_SW_TSTAMP) @@ -445,8 +446,28 @@ struct ubuf_info { void (*callback)(struct ubuf_info *, bool zerocopy_success); void *ctx; unsigned long desc; + u16 zerocopy:1; + atomic_t refcnt; }; +#define skb_uarg(SKB) ((struct ubuf_info *)(skb_shinfo(SKB)->destructor_arg)) + +struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size); + +static inline void sock_zerocopy_get(struct ubuf_info *uarg) +{ + atomic_inc(&uarg->refcnt); +} + +void sock_zerocopy_put(struct ubuf_info *uarg); +void sock_zerocopy_put_abort(struct ubuf_info *uarg); + +void sock_zerocopy_callback(struct ubuf_info *uarg, bool success); + +int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb, + struct msghdr *msg, int len, + struct ubuf_info *uarg); + /* This data is invariant across clones and lives at * the end of the header data, ie. at skb->end. */ @@ -1214,6 +1235,45 @@ static inline struct skb_shared_hwtstamps *skb_hwtstamps(struct sk_buff *skb) return &skb_shinfo(skb)->hwtstamps; } +static inline struct ubuf_info *skb_zcopy(struct sk_buff *skb) +{ + bool is_zcopy = skb && skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY; + + return is_zcopy ? skb_uarg(skb) : NULL; +} + +static inline void skb_zcopy_set(struct sk_buff *skb, struct ubuf_info *uarg) +{ + if (skb && uarg && !skb_zcopy(skb)) { + sock_zerocopy_get(uarg); + skb_shinfo(skb)->destructor_arg = uarg; + skb_shinfo(skb)->tx_flags |= SKBTX_ZEROCOPY_FRAG; + } +} + +/* Release a reference on a zerocopy structure */ +static inline void skb_zcopy_clear(struct sk_buff *skb, bool zerocopy) +{ + struct ubuf_info *uarg = skb_zcopy(skb); + + if (uarg) { + uarg->zerocopy = uarg->zerocopy && zerocopy; + sock_zerocopy_put(uarg); + skb_shinfo(skb)->tx_flags &= ~SKBTX_ZEROCOPY_FRAG; + } +} + +/* Abort a zerocopy operation and revert zckey on error in send syscall */ +static inline void skb_zcopy_abort(struct sk_buff *skb) +{ + struct ubuf_info *uarg = skb_zcopy(skb); + + if (uarg) { + sock_zerocopy_put_abort(uarg); + skb_shinfo(skb)->tx_flags &= ~SKBTX_ZEROCOPY_FRAG; + } +} + /** * skb_queue_empty - check if a queue is empty * @list: queue head diff --git a/include/linux/socket.h b/include/linux/socket.h index 8b13db5163cc..8ad963cdc88c 100644 --- a/include/linux/socket.h +++ b/include/linux/socket.h @@ -287,6 +287,7 @@ struct ucred { #define MSG_BATCH 0x40000 /* sendmmsg(): more messages coming */ #define MSG_EOF MSG_FIN +#define MSG_ZEROCOPY 0x4000000 /* Use user data in kernel path */ #define MSG_FASTOPEN 0x20000000 /* Send data in TCP SYN */ #define MSG_CMSG_CLOEXEC 0x40000000 /* Set close_on_exec for file descriptor received through diff --git a/include/net/sock.h b/include/net/sock.h index 0f778d3c4300..fe1a0bc25cd3 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -294,6 +294,7 @@ struct sock_common { * @sk_stamp: time stamp of last packet received * @sk_tsflags: SO_TIMESTAMPING socket options * @sk_tskey: counter to disambiguate concurrent tstamp requests + * @sk_zckey: counter to order MSG_ZEROCOPY notifications * @sk_socket: Identd and reporting IO signals * @sk_user_data: RPC layer private data * @sk_frag: cached page frag @@ -462,6 +463,7 @@ struct sock { u16 sk_tsflags; u8 sk_shutdown; u32 sk_tskey; + atomic_t sk_zckey; struct socket *sk_socket; void *sk_user_data; #ifdef CONFIG_SECURITY diff --git a/include/uapi/linux/errqueue.h b/include/uapi/linux/errqueue.h index 07bdce1f444a..78fdf52d6b2f 100644 --- a/include/uapi/linux/errqueue.h +++ b/include/uapi/linux/errqueue.h @@ -18,10 +18,13 @@ struct sock_extended_err { #define SO_EE_ORIGIN_ICMP 2 #define SO_EE_ORIGIN_ICMP6 3 #define SO_EE_ORIGIN_TXSTATUS 4 +#define SO_EE_ORIGIN_ZEROCOPY 5 #define SO_EE_ORIGIN_TIMESTAMPING SO_EE_ORIGIN_TXSTATUS #define SO_EE_OFFENDER(ee) ((struct sockaddr*)((ee)+1)) +#define SO_EE_CODE_ZEROCOPY_COPIED 1 + /** * struct scm_timestamping - timestamps exposed through cmsg * diff --git a/net/core/datagram.c b/net/core/datagram.c index ee5647bd91b3..2f3277945d35 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -573,27 +573,12 @@ fault: } EXPORT_SYMBOL(skb_copy_datagram_from_iter); -/** - * zerocopy_sg_from_iter - Build a zerocopy datagram from an iov_iter - * @skb: buffer to copy - * @from: the source to copy from - * - * The function will first copy up to headlen, and then pin the userspace - * pages and build frags through them. - * - * Returns 0, -EFAULT or -EMSGSIZE. - */ -int zerocopy_sg_from_iter(struct sk_buff *skb, struct iov_iter *from) +int __zerocopy_sg_from_iter(struct sock *sk, struct sk_buff *skb, + struct iov_iter *from, size_t length) { - int len = iov_iter_count(from); - int copy = min_t(int, skb_headlen(skb), len); - int frag = 0; + int frag = skb_shinfo(skb)->nr_frags; - /* copy up to skb headlen */ - if (skb_copy_datagram_from_iter(skb, 0, from, copy)) - return -EFAULT; - - while (iov_iter_count(from)) { + while (length && iov_iter_count(from)) { struct page *pages[MAX_SKB_FRAGS]; size_t start; ssize_t copied; @@ -603,18 +588,24 @@ int zerocopy_sg_from_iter(struct sk_buff *skb, struct iov_iter *from) if (frag == MAX_SKB_FRAGS) return -EMSGSIZE; - copied = iov_iter_get_pages(from, pages, ~0U, + copied = iov_iter_get_pages(from, pages, length, MAX_SKB_FRAGS - frag, &start); if (copied < 0) return -EFAULT; iov_iter_advance(from, copied); + length -= copied; truesize = PAGE_ALIGN(copied + start); skb->data_len += copied; skb->len += copied; skb->truesize += truesize; - refcount_add(truesize, &skb->sk->sk_wmem_alloc); + if (sk && sk->sk_type == SOCK_STREAM) { + sk->sk_wmem_queued += truesize; + sk_mem_charge(sk, truesize); + } else { + refcount_add(truesize, &skb->sk->sk_wmem_alloc); + } while (copied) { int size = min_t(int, copied, PAGE_SIZE - start); skb_fill_page_desc(skb, frag++, pages[n], start, size); @@ -625,6 +616,28 @@ int zerocopy_sg_from_iter(struct sk_buff *skb, struct iov_iter *from) } return 0; } +EXPORT_SYMBOL(__zerocopy_sg_from_iter); + +/** + * zerocopy_sg_from_iter - Build a zerocopy datagram from an iov_iter + * @skb: buffer to copy + * @from: the source to copy from + * + * The function will first copy up to headlen, and then pin the userspace + * pages and build frags through them. + * + * Returns 0, -EFAULT or -EMSGSIZE. + */ +int zerocopy_sg_from_iter(struct sk_buff *skb, struct iov_iter *from) +{ + int copy = min_t(int, skb_headlen(skb), iov_iter_count(from)); + + /* copy up to skb headlen */ + if (skb_copy_datagram_from_iter(skb, 0, from, copy)) + return -EFAULT; + + return __zerocopy_sg_from_iter(NULL, skb, from, ~0U); +} EXPORT_SYMBOL(zerocopy_sg_from_iter); static int skb_copy_and_csum_datagram(const struct sk_buff *skb, int offset, diff --git a/net/core/skbuff.c b/net/core/skbuff.c index a95877a8ac8b..0603e44950da 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -915,6 +915,139 @@ struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src) } EXPORT_SYMBOL_GPL(skb_morph); +struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size) +{ + struct ubuf_info *uarg; + struct sk_buff *skb; + + WARN_ON_ONCE(!in_task()); + + skb = sock_omalloc(sk, 0, GFP_KERNEL); + if (!skb) + return NULL; + + BUILD_BUG_ON(sizeof(*uarg) > sizeof(skb->cb)); + uarg = (void *)skb->cb; + + uarg->callback = sock_zerocopy_callback; + uarg->desc = atomic_inc_return(&sk->sk_zckey) - 1; + uarg->zerocopy = 1; + atomic_set(&uarg->refcnt, 0); + sock_hold(sk); + + return uarg; +} +EXPORT_SYMBOL_GPL(sock_zerocopy_alloc); + +static inline struct sk_buff *skb_from_uarg(struct ubuf_info *uarg) +{ + return container_of((void *)uarg, struct sk_buff, cb); +} + +void sock_zerocopy_callback(struct ubuf_info *uarg, bool success) +{ + struct sk_buff *skb = skb_from_uarg(uarg); + struct sock_exterr_skb *serr; + struct sock *sk = skb->sk; + u16 id = uarg->desc; + + if (sock_flag(sk, SOCK_DEAD)) + goto release; + + serr = SKB_EXT_ERR(skb); + memset(serr, 0, sizeof(*serr)); + serr->ee.ee_errno = 0; + serr->ee.ee_origin = SO_EE_ORIGIN_ZEROCOPY; + serr->ee.ee_data = id; + if (!success) + serr->ee.ee_code |= SO_EE_CODE_ZEROCOPY_COPIED; + + skb_queue_tail(&sk->sk_error_queue, skb); + skb = NULL; + + sk->sk_error_report(sk); + +release: + consume_skb(skb); + sock_put(sk); +} +EXPORT_SYMBOL_GPL(sock_zerocopy_callback); + +void sock_zerocopy_put(struct ubuf_info *uarg) +{ + if (uarg && atomic_dec_and_test(&uarg->refcnt)) { + if (uarg->callback) + uarg->callback(uarg, uarg->zerocopy); + else + consume_skb(skb_from_uarg(uarg)); + } +} +EXPORT_SYMBOL_GPL(sock_zerocopy_put); + +void sock_zerocopy_put_abort(struct ubuf_info *uarg) +{ + if (uarg) { + struct sock *sk = skb_from_uarg(uarg)->sk; + + atomic_dec(&sk->sk_zckey); + + /* sock_zerocopy_put expects a ref. Most sockets take one per + * skb, which is zero on abort. tcp_sendmsg holds one extra, to + * avoid an skb send inside the main loop triggering uarg free. + */ + if (sk->sk_type != SOCK_STREAM) + atomic_inc(&uarg->refcnt); + + sock_zerocopy_put(uarg); + } +} +EXPORT_SYMBOL_GPL(sock_zerocopy_put_abort); + +extern int __zerocopy_sg_from_iter(struct sock *sk, struct sk_buff *skb, + struct iov_iter *from, size_t length); + +int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb, + struct msghdr *msg, int len, + struct ubuf_info *uarg) +{ + struct iov_iter orig_iter = msg->msg_iter; + int err, orig_len = skb->len; + + err = __zerocopy_sg_from_iter(sk, skb, &msg->msg_iter, len); + if (err == -EFAULT || (err == -EMSGSIZE && skb->len == orig_len)) { + /* Streams do not free skb on error. Reset to prev state. */ + msg->msg_iter = orig_iter; + ___pskb_trim(skb, orig_len); + return err; + } + + skb_zcopy_set(skb, uarg); + return skb->len - orig_len; +} +EXPORT_SYMBOL_GPL(skb_zerocopy_iter_stream); + +/* unused only until next patch in the series; will remove attribute */ +static int __attribute__((unused)) + skb_zerocopy_clone(struct sk_buff *nskb, struct sk_buff *orig, + gfp_t gfp_mask) +{ + if (skb_zcopy(orig)) { + if (skb_zcopy(nskb)) { + /* !gfp_mask callers are verified to !skb_zcopy(nskb) */ + if (!gfp_mask) { + WARN_ON_ONCE(1); + return -ENOMEM; + } + if (skb_uarg(nskb) == skb_uarg(orig)) + return 0; + if (skb_copy_ubufs(nskb, GFP_ATOMIC)) + return -EIO; + } + skb_zcopy_set(nskb, skb_uarg(orig)); + } + return 0; +} + /** * skb_copy_ubufs - copy userspace skb frags buffers to kernel * @skb: the skb to modify diff --git a/net/core/sock.c b/net/core/sock.c index 1261880bdcc8..e8b696858cad 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1670,6 +1670,7 @@ struct sock *sk_clone_lock(const struct sock *sk, const gfp_t priority) atomic_set(&newsk->sk_drops, 0); newsk->sk_send_head = NULL; newsk->sk_userlocks = sk->sk_userlocks & ~SOCK_BINDPORT_LOCK; + atomic_set(&newsk->sk_zckey, 0); sock_reset_flag(newsk, SOCK_DONE); @@ -2722,6 +2723,7 @@ void sock_init_data(struct socket *sock, struct sock *sk) sk->sk_sndtimeo = MAX_SCHEDULE_TIMEOUT; sk->sk_stamp = SK_DEFAULT_STAMP; + atomic_set(&sk->sk_zckey, 0); #ifdef CONFIG_NET_RX_BUSY_POLL sk->sk_napi_id = 0; -- cgit v1.2.3 From 76851d1212c11365362525e1e2c0a18c97478e6b Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 3 Aug 2017 16:29:40 -0400 Subject: sock: add SOCK_ZEROCOPY sockopt The send call ignores unknown flags. Legacy applications may already unwittingly pass MSG_ZEROCOPY. Continue to ignore this flag unless a socket opts in to zerocopy. Introduce socket option SO_ZEROCOPY to enable MSG_ZEROCOPY processing. Processes can also query this socket option to detect kernel support for the feature. Older kernels will return ENOPROTOOPT. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- arch/alpha/include/uapi/asm/socket.h | 2 ++ arch/frv/include/uapi/asm/socket.h | 2 ++ arch/ia64/include/uapi/asm/socket.h | 2 ++ arch/m32r/include/uapi/asm/socket.h | 2 ++ arch/mips/include/uapi/asm/socket.h | 2 ++ arch/mn10300/include/uapi/asm/socket.h | 2 ++ arch/parisc/include/uapi/asm/socket.h | 2 ++ arch/s390/include/uapi/asm/socket.h | 2 ++ arch/sparc/include/uapi/asm/socket.h | 2 ++ arch/xtensa/include/uapi/asm/socket.h | 2 ++ include/uapi/asm-generic/socket.h | 2 ++ net/core/skbuff.c | 3 +++ net/core/sock.c | 18 ++++++++++++++++++ 13 files changed, 43 insertions(+) (limited to 'net') diff --git a/arch/alpha/include/uapi/asm/socket.h b/arch/alpha/include/uapi/asm/socket.h index 7b285dd4fe05..c6133a045352 100644 --- a/arch/alpha/include/uapi/asm/socket.h +++ b/arch/alpha/include/uapi/asm/socket.h @@ -109,4 +109,6 @@ #define SO_PEERGROUPS 59 +#define SO_ZEROCOPY 60 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/frv/include/uapi/asm/socket.h b/arch/frv/include/uapi/asm/socket.h index f1e3b20dce9f..9abf02d6855a 100644 --- a/arch/frv/include/uapi/asm/socket.h +++ b/arch/frv/include/uapi/asm/socket.h @@ -102,5 +102,7 @@ #define SO_PEERGROUPS 59 +#define SO_ZEROCOPY 60 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/ia64/include/uapi/asm/socket.h b/arch/ia64/include/uapi/asm/socket.h index 5dd5c5d0d642..002eb85a6941 100644 --- a/arch/ia64/include/uapi/asm/socket.h +++ b/arch/ia64/include/uapi/asm/socket.h @@ -111,4 +111,6 @@ #define SO_PEERGROUPS 59 +#define SO_ZEROCOPY 60 + #endif /* _ASM_IA64_SOCKET_H */ diff --git a/arch/m32r/include/uapi/asm/socket.h b/arch/m32r/include/uapi/asm/socket.h index f8f7b47e247f..e268e51a38d1 100644 --- a/arch/m32r/include/uapi/asm/socket.h +++ b/arch/m32r/include/uapi/asm/socket.h @@ -102,4 +102,6 @@ #define SO_PEERGROUPS 59 +#define SO_ZEROCOPY 60 + #endif /* _ASM_M32R_SOCKET_H */ diff --git a/arch/mips/include/uapi/asm/socket.h b/arch/mips/include/uapi/asm/socket.h index 882823bec153..6c755bc07975 100644 --- a/arch/mips/include/uapi/asm/socket.h +++ b/arch/mips/include/uapi/asm/socket.h @@ -120,4 +120,6 @@ #define SO_PEERGROUPS 59 +#define SO_ZEROCOPY 60 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/mn10300/include/uapi/asm/socket.h b/arch/mn10300/include/uapi/asm/socket.h index c710db354ff2..ac82a3f26dbf 100644 --- a/arch/mn10300/include/uapi/asm/socket.h +++ b/arch/mn10300/include/uapi/asm/socket.h @@ -102,4 +102,6 @@ #define SO_PEERGROUPS 59 +#define SO_ZEROCOPY 60 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/parisc/include/uapi/asm/socket.h b/arch/parisc/include/uapi/asm/socket.h index a0d4dc9f4eb2..3b2bf7ae703b 100644 --- a/arch/parisc/include/uapi/asm/socket.h +++ b/arch/parisc/include/uapi/asm/socket.h @@ -101,4 +101,6 @@ #define SO_PEERGROUPS 0x4034 +#define SO_ZEROCOPY 0x4035 + #endif /* _UAPI_ASM_SOCKET_H */ diff --git a/arch/s390/include/uapi/asm/socket.h b/arch/s390/include/uapi/asm/socket.h index 52a63f4175cb..a56916c83565 100644 --- a/arch/s390/include/uapi/asm/socket.h +++ b/arch/s390/include/uapi/asm/socket.h @@ -108,4 +108,6 @@ #define SO_PEERGROUPS 59 +#define SO_ZEROCOPY 60 + #endif /* _ASM_SOCKET_H */ diff --git a/arch/sparc/include/uapi/asm/socket.h b/arch/sparc/include/uapi/asm/socket.h index 186fd8199f54..b2f5c50d0947 100644 --- a/arch/sparc/include/uapi/asm/socket.h +++ b/arch/sparc/include/uapi/asm/socket.h @@ -98,6 +98,8 @@ #define SO_PEERGROUPS 0x003d +#define SO_ZEROCOPY 0x003e + /* Security levels - as per NRL IPv6 - don't actually do anything */ #define SO_SECURITY_AUTHENTICATION 0x5001 #define SO_SECURITY_ENCRYPTION_TRANSPORT 0x5002 diff --git a/arch/xtensa/include/uapi/asm/socket.h b/arch/xtensa/include/uapi/asm/socket.h index 3eed2761c149..220059999e74 100644 --- a/arch/xtensa/include/uapi/asm/socket.h +++ b/arch/xtensa/include/uapi/asm/socket.h @@ -113,4 +113,6 @@ #define SO_PEERGROUPS 59 +#define SO_ZEROCOPY 60 + #endif /* _XTENSA_SOCKET_H */ diff --git a/include/uapi/asm-generic/socket.h b/include/uapi/asm-generic/socket.h index 9861be8da65e..e47c9e436221 100644 --- a/include/uapi/asm-generic/socket.h +++ b/include/uapi/asm-generic/socket.h @@ -104,4 +104,6 @@ #define SO_PEERGROUPS 59 +#define SO_ZEROCOPY 60 + #endif /* __ASM_GENERIC_SOCKET_H */ diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 0603e44950da..29e34bc6a17c 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -922,6 +922,9 @@ struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size) WARN_ON_ONCE(!in_task()); + if (!sock_flag(sk, SOCK_ZEROCOPY)) + return NULL; + skb = sock_omalloc(sk, 0, GFP_KERNEL); if (!skb) return NULL; diff --git a/net/core/sock.c b/net/core/sock.c index e8b696858cad..9ea988d25b0a 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -1055,6 +1055,20 @@ set_rcvbuf: if (val == 1) dst_negative_advice(sk); break; + + case SO_ZEROCOPY: + if (sk->sk_family != PF_INET && sk->sk_family != PF_INET6) + ret = -ENOTSUPP; + else if (sk->sk_protocol != IPPROTO_TCP) + ret = -ENOTSUPP; + else if (sk->sk_state != TCP_CLOSE) + ret = -EBUSY; + else if (val < 0 || val > 1) + ret = -EINVAL; + else + sock_valbool_flag(sk, SOCK_ZEROCOPY, valbool); + break; + default: ret = -ENOPROTOOPT; break; @@ -1383,6 +1397,10 @@ int sock_getsockopt(struct socket *sock, int level, int optname, v.val64 = sock_gen_cookie(sk); break; + case SO_ZEROCOPY: + v.val = sock_flag(sk, SOCK_ZEROCOPY); + break; + default: /* We implement the SO_SNDLOWAT etc to not be settable * (1003.1g 7). -- cgit v1.2.3 From 1f8b977ab32dc5d148f103326e80d9097f1cefb5 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 3 Aug 2017 16:29:41 -0400 Subject: sock: enable MSG_ZEROCOPY Prepare the datapath for refcounted ubuf_info. Clone ubuf_info with skb_zerocopy_clone() wherever needed due to skb split, merge, resize or clone. Split skb_orphan_frags into two variants. The split, merge, .. paths support reference counted zerocopy buffers, so do not do a deep copy. Add skb_orphan_frags_rx for paths that may loop packets to receive sockets. That is not allowed, as it may cause unbounded latency. Deep copy all zerocopy copy buffers, ref-counted or not, in this path. The exact locations to modify were chosen by exhaustively searching through all code that might modify skb_frag references and/or the the SKBTX_DEV_ZEROCOPY tx_flags bit. The changes err on the safe side, in two ways. (1) legacy ubuf_info paths virtio and tap are not modified. They keep a 1:1 ubuf_info to sk_buff relationship. Calls to skb_orphan_frags still call skb_copy_ubufs and thus copy frags in this case. (2) not all copies deep in the stack are addressed yet. skb_shift, skb_split and skb_try_coalesce can be refined to avoid copying. These are not in the hot path and this patch is hairy enough as is, so that is left for future refinement. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- drivers/net/tun.c | 2 +- drivers/vhost/net.c | 1 + include/linux/skbuff.h | 14 +++++++++++++- net/core/dev.c | 4 ++-- net/core/skbuff.c | 48 +++++++++++++++++++----------------------------- 5 files changed, 36 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/drivers/net/tun.c b/drivers/net/tun.c index 68add55f8460..d21510d47aa2 100644 --- a/drivers/net/tun.c +++ b/drivers/net/tun.c @@ -892,7 +892,7 @@ static netdev_tx_t tun_net_xmit(struct sk_buff *skb, struct net_device *dev) sk_filter(tfile->socket.sk, skb)) goto drop; - if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) + if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) goto drop; skb_tx_timestamp(skb); diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index 06d044862e58..ba08b78ed630 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -533,6 +533,7 @@ static void handle_tx(struct vhost_net *net) ubuf->callback = vhost_zerocopy_callback; ubuf->ctx = nvq->ubufs; ubuf->desc = nvq->upend_idx; + atomic_set(&ubuf->refcnt, 1); msg.msg_control = ubuf; msg.msg_controllen = sizeof(ubuf); ubufs = nvq->ubufs; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index 59cff7aa494e..e5387932c266 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -2512,7 +2512,17 @@ static inline void skb_orphan(struct sk_buff *skb) */ static inline int skb_orphan_frags(struct sk_buff *skb, gfp_t gfp_mask) { - if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY))) + if (likely(!skb_zcopy(skb))) + return 0; + if (skb_uarg(skb)->callback == sock_zerocopy_callback) + return 0; + return skb_copy_ubufs(skb, gfp_mask); +} + +/* Frags must be orphaned, even if refcounted, if skb might loop to rx path */ +static inline int skb_orphan_frags_rx(struct sk_buff *skb, gfp_t gfp_mask) +{ + if (likely(!skb_zcopy(skb))) return 0; return skb_copy_ubufs(skb, gfp_mask); } @@ -2944,6 +2954,8 @@ static inline int skb_add_data(struct sk_buff *skb, static inline bool skb_can_coalesce(struct sk_buff *skb, int i, const struct page *page, int off) { + if (skb_zcopy(skb)) + return false; if (i) { const struct skb_frag_struct *frag = &skb_shinfo(skb)->frags[i - 1]; diff --git a/net/core/dev.c b/net/core/dev.c index 8ea6b4b42611..1d75499add72 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -1853,7 +1853,7 @@ static inline int deliver_skb(struct sk_buff *skb, struct packet_type *pt_prev, struct net_device *orig_dev) { - if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) + if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) return -ENOMEM; refcount_inc(&skb->users); return pt_prev->func(skb, skb->dev, pt_prev, orig_dev); @@ -4412,7 +4412,7 @@ skip_classify: } if (pt_prev) { - if (unlikely(skb_orphan_frags(skb, GFP_ATOMIC))) + if (unlikely(skb_orphan_frags_rx(skb, GFP_ATOMIC))) goto drop; else ret = pt_prev->func(skb, skb->dev, pt_prev, orig_dev); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 29e34bc6a17c..74d3c36f8419 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -567,21 +567,10 @@ static void skb_release_data(struct sk_buff *skb) for (i = 0; i < shinfo->nr_frags; i++) __skb_frag_unref(&shinfo->frags[i]); - /* - * If skb buf is from userspace, we need to notify the caller - * the lower device DMA has done; - */ - if (shinfo->tx_flags & SKBTX_DEV_ZEROCOPY) { - struct ubuf_info *uarg; - - uarg = shinfo->destructor_arg; - if (uarg->callback) - uarg->callback(uarg, true); - } - if (shinfo->frag_list) kfree_skb_list(shinfo->frag_list); + skb_zcopy_clear(skb, true); skb_free_head(skb); } @@ -695,14 +684,7 @@ EXPORT_SYMBOL(kfree_skb_list); */ void skb_tx_error(struct sk_buff *skb) { - if (skb_shinfo(skb)->tx_flags & SKBTX_DEV_ZEROCOPY) { - struct ubuf_info *uarg; - - uarg = skb_shinfo(skb)->destructor_arg; - if (uarg->callback) - uarg->callback(uarg, false); - skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; - } + skb_zcopy_clear(skb, true); } EXPORT_SYMBOL(skb_tx_error); @@ -1029,9 +1011,7 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb, } EXPORT_SYMBOL_GPL(skb_zerocopy_iter_stream); -/* unused only until next patch in the series; will remove attribute */ -static int __attribute__((unused)) - skb_zerocopy_clone(struct sk_buff *nskb, struct sk_buff *orig, +static int skb_zerocopy_clone(struct sk_buff *nskb, struct sk_buff *orig, gfp_t gfp_mask) { if (skb_zcopy(orig)) { @@ -1068,7 +1048,6 @@ static int __attribute__((unused)) */ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) { - struct ubuf_info *uarg = skb_shinfo(skb)->destructor_arg; int num_frags = skb_shinfo(skb)->nr_frags; struct page *page, *head = NULL; int i, new_frags; @@ -1127,8 +1106,6 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) for (i = 0; i < num_frags; i++) skb_frag_unref(skb, i); - uarg->callback(uarg, false); - /* skb frags point to kernel buffers */ for (i = 0; i < new_frags - 1; i++) { __skb_fill_page_desc(skb, i, head, 0, PAGE_SIZE); @@ -1137,7 +1114,7 @@ int skb_copy_ubufs(struct sk_buff *skb, gfp_t gfp_mask) __skb_fill_page_desc(skb, new_frags - 1, head, 0, d_off); skb_shinfo(skb)->nr_frags = new_frags; - skb_shinfo(skb)->tx_flags &= ~SKBTX_DEV_ZEROCOPY; + skb_zcopy_clear(skb, false); return 0; } EXPORT_SYMBOL_GPL(skb_copy_ubufs); @@ -1298,7 +1275,8 @@ struct sk_buff *__pskb_copy_fclone(struct sk_buff *skb, int headroom, if (skb_shinfo(skb)->nr_frags) { int i; - if (skb_orphan_frags(skb, gfp_mask)) { + if (skb_orphan_frags(skb, gfp_mask) || + skb_zerocopy_clone(n, skb, gfp_mask)) { kfree_skb(n); n = NULL; goto out; @@ -1375,9 +1353,10 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, * be since all we did is relocate the values */ if (skb_cloned(skb)) { - /* copy this zero copy skb frags */ if (skb_orphan_frags(skb, gfp_mask)) goto nofrags; + if (skb_zcopy(skb)) + atomic_inc(&skb_uarg(skb)->refcnt); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) skb_frag_ref(skb, i); @@ -1872,6 +1851,9 @@ end: skb->tail += delta; skb->data_len -= delta; + if (!skb->data_len) + skb_zcopy_clear(skb, false); + return skb_tail_pointer(skb); } EXPORT_SYMBOL(__pskb_pull_tail); @@ -2627,6 +2609,7 @@ skb_zerocopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen) skb_tx_error(from); return -ENOMEM; } + skb_zerocopy_clone(to, from, GFP_ATOMIC); for (i = 0; i < skb_shinfo(from)->nr_frags; i++) { if (!len) @@ -2924,6 +2907,7 @@ void skb_split(struct sk_buff *skb, struct sk_buff *skb1, const u32 len) skb_shinfo(skb1)->tx_flags |= skb_shinfo(skb)->tx_flags & SKBTX_SHARED_FRAG; + skb_zerocopy_clone(skb1, skb, 0); if (len < pos) /* Split line is inside header. */ skb_split_inside_header(skb, skb1, len, pos); else /* Second chunk has no header, nothing to copy. */ @@ -2967,6 +2951,8 @@ int skb_shift(struct sk_buff *tgt, struct sk_buff *skb, int shiftlen) if (skb_headlen(skb)) return 0; + if (skb_zcopy(tgt) || skb_zcopy(skb)) + return 0; todo = shiftlen; from = 0; @@ -3540,6 +3526,8 @@ normal: skb_shinfo(nskb)->tx_flags |= skb_shinfo(head_skb)->tx_flags & SKBTX_SHARED_FRAG; + if (skb_zerocopy_clone(nskb, head_skb, GFP_ATOMIC)) + goto err; while (pos < offset + len) { if (i >= nfrags) { @@ -4663,6 +4651,8 @@ bool skb_try_coalesce(struct sk_buff *to, struct sk_buff *from, if (skb_has_frag_list(to) || skb_has_frag_list(from)) return false; + if (skb_zcopy(to) || skb_zcopy(from)) + return false; if (skb_headlen(from) != 0) { struct page *page; -- cgit v1.2.3 From 4ab6c99d99bb1bf0fbba8ff4e52114c66109992f Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 3 Aug 2017 16:29:42 -0400 Subject: sock: MSG_ZEROCOPY notification coalescing In the simple case, each sendmsg() call generates data and eventually a zerocopy ready notification N, where N indicates the Nth successful invocation of sendmsg() with the MSG_ZEROCOPY flag on this socket. TCP and corked sockets can cause send() calls to append new data to an existing sk_buff and, thus, ubuf_info. In that case the notification must hold a range. odify ubuf_info to store a inclusive range [N..N+m] and add skb_zerocopy_realloc() to optionally extend an existing range. Also coalesce notifications in this common case: if a notification [1, 1] is about to be queued while [0, 0] is the queue tail, just modify the head of the queue to read [0, 1]. Coalescing is limited to a few TSO frames worth of data to bound notification latency. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/linux/skbuff.h | 17 +++++++-- net/core/skbuff.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 106 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index e5387932c266..f5bdd93a87da 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -444,15 +444,26 @@ enum { */ struct ubuf_info { void (*callback)(struct ubuf_info *, bool zerocopy_success); - void *ctx; - unsigned long desc; - u16 zerocopy:1; + union { + struct { + unsigned long desc; + void *ctx; + }; + struct { + u32 id; + u16 len; + u16 zerocopy:1; + u32 bytelen; + }; + }; atomic_t refcnt; }; #define skb_uarg(SKB) ((struct ubuf_info *)(skb_shinfo(SKB)->destructor_arg)) struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size); +struct ubuf_info *sock_zerocopy_realloc(struct sock *sk, size_t size, + struct ubuf_info *uarg); static inline void sock_zerocopy_get(struct ubuf_info *uarg) { diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 74d3c36f8419..dcee0f64f1fa 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -915,7 +915,9 @@ struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size) uarg = (void *)skb->cb; uarg->callback = sock_zerocopy_callback; - uarg->desc = atomic_inc_return(&sk->sk_zckey) - 1; + uarg->id = ((u32)atomic_inc_return(&sk->sk_zckey)) - 1; + uarg->len = 1; + uarg->bytelen = size; uarg->zerocopy = 1; atomic_set(&uarg->refcnt, 0); sock_hold(sk); @@ -929,26 +931,101 @@ static inline struct sk_buff *skb_from_uarg(struct ubuf_info *uarg) return container_of((void *)uarg, struct sk_buff, cb); } +struct ubuf_info *sock_zerocopy_realloc(struct sock *sk, size_t size, + struct ubuf_info *uarg) +{ + if (uarg) { + const u32 byte_limit = 1 << 19; /* limit to a few TSO */ + u32 bytelen, next; + + /* realloc only when socket is locked (TCP, UDP cork), + * so uarg->len and sk_zckey access is serialized + */ + if (!sock_owned_by_user(sk)) { + WARN_ON_ONCE(1); + return NULL; + } + + bytelen = uarg->bytelen + size; + if (uarg->len == USHRT_MAX - 1 || bytelen > byte_limit) { + /* TCP can create new skb to attach new uarg */ + if (sk->sk_type == SOCK_STREAM) + goto new_alloc; + return NULL; + } + + next = (u32)atomic_read(&sk->sk_zckey); + if ((u32)(uarg->id + uarg->len) == next) { + uarg->len++; + uarg->bytelen = bytelen; + atomic_set(&sk->sk_zckey, ++next); + return uarg; + } + } + +new_alloc: + return sock_zerocopy_alloc(sk, size); +} +EXPORT_SYMBOL_GPL(sock_zerocopy_realloc); + +static bool skb_zerocopy_notify_extend(struct sk_buff *skb, u32 lo, u16 len) +{ + struct sock_exterr_skb *serr = SKB_EXT_ERR(skb); + u32 old_lo, old_hi; + u64 sum_len; + + old_lo = serr->ee.ee_info; + old_hi = serr->ee.ee_data; + sum_len = old_hi - old_lo + 1ULL + len; + + if (sum_len >= (1ULL << 32)) + return false; + + if (lo != old_hi + 1) + return false; + + serr->ee.ee_data += len; + return true; +} + void sock_zerocopy_callback(struct ubuf_info *uarg, bool success) { - struct sk_buff *skb = skb_from_uarg(uarg); + struct sk_buff *tail, *skb = skb_from_uarg(uarg); struct sock_exterr_skb *serr; struct sock *sk = skb->sk; - u16 id = uarg->desc; + struct sk_buff_head *q; + unsigned long flags; + u32 lo, hi; + u16 len; - if (sock_flag(sk, SOCK_DEAD)) + /* if !len, there was only 1 call, and it was aborted + * so do not queue a completion notification + */ + if (!uarg->len || sock_flag(sk, SOCK_DEAD)) goto release; + len = uarg->len; + lo = uarg->id; + hi = uarg->id + len - 1; + serr = SKB_EXT_ERR(skb); memset(serr, 0, sizeof(*serr)); serr->ee.ee_errno = 0; serr->ee.ee_origin = SO_EE_ORIGIN_ZEROCOPY; - serr->ee.ee_data = id; + serr->ee.ee_data = hi; + serr->ee.ee_info = lo; if (!success) serr->ee.ee_code |= SO_EE_CODE_ZEROCOPY_COPIED; - skb_queue_tail(&sk->sk_error_queue, skb); - skb = NULL; + q = &sk->sk_error_queue; + spin_lock_irqsave(&q->lock, flags); + tail = skb_peek_tail(q); + if (!tail || SKB_EXT_ERR(tail)->ee.ee_origin != SO_EE_ORIGIN_ZEROCOPY || + !skb_zerocopy_notify_extend(tail, lo, len)) { + __skb_queue_tail(q, skb); + skb = NULL; + } + spin_unlock_irqrestore(&q->lock, flags); sk->sk_error_report(sk); @@ -975,6 +1052,7 @@ void sock_zerocopy_put_abort(struct ubuf_info *uarg) struct sock *sk = skb_from_uarg(uarg)->sk; atomic_dec(&sk->sk_zckey); + uarg->len--; /* sock_zerocopy_put expects a ref. Most sockets take one per * skb, which is zero on abort. tcp_sendmsg holds one extra, to @@ -995,9 +1073,16 @@ int skb_zerocopy_iter_stream(struct sock *sk, struct sk_buff *skb, struct msghdr *msg, int len, struct ubuf_info *uarg) { + struct ubuf_info *orig_uarg = skb_zcopy(skb); struct iov_iter orig_iter = msg->msg_iter; int err, orig_len = skb->len; + /* An skb can only point to one uarg. This edge case happens when + * TCP appends to an skb, but zerocopy_realloc triggered a new alloc. + */ + if (orig_uarg && uarg != orig_uarg) + return -EEXIST; + err = __zerocopy_sg_from_iter(sk, skb, &msg->msg_iter, len); if (err == -EFAULT || (err == -EMSGSIZE && skb->len == orig_len)) { /* Streams do not free skb on error. Reset to prev state. */ -- cgit v1.2.3 From a91dbff551a6f1865b68fa82b654591490b59901 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 3 Aug 2017 16:29:43 -0400 Subject: sock: ulimit on MSG_ZEROCOPY pages Bound the number of pages that a user may pin. Follow the lead of perf tools to maintain a per-user bound on memory locked pages commit 789f90fcf6b0 ("perf_counter: per user mlock gift") Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/linux/sched/user.h | 3 ++- include/linux/skbuff.h | 5 +++++ net/core/skbuff.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h index 5d5415e129d4..3c07e4135127 100644 --- a/include/linux/sched/user.h +++ b/include/linux/sched/user.h @@ -36,7 +36,8 @@ struct user_struct { struct hlist_node uidhash_node; kuid_t uid; -#if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL) +#if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL) || \ + defined(CONFIG_NET) atomic_long_t locked_vm; #endif }; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index f5bdd93a87da..8c0708d2e5e6 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -457,6 +457,11 @@ struct ubuf_info { }; }; atomic_t refcnt; + + struct mmpin { + struct user_struct *user; + unsigned int num_pg; + } mmp; }; #define skb_uarg(SKB) ((struct ubuf_info *)(skb_shinfo(SKB)->destructor_arg)) diff --git a/net/core/skbuff.c b/net/core/skbuff.c index dcee0f64f1fa..42b62c716a33 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -897,6 +897,44 @@ struct sk_buff *skb_morph(struct sk_buff *dst, struct sk_buff *src) } EXPORT_SYMBOL_GPL(skb_morph); +static int mm_account_pinned_pages(struct mmpin *mmp, size_t size) +{ + unsigned long max_pg, num_pg, new_pg, old_pg; + struct user_struct *user; + + if (capable(CAP_IPC_LOCK) || !size) + return 0; + + num_pg = (size >> PAGE_SHIFT) + 2; /* worst case */ + max_pg = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + user = mmp->user ? : current_user(); + + do { + old_pg = atomic_long_read(&user->locked_vm); + new_pg = old_pg + num_pg; + if (new_pg > max_pg) + return -ENOBUFS; + } while (atomic_long_cmpxchg(&user->locked_vm, old_pg, new_pg) != + old_pg); + + if (!mmp->user) { + mmp->user = get_uid(user); + mmp->num_pg = num_pg; + } else { + mmp->num_pg += num_pg; + } + + return 0; +} + +static void mm_unaccount_pinned_pages(struct mmpin *mmp) +{ + if (mmp->user) { + atomic_long_sub(mmp->num_pg, &mmp->user->locked_vm); + free_uid(mmp->user); + } +} + struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size) { struct ubuf_info *uarg; @@ -913,6 +951,12 @@ struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size) BUILD_BUG_ON(sizeof(*uarg) > sizeof(skb->cb)); uarg = (void *)skb->cb; + uarg->mmp.user = NULL; + + if (mm_account_pinned_pages(&uarg->mmp, size)) { + kfree_skb(skb); + return NULL; + } uarg->callback = sock_zerocopy_callback; uarg->id = ((u32)atomic_inc_return(&sk->sk_zckey)) - 1; @@ -956,6 +1000,8 @@ struct ubuf_info *sock_zerocopy_realloc(struct sock *sk, size_t size, next = (u32)atomic_read(&sk->sk_zckey); if ((u32)(uarg->id + uarg->len) == next) { + if (mm_account_pinned_pages(&uarg->mmp, size)) + return NULL; uarg->len++; uarg->bytelen = bytelen; atomic_set(&sk->sk_zckey, ++next); @@ -1038,6 +1084,8 @@ EXPORT_SYMBOL_GPL(sock_zerocopy_callback); void sock_zerocopy_put(struct ubuf_info *uarg) { if (uarg && atomic_dec_and_test(&uarg->refcnt)) { + mm_unaccount_pinned_pages(&uarg->mmp); + if (uarg->callback) uarg->callback(uarg, uarg->zerocopy); else -- cgit v1.2.3 From f214f915e7db99091f1312c48b30928c1e0c90b7 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Thu, 3 Aug 2017 16:29:44 -0400 Subject: tcp: enable MSG_ZEROCOPY Enable support for MSG_ZEROCOPY to the TCP stack. TSO and GSO are both supported. Only data sent to remote destinations is sent without copying. Packets looped onto a local destination have their payload copied to avoid unbounded latency. Tested: A 10x TCP_STREAM between two hosts showed a reduction in netserver process cycles by up to 70%, depending on packet size. Systemwide, savings are of course much less pronounced, at up to 20% best case. msg_zerocopy.sh 4 tcp: without zerocopy tx=121792 (7600 MB) txc=0 zc=n rx=60458 (7600 MB) with zerocopy tx=286257 (17863 MB) txc=286257 zc=y rx=140022 (17863 MB) This test opens a pair of sockets over veth, one one calls send with 64KB and optionally MSG_ZEROCOPY and on the other reads the initial bytes. The receiver truncates, so this is strictly an upper bound on what is achievable. It is more representative of sending data out of a physical NIC (when payload is not touched, either). Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 32 +++++++++++++++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 9dd6f4dba9b1..71b25567e787 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1165,6 +1165,7 @@ static int tcp_sendmsg_fastopen(struct sock *sk, struct msghdr *msg, int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size) { struct tcp_sock *tp = tcp_sk(sk); + struct ubuf_info *uarg = NULL; struct sk_buff *skb; struct sockcm_cookie sockc; int flags, err, copied = 0; @@ -1174,6 +1175,26 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size) long timeo; flags = msg->msg_flags; + + if (flags & MSG_ZEROCOPY && size) { + if (sk->sk_state != TCP_ESTABLISHED) { + err = -EINVAL; + goto out_err; + } + + skb = tcp_send_head(sk) ? tcp_write_queue_tail(sk) : NULL; + uarg = sock_zerocopy_realloc(sk, size, skb_zcopy(skb)); + if (!uarg) { + err = -ENOBUFS; + goto out_err; + } + + /* skb may be freed in main loop, keep extra ref on uarg */ + sock_zerocopy_get(uarg); + if (!(sk_check_csum_caps(sk) && sk->sk_route_caps & NETIF_F_SG)) + uarg->zerocopy = 0; + } + if (unlikely(flags & MSG_FASTOPEN || inet_sk(sk)->defer_connect)) { err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size); if (err == -EINPROGRESS && copied_syn > 0) @@ -1297,7 +1318,7 @@ new_segment: err = skb_add_data_nocache(sk, skb, &msg->msg_iter, copy); if (err) goto do_fault; - } else { + } else if (!uarg || !uarg->zerocopy) { bool merge = true; int i = skb_shinfo(skb)->nr_frags; struct page_frag *pfrag = sk_page_frag(sk); @@ -1335,6 +1356,13 @@ new_segment: page_ref_inc(pfrag->page); } pfrag->offset += copy; + } else { + err = skb_zerocopy_iter_stream(sk, skb, msg, copy, uarg); + if (err == -EMSGSIZE || err == -EEXIST) + goto new_segment; + if (err < 0) + goto do_error; + copy = err; } if (!copied) @@ -1381,6 +1409,7 @@ out: tcp_push(sk, flags, mss_now, tp->nonagle, size_goal); } out_nopush: + sock_zerocopy_put(uarg); return copied + copied_syn; do_fault: @@ -1397,6 +1426,7 @@ do_error: if (copied + copied_syn) goto out; out_err: + sock_zerocopy_put_abort(uarg); err = sk_stream_error(sk, flags, err); /* make sure we wake any epoll edge trigger waiter */ if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && -- cgit v1.2.3 From f7ebdff7575c3a5c099c8b1d661b0a65f4c6f10f Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:28:56 +0200 Subject: net: sched: sch_atm: use Qdisc_class_common structure Even if it is only for classid now, use this common struct a be aligned with the rest of the classful qdiscs. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/sch_atm.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 572fe2584e48..0af4b1c6f674 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -41,6 +41,7 @@ #define VCC2FLOW(vcc) ((struct atm_flow_data *) ((vcc)->user_back)) struct atm_flow_data { + struct Qdisc_class_common common; struct Qdisc *q; /* FIFO, TBF, etc. */ struct tcf_proto __rcu *filter_list; struct tcf_block *block; @@ -49,7 +50,6 @@ struct atm_flow_data { struct sk_buff *skb); /* chaining */ struct atm_qdisc_data *parent; /* parent qdisc */ struct socket *sock; /* for closing */ - u32 classid; /* x:y type ID */ int ref; /* reference count */ struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; @@ -75,7 +75,7 @@ static inline struct atm_flow_data *lookup_flow(struct Qdisc *sch, u32 classid) struct atm_flow_data *flow; list_for_each_entry(flow, &p->flows, list) { - if (flow->classid == classid) + if (flow->common.classid == classid) return flow; } return NULL; @@ -293,7 +293,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, flow->old_pop = flow->vcc->pop; flow->parent = p; flow->vcc->pop = sch_atm_pop; - flow->classid = classid; + flow->common.classid = classid; flow->ref = 1; flow->excess = excess; list_add(&flow->list, &p->link.list); @@ -549,7 +549,7 @@ static int atm_tc_init(struct Qdisc *sch, struct nlattr *opt) p->link.vcc = NULL; p->link.sock = NULL; - p->link.classid = sch->handle; + p->link.common.classid = sch->handle; p->link.ref = 1; tasklet_init(&p->task, sch_atm_dequeue, (unsigned long)sch); return 0; @@ -594,7 +594,7 @@ static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl, sch, p, flow, skb, tcm); if (list_empty(&flow->list)) return -EINVAL; - tcm->tcm_handle = flow->classid; + tcm->tcm_handle = flow->common.classid; tcm->tcm_info = flow->q->handle; nest = nla_nest_start(skb, TCA_OPTIONS); @@ -619,7 +619,7 @@ static int atm_tc_dump_class(struct Qdisc *sch, unsigned long cl, goto nla_put_failure; } if (flow->excess) { - if (nla_put_u32(skb, TCA_ATM_EXCESS, flow->classid)) + if (nla_put_u32(skb, TCA_ATM_EXCESS, flow->common.classid)) goto nla_put_failure; } else { if (nla_put_u32(skb, TCA_ATM_EXCESS, 0)) -- cgit v1.2.3 From 4ebc1e3cfcd8778e2150bdb799b19e85348b8efa Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:28:57 +0200 Subject: net: sched: remove unneeded tcf_em_tree_change Since tcf_em_tree_validate could be always called on a newly created filter, there is no need for this change function. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 21 --------------------- net/sched/cls_basic.c | 4 +--- net/sched/cls_cgroup.c | 4 +--- net/sched/cls_flow.c | 12 +++++------- 4 files changed, 7 insertions(+), 34 deletions(-) (limited to 'net') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 537d0a0ad4c4..f4462ec8b2f4 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -332,26 +332,6 @@ int tcf_em_tree_dump(struct sk_buff *, struct tcf_ematch_tree *, int); int __tcf_em_tree_match(struct sk_buff *, struct tcf_ematch_tree *, struct tcf_pkt_info *); -/** - * tcf_em_tree_change - replace ematch tree of a running classifier - * - * @tp: classifier kind handle - * @dst: destination ematch tree variable - * @src: source ematch tree (temporary tree from tcf_em_tree_validate) - * - * This functions replaces the ematch tree in @dst with the ematch - * tree in @src. The classifier in charge of the ematch tree may be - * running. - */ -static inline void tcf_em_tree_change(struct tcf_proto *tp, - struct tcf_ematch_tree *dst, - struct tcf_ematch_tree *src) -{ - tcf_tree_lock(tp); - memcpy(dst, src, sizeof(*dst)); - tcf_tree_unlock(tp); -} - /** * tcf_em_tree_match - evaulate an ematch tree * @@ -386,7 +366,6 @@ struct tcf_ematch_tree { #define tcf_em_tree_validate(tp, tb, t) ((void)(t), 0) #define tcf_em_tree_destroy(t) do { (void)(t); } while(0) #define tcf_em_tree_dump(skb, t, tlv) (0) -#define tcf_em_tree_change(tp, dst, src) do { } while(0) #define tcf_em_tree_match(skb, t, info) ((void)(info), 1) #endif /* CONFIG_NET_EMATCH */ diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index c4fd63a068f9..979cd2683b46 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -130,7 +130,6 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp, { int err; struct tcf_exts e; - struct tcf_ematch_tree t; err = tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE); if (err < 0) @@ -139,7 +138,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp, if (err < 0) goto errout; - err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &t); + err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &f->ematches); if (err < 0) goto errout; @@ -149,7 +148,6 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp, } tcf_exts_change(tp, &f->exts, &e); - tcf_em_tree_change(tp, &f->ematches, &t); f->tp = tp; return 0; diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index 12ce547eea04..ce7d38beab95 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -76,7 +76,6 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, struct nlattr *tb[TCA_CGROUP_MAX + 1]; struct cls_cgroup_head *head = rtnl_dereference(tp->root); struct cls_cgroup_head *new; - struct tcf_ematch_tree t; struct tcf_exts e; int err; @@ -112,14 +111,13 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, goto errout; } - err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &t); + err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &new->ematches); if (err < 0) { tcf_exts_destroy(&e); goto errout; } tcf_exts_change(tp, &new->exts, &e); - tcf_em_tree_change(tp, &new->ematches, &t); rcu_assign_pointer(tp->root, new); if (head) diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 3065752b9cda..71fd1af01726 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -389,7 +389,6 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, struct nlattr *opt = tca[TCA_OPTIONS]; struct nlattr *tb[TCA_FLOW_MAX + 1]; struct tcf_exts e; - struct tcf_ematch_tree t; unsigned int nkeys = 0; unsigned int perturb_period = 0; u32 baseclass = 0; @@ -432,13 +431,13 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, if (err < 0) goto err1; - err = tcf_em_tree_validate(tp, tb[TCA_FLOW_EMATCHES], &t); - if (err < 0) - goto err1; - err = -ENOBUFS; fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); if (!fnew) + goto err1; + + err = tcf_em_tree_validate(tp, tb[TCA_FLOW_EMATCHES], &fnew->ematches); + if (err < 0) goto err2; err = tcf_exts_init(&fnew->exts, TCA_FLOW_ACT, TCA_FLOW_POLICE); @@ -512,7 +511,6 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, (unsigned long)fnew); tcf_exts_change(tp, &fnew->exts, &e); - tcf_em_tree_change(tp, &fnew->ematches, &t); netif_keep_dst(qdisc_dev(tp->q)); @@ -554,8 +552,8 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, err3: tcf_exts_destroy(&fnew->exts); + tcf_em_tree_destroy(&fnew->ematches); err2: - tcf_em_tree_destroy(&t); kfree(fnew); err1: tcf_exts_destroy(&e); -- cgit v1.2.3 From 3bcc0cec818fa969fe555b44443347211ed787a3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:28:58 +0200 Subject: net: sched: change names of action number helpers to be aligned with the rest The rest of the helpers are named tcf_exts_*, so change the name of the action number helpers to be aligned. While at it, change to inline functions. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 4 +-- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +- drivers/net/ethernet/netronome/nfp/bpf/offload.c | 4 +-- include/net/pkt_cls.h | 36 ++++++++++++++++------ net/dsa/slave.c | 2 +- net/sched/cls_api.c | 2 +- 9 files changed, 37 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c index ef06ce8247ab..6f734c52ef25 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c @@ -96,7 +96,7 @@ static int fill_action_fields(struct adapter *adap, LIST_HEAD(actions); exts = cls->knode.exts; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; tcf_exts_to_list(exts, &actions); diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 96606e3eb965..091fcc7e6e43 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8953,7 +8953,7 @@ static int parse_tc_actions(struct ixgbe_adapter *adapter, LIST_HEAD(actions); int err; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; tcf_exts_to_list(exts, &actions); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 3c536f560dd2..78f50d9f621d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1326,7 +1326,7 @@ static int parse_tc_nic_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, LIST_HEAD(actions); int err; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; attr->flow_tag = MLX5_FS_DEFAULT_FLOW_TAG; @@ -1839,7 +1839,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, bool encap = false; int err = 0; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; memset(attr, 0, sizeof(*attr)); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 88b668ba0d8a..66d511d45c25 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1626,7 +1626,7 @@ static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, LIST_HEAD(actions); int err; - if (!tc_single_action(cls->exts)) { + if (!tcf_exts_has_one_action(cls->exts)) { netdev_err(mlxsw_sp_port->dev, "only singular actions are supported\n"); return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 400ad4081660..9be48d2e43ca 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -53,7 +53,7 @@ static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, LIST_HEAD(actions); int err; - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return 0; /* Count action is inserted first */ diff --git a/drivers/net/ethernet/netronome/nfp/bpf/offload.c b/drivers/net/ethernet/netronome/nfp/bpf/offload.c index 78d80a364edb..a88bb5bc0082 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/offload.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/offload.c @@ -115,14 +115,14 @@ nfp_net_bpf_get_act(struct nfp_net *nn, struct tc_cls_bpf_offload *cls_bpf) /* TC direct action */ if (cls_bpf->exts_integrated) { - if (tc_no_actions(cls_bpf->exts)) + if (!tcf_exts_has_actions(cls_bpf->exts)) return NN_ACT_DIRECT; return -EOPNOTSUPP; } /* TC legacy mode */ - if (!tc_single_action(cls_bpf->exts)) + if (!tcf_exts_has_one_action(cls_bpf->exts)) return -EOPNOTSUPP; tcf_exts_to_list(cls_bpf->exts, &actions); diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index f4462ec8b2f4..7f2563636df0 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -199,17 +199,35 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, return 0; } +/** + * tcf_exts_has_actions - check if at least one action is present + * @exts: tc filter extensions handle + * + * Returns true if at least one action is present. + */ +static inline bool tcf_exts_has_actions(struct tcf_exts *exts) +{ #ifdef CONFIG_NET_CLS_ACT + return exts->nr_actions; +#else + return false; +#endif +} -#define tc_no_actions(_exts) ((_exts)->nr_actions == 0) -#define tc_single_action(_exts) ((_exts)->nr_actions == 1) - -#else /* CONFIG_NET_CLS_ACT */ - -#define tc_no_actions(_exts) true -#define tc_single_action(_exts) false - -#endif /* CONFIG_NET_CLS_ACT */ +/** + * tcf_exts_has_one_action - check if exactly one action is present + * @exts: tc filter extensions handle + * + * Returns true if exactly one action is present. + */ +static inline bool tcf_exts_has_one_action(struct tcf_exts *exts) +{ +#ifdef CONFIG_NET_CLS_ACT + return exts->nr_actions == 1; +#else + return false; +#endif +} int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, struct nlattr *rate_tlv, diff --git a/net/dsa/slave.c b/net/dsa/slave.c index e196562035b1..83252e8426d7 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -779,7 +779,7 @@ static int dsa_slave_add_cls_matchall(struct net_device *dev, if (!ds->ops->port_mirror_add) return err; - if (!tc_single_action(cls->exts)) + if (!tcf_exts_has_one_action(cls->exts)) return err; tcf_exts_to_list(cls->exts, &actions); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 39da0c5801c9..287ae6cbf73b 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -972,7 +972,7 @@ int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, const struct tc_action *a; LIST_HEAD(actions); - if (tc_no_actions(exts)) + if (!tcf_exts_has_actions(exts)) return -EINVAL; tcf_exts_to_list(exts, &actions); -- cgit v1.2.3 From 6fc6d06e5371507e68c6904a3423622b0e465b64 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:00 +0200 Subject: net: sched: remove redundant helpers tcf_exts_is_predicative and tcf_exts_is_available These two helpers are doing the same as tcf_exts_has_actions, so remove them and use tcf_exts_has_actions instead. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 30 ------------------------------ net/sched/cls_fw.c | 2 +- net/sched/cls_route.c | 2 +- net/sched/cls_tcindex.c | 2 +- 4 files changed, 3 insertions(+), 33 deletions(-) (limited to 'net') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 322a2823cc6a..817badf733b5 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -113,36 +113,6 @@ static inline int tcf_exts_init(struct tcf_exts *exts, int action, int police) return 0; } -/** - * tcf_exts_is_predicative - check if a predicative extension is present - * @exts: tc filter extensions handle - * - * Returns 1 if a predicative extension is present, i.e. an extension which - * might cause further actions and thus overrule the regular tcf_result. - */ -static inline int -tcf_exts_is_predicative(struct tcf_exts *exts) -{ -#ifdef CONFIG_NET_CLS_ACT - return exts->nr_actions; -#else - return 0; -#endif -} - -/** - * tcf_exts_is_available - check if at least one extension is present - * @exts: tc filter extensions handle - * - * Returns 1 if at least one extension is present. - */ -static inline int -tcf_exts_is_available(struct tcf_exts *exts) -{ - /* All non-predicative extensions must be added here. */ - return tcf_exts_is_predicative(exts); -} - static inline void tcf_exts_to_list(const struct tcf_exts *exts, struct list_head *actions) { diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index d3885362e017..a53fa75dfc7b 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -387,7 +387,7 @@ static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, t->tcm_handle = f->id; - if (!f->res.classid && !tcf_exts_is_available(&f->exts)) + if (!f->res.classid && !tcf_exts_has_actions(&f->exts)) return skb->len; nest = nla_nest_start(skb, TCA_OPTIONS); diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index d63d5502ee02..26f863634862 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -113,7 +113,7 @@ static inline int route4_hash_wild(void) #define ROUTE4_APPLY_RESULT() \ { \ *res = f->res; \ - if (tcf_exts_is_available(&f->exts)) { \ + if (tcf_exts_has_actions(&f->exts)) { \ int r = tcf_exts_exec(skb, &f->exts, res); \ if (r < 0) { \ dont_cache = 1; \ diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 8a8a58357c39..66924d147e97 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -52,7 +52,7 @@ struct tcindex_data { static inline int tcindex_filter_is_set(struct tcindex_filter_result *r) { - return tcf_exts_is_predicative(&r->exts) || r->res.classid; + return tcf_exts_has_actions(&r->exts) || r->res.classid; } static struct tcindex_filter_result *tcindex_lookup(struct tcindex_data *p, -- cgit v1.2.3 From ec1a9cca0e13391167567964fd04e61a39d6a4ae Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:02 +0200 Subject: net: sched: remove check for number of actions in tcf_exts_exec Leave it to tcf_action_exec to return TC_ACT_OK in case there is no action present. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 4 +--- net/sched/act_api.c | 3 ++- 2 files changed, 3 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 61ce521688b2..b8959c9a190d 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -192,9 +192,7 @@ tcf_exts_exec(struct sk_buff *skb, struct tcf_exts *exts, struct tcf_result *res) { #ifdef CONFIG_NET_CLS_ACT - if (tcf_exts_has_actions(exts)) - return tcf_action_exec(skb, exts->actions, exts->nr_actions, - res); + return tcf_action_exec(skb, exts->actions, exts->nr_actions, res); #endif return TC_ACT_OK; } diff --git a/net/sched/act_api.c b/net/sched/act_api.c index f19b118df414..a2915d958279 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -473,9 +473,10 @@ static struct tc_action_ops *tc_lookup_action(struct nlattr *kind) int tcf_action_exec(struct sk_buff *skb, struct tc_action **actions, int nr_actions, struct tcf_result *res) { - int ret = -1, i; u32 jmp_prgcnt = 0; u32 jmp_ttl = TCA_ACT_MAX_PRIO; /*matches actions per filter */ + int i; + int ret = TC_ACT_OK; if (skb_skip_tc_classify(skb)) return TC_ACT_OK; -- cgit v1.2.3 From 978dfd8d14a503d20be1f1e2ae328c3eac675a2d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:03 +0200 Subject: net: sched: use tcf_exts_has_actions instead of exts->nr_actions For check in tcf_exts_dump use tcf_exts_has_actions helper instead of exts->nr_actions for checking if there are any actions present. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_api.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 287ae6cbf73b..735d556a5283 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -915,7 +915,7 @@ int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts) #ifdef CONFIG_NET_CLS_ACT struct nlattr *nest; - if (exts->action && exts->nr_actions) { + if (exts->action && tcf_exts_has_actions(exts)) { /* * again for backward compatible mode - we want * to work with both old and new modes of entering -- cgit v1.2.3 From 6a725c481df36b1ad471ea788a5bc64c25bf7af8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:04 +0200 Subject: net: sched: cls_bpf: rename cls_bpf_modify_existing function The name cls_bpf_modify_existing is highly misleading, as it indeed does not modify anything existing. It does not modify at all. Signed-off-by: Jiri Pirko Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/sched/cls_bpf.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index f57bd531ba98..e9ab8374a877 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -382,10 +382,9 @@ static int cls_bpf_prog_from_efd(struct nlattr **tb, struct cls_bpf_prog *prog, return 0; } -static int cls_bpf_modify_existing(struct net *net, struct tcf_proto *tp, - struct cls_bpf_prog *prog, - unsigned long base, struct nlattr **tb, - struct nlattr *est, bool ovr) +static int cls_bpf_set_parms(struct net *net, struct tcf_proto *tp, + struct cls_bpf_prog *prog, unsigned long base, + struct nlattr **tb, struct nlattr *est, bool ovr) { bool is_bpf, is_ebpf, have_exts = false; struct tcf_exts exts; @@ -508,8 +507,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, goto errout; } - ret = cls_bpf_modify_existing(net, tp, prog, base, tb, tca[TCA_RATE], - ovr); + ret = cls_bpf_set_parms(net, tp, prog, base, tb, tca[TCA_RATE], ovr); if (ret < 0) goto errout; -- cgit v1.2.3 From 1e5003af37352f37ba008f99014cc47c684ae808 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:05 +0200 Subject: net: sched: cls_fw: rename fw_change_attrs function Since the function name is misleading since it is not changing anything, name it similarly to other cls. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_fw.c | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index a53fa75dfc7b..358bf34f0dd0 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -190,10 +190,9 @@ static const struct nla_policy fw_policy[TCA_FW_MAX + 1] = { [TCA_FW_MASK] = { .type = NLA_U32 }, }; -static int -fw_change_attrs(struct net *net, struct tcf_proto *tp, struct fw_filter *f, - struct nlattr **tb, struct nlattr **tca, unsigned long base, - bool ovr) +static int fw_set_parms(struct net *net, struct tcf_proto *tp, + struct fw_filter *f, struct nlattr **tb, + struct nlattr **tca, unsigned long base, bool ovr) { struct fw_head *head = rtnl_dereference(tp->root); struct tcf_exts e; @@ -282,7 +281,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, return err; } - err = fw_change_attrs(net, tp, fnew, tb, tca, base, ovr); + err = fw_set_parms(net, tp, fnew, tb, tca, base, ovr); if (err < 0) { tcf_exts_destroy(&fnew->exts); kfree(fnew); @@ -330,7 +329,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, f->id = handle; f->tp = tp; - err = fw_change_attrs(net, tp, f, tb, tca, base, ovr); + err = fw_set_parms(net, tp, f, tb, tca, base, ovr); if (err < 0) goto errout; -- cgit v1.2.3 From 455075292b0d58f708c1afe7011a8b9223b2c9c8 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:06 +0200 Subject: net: sched: cls_flower: no need to call tcf_exts_change for newly allocated struct As the f struct was allocated right before fl_set_parms call, no need to use tcf_exts_change to do atomic change, and we can just fill-up the unused exts struct directly by tcf_exts_validate. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 7832eb93379b..7ab524fc43f9 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -852,15 +852,11 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, struct nlattr **tb, struct nlattr *est, bool ovr) { - struct tcf_exts e; int err; - err = tcf_exts_init(&e, TCA_FLOWER_ACT, 0); + err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr); if (err < 0) return err; - err = tcf_exts_validate(net, tp, tb, est, &e, ovr); - if (err < 0) - goto errout; if (tb[TCA_FLOWER_CLASSID]) { f->res.classid = nla_get_u32(tb[TCA_FLOWER_CLASSID]); @@ -869,17 +865,12 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp, err = fl_set_key(net, tb, &f->key, &mask->key); if (err) - goto errout; + return err; fl_mask_update_range(mask); fl_set_masked_key(&f->mkey, &f->key, mask); - tcf_exts_change(tp, &f->exts, &e); - return 0; -errout: - tcf_exts_destroy(&e); - return err; } static u32 fl_grab_new_handle(struct tcf_proto *tp, -- cgit v1.2.3 From 94611bff6e1e57c4a74a9242f3fe749aa4a31678 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:07 +0200 Subject: net: sched: cls_fw: no need to call tcf_exts_change for newly allocated struct As the f struct was allocated right before fw_set_parms call, no need to use tcf_exts_change to do atomic change, and we can just fill-up the unused exts struct directly by tcf_exts_validate. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_fw.c | 21 +++++---------------- 1 file changed, 5 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 358bf34f0dd0..11f178f1b2be 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -195,16 +195,12 @@ static int fw_set_parms(struct net *net, struct tcf_proto *tp, struct nlattr **tca, unsigned long base, bool ovr) { struct fw_head *head = rtnl_dereference(tp->root); - struct tcf_exts e; u32 mask; int err; - err = tcf_exts_init(&e, TCA_FW_ACT, TCA_FW_POLICE); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &f->exts, ovr); if (err < 0) return err; - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); - if (err < 0) - goto errout; if (tb[TCA_FW_CLASSID]) { f->res.classid = nla_get_u32(tb[TCA_FW_CLASSID]); @@ -215,10 +211,8 @@ static int fw_set_parms(struct net *net, struct tcf_proto *tp, if (tb[TCA_FW_INDEV]) { int ret; ret = tcf_change_indev(net, tb[TCA_FW_INDEV]); - if (ret < 0) { - err = ret; - goto errout; - } + if (ret < 0) + return ret; f->ifindex = ret; } #endif /* CONFIG_NET_CLS_IND */ @@ -227,16 +221,11 @@ static int fw_set_parms(struct net *net, struct tcf_proto *tp, if (tb[TCA_FW_MASK]) { mask = nla_get_u32(tb[TCA_FW_MASK]); if (mask != head->mask) - goto errout; + return err; } else if (head->mask != 0xFFFFFFFF) - goto errout; - - tcf_exts_change(tp, &f->exts, &e); + return err; return 0; -errout: - tcf_exts_destroy(&e); - return err; } static int fw_change(struct net *net, struct sk_buff *in_skb, -- cgit v1.2.3 From a74cb36980c3676c888a87dd66bcd410f7e75c08 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:08 +0200 Subject: net: sched: cls_matchall: no need to call tcf_exts_change for newly allocated struct As the head struct was allocated right before mall_set_parms call, no need to use tcf_exts_change to do atomic change, and we can just fill-up the unused exts struct directly by tcf_exts_validate. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_matchall.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 9dc26c32cf32..f35177b48373 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -120,27 +120,17 @@ static int mall_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, struct nlattr **tb, struct nlattr *est, bool ovr) { - struct tcf_exts e; int err; - err = tcf_exts_init(&e, TCA_MATCHALL_ACT, 0); - if (err) - return err; - err = tcf_exts_validate(net, tp, tb, est, &e, ovr); + err = tcf_exts_validate(net, tp, tb, est, &head->exts, ovr); if (err < 0) - goto errout; + return err; if (tb[TCA_MATCHALL_CLASSID]) { head->res.classid = nla_get_u32(tb[TCA_MATCHALL_CLASSID]); tcf_bind_filter(tp, &head->res, base); } - - tcf_exts_change(tp, &head->exts, &e); - return 0; -errout: - tcf_exts_destroy(&e); - return err; } static int mall_change(struct net *net, struct sk_buff *in_skb, -- cgit v1.2.3 From ff1f8ca0805ab03c31e14a9356aa2ac81d65c338 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:09 +0200 Subject: net: sched: cls_basic: no need to call tcf_exts_change for newly allocated struct As the f struct was allocated right before basic_set_parms call, no need to use tcf_exts_change to do atomic change, and we can just fill-up the unused exts struct directly by tcf_exts_validate. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_basic.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 979cd2683b46..7c7a82138f76 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -129,31 +129,22 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp, struct nlattr *est, bool ovr) { int err; - struct tcf_exts e; - err = tcf_exts_init(&e, TCA_BASIC_ACT, TCA_BASIC_POLICE); + err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr); if (err < 0) return err; - err = tcf_exts_validate(net, tp, tb, est, &e, ovr); - if (err < 0) - goto errout; err = tcf_em_tree_validate(tp, tb[TCA_BASIC_EMATCHES], &f->ematches); if (err < 0) - goto errout; + return err; if (tb[TCA_BASIC_CLASSID]) { f->res.classid = nla_get_u32(tb[TCA_BASIC_CLASSID]); tcf_bind_filter(tp, &f->res, base); } - tcf_exts_change(tp, &f->exts, &e); f->tp = tp; - return 0; -errout: - tcf_exts_destroy(&e); - return err; } static int basic_change(struct net *net, struct sk_buff *in_skb, -- cgit v1.2.3 From 6839da326dfcb98bf8020d42b416636471f1e462 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:10 +0200 Subject: net: sched: cls_bpf: no need to call tcf_exts_change for newly allocated struct As the prog struct was allocated right before cls_bpf_set_parms call, no need to use tcf_exts_change to do atomic change, and we can just fill-up the unused exts struct directly by tcf_exts_validate. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_bpf.c | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index e9ab8374a877..cf248c3137ad 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -387,7 +387,6 @@ static int cls_bpf_set_parms(struct net *net, struct tcf_proto *tp, struct nlattr **tb, struct nlattr *est, bool ovr) { bool is_bpf, is_ebpf, have_exts = false; - struct tcf_exts exts; u32 gen_flags = 0; int ret; @@ -396,30 +395,23 @@ static int cls_bpf_set_parms(struct net *net, struct tcf_proto *tp, if ((!is_bpf && !is_ebpf) || (is_bpf && is_ebpf)) return -EINVAL; - ret = tcf_exts_init(&exts, TCA_BPF_ACT, TCA_BPF_POLICE); + ret = tcf_exts_validate(net, tp, tb, est, &prog->exts, ovr); if (ret < 0) return ret; - ret = tcf_exts_validate(net, tp, tb, est, &exts, ovr); - if (ret < 0) - goto errout; if (tb[TCA_BPF_FLAGS]) { u32 bpf_flags = nla_get_u32(tb[TCA_BPF_FLAGS]); - if (bpf_flags & ~TCA_BPF_FLAG_ACT_DIRECT) { - ret = -EINVAL; - goto errout; - } + if (bpf_flags & ~TCA_BPF_FLAG_ACT_DIRECT) + return -EINVAL; have_exts = bpf_flags & TCA_BPF_FLAG_ACT_DIRECT; } if (tb[TCA_BPF_FLAGS_GEN]) { gen_flags = nla_get_u32(tb[TCA_BPF_FLAGS_GEN]); if (gen_flags & ~CLS_BPF_SUPPORTED_GEN_FLAGS || - !tc_flags_valid(gen_flags)) { - ret = -EINVAL; - goto errout; - } + !tc_flags_valid(gen_flags)) + return -EINVAL; } prog->exts_integrated = have_exts; @@ -428,19 +420,14 @@ static int cls_bpf_set_parms(struct net *net, struct tcf_proto *tp, ret = is_bpf ? cls_bpf_prog_from_ops(tb, prog) : cls_bpf_prog_from_efd(tb, prog, tp); if (ret < 0) - goto errout; + return ret; if (tb[TCA_BPF_CLASSID]) { prog->res.classid = nla_get_u32(tb[TCA_BPF_CLASSID]); tcf_bind_filter(tp, &prog->res, base); } - tcf_exts_change(tp, &prog->exts, &exts); return 0; - -errout: - tcf_exts_destroy(&exts); - return ret; } static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp, -- cgit v1.2.3 From 8cc6251381183191f99ecd6d49931251ebbbe27d Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:11 +0200 Subject: net: sched: cls_cgroup: no need to call tcf_exts_change for newly allocated struct As the new struct just was allocated, so no need to use tcf_exts_change to do atomic change, and we can just fill-up the unused exts struct directly by tcf_exts_validate. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_cgroup.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index ce7d38beab95..df7a582775df 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -76,7 +76,6 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, struct nlattr *tb[TCA_CGROUP_MAX + 1]; struct cls_cgroup_head *head = rtnl_dereference(tp->root); struct cls_cgroup_head *new; - struct tcf_exts e; int err; if (!tca[TCA_OPTIONS]) @@ -102,22 +101,13 @@ static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, if (err < 0) goto errout; - err = tcf_exts_init(&e, TCA_CGROUP_ACT, TCA_CGROUP_POLICE); + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &new->exts, ovr); if (err < 0) goto errout; - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); - if (err < 0) { - tcf_exts_destroy(&e); - goto errout; - } err = tcf_em_tree_validate(tp, tb[TCA_CGROUP_EMATCHES], &new->ematches); - if (err < 0) { - tcf_exts_destroy(&e); + if (err < 0) goto errout; - } - - tcf_exts_change(tp, &new->exts, &e); rcu_assign_pointer(tp->root, new); if (head) -- cgit v1.2.3 From c09fc2e11ed1b7fc8cfa97fb1da544225fc32277 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:12 +0200 Subject: net: sched: cls_flow: no need to call tcf_exts_change for newly allocated struct As the fnew struct just was allocated, so no need to use tcf_exts_change to do atomic change, and we can just fill-up the unused exts struct directly by tcf_exts_validate. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_flow.c | 41 ++++++++++++++++------------------------- 1 file changed, 16 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 71fd1af01726..55e281b20140 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -388,7 +388,6 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, struct flow_filter *fold, *fnew; struct nlattr *opt = tca[TCA_OPTIONS]; struct nlattr *tb[TCA_FLOW_MAX + 1]; - struct tcf_exts e; unsigned int nkeys = 0; unsigned int perturb_period = 0; u32 baseclass = 0; @@ -424,31 +423,27 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, return -EOPNOTSUPP; } - err = tcf_exts_init(&e, TCA_FLOW_ACT, TCA_FLOW_POLICE); - if (err < 0) - goto err1; - err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &e, ovr); - if (err < 0) - goto err1; - - err = -ENOBUFS; fnew = kzalloc(sizeof(*fnew), GFP_KERNEL); if (!fnew) - goto err1; + return -ENOBUFS; err = tcf_em_tree_validate(tp, tb[TCA_FLOW_EMATCHES], &fnew->ematches); if (err < 0) - goto err2; + goto err1; err = tcf_exts_init(&fnew->exts, TCA_FLOW_ACT, TCA_FLOW_POLICE); if (err < 0) - goto err3; + goto err2; + + err = tcf_exts_validate(net, tp, tb, tca[TCA_RATE], &fnew->exts, ovr); + if (err < 0) + goto err2; fold = (struct flow_filter *)*arg; if (fold) { err = -EINVAL; if (fold->handle != handle && handle) - goto err3; + goto err2; /* Copy fold into fnew */ fnew->tp = fold->tp; @@ -468,31 +463,31 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, if (tb[TCA_FLOW_MODE]) mode = nla_get_u32(tb[TCA_FLOW_MODE]); if (mode != FLOW_MODE_HASH && nkeys > 1) - goto err3; + goto err2; if (mode == FLOW_MODE_HASH) perturb_period = fold->perturb_period; if (tb[TCA_FLOW_PERTURB]) { if (mode != FLOW_MODE_HASH) - goto err3; + goto err2; perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; } } else { err = -EINVAL; if (!handle) - goto err3; + goto err2; if (!tb[TCA_FLOW_KEYS]) - goto err3; + goto err2; mode = FLOW_MODE_MAP; if (tb[TCA_FLOW_MODE]) mode = nla_get_u32(tb[TCA_FLOW_MODE]); if (mode != FLOW_MODE_HASH && nkeys > 1) - goto err3; + goto err2; if (tb[TCA_FLOW_PERTURB]) { if (mode != FLOW_MODE_HASH) - goto err3; + goto err2; perturb_period = nla_get_u32(tb[TCA_FLOW_PERTURB]) * HZ; } @@ -510,8 +505,6 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, setup_deferrable_timer(&fnew->perturb_timer, flow_perturbation, (unsigned long)fnew); - tcf_exts_change(tp, &fnew->exts, &e); - netif_keep_dst(qdisc_dev(tp->q)); if (tb[TCA_FLOW_KEYS]) { @@ -550,13 +543,11 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, call_rcu(&fold->rcu, flow_destroy_filter); return 0; -err3: +err2: tcf_exts_destroy(&fnew->exts); tcf_em_tree_destroy(&fnew->ematches); -err2: - kfree(fnew); err1: - tcf_exts_destroy(&e); + kfree(fnew); return err; } -- cgit v1.2.3 From 8c98d571bb0e9717fd7be7242945e8e0abebbaa3 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:13 +0200 Subject: net: sched: cls_route: no need to call tcf_exts_change for newly allocated struct As the f struct was allocated right before route4_set_parms call, no need to use tcf_exts_change to do atomic change, and we can just fill-up the unused exts struct directly by tcf_exts_validate. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_route.c | 30 +++++++++--------------------- 1 file changed, 9 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 26f863634862..f1e7d7850b44 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -372,37 +372,32 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, struct route4_filter *fp; unsigned int h1; struct route4_bucket *b; - struct tcf_exts e; int err; - err = tcf_exts_init(&e, TCA_ROUTE4_ACT, TCA_ROUTE4_POLICE); + err = tcf_exts_validate(net, tp, tb, est, &f->exts, ovr); if (err < 0) return err; - err = tcf_exts_validate(net, tp, tb, est, &e, ovr); - if (err < 0) - goto errout; - err = -EINVAL; if (tb[TCA_ROUTE4_TO]) { if (new && handle & 0x8000) - goto errout; + return -EINVAL; to = nla_get_u32(tb[TCA_ROUTE4_TO]); if (to > 0xFF) - goto errout; + return -EINVAL; nhandle = to; } if (tb[TCA_ROUTE4_FROM]) { if (tb[TCA_ROUTE4_IIF]) - goto errout; + return -EINVAL; id = nla_get_u32(tb[TCA_ROUTE4_FROM]); if (id > 0xFF) - goto errout; + return -EINVAL; nhandle |= id << 16; } else if (tb[TCA_ROUTE4_IIF]) { id = nla_get_u32(tb[TCA_ROUTE4_IIF]); if (id > 0x7FFF) - goto errout; + return -EINVAL; nhandle |= (id | 0x8000) << 16; } else nhandle |= 0xFFFF << 16; @@ -410,27 +405,25 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, if (handle && new) { nhandle |= handle & 0x7F00; if (nhandle != handle) - goto errout; + return -EINVAL; } h1 = to_hash(nhandle); b = rtnl_dereference(head->table[h1]); if (!b) { - err = -ENOBUFS; b = kzalloc(sizeof(struct route4_bucket), GFP_KERNEL); if (b == NULL) - goto errout; + return -ENOBUFS; rcu_assign_pointer(head->table[h1], b); } else { unsigned int h2 = from_hash(nhandle >> 16); - err = -EEXIST; for (fp = rtnl_dereference(b->ht[h2]); fp; fp = rtnl_dereference(fp->next)) if (fp->handle == f->handle) - goto errout; + return -EEXIST; } if (tb[TCA_ROUTE4_TO]) @@ -450,12 +443,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, tcf_bind_filter(tp, &f->res, base); } - tcf_exts_change(tp, &f->exts, &e); - return 0; -errout: - tcf_exts_destroy(&e); - return err; } static int route4_change(struct net *net, struct sk_buff *in_skb, -- cgit v1.2.3 From 705c7091262d02b09eb686c24491de61bf42fdb2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:14 +0200 Subject: net: sched: cls_u32: no need to call tcf_exts_change for newly allocated struct As the n struct was allocated right before u32_set_parms call, no need to use tcf_exts_change to do atomic change, and we can just fill-up the unused exts struct directly by tcf_exts_validate. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_u32.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 2d01195153e6..9fd243799fe7 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -723,29 +723,24 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, struct tc_u_knode *n, struct nlattr **tb, struct nlattr *est, bool ovr) { - struct tcf_exts e; int err; - err = tcf_exts_init(&e, TCA_U32_ACT, TCA_U32_POLICE); + err = tcf_exts_validate(net, tp, tb, est, &n->exts, ovr); if (err < 0) return err; - err = tcf_exts_validate(net, tp, tb, est, &e, ovr); - if (err < 0) - goto errout; - err = -EINVAL; if (tb[TCA_U32_LINK]) { u32 handle = nla_get_u32(tb[TCA_U32_LINK]); struct tc_u_hnode *ht_down = NULL, *ht_old; if (TC_U32_KEY(handle)) - goto errout; + return -EINVAL; if (handle) { ht_down = u32_lookup_ht(ht->tp_c, handle); if (ht_down == NULL) - goto errout; + return -EINVAL; ht_down->refcnt++; } @@ -765,16 +760,11 @@ static int u32_set_parms(struct net *net, struct tcf_proto *tp, int ret; ret = tcf_change_indev(net, tb[TCA_U32_INDEV]); if (ret < 0) - goto errout; + return -EINVAL; n->ifindex = ret; } #endif - tcf_exts_change(tp, &n->exts, &e); - return 0; -errout: - tcf_exts_destroy(&e); - return err; } static void u32_replace_knode(struct tcf_proto *tp, struct tc_u_common *tp_c, -- cgit v1.2.3 From 9b0d4446b56904b59ae3809913b0ac760fa941a6 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Fri, 4 Aug 2017 14:29:15 +0200 Subject: net: sched: avoid atomic swap in tcf_exts_change tcf_exts_change is always called on newly created exts, which are not used on fastpath. Therefore, simple struct copy is enough. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 3 +-- net/sched/cls_api.c | 10 ++-------- net/sched/cls_rsvp.h | 4 ++-- net/sched/cls_tcindex.c | 6 +++--- 4 files changed, 8 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index b8959c9a190d..e0c54f111467 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -201,8 +201,7 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, struct nlattr *rate_tlv, struct tcf_exts *exts, bool ovr); void tcf_exts_destroy(struct tcf_exts *exts); -void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, - struct tcf_exts *src); +void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src); int tcf_exts_dump(struct sk_buff *skb, struct tcf_exts *exts); int tcf_exts_dump_stats(struct sk_buff *skb, struct tcf_exts *exts); int tcf_exts_get_dev(struct net_device *dev, struct tcf_exts *exts, diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 735d556a5283..e655221c654e 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -883,18 +883,12 @@ int tcf_exts_validate(struct net *net, struct tcf_proto *tp, struct nlattr **tb, } EXPORT_SYMBOL(tcf_exts_validate); -void tcf_exts_change(struct tcf_proto *tp, struct tcf_exts *dst, - struct tcf_exts *src) +void tcf_exts_change(struct tcf_exts *dst, struct tcf_exts *src) { #ifdef CONFIG_NET_CLS_ACT struct tcf_exts old = *dst; - tcf_tree_lock(tp); - dst->nr_actions = src->nr_actions; - dst->actions = src->actions; - dst->type = src->type; - tcf_tree_unlock(tp); - + *dst = *src; tcf_exts_destroy(&old); #endif } diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 0d9d07798699..4adb67a73491 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -518,7 +518,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, tcf_bind_filter(tp, &n->res, base); } - tcf_exts_change(tp, &n->exts, &e); + tcf_exts_change(&n->exts, &e); rsvp_replace(tp, n, handle); return 0; } @@ -591,7 +591,7 @@ insert: if (f->tunnelhdr == 0) tcf_bind_filter(tp, &f->res, base); - tcf_exts_change(tp, &f->exts, &e); + tcf_exts_change(&f->exts, &e); fp = &s->ht[h2]; for (nfp = rtnl_dereference(*fp); nfp; diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index 66924d147e97..d69f828f3fed 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -419,9 +419,9 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, } if (old_r) - tcf_exts_change(tp, &r->exts, &e); + tcf_exts_change(&r->exts, &e); else - tcf_exts_change(tp, &cr.exts, &e); + tcf_exts_change(&cr.exts, &e); if (old_r && old_r != r) { err = tcindex_filter_result_init(old_r); @@ -439,7 +439,7 @@ tcindex_set_parms(struct net *net, struct tcf_proto *tp, unsigned long base, struct tcindex_filter *nfp; struct tcindex_filter __rcu **fp; - tcf_exts_change(tp, &f->result.exts, &r->exts); + tcf_exts_change(&f->result.exts, &r->exts); fp = cp->h + (handle % cp->hash); for (nfp = rtnl_dereference(*fp); -- cgit v1.2.3 From a1e155ece1a5b68c4f845788e03a567574f606aa Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 3 Aug 2017 18:07:05 +0200 Subject: IP: do not modify ingress packet IP option in ip_options_echo() While computing the response option set for LSRR, ip_options_echo() also changes the ingress packet LSRR addresses list, setting the last one to the dst specific address for the ingress packet - via memset(start[ ... The only visible effect of such change - beyond possibly damaging shared/cloned skbs - is modifying the data carried by ICMP replies changing the header information for reported the ingress packet, which violates RFC1122 3.2.2.6. All the others call sites just ignore the ingress packet IP options after calling ip_options_echo() Note that the last element in the LSRR option address list for the reply packet will be properly set later in the ip output path via ip_options_build(). This buggy memset() predates git history and apparently was present into the initial ip_options_echo() implementation in linux 1.3.30 but still looks wrong. The removal of the fib_compute_spec_dst() call will help completely dropping the skb->dst usage by __ip_options_echo() with a later patch. Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller --- net/ipv4/ip_options.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index 93157f2f4758..fdda97308c0b 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -174,9 +174,6 @@ int __ip_options_echo(struct ip_options *dopt, struct sk_buff *skb, doffset -= 4; } if (doffset > 3) { - __be32 daddr = fib_compute_spec_dst(skb); - - memcpy(&start[doffset-1], &daddr, 4); dopt->faddr = faddr; dptr[0] = start[0]; dptr[1] = doffset+3; -- cgit v1.2.3 From 91ed1e666a4ea2e260452a7d7d311ac5ae852cba Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 3 Aug 2017 18:07:06 +0200 Subject: ip/options: explicitly provide net ns to __ip_options_echo() __ip_options_echo() uses the current network namespace, and currently retrives it via skb->dst->dev. This commit adds an explicit 'net' argument to __ip_options_echo() and update all the call sites to provide it, usually via a simpler sock_net(). After this change, __ip_options_echo() no more needs to access skb->dst and we can drop a couple of hack to preserve such info in the rx path. Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller --- include/net/ip.h | 9 +++++---- include/net/tcp.h | 5 +++-- net/ipv4/icmp.c | 4 ++-- net/ipv4/ip_options.c | 6 +++--- net/ipv4/ip_output.c | 2 +- net/ipv4/ip_sockglue.c | 7 ++++--- net/ipv4/syncookies.c | 2 +- net/ipv4/tcp_ipv4.c | 2 +- 8 files changed, 20 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/include/net/ip.h b/include/net/ip.h index 821cedcc8e73..9e59dcf1787a 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -567,11 +567,12 @@ int ip_forward(struct sk_buff *skb); void ip_options_build(struct sk_buff *skb, struct ip_options *opt, __be32 daddr, struct rtable *rt, int is_frag); -int __ip_options_echo(struct ip_options *dopt, struct sk_buff *skb, - const struct ip_options *sopt); -static inline int ip_options_echo(struct ip_options *dopt, struct sk_buff *skb) +int __ip_options_echo(struct net *net, struct ip_options *dopt, + struct sk_buff *skb, const struct ip_options *sopt); +static inline int ip_options_echo(struct net *net, struct ip_options *dopt, + struct sk_buff *skb) { - return __ip_options_echo(dopt, skb, &IPCB(skb)->opt); + return __ip_options_echo(net, dopt, skb, &IPCB(skb)->opt); } void ip_options_fragment(struct sk_buff *skb); diff --git a/include/net/tcp.h b/include/net/tcp.h index bb1881b4ce48..5173fecde495 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1885,7 +1885,8 @@ extern void tcp_rack_reo_timeout(struct sock *sk); /* * Save and compile IPv4 options, return a pointer to it */ -static inline struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb) +static inline struct ip_options_rcu *tcp_v4_save_options(struct net *net, + struct sk_buff *skb) { const struct ip_options *opt = &TCP_SKB_CB(skb)->header.h4.opt; struct ip_options_rcu *dopt = NULL; @@ -1894,7 +1895,7 @@ static inline struct ip_options_rcu *tcp_v4_save_options(struct sk_buff *skb) int opt_size = sizeof(*dopt) + opt->optlen; dopt = kmalloc(opt_size, GFP_ATOMIC); - if (dopt && __ip_options_echo(&dopt->opt, skb, opt)) { + if (dopt && __ip_options_echo(net, &dopt->opt, skb, opt)) { kfree(dopt); dopt = NULL; } diff --git a/net/ipv4/icmp.c b/net/ipv4/icmp.c index c2be26b98b5f..681e33998e03 100644 --- a/net/ipv4/icmp.c +++ b/net/ipv4/icmp.c @@ -412,7 +412,7 @@ static void icmp_reply(struct icmp_bxm *icmp_param, struct sk_buff *skb) int type = icmp_param->data.icmph.type; int code = icmp_param->data.icmph.code; - if (ip_options_echo(&icmp_param->replyopts.opt.opt, skb)) + if (ip_options_echo(net, &icmp_param->replyopts.opt.opt, skb)) return; /* Needed by both icmp_global_allow and icmp_xmit_lock */ @@ -694,7 +694,7 @@ void icmp_send(struct sk_buff *skb_in, int type, int code, __be32 info) iph->tos; mark = IP4_REPLY_MARK(net, skb_in->mark); - if (ip_options_echo(&icmp_param.replyopts.opt.opt, skb_in)) + if (ip_options_echo(net, &icmp_param.replyopts.opt.opt, skb_in)) goto out_unlock; diff --git a/net/ipv4/ip_options.c b/net/ipv4/ip_options.c index fdda97308c0b..525ae88d1e58 100644 --- a/net/ipv4/ip_options.c +++ b/net/ipv4/ip_options.c @@ -86,8 +86,8 @@ void ip_options_build(struct sk_buff *skb, struct ip_options *opt, * NOTE: dopt cannot point to skb. */ -int __ip_options_echo(struct ip_options *dopt, struct sk_buff *skb, - const struct ip_options *sopt) +int __ip_options_echo(struct net *net, struct ip_options *dopt, + struct sk_buff *skb, const struct ip_options *sopt) { unsigned char *sptr, *dptr; int soffset, doffset; @@ -140,7 +140,7 @@ int __ip_options_echo(struct ip_options *dopt, struct sk_buff *skb, __be32 addr; memcpy(&addr, dptr+soffset-1, 4); - if (inet_addr_type(dev_net(skb_dst(skb)->dev), addr) != RTN_UNICAST) { + if (inet_addr_type(net, addr) != RTN_UNICAST) { dopt->ts_needtime = 1; soffset += 8; } diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index b631ec685d77..73b0b15245b6 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1525,7 +1525,7 @@ void ip_send_unicast_reply(struct sock *sk, struct sk_buff *skb, int err; int oif; - if (__ip_options_echo(&replyopts.opt.opt, skb, sopt)) + if (__ip_options_echo(net, &replyopts.opt.opt, skb, sopt)) return; ipc.addr = daddr; diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index ecc4b4a2413e..1c3354d028a4 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -80,7 +80,8 @@ static void ip_cmsg_recv_opts(struct msghdr *msg, struct sk_buff *skb) } -static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) +static void ip_cmsg_recv_retopts(struct net *net, struct msghdr *msg, + struct sk_buff *skb) { unsigned char optbuf[sizeof(struct ip_options) + 40]; struct ip_options *opt = (struct ip_options *)optbuf; @@ -88,7 +89,7 @@ static void ip_cmsg_recv_retopts(struct msghdr *msg, struct sk_buff *skb) if (IPCB(skb)->opt.optlen == 0) return; - if (ip_options_echo(opt, skb)) { + if (ip_options_echo(net, opt, skb)) { msg->msg_flags |= MSG_CTRUNC; return; } @@ -204,7 +205,7 @@ void ip_cmsg_recv_offset(struct msghdr *msg, struct sock *sk, } if (flags & IP_CMSG_RETOPTS) { - ip_cmsg_recv_retopts(msg, skb); + ip_cmsg_recv_retopts(sock_net(sk), msg, skb); flags &= ~IP_CMSG_RETOPTS; if (!flags) diff --git a/net/ipv4/syncookies.c b/net/ipv4/syncookies.c index 03ad8778c395..b1bb1b3a1082 100644 --- a/net/ipv4/syncookies.c +++ b/net/ipv4/syncookies.c @@ -355,7 +355,7 @@ struct sock *cookie_v4_check(struct sock *sk, struct sk_buff *skb) /* We throwed the options of the initial SYN away, so we hope * the ACK carries the same options again (see RFC1122 4.2.3.8) */ - ireq->opt = tcp_v4_save_options(skb); + ireq->opt = tcp_v4_save_options(sock_net(sk), skb); if (security_inet_conn_request(sk, skb, req)) { reqsk_free(req); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 9b51663cd5a4..5f708c85110e 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1267,7 +1267,7 @@ static void tcp_v4_init_req(struct request_sock *req, sk_rcv_saddr_set(req_to_sk(req), ip_hdr(skb)->daddr); sk_daddr_set(req_to_sk(req), ip_hdr(skb)->saddr); - ireq->opt = tcp_v4_save_options(skb); + ireq->opt = tcp_v4_save_options(sock_net(sk_listener), skb); } static struct dst_entry *tcp_v4_route_req(const struct sock *sk, -- cgit v1.2.3 From 61a1030bad628f7264cd5e5d0f4d71b5488eb4a4 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 3 Aug 2017 18:07:07 +0200 Subject: Revert "ipv4: keep skb->dst around in presence of IP options" ip_options_echo() does not use anymore the skb->dst and don't need to keep the dst around for options's sake only. This reverts commit 34b2cef20f19c87999fff3da4071e66937db9644. Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller --- net/ipv4/ip_sockglue.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index 1c3354d028a4..dd68a9ed5e40 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -1228,14 +1228,7 @@ void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) pktinfo->ipi_ifindex = 0; pktinfo->ipi_spec_dst.s_addr = 0; } - /* We need to keep the dst for __ip_options_echo() - * We could restrict the test to opt.ts_needtime || opt.srr, - * but the following is good enough as IP options are not often used. - */ - if (unlikely(IPCB(skb)->opt.optlen)) - skb_dst_force(skb); - else - skb_dst_drop(skb); + skb_dst_drop(skb); } int ip_setsockopt(struct sock *sk, int level, -- cgit v1.2.3 From 3bdefdf9d9c2a972085742578b08d99f14c09555 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Thu, 3 Aug 2017 18:07:08 +0200 Subject: udp: no need to preserve skb->dst __ip_options_echo() does not need anymore skb->dst, so we can avoid explicitly preserving it for its own sake. This is almost a revert of commit 0ddf3fb2c43d ("udp: preserve skb->dst if required for IP options processing") plus some lifting to fit later changes. Signed-off-by: Paolo Abeni Signed-off-by: David S. Miller --- net/ipv4/udp.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index e6276fa3750b..38bca2c4897d 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1176,7 +1176,11 @@ static void udp_set_dev_scratch(struct sk_buff *skb) scratch->csum_unnecessary = !!skb_csum_unnecessary(skb); scratch->is_linear = !skb_is_nonlinear(skb); #endif - if (likely(!skb->_skb_refdst)) + /* all head states execept sp (dst, sk, nf) are always cleared by + * udp_rcv() and we need to preserve secpath, if present, to eventually + * process IP_CMSG_PASSSEC at recvmsg() time + */ + if (likely(!skb_sec_path(skb))) scratch->_tsize_state |= UDP_SKB_IS_STATELESS; } @@ -1782,13 +1786,6 @@ static int __udp_queue_rcv_skb(struct sock *sk, struct sk_buff *skb) sk_mark_napi_id_once(sk, skb); } - /* At recvmsg() time we may access skb->dst or skb->sp depending on - * the IP options and the cmsg flags, elsewhere can we clear all - * pending head states while they are hot in the cache - */ - if (likely(IPCB(skb)->opt.optlen == 0 && !skb_sec_path(skb))) - skb_release_head_state(skb); - rc = __udp_enqueue_schedule_skb(sk, skb); if (rc < 0) { int is_udplite = IS_UDPLITE(sk); -- cgit v1.2.3 From 4faf783998b8cb88294e9df89032f473f8771b78 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Thu, 3 Aug 2017 20:38:51 -0700 Subject: tcp: fix cwnd undo in Reno and HTCP congestion controls Using ssthresh to revert cwnd is less reliable when ssthresh is bounded to 2 packets. This patch uses an existing variable in TCP "prior_cwnd" that snapshots the cwnd right before entering fast recovery and RTO recovery in Reno. This fixes the issue discussed in netdev thread: "A buggy behavior for Linux TCP Reno and HTCP" https://www.spinics.net/lists/netdev/msg444955.html Suggested-by: Neal Cardwell Reported-by: Wei Sun Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- include/linux/tcp.h | 2 +- net/ipv4/tcp_cong.c | 2 +- net/ipv4/tcp_htcp.c | 3 +-- net/ipv4/tcp_input.c | 1 + 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index d7389ea36e10..267164a1d559 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -258,7 +258,7 @@ struct tcp_sock { u32 snd_cwnd_clamp; /* Do not allow snd_cwnd to grow above this */ u32 snd_cwnd_used; u32 snd_cwnd_stamp; - u32 prior_cwnd; /* Congestion window at start of Recovery. */ + u32 prior_cwnd; /* cwnd right before starting loss recovery */ u32 prr_delivered; /* Number of newly delivered packets to * receiver in Recovery. */ u32 prr_out; /* Total number of pkts sent during Recovery. */ diff --git a/net/ipv4/tcp_cong.c b/net/ipv4/tcp_cong.c index fde983f6376b..c2b174469645 100644 --- a/net/ipv4/tcp_cong.c +++ b/net/ipv4/tcp_cong.c @@ -456,7 +456,7 @@ u32 tcp_reno_undo_cwnd(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); - return max(tp->snd_cwnd, tp->snd_ssthresh << 1); + return max(tp->snd_cwnd, tp->prior_cwnd); } EXPORT_SYMBOL_GPL(tcp_reno_undo_cwnd); diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c index 3eb78cde6ff0..082d479462fa 100644 --- a/net/ipv4/tcp_htcp.c +++ b/net/ipv4/tcp_htcp.c @@ -66,7 +66,6 @@ static inline void htcp_reset(struct htcp *ca) static u32 htcp_cwnd_undo(struct sock *sk) { - const struct tcp_sock *tp = tcp_sk(sk); struct htcp *ca = inet_csk_ca(sk); if (ca->undo_last_cong) { @@ -76,7 +75,7 @@ static u32 htcp_cwnd_undo(struct sock *sk) ca->undo_last_cong = 0; } - return max(tp->snd_cwnd, (tp->snd_ssthresh << 7) / ca->beta); + return tcp_reno_undo_cwnd(sk); } static inline void measure_rtt(struct sock *sk, u32 srtt) diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 99cdf4ccabb8..842ed75ccb25 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -1950,6 +1950,7 @@ void tcp_enter_loss(struct sock *sk) !after(tp->high_seq, tp->snd_una) || (icsk->icsk_ca_state == TCP_CA_Loss && !icsk->icsk_retransmits)) { tp->prior_ssthresh = tcp_current_ssthresh(sk); + tp->prior_cwnd = tp->snd_cwnd; tp->snd_ssthresh = icsk->icsk_ca_ops->ssthresh(sk); tcp_ca_event(sk, CA_EVENT_LOSS); tcp_init_undo(tp); -- cgit v1.2.3 From f1722a1be19dc38e0a4b282d4e6e6ec5e1b11a67 Mon Sep 17 00:00:00 2001 From: Yuchung Cheng Date: Thu, 3 Aug 2017 20:38:52 -0700 Subject: tcp: consolidate congestion control undo functions Most TCP congestion controls are using identical logic to undo cwnd except BBR. This patch consolidates these similar functions to the one used currently by Reno and others. Suggested-by: Neal Cardwell Signed-off-by: Yuchung Cheng Signed-off-by: Neal Cardwell Signed-off-by: David S. Miller --- net/ipv4/tcp_bic.c | 14 +------------- net/ipv4/tcp_cdg.c | 12 +----------- net/ipv4/tcp_cubic.c | 13 +------------ net/ipv4/tcp_highspeed.c | 11 +---------- net/ipv4/tcp_illinois.c | 11 +---------- net/ipv4/tcp_nv.c | 13 +------------ net/ipv4/tcp_scalable.c | 16 +--------------- net/ipv4/tcp_veno.c | 11 +---------- net/ipv4/tcp_yeah.c | 11 +---------- 9 files changed, 9 insertions(+), 103 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_bic.c b/net/ipv4/tcp_bic.c index 609965f0e298..fc3614377413 100644 --- a/net/ipv4/tcp_bic.c +++ b/net/ipv4/tcp_bic.c @@ -49,7 +49,6 @@ MODULE_PARM_DESC(smooth_part, "log(B/(B*Smin))/log(B/(B-1))+B, # of RTT from Wma struct bictcp { u32 cnt; /* increase cwnd by 1 after ACKs */ u32 last_max_cwnd; /* last maximum snd_cwnd */ - u32 loss_cwnd; /* congestion window at last loss */ u32 last_cwnd; /* the last snd_cwnd */ u32 last_time; /* time when updated last_cwnd */ u32 epoch_start; /* beginning of an epoch */ @@ -72,7 +71,6 @@ static void bictcp_init(struct sock *sk) struct bictcp *ca = inet_csk_ca(sk); bictcp_reset(ca); - ca->loss_cwnd = 0; if (initial_ssthresh) tcp_sk(sk)->snd_ssthresh = initial_ssthresh; @@ -172,22 +170,12 @@ static u32 bictcp_recalc_ssthresh(struct sock *sk) else ca->last_max_cwnd = tp->snd_cwnd; - ca->loss_cwnd = tp->snd_cwnd; - if (tp->snd_cwnd <= low_window) return max(tp->snd_cwnd >> 1U, 2U); else return max((tp->snd_cwnd * beta) / BICTCP_BETA_SCALE, 2U); } -static u32 bictcp_undo_cwnd(struct sock *sk) -{ - const struct tcp_sock *tp = tcp_sk(sk); - const struct bictcp *ca = inet_csk_ca(sk); - - return max(tp->snd_cwnd, ca->loss_cwnd); -} - static void bictcp_state(struct sock *sk, u8 new_state) { if (new_state == TCP_CA_Loss) @@ -214,7 +202,7 @@ static struct tcp_congestion_ops bictcp __read_mostly = { .ssthresh = bictcp_recalc_ssthresh, .cong_avoid = bictcp_cong_avoid, .set_state = bictcp_state, - .undo_cwnd = bictcp_undo_cwnd, + .undo_cwnd = tcp_reno_undo_cwnd, .pkts_acked = bictcp_acked, .owner = THIS_MODULE, .name = "bic", diff --git a/net/ipv4/tcp_cdg.c b/net/ipv4/tcp_cdg.c index 50a0f3e51d5b..66ac69f7bd19 100644 --- a/net/ipv4/tcp_cdg.c +++ b/net/ipv4/tcp_cdg.c @@ -85,7 +85,6 @@ struct cdg { u8 state; u8 delack; u32 rtt_seq; - u32 undo_cwnd; u32 shadow_wnd; u16 backoff_cnt; u16 sample_cnt; @@ -330,8 +329,6 @@ static u32 tcp_cdg_ssthresh(struct sock *sk) struct cdg *ca = inet_csk_ca(sk); struct tcp_sock *tp = tcp_sk(sk); - ca->undo_cwnd = tp->snd_cwnd; - if (ca->state == CDG_BACKOFF) return max(2U, (tp->snd_cwnd * min(1024U, backoff_beta)) >> 10); @@ -344,13 +341,6 @@ static u32 tcp_cdg_ssthresh(struct sock *sk) return max(2U, tp->snd_cwnd >> 1); } -static u32 tcp_cdg_undo_cwnd(struct sock *sk) -{ - struct cdg *ca = inet_csk_ca(sk); - - return max(tcp_sk(sk)->snd_cwnd, ca->undo_cwnd); -} - static void tcp_cdg_cwnd_event(struct sock *sk, const enum tcp_ca_event ev) { struct cdg *ca = inet_csk_ca(sk); @@ -403,7 +393,7 @@ struct tcp_congestion_ops tcp_cdg __read_mostly = { .cong_avoid = tcp_cdg_cong_avoid, .cwnd_event = tcp_cdg_cwnd_event, .pkts_acked = tcp_cdg_acked, - .undo_cwnd = tcp_cdg_undo_cwnd, + .undo_cwnd = tcp_reno_undo_cwnd, .ssthresh = tcp_cdg_ssthresh, .release = tcp_cdg_release, .init = tcp_cdg_init, diff --git a/net/ipv4/tcp_cubic.c b/net/ipv4/tcp_cubic.c index 57ae5b5ae643..78bfadfcf342 100644 --- a/net/ipv4/tcp_cubic.c +++ b/net/ipv4/tcp_cubic.c @@ -83,7 +83,6 @@ MODULE_PARM_DESC(hystart_ack_delta, "spacing between ack's indicating train (mse struct bictcp { u32 cnt; /* increase cwnd by 1 after ACKs */ u32 last_max_cwnd; /* last maximum snd_cwnd */ - u32 loss_cwnd; /* congestion window at last loss */ u32 last_cwnd; /* the last snd_cwnd */ u32 last_time; /* time when updated last_cwnd */ u32 bic_origin_point;/* origin point of bic function */ @@ -142,7 +141,6 @@ static void bictcp_init(struct sock *sk) struct bictcp *ca = inet_csk_ca(sk); bictcp_reset(ca); - ca->loss_cwnd = 0; if (hystart) bictcp_hystart_reset(sk); @@ -366,18 +364,9 @@ static u32 bictcp_recalc_ssthresh(struct sock *sk) else ca->last_max_cwnd = tp->snd_cwnd; - ca->loss_cwnd = tp->snd_cwnd; - return max((tp->snd_cwnd * beta) / BICTCP_BETA_SCALE, 2U); } -static u32 bictcp_undo_cwnd(struct sock *sk) -{ - struct bictcp *ca = inet_csk_ca(sk); - - return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); -} - static void bictcp_state(struct sock *sk, u8 new_state) { if (new_state == TCP_CA_Loss) { @@ -470,7 +459,7 @@ static struct tcp_congestion_ops cubictcp __read_mostly = { .ssthresh = bictcp_recalc_ssthresh, .cong_avoid = bictcp_cong_avoid, .set_state = bictcp_state, - .undo_cwnd = bictcp_undo_cwnd, + .undo_cwnd = tcp_reno_undo_cwnd, .cwnd_event = bictcp_cwnd_event, .pkts_acked = bictcp_acked, .owner = THIS_MODULE, diff --git a/net/ipv4/tcp_highspeed.c b/net/ipv4/tcp_highspeed.c index 6d9879e93648..d1c33c91eadc 100644 --- a/net/ipv4/tcp_highspeed.c +++ b/net/ipv4/tcp_highspeed.c @@ -94,7 +94,6 @@ static const struct hstcp_aimd_val { struct hstcp { u32 ai; - u32 loss_cwnd; }; static void hstcp_init(struct sock *sk) @@ -153,22 +152,14 @@ static u32 hstcp_ssthresh(struct sock *sk) const struct tcp_sock *tp = tcp_sk(sk); struct hstcp *ca = inet_csk_ca(sk); - ca->loss_cwnd = tp->snd_cwnd; /* Do multiplicative decrease */ return max(tp->snd_cwnd - ((tp->snd_cwnd * hstcp_aimd_vals[ca->ai].md) >> 8), 2U); } -static u32 hstcp_cwnd_undo(struct sock *sk) -{ - const struct hstcp *ca = inet_csk_ca(sk); - - return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); -} - static struct tcp_congestion_ops tcp_highspeed __read_mostly = { .init = hstcp_init, .ssthresh = hstcp_ssthresh, - .undo_cwnd = hstcp_cwnd_undo, + .undo_cwnd = tcp_reno_undo_cwnd, .cong_avoid = hstcp_cong_avoid, .owner = THIS_MODULE, diff --git a/net/ipv4/tcp_illinois.c b/net/ipv4/tcp_illinois.c index 60352ff4f5a8..7c843578f233 100644 --- a/net/ipv4/tcp_illinois.c +++ b/net/ipv4/tcp_illinois.c @@ -48,7 +48,6 @@ struct illinois { u32 end_seq; /* right edge of current RTT */ u32 alpha; /* Additive increase */ u32 beta; /* Muliplicative decrease */ - u32 loss_cwnd; /* cwnd on loss */ u16 acked; /* # packets acked by current ACK */ u8 rtt_above; /* average rtt has gone above threshold */ u8 rtt_low; /* # of rtts measurements below threshold */ @@ -297,18 +296,10 @@ static u32 tcp_illinois_ssthresh(struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); struct illinois *ca = inet_csk_ca(sk); - ca->loss_cwnd = tp->snd_cwnd; /* Multiplicative decrease */ return max(tp->snd_cwnd - ((tp->snd_cwnd * ca->beta) >> BETA_SHIFT), 2U); } -static u32 tcp_illinois_cwnd_undo(struct sock *sk) -{ - const struct illinois *ca = inet_csk_ca(sk); - - return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); -} - /* Extract info for Tcp socket info provided via netlink. */ static size_t tcp_illinois_info(struct sock *sk, u32 ext, int *attr, union tcp_cc_info *info) @@ -336,7 +327,7 @@ static size_t tcp_illinois_info(struct sock *sk, u32 ext, int *attr, static struct tcp_congestion_ops tcp_illinois __read_mostly = { .init = tcp_illinois_init, .ssthresh = tcp_illinois_ssthresh, - .undo_cwnd = tcp_illinois_cwnd_undo, + .undo_cwnd = tcp_reno_undo_cwnd, .cong_avoid = tcp_illinois_cong_avoid, .set_state = tcp_illinois_state, .get_info = tcp_illinois_info, diff --git a/net/ipv4/tcp_nv.c b/net/ipv4/tcp_nv.c index 6d650ed3cb59..1ff73982e28c 100644 --- a/net/ipv4/tcp_nv.c +++ b/net/ipv4/tcp_nv.c @@ -86,7 +86,6 @@ struct tcpnv { * < 0 => less than 1 packet/RTT */ u8 available8; u16 available16; - u32 loss_cwnd; /* cwnd at last loss */ u8 nv_allow_cwnd_growth:1, /* whether cwnd can grow */ nv_reset:1, /* whether to reset values */ nv_catchup:1; /* whether we are growing because @@ -121,7 +120,6 @@ static inline void tcpnv_reset(struct tcpnv *ca, struct sock *sk) struct tcp_sock *tp = tcp_sk(sk); ca->nv_reset = 0; - ca->loss_cwnd = 0; ca->nv_no_cong_cnt = 0; ca->nv_rtt_cnt = 0; ca->nv_last_rtt = 0; @@ -177,19 +175,10 @@ static void tcpnv_cong_avoid(struct sock *sk, u32 ack, u32 acked) static u32 tcpnv_recalc_ssthresh(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); - struct tcpnv *ca = inet_csk_ca(sk); - ca->loss_cwnd = tp->snd_cwnd; return max((tp->snd_cwnd * nv_loss_dec_factor) >> 10, 2U); } -static u32 tcpnv_undo_cwnd(struct sock *sk) -{ - struct tcpnv *ca = inet_csk_ca(sk); - - return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); -} - static void tcpnv_state(struct sock *sk, u8 new_state) { struct tcpnv *ca = inet_csk_ca(sk); @@ -446,7 +435,7 @@ static struct tcp_congestion_ops tcpnv __read_mostly = { .ssthresh = tcpnv_recalc_ssthresh, .cong_avoid = tcpnv_cong_avoid, .set_state = tcpnv_state, - .undo_cwnd = tcpnv_undo_cwnd, + .undo_cwnd = tcp_reno_undo_cwnd, .pkts_acked = tcpnv_acked, .get_info = tcpnv_get_info, diff --git a/net/ipv4/tcp_scalable.c b/net/ipv4/tcp_scalable.c index f2123075ce6e..addc122f8818 100644 --- a/net/ipv4/tcp_scalable.c +++ b/net/ipv4/tcp_scalable.c @@ -15,10 +15,6 @@ #define TCP_SCALABLE_AI_CNT 50U #define TCP_SCALABLE_MD_SCALE 3 -struct scalable { - u32 loss_cwnd; -}; - static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked) { struct tcp_sock *tp = tcp_sk(sk); @@ -36,23 +32,13 @@ static void tcp_scalable_cong_avoid(struct sock *sk, u32 ack, u32 acked) static u32 tcp_scalable_ssthresh(struct sock *sk) { const struct tcp_sock *tp = tcp_sk(sk); - struct scalable *ca = inet_csk_ca(sk); - - ca->loss_cwnd = tp->snd_cwnd; return max(tp->snd_cwnd - (tp->snd_cwnd>>TCP_SCALABLE_MD_SCALE), 2U); } -static u32 tcp_scalable_cwnd_undo(struct sock *sk) -{ - const struct scalable *ca = inet_csk_ca(sk); - - return max(tcp_sk(sk)->snd_cwnd, ca->loss_cwnd); -} - static struct tcp_congestion_ops tcp_scalable __read_mostly = { .ssthresh = tcp_scalable_ssthresh, - .undo_cwnd = tcp_scalable_cwnd_undo, + .undo_cwnd = tcp_reno_undo_cwnd, .cong_avoid = tcp_scalable_cong_avoid, .owner = THIS_MODULE, diff --git a/net/ipv4/tcp_veno.c b/net/ipv4/tcp_veno.c index 76005d4b8dfc..6fcf482d611b 100644 --- a/net/ipv4/tcp_veno.c +++ b/net/ipv4/tcp_veno.c @@ -30,7 +30,6 @@ struct veno { u32 basertt; /* the min of all Veno rtt measurements seen (in usec) */ u32 inc; /* decide whether to increase cwnd */ u32 diff; /* calculate the diff rate */ - u32 loss_cwnd; /* cwnd when loss occured */ }; /* There are several situations when we must "re-start" Veno: @@ -194,7 +193,6 @@ static u32 tcp_veno_ssthresh(struct sock *sk) const struct tcp_sock *tp = tcp_sk(sk); struct veno *veno = inet_csk_ca(sk); - veno->loss_cwnd = tp->snd_cwnd; if (veno->diff < beta) /* in "non-congestive state", cut cwnd by 1/5 */ return max(tp->snd_cwnd * 4 / 5, 2U); @@ -203,17 +201,10 @@ static u32 tcp_veno_ssthresh(struct sock *sk) return max(tp->snd_cwnd >> 1U, 2U); } -static u32 tcp_veno_cwnd_undo(struct sock *sk) -{ - const struct veno *veno = inet_csk_ca(sk); - - return max(tcp_sk(sk)->snd_cwnd, veno->loss_cwnd); -} - static struct tcp_congestion_ops tcp_veno __read_mostly = { .init = tcp_veno_init, .ssthresh = tcp_veno_ssthresh, - .undo_cwnd = tcp_veno_cwnd_undo, + .undo_cwnd = tcp_reno_undo_cwnd, .cong_avoid = tcp_veno_cong_avoid, .pkts_acked = tcp_veno_pkts_acked, .set_state = tcp_veno_state, diff --git a/net/ipv4/tcp_yeah.c b/net/ipv4/tcp_yeah.c index e6ff99c4bd3b..96e829b2e2fc 100644 --- a/net/ipv4/tcp_yeah.c +++ b/net/ipv4/tcp_yeah.c @@ -37,7 +37,6 @@ struct yeah { u32 fast_count; u32 pkts_acked; - u32 loss_cwnd; }; static void tcp_yeah_init(struct sock *sk) @@ -220,22 +219,14 @@ static u32 tcp_yeah_ssthresh(struct sock *sk) yeah->fast_count = 0; yeah->reno_count = max(yeah->reno_count>>1, 2U); - yeah->loss_cwnd = tp->snd_cwnd; return max_t(int, tp->snd_cwnd - reduction, 2); } -static u32 tcp_yeah_cwnd_undo(struct sock *sk) -{ - const struct yeah *yeah = inet_csk_ca(sk); - - return max(tcp_sk(sk)->snd_cwnd, yeah->loss_cwnd); -} - static struct tcp_congestion_ops tcp_yeah __read_mostly = { .init = tcp_yeah_init, .ssthresh = tcp_yeah_ssthresh, - .undo_cwnd = tcp_yeah_cwnd_undo, + .undo_cwnd = tcp_reno_undo_cwnd, .cong_avoid = tcp_yeah_cong_avoid, .set_state = tcp_vegas_state, .cwnd_event = tcp_vegas_cwnd_event, -- cgit v1.2.3 From 5f6b4e14cada6ddc662b80cbd670d9cd2922aea1 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 3 Aug 2017 21:33:27 -0700 Subject: net: dsa: User per-cpu 64-bit statistics During testing with a background iperf pushing 1Gbit/sec worth of traffic and having both ifconfig and ethtool collect statistics, we could see quite frequent deadlocks. Convert the often accessed DSA slave network devices statistics to per-cpu 64-bit statistics to remove these deadlocks and provide fast efficient statistics updates. Fixes: f613ed665bb3 ("net: dsa: Add support for 64-bit statistics") Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa.c | 10 +++++--- net/dsa/dsa_priv.h | 2 +- net/dsa/slave.c | 72 +++++++++++++++++++++++++++++++++++++++--------------- 3 files changed, 59 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 0ba842c08dd3..a91e520e735f 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -190,6 +190,7 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, { struct dsa_switch_tree *dst = dev->dsa_ptr; struct sk_buff *nskb = NULL; + struct pcpu_sw_netstats *s; struct dsa_slave_priv *p; if (unlikely(dst == NULL)) { @@ -213,10 +214,11 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, skb->pkt_type = PACKET_HOST; skb->protocol = eth_type_trans(skb, skb->dev); - u64_stats_update_begin(&p->stats64.syncp); - p->stats64.rx_packets++; - p->stats64.rx_bytes += skb->len; - u64_stats_update_end(&p->stats64.syncp); + s = this_cpu_ptr(p->stats64); + u64_stats_update_begin(&s->syncp); + s->rx_packets++; + s->rx_bytes += skb->len; + u64_stats_update_end(&s->syncp); netif_receive_skb(skb); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 7aa0656296c2..306cff229def 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -77,7 +77,7 @@ struct dsa_slave_priv { struct sk_buff * (*xmit)(struct sk_buff *skb, struct net_device *dev); - struct pcpu_sw_netstats stats64; + struct pcpu_sw_netstats *stats64; /* DSA port data, such as switch, port index, etc. */ struct dsa_port *dp; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 83252e8426d7..75c5c5808220 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -352,12 +352,14 @@ static inline netdev_tx_t dsa_netpoll_send_skb(struct dsa_slave_priv *p, static netdev_tx_t dsa_slave_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); + struct pcpu_sw_netstats *s; struct sk_buff *nskb; - u64_stats_update_begin(&p->stats64.syncp); - p->stats64.tx_packets++; - p->stats64.tx_bytes += skb->len; - u64_stats_update_end(&p->stats64.syncp); + s = this_cpu_ptr(p->stats64); + u64_stats_update_begin(&s->syncp); + s->tx_packets++; + s->tx_bytes += skb->len; + u64_stats_update_end(&s->syncp); /* Transmit function may have to reallocate the original SKB, * in which case it must have freed it. Only free it here on error. @@ -596,15 +598,26 @@ static void dsa_slave_get_ethtool_stats(struct net_device *dev, { struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_switch *ds = p->dp->ds; + struct pcpu_sw_netstats *s; unsigned int start; - - do { - start = u64_stats_fetch_begin_irq(&p->stats64.syncp); - data[0] = p->stats64.tx_packets; - data[1] = p->stats64.tx_bytes; - data[2] = p->stats64.rx_packets; - data[3] = p->stats64.rx_bytes; - } while (u64_stats_fetch_retry_irq(&p->stats64.syncp, start)); + int i; + + for_each_possible_cpu(i) { + u64 tx_packets, tx_bytes, rx_packets, rx_bytes; + + s = per_cpu_ptr(p->stats64, i); + do { + start = u64_stats_fetch_begin_irq(&s->syncp); + tx_packets = s->tx_packets; + tx_bytes = s->tx_bytes; + rx_packets = s->rx_packets; + rx_bytes = s->rx_bytes; + } while (u64_stats_fetch_retry_irq(&s->syncp, start)); + data[0] += tx_packets; + data[1] += tx_bytes; + data[2] += rx_packets; + data[3] += rx_bytes; + } if (ds->ops->get_ethtool_stats) ds->ops->get_ethtool_stats(ds, p->dp->index, data + 4); } @@ -879,16 +892,28 @@ static void dsa_slave_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats) { struct dsa_slave_priv *p = netdev_priv(dev); + struct pcpu_sw_netstats *s; unsigned int start; + int i; netdev_stats_to_stats64(stats, &dev->stats); - do { - start = u64_stats_fetch_begin_irq(&p->stats64.syncp); - stats->tx_packets = p->stats64.tx_packets; - stats->tx_bytes = p->stats64.tx_bytes; - stats->rx_packets = p->stats64.rx_packets; - stats->rx_bytes = p->stats64.rx_bytes; - } while (u64_stats_fetch_retry_irq(&p->stats64.syncp, start)); + for_each_possible_cpu(i) { + u64 tx_packets, tx_bytes, rx_packets, rx_bytes; + + s = per_cpu_ptr(p->stats64, i); + do { + start = u64_stats_fetch_begin_irq(&s->syncp); + tx_packets = s->tx_packets; + tx_bytes = s->tx_bytes; + rx_packets = s->rx_packets; + rx_bytes = s->rx_bytes; + } while (u64_stats_fetch_retry_irq(&s->syncp, start)); + + stats->tx_packets += tx_packets; + stats->tx_bytes += tx_bytes; + stats->rx_packets += rx_packets; + stats->rx_bytes += rx_bytes; + } } void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops) @@ -1202,7 +1227,11 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, slave_dev->vlan_features = master->vlan_features; p = netdev_priv(slave_dev); - u64_stats_init(&p->stats64.syncp); + p->stats64 = netdev_alloc_pcpu_stats(struct pcpu_sw_netstats); + if (!p->stats64) { + free_netdev(slave_dev); + return -ENOMEM; + } p->dp = &ds->ports[port]; INIT_LIST_HEAD(&p->mall_tc_list); p->xmit = dst->tag_ops->xmit; @@ -1217,6 +1246,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, netdev_err(master, "error %d registering interface %s\n", ret, slave_dev->name); ds->ports[port].netdev = NULL; + free_percpu(p->stats64); free_netdev(slave_dev); return ret; } @@ -1227,6 +1257,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, if (ret) { netdev_err(master, "error %d setting up slave phy\n", ret); unregister_netdev(slave_dev); + free_percpu(p->stats64); free_netdev(slave_dev); return ret; } @@ -1249,6 +1280,7 @@ void dsa_slave_destroy(struct net_device *slave_dev) of_phy_deregister_fixed_link(port_dn); } unregister_netdev(slave_dev); + free_percpu(p->stats64); free_netdev(slave_dev); } -- cgit v1.2.3 From 233e7936c84b7d7dd90c10e2ba27abb5ab42956f Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 19:59:51 +0800 Subject: sctp: remove the typedef sctp_lower_cwnd_t This patch is to remove the typedef sctp_lower_cwnd_t, and replace with enum sctp_lower_cwnd in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 4 ++-- include/net/sctp/structs.h | 3 ++- net/sctp/transport.c | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 9b18044c551e..761064ee3ac5 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -378,12 +378,12 @@ typedef enum { } sctp_retransmit_reason_t; /* Reasons to lower cwnd. */ -typedef enum { +enum sctp_lower_cwnd { SCTP_LOWER_CWND_T3_RTX, SCTP_LOWER_CWND_FAST_RTX, SCTP_LOWER_CWND_ECNE, SCTP_LOWER_CWND_INACTIVE, -} sctp_lower_cwnd_t; +}; /* SCTP-AUTH Necessary constants */ diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 66cd7639b912..53802d8872d7 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -950,7 +950,8 @@ int sctp_transport_hold(struct sctp_transport *); void sctp_transport_put(struct sctp_transport *); void sctp_transport_update_rto(struct sctp_transport *, __u32); void sctp_transport_raise_cwnd(struct sctp_transport *, __u32, __u32); -void sctp_transport_lower_cwnd(struct sctp_transport *, sctp_lower_cwnd_t); +void sctp_transport_lower_cwnd(struct sctp_transport *t, + enum sctp_lower_cwnd reason); void sctp_transport_burst_limited(struct sctp_transport *); void sctp_transport_burst_reset(struct sctp_transport *); unsigned long sctp_transport_timeout(struct sctp_transport *); diff --git a/net/sctp/transport.c b/net/sctp/transport.c index 80a97c8501a7..2d9bd3776bc8 100644 --- a/net/sctp/transport.c +++ b/net/sctp/transport.c @@ -490,7 +490,7 @@ void sctp_transport_raise_cwnd(struct sctp_transport *transport, * detected. */ void sctp_transport_lower_cwnd(struct sctp_transport *transport, - sctp_lower_cwnd_t reason) + enum sctp_lower_cwnd reason) { struct sctp_association *asoc = transport->asoc; -- cgit v1.2.3 From 125c29820252bfa5bf8081e75618e4ee7e9487da Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 19:59:52 +0800 Subject: sctp: remove the typedef sctp_retransmit_reason_t This patch is to remove the typedef sctp_retransmit_reason_t, and replace with enum sctp_retransmit_reason in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 4 ++-- include/net/sctp/structs.h | 4 ++-- net/sctp/outqueue.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 761064ee3ac5..922fba5880d6 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -370,12 +370,12 @@ typedef enum { peer */ /* Reasons to retransmit. */ -typedef enum { +enum sctp_retransmit_reason { SCTP_RTXR_T3_RTX, SCTP_RTXR_FAST_RTX, SCTP_RTXR_PMTUD, SCTP_RTXR_T1_RTX, -} sctp_retransmit_reason_t; +}; /* Reasons to lower cwnd. */ enum sctp_lower_cwnd { diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 53802d8872d7..5e872acee6e3 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1054,8 +1054,8 @@ int sctp_outq_sack(struct sctp_outq *, struct sctp_chunk *); int sctp_outq_is_empty(const struct sctp_outq *); void sctp_outq_restart(struct sctp_outq *); -void sctp_retransmit(struct sctp_outq *, struct sctp_transport *, - sctp_retransmit_reason_t); +void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, + enum sctp_retransmit_reason reason); void sctp_retransmit_mark(struct sctp_outq *, struct sctp_transport *, __u8); void sctp_outq_uncork(struct sctp_outq *, gfp_t gfp); void sctp_prsctp_prune(struct sctp_association *asoc, diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index d2a8adfd4a6f..08ee0ed9a0c6 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -534,7 +534,7 @@ void sctp_retransmit_mark(struct sctp_outq *q, * one packet out. */ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, - sctp_retransmit_reason_t reason) + enum sctp_retransmit_reason reason) { struct net *net = sock_net(q->asoc->base.sk); -- cgit v1.2.3 From 701ef3e6c74be771a76be39817941e68e7228644 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 19:59:53 +0800 Subject: sctp: remove the typedef sctp_scope_policy_t This patch is to remove the typedef sctp_scope_policy_t and keep it's members as an anonymous enum. It is also to define SCTP_SCOPE_POLICY_MAX to replace the num 3 in sysctl.c to make codes clear. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 6 ++++-- net/sctp/sysctl.c | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 922fba5880d6..acb03eb64842 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -341,12 +341,14 @@ typedef enum { SCTP_SCOPE_UNUSABLE, /* IPv4 unusable addresses */ } sctp_scope_t; -typedef enum { +enum { SCTP_SCOPE_POLICY_DISABLE, /* Disable IPv4 address scoping */ SCTP_SCOPE_POLICY_ENABLE, /* Enable IPv4 address scoping */ SCTP_SCOPE_POLICY_PRIVATE, /* Follow draft but allow IPv4 private addresses */ SCTP_SCOPE_POLICY_LINK, /* Follow draft but allow IPv4 link local addresses */ -} sctp_scope_policy_t; +}; + +#define SCTP_SCOPE_POLICY_MAX SCTP_SCOPE_POLICY_LINK /* Based on IPv4 scoping , * SCTP IPv4 unusable addresses: 0.0.0.0/8, 224.0.0.0/4, 198.18.0.0/24, diff --git a/net/sctp/sysctl.c b/net/sctp/sysctl.c index 0e732f68c2bf..ef7ca44d6e6a 100644 --- a/net/sctp/sysctl.c +++ b/net/sctp/sysctl.c @@ -46,7 +46,7 @@ static int timer_max = 86400000; /* ms in one day */ static int int_max = INT_MAX; static int sack_timer_min = 1; static int sack_timer_max = 500; -static int addr_scope_max = 3; /* check sctp_scope_policy_t in include/net/sctp/constants.h for max entries */ +static int addr_scope_max = SCTP_SCOPE_POLICY_MAX; static int rwnd_scale_max = 16; static int rto_alpha_min = 0; static int rto_beta_min = 0; -- cgit v1.2.3 From 1c662018d2d41ecc5550cbd74d29d2d32c164ed3 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 19:59:54 +0800 Subject: sctp: remove the typedef sctp_scope_t This patch is to remove the typedef sctp_scope_t, and replace with enum sctp_scope in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 4 ++-- include/net/sctp/sctp.h | 4 ++-- include/net/sctp/structs.h | 17 +++++++++-------- net/sctp/associola.c | 17 ++++++++--------- net/sctp/bind_addr.c | 20 ++++++++++---------- net/sctp/ipv6.c | 6 +++--- net/sctp/protocol.c | 6 +++--- net/sctp/sm_make_chunk.c | 6 +++--- net/sctp/socket.c | 4 ++-- 9 files changed, 42 insertions(+), 42 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index acb03eb64842..0503bb70e5d9 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -333,13 +333,13 @@ typedef enum { * At this point, the IPv6 scopes will be mapped to these internal scopes * as much as possible. */ -typedef enum { +enum sctp_scope { SCTP_SCOPE_GLOBAL, /* IPv4 global addresses */ SCTP_SCOPE_PRIVATE, /* IPv4 private addresses */ SCTP_SCOPE_LINK, /* IPv4 link local address */ SCTP_SCOPE_LOOPBACK, /* IPv4 loopback address */ SCTP_SCOPE_UNUSABLE, /* IPv4 unusable addresses */ -} sctp_scope_t; +}; enum { SCTP_SCOPE_POLICY_DISABLE, /* Disable IPv4 address scoping */ diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 84650fed1e6a..ca66b033ec38 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -94,8 +94,8 @@ /* * sctp/protocol.c */ -int sctp_copy_local_addr_list(struct net *, struct sctp_bind_addr *, - sctp_scope_t, gfp_t gfp, int flags); +int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *addr, + enum sctp_scope, gfp_t gfp, int flags); struct sctp_pf *sctp_get_pf_specific(sa_family_t family); int sctp_register_pf(struct sctp_pf *, sa_family_t); void sctp_addr_wq_mgmt(struct net *, struct sctp_sockaddr_entry *, int); diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 5e872acee6e3..d771d418481f 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -449,7 +449,7 @@ struct sctp_af { int (*addr_valid) (union sctp_addr *, struct sctp_sock *, const struct sk_buff *); - sctp_scope_t (*scope) (union sctp_addr *); + enum sctp_scope (*scope)(union sctp_addr *); void (*inaddr_any) (union sctp_addr *, __be16); int (*is_any) (const union sctp_addr *); int (*available) (union sctp_addr *, @@ -1111,7 +1111,7 @@ void sctp_bind_addr_init(struct sctp_bind_addr *, __u16 port); void sctp_bind_addr_free(struct sctp_bind_addr *); int sctp_bind_addr_copy(struct net *net, struct sctp_bind_addr *dest, const struct sctp_bind_addr *src, - sctp_scope_t scope, gfp_t gfp, + enum sctp_scope scope, gfp_t gfp, int flags); int sctp_bind_addr_dup(struct sctp_bind_addr *dest, const struct sctp_bind_addr *src, @@ -1135,8 +1135,9 @@ union sctp_params sctp_bind_addrs_to_raw(const struct sctp_bind_addr *bp, int sctp_raw_to_bind_addrs(struct sctp_bind_addr *bp, __u8 *raw, int len, __u16 port, gfp_t gfp); -sctp_scope_t sctp_scope(const union sctp_addr *); -int sctp_in_scope(struct net *net, const union sctp_addr *addr, const sctp_scope_t scope); +enum sctp_scope sctp_scope(const union sctp_addr *addr); +int sctp_in_scope(struct net *net, const union sctp_addr *addr, + const enum sctp_scope scope); int sctp_is_any(struct sock *sk, const union sctp_addr *addr); int sctp_is_ep_boundall(struct sock *sk); @@ -1925,8 +1926,8 @@ static inline struct sctp_association *sctp_assoc(struct sctp_ep_common *base) struct sctp_association * -sctp_association_new(const struct sctp_endpoint *, const struct sock *, - sctp_scope_t scope, gfp_t gfp); +sctp_association_new(const struct sctp_endpoint *ep, const struct sock *sk, + enum sctp_scope scope, gfp_t gfp); void sctp_association_free(struct sctp_association *); void sctp_association_put(struct sctp_association *); void sctp_association_hold(struct sctp_association *); @@ -1967,8 +1968,8 @@ void sctp_assoc_set_primary(struct sctp_association *, struct sctp_transport *); void sctp_assoc_del_nonprimary_peers(struct sctp_association *, struct sctp_transport *); -int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *, - sctp_scope_t, gfp_t); +int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *asoc, + enum sctp_scope scope, gfp_t gfp); int sctp_assoc_set_bind_addr_from_cookie(struct sctp_association *, struct sctp_cookie*, gfp_t gfp); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 40ec83679d6e..4c1f1bb2aaad 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -63,11 +63,11 @@ static void sctp_assoc_free_asconf_queue(struct sctp_association *asoc); /* 1st Level Abstractions. */ /* Initialize a new association from provided memory. */ -static struct sctp_association *sctp_association_init(struct sctp_association *asoc, - const struct sctp_endpoint *ep, - const struct sock *sk, - sctp_scope_t scope, - gfp_t gfp) +static struct sctp_association *sctp_association_init( + struct sctp_association *asoc, + const struct sctp_endpoint *ep, + const struct sock *sk, + enum sctp_scope scope, gfp_t gfp) { struct net *net = sock_net(sk); struct sctp_sock *sp; @@ -301,9 +301,8 @@ fail_init: /* Allocate and initialize a new association */ struct sctp_association *sctp_association_new(const struct sctp_endpoint *ep, - const struct sock *sk, - sctp_scope_t scope, - gfp_t gfp) + const struct sock *sk, + enum sctp_scope scope, gfp_t gfp) { struct sctp_association *asoc; @@ -1564,7 +1563,7 @@ void sctp_assoc_rwnd_decrease(struct sctp_association *asoc, unsigned int len) * local endpoint and the remote peer. */ int sctp_assoc_set_bind_addr_from_ep(struct sctp_association *asoc, - sctp_scope_t scope, gfp_t gfp) + enum sctp_scope scope, gfp_t gfp) { int flags; diff --git a/net/sctp/bind_addr.c b/net/sctp/bind_addr.c index 1ebc184a0e23..7df3704982f5 100644 --- a/net/sctp/bind_addr.c +++ b/net/sctp/bind_addr.c @@ -45,9 +45,9 @@ #include /* Forward declarations for internal helpers. */ -static int sctp_copy_one_addr(struct net *, struct sctp_bind_addr *, - union sctp_addr *, sctp_scope_t scope, gfp_t gfp, - int flags); +static int sctp_copy_one_addr(struct net *net, struct sctp_bind_addr *dest, + union sctp_addr *addr, enum sctp_scope scope, + gfp_t gfp, int flags); static void sctp_bind_addr_clean(struct sctp_bind_addr *); /* First Level Abstractions. */ @@ -57,7 +57,7 @@ static void sctp_bind_addr_clean(struct sctp_bind_addr *); */ int sctp_bind_addr_copy(struct net *net, struct sctp_bind_addr *dest, const struct sctp_bind_addr *src, - sctp_scope_t scope, gfp_t gfp, + enum sctp_scope scope, gfp_t gfp, int flags) { struct sctp_sockaddr_entry *addr; @@ -440,9 +440,8 @@ union sctp_addr *sctp_find_unmatch_addr(struct sctp_bind_addr *bp, /* Copy out addresses from the global local address list. */ static int sctp_copy_one_addr(struct net *net, struct sctp_bind_addr *dest, - union sctp_addr *addr, - sctp_scope_t scope, gfp_t gfp, - int flags) + union sctp_addr *addr, enum sctp_scope scope, + gfp_t gfp, int flags) { int error = 0; @@ -485,9 +484,10 @@ int sctp_is_any(struct sock *sk, const union sctp_addr *addr) } /* Is 'addr' valid for 'scope'? */ -int sctp_in_scope(struct net *net, const union sctp_addr *addr, sctp_scope_t scope) +int sctp_in_scope(struct net *net, const union sctp_addr *addr, + enum sctp_scope scope) { - sctp_scope_t addr_scope = sctp_scope(addr); + enum sctp_scope addr_scope = sctp_scope(addr); /* The unusable SCTP addresses will not be considered with * any defined scopes. @@ -545,7 +545,7 @@ int sctp_is_ep_boundall(struct sock *sk) ********************************************************************/ /* What is the scope of 'addr'? */ -sctp_scope_t sctp_scope(const union sctp_addr *addr) +enum sctp_scope sctp_scope(const union sctp_addr *addr) { struct sctp_af *af; diff --git a/net/sctp/ipv6.c b/net/sctp/ipv6.c index 107d7c912922..a2a1c1d08d51 100644 --- a/net/sctp/ipv6.c +++ b/net/sctp/ipv6.c @@ -243,8 +243,8 @@ static void sctp_v6_get_dst(struct sctp_transport *t, union sctp_addr *saddr, union sctp_addr *daddr = &t->ipaddr; union sctp_addr dst_saddr; struct in6_addr *final_p, final; + enum sctp_scope scope; __u8 matchlen = 0; - sctp_scope_t scope; memset(fl6, 0, sizeof(struct flowi6)); fl6->daddr = daddr->v6.sin6_addr; @@ -624,10 +624,10 @@ static int sctp_v6_addr_valid(union sctp_addr *addr, } /* What is the scope of 'addr'? */ -static sctp_scope_t sctp_v6_scope(union sctp_addr *addr) +static enum sctp_scope sctp_v6_scope(union sctp_addr *addr) { + enum sctp_scope retval; int v6scope; - sctp_scope_t retval; /* The IPv6 scope is really a set of bit fields. * See IFA_* in . Map to a generic SCTP scope. diff --git a/net/sctp/protocol.c b/net/sctp/protocol.c index 852556d67ae3..fcd80feb293f 100644 --- a/net/sctp/protocol.c +++ b/net/sctp/protocol.c @@ -196,7 +196,7 @@ static void sctp_free_local_addr_list(struct net *net) /* Copy the local addresses which are valid for 'scope' into 'bp'. */ int sctp_copy_local_addr_list(struct net *net, struct sctp_bind_addr *bp, - sctp_scope_t scope, gfp_t gfp, int copy_flags) + enum sctp_scope scope, gfp_t gfp, int copy_flags) { struct sctp_sockaddr_entry *addr; union sctp_addr laddr; @@ -400,9 +400,9 @@ static int sctp_v4_available(union sctp_addr *addr, struct sctp_sock *sp) * IPv4 scoping can be controlled through sysctl option * net.sctp.addr_scope_policy */ -static sctp_scope_t sctp_v4_scope(union sctp_addr *addr) +static enum sctp_scope sctp_v4_scope(union sctp_addr *addr) { - sctp_scope_t retval; + enum sctp_scope retval; /* Check for unusable SCTP addresses. */ if (IS_IPV4_UNUSABLE_ADDRESS(addr->v4.sin_addr.s_addr)) { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index d17e8d1f2ed9..a034d842e335 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1578,8 +1578,8 @@ struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep, gfp_t gfp) { struct sctp_association *asoc; + enum sctp_scope scope; struct sk_buff *skb; - sctp_scope_t scope; /* Create the bare association. */ scope = sctp_scope(sctp_source(chunk)); @@ -1701,7 +1701,7 @@ struct sctp_association *sctp_unpack_cookie( int headersize, bodysize, fixed_size; __u8 *digest = ep->digest; unsigned int len; - sctp_scope_t scope; + enum sctp_scope scope; struct sk_buff *skb = chunk->skb; ktime_t kt; @@ -2502,7 +2502,7 @@ static int sctp_process_param(struct sctp_association *asoc, int i; __u16 sat; int retval = 1; - sctp_scope_t scope; + enum sctp_scope scope; u32 stale; struct sctp_af *af; union sctp_addr_param *addr_param; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 1db478e34520..a1e2113806dd 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1055,7 +1055,7 @@ static int __sctp_connect(struct sock *sk, struct sctp_association *asoc2; struct sctp_transport *transport; union sctp_addr to; - sctp_scope_t scope; + enum sctp_scope scope; long timeo; int err = 0; int addrcnt = 0; @@ -1610,7 +1610,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) struct sctp_initmsg *sinit; sctp_assoc_t associd = 0; sctp_cmsgs_t cmsgs = { NULL }; - sctp_scope_t scope; + enum sctp_scope scope; bool fill_sinfo_ttl = false, wait_connect = false; struct sctp_datamsg *datamsg; int msg_flags = msg->msg_flags; -- cgit v1.2.3 From 0ceaeebe28d46e49c865f88a3e3ca75f6cf13e1f Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 19:59:55 +0800 Subject: sctp: remove the typedef sctp_transport_cmd_t This patch is to remove the typedef sctp_transport_cmd_t, and replace with enum sctp_transport_cmd in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 4 ++-- include/net/sctp/structs.h | 7 ++++--- net/sctp/associola.c | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 0503bb70e5d9..8ce6d3263e41 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -320,11 +320,11 @@ typedef enum { } sctp_xmit_t; /* These are the commands for manipulating transports. */ -typedef enum { +enum sctp_transport_cmd { SCTP_TRANSPORT_UP, SCTP_TRANSPORT_DOWN, SCTP_TRANSPORT_PF, -} sctp_transport_cmd_t; +}; /* These are the address scopes defined mainly for IPv4 addresses * based on draft of SCTP IPv4 scoping . diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index d771d418481f..d098d1c4ed74 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1947,9 +1947,10 @@ void sctp_assoc_del_peer(struct sctp_association *asoc, const union sctp_addr *addr); void sctp_assoc_rm_peer(struct sctp_association *asoc, struct sctp_transport *peer); -void sctp_assoc_control_transport(struct sctp_association *, - struct sctp_transport *, - sctp_transport_cmd_t, sctp_sn_error_t); +void sctp_assoc_control_transport(struct sctp_association *asoc, + struct sctp_transport *transport, + enum sctp_transport_cmd command, + sctp_sn_error_t error); struct sctp_transport *sctp_assoc_lookup_tsn(struct sctp_association *, __u32); struct sctp_transport *sctp_assoc_is_match(struct sctp_association *, struct net *, diff --git a/net/sctp/associola.c b/net/sctp/associola.c index 4c1f1bb2aaad..b53efed8ff71 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -796,7 +796,7 @@ void sctp_assoc_del_nonprimary_peers(struct sctp_association *asoc, */ void sctp_assoc_control_transport(struct sctp_association *asoc, struct sctp_transport *transport, - sctp_transport_cmd_t command, + enum sctp_transport_cmd command, sctp_sn_error_t error) { struct sctp_ulpevent *event; -- cgit v1.2.3 From 86b36f2a9b9ea58dd2100b0e6f1f45a1f67ee95e Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 19:59:57 +0800 Subject: sctp: remove the typedef sctp_xmit_t This patch is to remove the typedef sctp_xmit_t, and replace with enum sctp_xmit in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 4 +-- include/net/sctp/structs.h | 9 ++++--- net/sctp/output.c | 60 ++++++++++++++++++++++---------------------- net/sctp/outqueue.c | 8 +++--- 4 files changed, 41 insertions(+), 40 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 049868ea7ae9..311b2e3773e8 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -312,12 +312,12 @@ enum { SCTP_MAX_GABS = 16 }; /* These return values describe the success or failure of a number of * routines which form the lower interface to SCTP_outqueue. */ -typedef enum { +enum sctp_xmit { SCTP_XMIT_OK, SCTP_XMIT_PMTU_FULL, SCTP_XMIT_RWND_FULL, SCTP_XMIT_DELAY, -} sctp_xmit_t; +}; /* These are the commands for manipulating transports. */ enum sctp_transport_cmd { diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index d098d1c4ed74..6fab67e7f1e5 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -697,10 +697,11 @@ struct sctp_packet { void sctp_packet_init(struct sctp_packet *, struct sctp_transport *, __u16 sport, __u16 dport); void sctp_packet_config(struct sctp_packet *, __u32 vtag, int); -sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *, - struct sctp_chunk *, int, gfp_t); -sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *, - struct sctp_chunk *); +enum sctp_xmit sctp_packet_transmit_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk, + int one_packet, gfp_t gfp); +enum sctp_xmit sctp_packet_append_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk); int sctp_packet_transmit(struct sctp_packet *, gfp_t); void sctp_packet_free(struct sctp_packet *); diff --git a/net/sctp/output.c b/net/sctp/output.c index 9d8504985744..4a865cd06d76 100644 --- a/net/sctp/output.c +++ b/net/sctp/output.c @@ -57,15 +57,15 @@ #include /* Forward declarations for private helpers. */ -static sctp_xmit_t __sctp_packet_append_chunk(struct sctp_packet *packet, - struct sctp_chunk *chunk); -static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, - struct sctp_chunk *chunk); +static enum sctp_xmit __sctp_packet_append_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk); +static enum sctp_xmit sctp_packet_can_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk); static void sctp_packet_append_data(struct sctp_packet *packet, - struct sctp_chunk *chunk); -static sctp_xmit_t sctp_packet_will_fit(struct sctp_packet *packet, - struct sctp_chunk *chunk, - u16 chunk_len); + struct sctp_chunk *chunk); +static enum sctp_xmit sctp_packet_will_fit(struct sctp_packet *packet, + struct sctp_chunk *chunk, + u16 chunk_len); static void sctp_packet_reset(struct sctp_packet *packet) { @@ -181,11 +181,11 @@ void sctp_packet_free(struct sctp_packet *packet) * as it can fit in the packet, but any more data that does not fit in this * packet can be sent only after receiving the COOKIE_ACK. */ -sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, - struct sctp_chunk *chunk, - int one_packet, gfp_t gfp) +enum sctp_xmit sctp_packet_transmit_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk, + int one_packet, gfp_t gfp) { - sctp_xmit_t retval; + enum sctp_xmit retval; pr_debug("%s: packet:%p size:%zu chunk:%p size:%d\n", __func__, packet, packet->size, chunk, chunk->skb ? chunk->skb->len : -1); @@ -218,12 +218,12 @@ sctp_xmit_t sctp_packet_transmit_chunk(struct sctp_packet *packet, } /* Try to bundle an auth chunk into the packet. */ -static sctp_xmit_t sctp_packet_bundle_auth(struct sctp_packet *pkt, - struct sctp_chunk *chunk) +static enum sctp_xmit sctp_packet_bundle_auth(struct sctp_packet *pkt, + struct sctp_chunk *chunk) { struct sctp_association *asoc = pkt->transport->asoc; + enum sctp_xmit retval = SCTP_XMIT_OK; struct sctp_chunk *auth; - sctp_xmit_t retval = SCTP_XMIT_OK; /* if we don't have an association, we can't do authentication */ if (!asoc) @@ -254,10 +254,10 @@ static sctp_xmit_t sctp_packet_bundle_auth(struct sctp_packet *pkt, } /* Try to bundle a SACK with the packet. */ -static sctp_xmit_t sctp_packet_bundle_sack(struct sctp_packet *pkt, - struct sctp_chunk *chunk) +static enum sctp_xmit sctp_packet_bundle_sack(struct sctp_packet *pkt, + struct sctp_chunk *chunk) { - sctp_xmit_t retval = SCTP_XMIT_OK; + enum sctp_xmit retval = SCTP_XMIT_OK; /* If sending DATA and haven't aleady bundled a SACK, try to * bundle one in to the packet. @@ -299,11 +299,11 @@ out: /* Append a chunk to the offered packet reporting back any inability to do * so. */ -static sctp_xmit_t __sctp_packet_append_chunk(struct sctp_packet *packet, - struct sctp_chunk *chunk) +static enum sctp_xmit __sctp_packet_append_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk) { - sctp_xmit_t retval = SCTP_XMIT_OK; __u16 chunk_len = SCTP_PAD4(ntohs(chunk->chunk_hdr->length)); + enum sctp_xmit retval = SCTP_XMIT_OK; /* Check to see if this chunk will fit into the packet */ retval = sctp_packet_will_fit(packet, chunk, chunk_len); @@ -353,10 +353,10 @@ finish: /* Append a chunk to the offered packet reporting back any inability to do * so. */ -sctp_xmit_t sctp_packet_append_chunk(struct sctp_packet *packet, - struct sctp_chunk *chunk) +enum sctp_xmit sctp_packet_append_chunk(struct sctp_packet *packet, + struct sctp_chunk *chunk) { - sctp_xmit_t retval = SCTP_XMIT_OK; + enum sctp_xmit retval = SCTP_XMIT_OK; pr_debug("%s: packet:%p chunk:%p\n", __func__, packet, chunk); @@ -653,8 +653,8 @@ out: ********************************************************************/ /* This private function check to see if a chunk can be added */ -static sctp_xmit_t sctp_packet_can_append_data(struct sctp_packet *packet, - struct sctp_chunk *chunk) +static enum sctp_xmit sctp_packet_can_append_data(struct sctp_packet *packet, + struct sctp_chunk *chunk) { size_t datasize, rwnd, inflight, flight_size; struct sctp_transport *transport = packet->transport; @@ -762,12 +762,12 @@ static void sctp_packet_append_data(struct sctp_packet *packet, sctp_chunk_assign_ssn(chunk); } -static sctp_xmit_t sctp_packet_will_fit(struct sctp_packet *packet, - struct sctp_chunk *chunk, - u16 chunk_len) +static enum sctp_xmit sctp_packet_will_fit(struct sctp_packet *packet, + struct sctp_chunk *chunk, + u16 chunk_len) { + enum sctp_xmit retval = SCTP_XMIT_OK; size_t psize, pmtu, maxsize; - sctp_xmit_t retval = SCTP_XMIT_OK; psize = packet->size; if (packet->transport->asoc) diff --git a/net/sctp/outqueue.c b/net/sctp/outqueue.c index 08ee0ed9a0c6..2966ff400755 100644 --- a/net/sctp/outqueue.c +++ b/net/sctp/outqueue.c @@ -594,14 +594,14 @@ void sctp_retransmit(struct sctp_outq *q, struct sctp_transport *transport, static int sctp_outq_flush_rtx(struct sctp_outq *q, struct sctp_packet *pkt, int rtx_timeout, int *start_timer) { - struct list_head *lqueue; struct sctp_transport *transport = pkt->transport; - sctp_xmit_t status; struct sctp_chunk *chunk, *chunk1; - int fast_rtx; + struct list_head *lqueue; + enum sctp_xmit status; int error = 0; int timer = 0; int done = 0; + int fast_rtx; lqueue = &q->retransmit; fast_rtx = q->fast_rtx; @@ -781,7 +781,7 @@ static void sctp_outq_flush(struct sctp_outq *q, int rtx_timeout, gfp_t gfp) struct sctp_transport *transport = NULL; struct sctp_transport *new_transport; struct sctp_chunk *chunk, *tmp; - sctp_xmit_t status; + enum sctp_xmit status; int error = 0; int start_timer = 0; int one_packet = 0; -- cgit v1.2.3 From 4785c7ae1848244da3435ba5269f6288c15975c7 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 19:59:58 +0800 Subject: sctp: remove the typedef sctp_ierror_t This patch is to remove the typedef sctp_ierror_t, and replace with enum sctp_ierror in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 5 ++--- net/sctp/sm_make_chunk.c | 23 ++++++++++++----------- net/sctp/sm_statefuns.c | 28 +++++++++++++++------------- 3 files changed, 29 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 311b2e3773e8..9a694653b708 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -155,8 +155,7 @@ SCTP_SUBTYPE_CONSTRUCTOR(PRIMITIVE, sctp_event_primitive_t, primitive) - sizeof(struct sctp_data_chunk))) /* Internal error codes */ -typedef enum { - +enum sctp_ierror { SCTP_IERROR_NO_ERROR = 0, SCTP_IERROR_BASE = 1000, SCTP_IERROR_NO_COOKIE, @@ -177,7 +176,7 @@ typedef enum { SCTP_IERROR_PROTO_VIOLATION, SCTP_IERROR_ERROR, SCTP_IERROR_ABORT, -} sctp_ierror_t; +}; diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index a034d842e335..3a8fb1dffbc1 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -2065,10 +2065,11 @@ static void sctp_process_ext_param(struct sctp_association *asoc, * SCTP_IERROR_ERROR - stop and report an error. * SCTP_IERROR_NOMEME - out of memory. */ -static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc, - union sctp_params param, - struct sctp_chunk *chunk, - struct sctp_chunk **errp) +static enum sctp_ierror sctp_process_unk_param( + const struct sctp_association *asoc, + union sctp_params param, + struct sctp_chunk *chunk, + struct sctp_chunk **errp) { int retval = SCTP_IERROR_NO_ERROR; @@ -2117,13 +2118,13 @@ static sctp_ierror_t sctp_process_unk_param(const struct sctp_association *asoc, * SCTP_IERROR_ERROR - stop processing, trigger an ERROR * SCTP_IERROR_NO_ERROR - continue with the chunk */ -static sctp_ierror_t sctp_verify_param(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - union sctp_params param, - enum sctp_cid cid, - struct sctp_chunk *chunk, - struct sctp_chunk **err_chunk) +static enum sctp_ierror sctp_verify_param(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + union sctp_params param, + enum sctp_cid cid, + struct sctp_chunk *chunk, + struct sctp_chunk **err_chunk) { struct sctp_hmac_algo_param *hmacs; int retval = SCTP_IERROR_NO_ERROR; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 8af90a5f23cd..5381697333df 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -144,11 +144,12 @@ static sctp_disposition_t sctp_sf_violation_chunk( void *arg, sctp_cmd_seq_t *commands); -static sctp_ierror_t sctp_sf_authenticate(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const sctp_subtype_t type, - struct sctp_chunk *chunk); +static enum sctp_ierror sctp_sf_authenticate( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + struct sctp_chunk *chunk); static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, const struct sctp_endpoint *ep, @@ -756,7 +757,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net, */ if (chunk->auth_chunk) { struct sctp_chunk auth; - sctp_ierror_t ret; + enum sctp_ierror ret; /* Make sure that we and the peer are AUTH capable */ if (!net->sctp.auth_enable || !new_asoc->peer.auth_capable) { @@ -4077,11 +4078,12 @@ gen_shutdown: * * The return value is the disposition of the chunk. */ -static sctp_ierror_t sctp_sf_authenticate(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const sctp_subtype_t type, - struct sctp_chunk *chunk) +static enum sctp_ierror sctp_sf_authenticate( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const sctp_subtype_t type, + struct sctp_chunk *chunk) { struct sctp_authhdr *auth_hdr; struct sctp_hmac *hmac; @@ -4159,10 +4161,10 @@ sctp_disposition_t sctp_sf_eat_auth(struct net *net, void *arg, sctp_cmd_seq_t *commands) { - struct sctp_authhdr *auth_hdr; struct sctp_chunk *chunk = arg; + struct sctp_authhdr *auth_hdr; struct sctp_chunk *err_chunk; - sctp_ierror_t error; + enum sctp_ierror error; /* Make sure that the peer has AUTH capable */ if (!asoc->peer.auth_capable) -- cgit v1.2.3 From 5210601945f5aedaf2d7f13a88436e27a39c6a8a Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 19:59:59 +0800 Subject: sctp: remove the typedef sctp_state_t This patch is to remove the typedef sctp_state_t, and replace with enum sctp_state in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/command.h | 4 ++-- include/net/sctp/constants.h | 4 ++-- include/net/sctp/sctp.h | 2 +- include/net/sctp/sm.h | 10 +++++----- include/net/sctp/structs.h | 2 +- net/sctp/endpointola.c | 2 +- net/sctp/primitive.c | 2 +- net/sctp/sm_sideeffect.c | 12 ++++++------ net/sctp/sm_statetable.c | 16 +++++++++------- 9 files changed, 28 insertions(+), 26 deletions(-) (limited to 'net') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 1d5f6ff3f440..be12ec946628 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -126,7 +126,7 @@ typedef union { __u8 u8; int error; __be16 err; - sctp_state_t state; + enum sctp_state state; sctp_event_timeout_t to; struct sctp_chunk *chunk; struct sctp_association *asoc; @@ -167,7 +167,7 @@ SCTP_ARG_CONSTRUCTOR(U16, __u16, u16) SCTP_ARG_CONSTRUCTOR(U8, __u8, u8) SCTP_ARG_CONSTRUCTOR(ERROR, int, error) SCTP_ARG_CONSTRUCTOR(PERR, __be16, err) /* protocol error */ -SCTP_ARG_CONSTRUCTOR(STATE, sctp_state_t, state) +SCTP_ARG_CONSTRUCTOR(STATE, enum sctp_state, state) SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to) SCTP_ARG_CONSTRUCTOR(CHUNK, struct sctp_chunk *, chunk) SCTP_ARG_CONSTRUCTOR(ASOC, struct sctp_association *, asoc) diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 9a694653b708..db9f40b657ba 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -181,7 +181,7 @@ enum sctp_ierror { /* SCTP state defines for internal state machine */ -typedef enum { +enum sctp_state { SCTP_STATE_CLOSED = 0, SCTP_STATE_COOKIE_WAIT = 1, @@ -192,7 +192,7 @@ typedef enum { SCTP_STATE_SHUTDOWN_RECEIVED = 6, SCTP_STATE_SHUTDOWN_ACK_SENT = 7, -} sctp_state_t; +}; #define SCTP_STATE_MAX SCTP_STATE_SHUTDOWN_ACK_SENT #define SCTP_STATE_NUM_STATES (SCTP_STATE_MAX + 1) diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 0022bc713434..24ff7931d38c 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -558,7 +558,7 @@ static inline int __sctp_style(const struct sock *sk, sctp_socket_type_t style) /* Is the association in this state? */ #define sctp_state(asoc, state) __sctp_state((asoc), (SCTP_STATE_##state)) static inline int __sctp_state(const struct sctp_association *asoc, - sctp_state_t state) + enum sctp_state state) { return asoc->state == state; } diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 860f378333b5..281e8d1e0752 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -175,10 +175,10 @@ sctp_state_fn_t sctp_sf_autoclose_timer_expire; /* Prototypes for utility support functions. */ __u8 sctp_get_chunk_type(struct sctp_chunk *chunk); -const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *, - sctp_event_t, - sctp_state_t, - sctp_subtype_t); +const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *net, + sctp_event_t event_type, + enum sctp_state state, + sctp_subtype_t event_subtype); int sctp_chunk_iif(const struct sctp_chunk *); struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *, struct sctp_chunk *, @@ -313,7 +313,7 @@ struct sctp_chunk *sctp_process_strreset_resp( /* Prototypes for statetable processing. */ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, + enum sctp_state state, struct sctp_endpoint *, struct sctp_association *asoc, void *event_arg, diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 6fab67e7f1e5..fbe6e81b889b 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1577,7 +1577,7 @@ struct sctp_association { * * State takes values from SCTP_STATE_*. */ - sctp_state_t state; + enum sctp_state state; /* Overall : The overall association error count. * Error Count : [Clear this any time I get something.] diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 3d506b2f6193..4111c00a9d9d 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -383,7 +383,7 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work) struct sctp_chunk *chunk; struct sctp_inq *inqueue; sctp_subtype_t subtype; - sctp_state_t state; + enum sctp_state state; int error = 0; int first_time = 1; /* is this the first time through the loop */ diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c index f0553a022859..1fb5b9bb3c6d 100644 --- a/net/sctp/primitive.c +++ b/net/sctp/primitive.c @@ -54,7 +54,7 @@ int sctp_primitive_ ## name(struct net *net, struct sctp_association *asoc, \ void *arg) { \ int error = 0; \ sctp_event_t event_type; sctp_subtype_t subtype; \ - sctp_state_t state; \ + enum sctp_state state; \ struct sctp_endpoint *ep; \ \ event_type = SCTP_EVENT_T_PRIMITIVE; \ diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 5dda8c42b5f6..b77a81aa907b 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -53,7 +53,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, + enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, @@ -61,7 +61,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, sctp_cmd_seq_t *commands, gfp_t gfp); static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, + enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association **asoc, void *event_arg, @@ -843,7 +843,7 @@ static void sctp_cmd_assoc_update(sctp_cmd_seq_t *cmds, /* Helper function to change the state of an association. */ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, struct sctp_association *asoc, - sctp_state_t state) + enum sctp_state state) { struct sock *sk = asoc->base.sk; @@ -1140,7 +1140,7 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc) * good place to start. */ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, + enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, @@ -1179,7 +1179,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, * This the master state function side effect processing function. *****************************************************************/ static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, + enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association **asoc, void *event_arg, @@ -1265,7 +1265,7 @@ bail: /* This is the side-effect interpreter. */ static int sctp_cmd_interpreter(sctp_event_t event_type, sctp_subtype_t subtype, - sctp_state_t state, + enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index 3e958c1c4b95..f46c11bffea7 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -52,9 +52,10 @@ other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES]; static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES]; -static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(struct net *net, - enum sctp_cid cid, - sctp_state_t state); +static const sctp_sm_table_entry_t *sctp_chunk_event_lookup( + struct net *net, + enum sctp_cid cid, + enum sctp_state state); static const sctp_sm_table_entry_t bug = { @@ -78,7 +79,7 @@ static const sctp_sm_table_entry_t bug = { const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *net, sctp_event_t event_type, - sctp_state_t state, + enum sctp_state state, sctp_subtype_t event_subtype) { switch (event_type) { @@ -967,9 +968,10 @@ static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][S TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE, }; -static const sctp_sm_table_entry_t *sctp_chunk_event_lookup(struct net *net, - enum sctp_cid cid, - sctp_state_t state) +static const sctp_sm_table_entry_t *sctp_chunk_event_lookup( + struct net *net, + enum sctp_cid cid, + enum sctp_state state) { if (state > SCTP_STATE_MAX) return &bug; -- cgit v1.2.3 From 19cd1592a24754e16d48398812d5f69b63f674dd Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 20:00:02 +0800 Subject: sctp: remove the typedef sctp_event_timeout_t This patch is to remove the typedef sctp_event_timeout_t, and replace with enum sctp_event_timeout in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/command.h | 4 ++-- include/net/sctp/constants.h | 8 ++++---- net/sctp/sm_sideeffect.c | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index be12ec946628..376cb78b6247 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -127,7 +127,7 @@ typedef union { int error; __be16 err; enum sctp_state state; - sctp_event_timeout_t to; + enum sctp_event_timeout to; struct sctp_chunk *chunk; struct sctp_association *asoc; struct sctp_transport *transport; @@ -168,7 +168,7 @@ SCTP_ARG_CONSTRUCTOR(U8, __u8, u8) SCTP_ARG_CONSTRUCTOR(ERROR, int, error) SCTP_ARG_CONSTRUCTOR(PERR, __be16, err) /* protocol error */ SCTP_ARG_CONSTRUCTOR(STATE, enum sctp_state, state) -SCTP_ARG_CONSTRUCTOR(TO, sctp_event_timeout_t, to) +SCTP_ARG_CONSTRUCTOR(TO, enum sctp_event_timeout, to) SCTP_ARG_CONSTRUCTOR(CHUNK, struct sctp_chunk *, chunk) SCTP_ARG_CONSTRUCTOR(ASOC, struct sctp_association *, asoc) SCTP_ARG_CONSTRUCTOR(TRANSPORT, struct sctp_transport *, transport) diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index fd8a80ec573f..fb931f07e83a 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -84,7 +84,7 @@ typedef enum { * SCTP_ULP_* to the list of possible chunks. */ -typedef enum { +enum sctp_event_timeout { SCTP_EVENT_TIMEOUT_NONE = 0, SCTP_EVENT_TIMEOUT_T1_COOKIE, SCTP_EVENT_TIMEOUT_T1_INIT, @@ -96,7 +96,7 @@ typedef enum { SCTP_EVENT_TIMEOUT_RECONF, SCTP_EVENT_TIMEOUT_SACK, SCTP_EVENT_TIMEOUT_AUTOCLOSE, -} sctp_event_timeout_t; +}; #define SCTP_EVENT_TIMEOUT_MAX SCTP_EVENT_TIMEOUT_AUTOCLOSE #define SCTP_NUM_TIMEOUT_TYPES (SCTP_EVENT_TIMEOUT_MAX + 1) @@ -131,7 +131,7 @@ enum sctp_event_primitive { typedef union { enum sctp_cid chunk; - sctp_event_timeout_t timeout; + enum sctp_event_timeout timeout; enum sctp_event_other other; enum sctp_event_primitive primitive; } sctp_subtype_t; @@ -142,7 +142,7 @@ SCTP_ST_## _name (_type _arg) \ { sctp_subtype_t _retval; _retval._elt = _arg; return _retval; } SCTP_SUBTYPE_CONSTRUCTOR(CHUNK, enum sctp_cid, chunk) -SCTP_SUBTYPE_CONSTRUCTOR(TIMEOUT, sctp_event_timeout_t, timeout) +SCTP_SUBTYPE_CONSTRUCTOR(TIMEOUT, enum sctp_event_timeout, timeout) SCTP_SUBTYPE_CONSTRUCTOR(OTHER, enum sctp_event_other, other) SCTP_SUBTYPE_CONSTRUCTOR(PRIMITIVE, enum sctp_event_primitive, primitive) diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index b77a81aa907b..11a344896b71 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -280,7 +280,7 @@ out_unlock: * for timeouts which use the association as their parameter. */ static void sctp_generate_timeout_event(struct sctp_association *asoc, - sctp_event_timeout_t timeout_type) + enum sctp_event_timeout timeout_type) { struct sock *sk = asoc->base.sk; struct net *net = sock_net(sk); @@ -1052,8 +1052,8 @@ static void sctp_cmd_adaptation_ind(sctp_cmd_seq_t *commands, static void sctp_cmd_t1_timer_update(struct sctp_association *asoc, - sctp_event_timeout_t timer, - char *name) + enum sctp_event_timeout timer, + char *name) { struct sctp_transport *t; -- cgit v1.2.3 From 61f0eb072294a148f707335d4d7f858b2af73770 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 20:00:03 +0800 Subject: sctp: remove the typedef sctp_event_t This patch is to remove the typedef sctp_event_t, and replace with enum sctp_event in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 6 ++---- include/net/sctp/sm.h | 12 +++++------- net/sctp/primitive.c | 2 +- net/sctp/sm_sideeffect.c | 20 +++++++++----------- net/sctp/sm_statetable.c | 2 +- 5 files changed, 18 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index fb931f07e83a..3181e0f95b60 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -71,14 +71,12 @@ enum { SCTP_DEFAULT_INSTREAMS = SCTP_MAX_STREAM }; SCTP_NUM_AUTH_CHUNK_TYPES) /* These are the different flavours of event. */ -typedef enum { - +enum sctp_event { SCTP_EVENT_T_CHUNK = 1, SCTP_EVENT_T_TIMEOUT, SCTP_EVENT_T_OTHER, SCTP_EVENT_T_PRIMITIVE - -} sctp_event_t; +}; /* As a convenience for the state machine, we append SCTP_EVENT_* and * SCTP_ULP_* to the list of possible chunks. diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 281e8d1e0752..96f54cff7964 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -176,7 +176,7 @@ sctp_state_fn_t sctp_sf_autoclose_timer_expire; /* Prototypes for utility support functions. */ __u8 sctp_get_chunk_type(struct sctp_chunk *chunk); const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *net, - sctp_event_t event_type, + enum sctp_event event_type, enum sctp_state state, sctp_subtype_t event_subtype); int sctp_chunk_iif(const struct sctp_chunk *); @@ -312,12 +312,10 @@ struct sctp_chunk *sctp_process_strreset_resp( /* Prototypes for statetable processing. */ -int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, - enum sctp_state state, - struct sctp_endpoint *, - struct sctp_association *asoc, - void *event_arg, - gfp_t gfp); +int sctp_do_sm(struct net *net, enum sctp_event event_type, + sctp_subtype_t subtype, enum sctp_state state, + struct sctp_endpoint *ep, struct sctp_association *asoc, + void *event_arg, gfp_t gfp); /* 2nd level prototypes */ void sctp_generate_t3_rtx_event(unsigned long peer); diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c index 1fb5b9bb3c6d..c914166984b3 100644 --- a/net/sctp/primitive.c +++ b/net/sctp/primitive.c @@ -53,7 +53,7 @@ int sctp_primitive_ ## name(struct net *net, struct sctp_association *asoc, \ void *arg) { \ int error = 0; \ - sctp_event_t event_type; sctp_subtype_t subtype; \ + enum sctp_event event_type; sctp_subtype_t subtype; \ enum sctp_state state; \ struct sctp_endpoint *ep; \ \ diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 11a344896b71..b545c768cb9e 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -51,7 +51,7 @@ #include #include -static int sctp_cmd_interpreter(sctp_event_t event_type, +static int sctp_cmd_interpreter(enum sctp_event event_type, sctp_subtype_t subtype, enum sctp_state state, struct sctp_endpoint *ep, @@ -60,7 +60,7 @@ static int sctp_cmd_interpreter(sctp_event_t event_type, sctp_disposition_t status, sctp_cmd_seq_t *commands, gfp_t gfp); -static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, +static int sctp_side_effects(enum sctp_event event_type, sctp_subtype_t subtype, enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association **asoc, @@ -602,7 +602,7 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, /* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, struct sctp_association *asoc, - sctp_event_t event_type, + enum sctp_event event_type, sctp_subtype_t subtype, struct sctp_chunk *chunk, unsigned int error) @@ -1139,12 +1139,10 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc) * If you want to understand all of lksctp, this is a * good place to start. */ -int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, - enum sctp_state state, - struct sctp_endpoint *ep, - struct sctp_association *asoc, - void *event_arg, - gfp_t gfp) +int sctp_do_sm(struct net *net, enum sctp_event event_type, + sctp_subtype_t subtype, enum sctp_state state, + struct sctp_endpoint *ep, struct sctp_association *asoc, + void *event_arg, gfp_t gfp) { sctp_cmd_seq_t commands; const sctp_sm_table_entry_t *state_fn; @@ -1178,7 +1176,7 @@ int sctp_do_sm(struct net *net, sctp_event_t event_type, sctp_subtype_t subtype, /***************************************************************** * This the master state function side effect processing function. *****************************************************************/ -static int sctp_side_effects(sctp_event_t event_type, sctp_subtype_t subtype, +static int sctp_side_effects(enum sctp_event event_type, sctp_subtype_t subtype, enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association **asoc, @@ -1263,7 +1261,7 @@ bail: ********************************************************************/ /* This is the side-effect interpreter. */ -static int sctp_cmd_interpreter(sctp_event_t event_type, +static int sctp_cmd_interpreter(enum sctp_event event_type, sctp_subtype_t subtype, enum sctp_state state, struct sctp_endpoint *ep, diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index f46c11bffea7..f7cdb7014244 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -78,7 +78,7 @@ static const sctp_sm_table_entry_t bug = { }) const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *net, - sctp_event_t event_type, + enum sctp_event event_type, enum sctp_state state, sctp_subtype_t event_subtype) { -- cgit v1.2.3 From bfc6f8270fefb323662d1d7713f940149f27b7f1 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Sat, 5 Aug 2017 20:00:04 +0800 Subject: sctp: remove the typedef sctp_subtype_t This patch is to remove the typedef sctp_subtype_t, and replace with union sctp_subtype in the places where it's using this typedef. Note that it doesn't fix many indents although it should, as sctp_disposition_t's removal would mess them up again. So better to fix them when removing sctp_disposition_t in later patch. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/constants.h | 18 ++-- include/net/sctp/sm.h | 13 +-- net/sctp/associola.c | 2 +- net/sctp/debug.c | 8 +- net/sctp/endpointola.c | 2 +- net/sctp/primitive.c | 2 +- net/sctp/probe.c | 2 +- net/sctp/sm_sideeffect.c | 16 ++-- net/sctp/sm_statefuns.c | 191 ++++++++++++++++++++++--------------------- net/sctp/sm_statetable.c | 9 +- 10 files changed, 134 insertions(+), 129 deletions(-) (limited to 'net') diff --git a/include/net/sctp/constants.h b/include/net/sctp/constants.h index 3181e0f95b60..deaafa9b09cb 100644 --- a/include/net/sctp/constants.h +++ b/include/net/sctp/constants.h @@ -124,20 +124,20 @@ enum sctp_event_primitive { /* We define here a utility type for manipulating subtypes. * The subtype constructors all work like this: * - * sctp_subtype_t foo = SCTP_ST_CHUNK(SCTP_CID_INIT); + * union sctp_subtype foo = SCTP_ST_CHUNK(SCTP_CID_INIT); */ -typedef union { +union sctp_subtype { enum sctp_cid chunk; enum sctp_event_timeout timeout; enum sctp_event_other other; enum sctp_event_primitive primitive; -} sctp_subtype_t; +}; #define SCTP_SUBTYPE_CONSTRUCTOR(_name, _type, _elt) \ -static inline sctp_subtype_t \ +static inline union sctp_subtype \ SCTP_ST_## _name (_type _arg) \ -{ sctp_subtype_t _retval; _retval._elt = _arg; return _retval; } +{ union sctp_subtype _retval; _retval._elt = _arg; return _retval; } SCTP_SUBTYPE_CONSTRUCTOR(CHUNK, enum sctp_cid, chunk) SCTP_SUBTYPE_CONSTRUCTOR(TIMEOUT, enum sctp_event_timeout, timeout) @@ -220,10 +220,10 @@ enum sctp_sock_state { }; /* These functions map various type to printable names. */ -const char *sctp_cname(const sctp_subtype_t); /* chunk types */ -const char *sctp_oname(const sctp_subtype_t); /* other events */ -const char *sctp_tname(const sctp_subtype_t); /* timeouts */ -const char *sctp_pname(const sctp_subtype_t); /* primitives */ +const char *sctp_cname(const union sctp_subtype id); /* chunk types */ +const char *sctp_oname(const union sctp_subtype id); /* other events */ +const char *sctp_tname(const union sctp_subtype id); /* timeouts */ +const char *sctp_pname(const union sctp_subtype id); /* primitives */ /* This is a table of printable names of sctp_state_t's. */ extern const char *const sctp_state_tbl[]; diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 96f54cff7964..1e7651c3b158 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -73,7 +73,7 @@ typedef struct { typedef sctp_disposition_t (sctp_state_fn_t) (struct net *, const struct sctp_endpoint *, const struct sctp_association *, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *); typedef void (sctp_timer_event_t) (unsigned long); @@ -175,10 +175,11 @@ sctp_state_fn_t sctp_sf_autoclose_timer_expire; /* Prototypes for utility support functions. */ __u8 sctp_get_chunk_type(struct sctp_chunk *chunk); -const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *net, - enum sctp_event event_type, - enum sctp_state state, - sctp_subtype_t event_subtype); +const sctp_sm_table_entry_t *sctp_sm_lookup_event( + struct net *net, + enum sctp_event event_type, + enum sctp_state state, + union sctp_subtype event_subtype); int sctp_chunk_iif(const struct sctp_chunk *); struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *, struct sctp_chunk *, @@ -313,7 +314,7 @@ struct sctp_chunk *sctp_process_strreset_resp( /* Prototypes for statetable processing. */ int sctp_do_sm(struct net *net, enum sctp_event event_type, - sctp_subtype_t subtype, enum sctp_state state, + union sctp_subtype subtype, enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, gfp_t gfp); diff --git a/net/sctp/associola.c b/net/sctp/associola.c index b53efed8ff71..dfb9651e818b 100644 --- a/net/sctp/associola.c +++ b/net/sctp/associola.c @@ -1021,11 +1021,11 @@ static void sctp_assoc_bh_rcv(struct work_struct *work) container_of(work, struct sctp_association, base.inqueue.immediate); struct net *net = sock_net(asoc->base.sk); + union sctp_subtype subtype; struct sctp_endpoint *ep; struct sctp_chunk *chunk; struct sctp_inq *inqueue; int state; - sctp_subtype_t subtype; int error = 0; /* The association should be held so we should be safe. */ diff --git a/net/sctp/debug.c b/net/sctp/debug.c index 2e47eb2f05cb..3f619fdcbf0a 100644 --- a/net/sctp/debug.c +++ b/net/sctp/debug.c @@ -60,7 +60,7 @@ static const char *const sctp_cid_tbl[SCTP_NUM_BASE_CHUNK_TYPES] = { }; /* Lookup "chunk type" debug name. */ -const char *sctp_cname(const sctp_subtype_t cid) +const char *sctp_cname(const union sctp_subtype cid) { if (cid.chunk <= SCTP_CID_BASE_MAX) return sctp_cid_tbl[cid.chunk]; @@ -130,7 +130,7 @@ static const char *const sctp_primitive_tbl[SCTP_NUM_PRIMITIVE_TYPES] = { }; /* Lookup primitive debug name. */ -const char *sctp_pname(const sctp_subtype_t id) +const char *sctp_pname(const union sctp_subtype id) { if (id.primitive <= SCTP_EVENT_PRIMITIVE_MAX) return sctp_primitive_tbl[id.primitive]; @@ -143,7 +143,7 @@ static const char *const sctp_other_tbl[] = { }; /* Lookup "other" debug name. */ -const char *sctp_oname(const sctp_subtype_t id) +const char *sctp_oname(const union sctp_subtype id) { if (id.other <= SCTP_EVENT_OTHER_MAX) return sctp_other_tbl[id.other]; @@ -165,7 +165,7 @@ static const char *const sctp_timer_tbl[] = { }; /* Lookup timer debug name. */ -const char *sctp_tname(const sctp_subtype_t id) +const char *sctp_tname(const union sctp_subtype id) { BUILD_BUG_ON(SCTP_EVENT_TIMEOUT_MAX + 1 != ARRAY_SIZE(sctp_timer_tbl)); diff --git a/net/sctp/endpointola.c b/net/sctp/endpointola.c index 4111c00a9d9d..ee1e601a0b11 100644 --- a/net/sctp/endpointola.c +++ b/net/sctp/endpointola.c @@ -382,7 +382,7 @@ static void sctp_endpoint_bh_rcv(struct work_struct *work) struct sctp_transport *transport; struct sctp_chunk *chunk; struct sctp_inq *inqueue; - sctp_subtype_t subtype; + union sctp_subtype subtype; enum sctp_state state; int error = 0; int first_time = 1; /* is this the first time through the loop */ diff --git a/net/sctp/primitive.c b/net/sctp/primitive.c index c914166984b3..c0817f7a8964 100644 --- a/net/sctp/primitive.c +++ b/net/sctp/primitive.c @@ -53,7 +53,7 @@ int sctp_primitive_ ## name(struct net *net, struct sctp_association *asoc, \ void *arg) { \ int error = 0; \ - enum sctp_event event_type; sctp_subtype_t subtype; \ + enum sctp_event event_type; union sctp_subtype subtype; \ enum sctp_state state; \ struct sctp_endpoint *ep; \ \ diff --git a/net/sctp/probe.c b/net/sctp/probe.c index 6cc2152e0740..43837dfc86a7 100644 --- a/net/sctp/probe.c +++ b/net/sctp/probe.c @@ -130,7 +130,7 @@ static const struct file_operations sctpprobe_fops = { static sctp_disposition_t jsctp_sf_eat_sack(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index b545c768cb9e..4a12d29d9aa1 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -52,7 +52,7 @@ #include static int sctp_cmd_interpreter(enum sctp_event event_type, - sctp_subtype_t subtype, + union sctp_subtype subtype, enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association *asoc, @@ -60,7 +60,8 @@ static int sctp_cmd_interpreter(enum sctp_event event_type, sctp_disposition_t status, sctp_cmd_seq_t *commands, gfp_t gfp); -static int sctp_side_effects(enum sctp_event event_type, sctp_subtype_t subtype, +static int sctp_side_effects(enum sctp_event event_type, + union sctp_subtype subtype, enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association **asoc, @@ -603,7 +604,7 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, struct sctp_association *asoc, enum sctp_event event_type, - sctp_subtype_t subtype, + union sctp_subtype subtype, struct sctp_chunk *chunk, unsigned int error) { @@ -1140,7 +1141,7 @@ static void sctp_cmd_send_asconf(struct sctp_association *asoc) * good place to start. */ int sctp_do_sm(struct net *net, enum sctp_event event_type, - sctp_subtype_t subtype, enum sctp_state state, + union sctp_subtype subtype, enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, gfp_t gfp) { @@ -1148,7 +1149,7 @@ int sctp_do_sm(struct net *net, enum sctp_event event_type, const sctp_sm_table_entry_t *state_fn; sctp_disposition_t status; int error = 0; - typedef const char *(printfn_t)(sctp_subtype_t); + typedef const char *(printfn_t)(union sctp_subtype); static printfn_t *table[] = { NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname, }; @@ -1176,7 +1177,8 @@ int sctp_do_sm(struct net *net, enum sctp_event event_type, /***************************************************************** * This the master state function side effect processing function. *****************************************************************/ -static int sctp_side_effects(enum sctp_event event_type, sctp_subtype_t subtype, +static int sctp_side_effects(enum sctp_event event_type, + union sctp_subtype subtype, enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association **asoc, @@ -1262,7 +1264,7 @@ bail: /* This is the side-effect interpreter. */ static int sctp_cmd_interpreter(enum sctp_event event_type, - sctp_subtype_t subtype, + union sctp_subtype subtype, enum sctp_state state, struct sctp_endpoint *ep, struct sctp_association *asoc, diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 5381697333df..ac6aaa046529 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -80,19 +80,19 @@ static void sctp_send_stale_cookie_err(struct net *net, static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands); static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands); static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands); static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk); @@ -116,7 +116,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands); @@ -124,7 +124,7 @@ static sctp_disposition_t sctp_sf_violation_paramlen( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, void *ext, sctp_cmd_seq_t *commands); @@ -132,7 +132,7 @@ static sctp_disposition_t sctp_sf_violation_ctsn( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands); @@ -140,7 +140,7 @@ static sctp_disposition_t sctp_sf_violation_chunk( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands); @@ -148,13 +148,13 @@ static enum sctp_ierror sctp_sf_authenticate( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, struct sctp_chunk *chunk); static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands); @@ -217,7 +217,7 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk, __u16 required_length) sctp_disposition_t sctp_sf_do_4_C(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -303,7 +303,7 @@ sctp_disposition_t sctp_sf_do_4_C(struct net *net, sctp_disposition_t sctp_sf_do_5_1B_init(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -497,7 +497,7 @@ nomem: sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -647,7 +647,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, void *arg, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; @@ -874,7 +874,7 @@ nomem: sctp_disposition_t sctp_sf_do_5_1E_ca(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, void *arg, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; @@ -951,7 +951,7 @@ nomem: /* Generate and sendout a heartbeat packet. */ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -977,7 +977,7 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, sctp_disposition_t sctp_sf_sendbeat_8_3(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -1025,7 +1025,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(struct net *net, sctp_disposition_t sctp_sf_send_reconf(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, void *arg, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_transport *transport = arg; @@ -1076,7 +1076,7 @@ sctp_disposition_t sctp_sf_send_reconf(struct net *net, sctp_disposition_t sctp_sf_beat_8_3(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -1151,7 +1151,7 @@ nomem: sctp_disposition_t sctp_sf_backbeat_8_3(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -1416,7 +1416,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg, *repl, *err_chunk; @@ -1627,7 +1627,7 @@ cleanup: sctp_disposition_t sctp_sf_do_5_2_1_siminit(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -1681,7 +1681,7 @@ sctp_disposition_t sctp_sf_do_5_2_1_siminit(struct net *net, sctp_disposition_t sctp_sf_do_5_2_2_dupinit(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -1704,7 +1704,7 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(struct net *net, sctp_disposition_t sctp_sf_do_5_2_3_initack(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { /* Per the above section, we'll discard the chunk if we have an @@ -2027,7 +2027,7 @@ nomem: sctp_disposition_t sctp_sf_do_5_2_4_dupcook(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2146,7 +2146,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2188,7 +2188,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( sctp_disposition_t sctp_sf_shutdown_sent_abort(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2239,7 +2239,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2266,7 +2266,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort( sctp_disposition_t sctp_sf_cookie_echoed_err(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2330,7 +2330,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(struct net *net, static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2452,7 +2452,7 @@ nomem: sctp_disposition_t sctp_sf_do_9_1_abort(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2489,7 +2489,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(struct net *net, static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2527,7 +2527,7 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, sctp_disposition_t sctp_sf_cookie_wait_abort(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2566,7 +2566,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(struct net *net, sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2581,7 +2581,7 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(struct net *net, sctp_disposition_t sctp_sf_cookie_echoed_abort(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2653,7 +2653,7 @@ static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2742,7 +2742,7 @@ out: sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2795,7 +2795,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, sctp_disposition_t sctp_sf_do_9_2_reshutack(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2859,7 +2859,7 @@ nomem: sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2915,7 +2915,7 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net, sctp_disposition_t sctp_sf_do_ecne(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -2972,7 +2972,7 @@ sctp_disposition_t sctp_sf_do_ecne(struct net *net, sctp_disposition_t sctp_sf_eat_data_6_2(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3092,7 +3092,7 @@ discard_noforce: sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3183,7 +3183,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net, sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3257,7 +3257,7 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net, static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3307,7 +3307,7 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, sctp_disposition_t sctp_sf_operr_notify(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3345,7 +3345,7 @@ sctp_disposition_t sctp_sf_operr_notify(struct net *net, sctp_disposition_t sctp_sf_do_9_2_final(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3428,7 +3428,7 @@ nomem: sctp_disposition_t sctp_sf_ootb(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3521,7 +3521,7 @@ sctp_disposition_t sctp_sf_ootb(struct net *net, static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3583,7 +3583,7 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, sctp_disposition_t sctp_sf_do_8_5_1_E_sa(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3608,7 +3608,7 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(struct net *net, sctp_disposition_t sctp_sf_do_asconf(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, void *arg, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; @@ -3725,7 +3725,8 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, void *arg, + const union sctp_subtype type, + void *arg, sctp_cmd_seq_t *commands) { struct sctp_chunk *asconf_ack = arg; @@ -3843,7 +3844,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, sctp_disposition_t sctp_sf_do_reconf(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, void *arg, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_paramhdr *err_param = NULL; @@ -3919,7 +3920,7 @@ sctp_disposition_t sctp_sf_do_reconf(struct net *net, sctp_disposition_t sctp_sf_eat_fwd_tsn(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -3990,7 +3991,7 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4082,7 +4083,7 @@ static enum sctp_ierror sctp_sf_authenticate( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, struct sctp_chunk *chunk) { struct sctp_authhdr *auth_hdr; @@ -4157,7 +4158,7 @@ nomem: sctp_disposition_t sctp_sf_eat_auth(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4254,7 +4255,7 @@ sctp_disposition_t sctp_sf_eat_auth(struct net *net, sctp_disposition_t sctp_sf_unk_chunk(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4334,7 +4335,7 @@ sctp_disposition_t sctp_sf_unk_chunk(struct net *net, sctp_disposition_t sctp_sf_discard_chunk(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4374,7 +4375,7 @@ sctp_disposition_t sctp_sf_discard_chunk(struct net *net, sctp_disposition_t sctp_sf_pdiscard(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4402,7 +4403,7 @@ sctp_disposition_t sctp_sf_pdiscard(struct net *net, sctp_disposition_t sctp_sf_violation(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4540,7 +4541,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4560,7 +4561,7 @@ static sctp_disposition_t sctp_sf_violation_paramlen( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, void *ext, sctp_cmd_seq_t *commands) { @@ -4603,7 +4604,7 @@ static sctp_disposition_t sctp_sf_violation_ctsn( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4623,7 +4624,7 @@ static sctp_disposition_t sctp_sf_violation_chunk( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4698,7 +4699,7 @@ static sctp_disposition_t sctp_sf_violation_chunk( sctp_disposition_t sctp_sf_do_prm_asoc(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4810,7 +4811,7 @@ nomem: sctp_disposition_t sctp_sf_do_prm_send(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4850,7 +4851,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4906,7 +4907,7 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4943,7 +4944,7 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( sctp_disposition_t sctp_sf_error_closed(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4957,7 +4958,7 @@ sctp_disposition_t sctp_sf_error_closed(struct net *net, sctp_disposition_t sctp_sf_error_shutdown(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -4984,7 +4985,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5019,7 +5020,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { /* There is a single T1 timer, so we should be able to use @@ -5046,7 +5047,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5095,7 +5096,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_abort( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5121,7 +5122,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_prm_abort( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5148,7 +5149,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_prm_abort( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5179,7 +5180,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5215,7 +5216,7 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5247,7 +5248,7 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat( sctp_disposition_t sctp_sf_do_prm_asconf(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5264,7 +5265,7 @@ sctp_disposition_t sctp_sf_do_prm_asconf(struct net *net, sctp_disposition_t sctp_sf_do_prm_reconf(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { struct sctp_chunk *chunk = arg; @@ -5282,7 +5283,7 @@ sctp_disposition_t sctp_sf_ignore_primitive( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5306,7 +5307,7 @@ sctp_disposition_t sctp_sf_do_no_pending_tsn( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5338,7 +5339,7 @@ sctp_disposition_t sctp_sf_do_9_2_start_shutdown( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5408,7 +5409,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5481,7 +5482,7 @@ nomem: sctp_disposition_t sctp_sf_ignore_other(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5509,7 +5510,7 @@ sctp_disposition_t sctp_sf_ignore_other(struct net *net, sctp_disposition_t sctp_sf_do_6_3_3_rtx(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5597,7 +5598,7 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(struct net *net, sctp_disposition_t sctp_sf_do_6_2_sack(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5628,7 +5629,7 @@ sctp_disposition_t sctp_sf_do_6_2_sack(struct net *net, sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5692,7 +5693,7 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net, sctp_disposition_t sctp_sf_t1_cookie_timer_expire(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5742,7 +5743,7 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(struct net *net, sctp_disposition_t sctp_sf_t2_timer_expire(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5813,7 +5814,7 @@ sctp_disposition_t sctp_sf_t4_timer_expire( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5884,7 +5885,7 @@ sctp_disposition_t sctp_sf_t4_timer_expire( sctp_disposition_t sctp_sf_t5_timer_expire(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5921,7 +5922,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5963,7 +5964,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire( sctp_disposition_t sctp_sf_not_impl(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -5981,7 +5982,7 @@ sctp_disposition_t sctp_sf_not_impl(struct net *net, sctp_disposition_t sctp_sf_bug(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { @@ -6002,7 +6003,7 @@ sctp_disposition_t sctp_sf_bug(struct net *net, sctp_disposition_t sctp_sf_timer_ignore(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, - const sctp_subtype_t type, + const union sctp_subtype type, void *arg, sctp_cmd_seq_t *commands) { diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index f7cdb7014244..d437f3801399 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -77,10 +77,11 @@ static const sctp_sm_table_entry_t bug = { rtn; \ }) -const sctp_sm_table_entry_t *sctp_sm_lookup_event(struct net *net, - enum sctp_event event_type, - enum sctp_state state, - sctp_subtype_t event_subtype) +const sctp_sm_table_entry_t *sctp_sm_lookup_event( + struct net *net, + enum sctp_event event_type, + enum sctp_state state, + union sctp_subtype event_subtype) { switch (event_type) { case SCTP_EVENT_T_CHUNK: -- cgit v1.2.3 From 2572ac53c46f58e500b9d8d0f99785666038c590 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:17 +0200 Subject: net: sched: make type an argument for ndo_setup_tc Since the type is always present, push it to be a separate argument to ndo_setup_tc. On the way, name the type enum and use it for arg type. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 6 +++--- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 7 ++++--- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 5 +++-- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 7 ++++--- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 7 ++++--- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 7 ++++--- .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 6 +++--- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 7 ++++--- drivers/net/ethernet/intel/i40e/i40e_main.c | 6 +++--- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 9 +++++---- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 6 +++--- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 8 ++++---- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 9 +++++---- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 10 +++++----- drivers/net/ethernet/netronome/nfp/bpf/main.c | 5 +++-- drivers/net/ethernet/netronome/nfp/flower/main.h | 3 ++- drivers/net/ethernet/netronome/nfp/flower/offload.c | 5 +++-- drivers/net/ethernet/netronome/nfp/nfp_app.h | 6 ++++-- drivers/net/ethernet/netronome/nfp/nfp_port.c | 7 ++++--- drivers/net/ethernet/netronome/nfp/nfp_port.h | 5 +++-- drivers/net/ethernet/sfc/efx.h | 5 +++-- drivers/net/ethernet/sfc/falcon/efx.h | 5 +++-- drivers/net/ethernet/sfc/falcon/tx.c | 7 ++++--- drivers/net/ethernet/sfc/tx.c | 7 ++++--- drivers/net/ethernet/ti/netcp_core.c | 7 ++++--- include/linux/netdevice.h | 9 +++++---- net/dsa/slave.c | 6 +++--- net/sched/cls_bpf.c | 4 ++-- net/sched/cls_flower.c | 14 ++++++-------- net/sched/cls_matchall.c | 10 ++++------ net/sched/cls_u32.c | 20 ++++++++------------ net/sched/sch_mqprio.c | 13 ++++++------- 32 files changed, 125 insertions(+), 113 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index ecef3ee87b17..6a6ea3bdd056 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1918,14 +1918,14 @@ static void xgbe_poll_controller(struct net_device *netdev) } #endif /* End CONFIG_NET_POLL_CONTROLLER */ -static int xgbe_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, +static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc_to_netdev) { struct xgbe_prv_data *pdata = netdev_priv(netdev); u8 tc; - if (tc_to_netdev->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc_to_netdev->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 67fe3d826566..4395d1cac86f 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4284,10 +4284,11 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) return 0; } -int __bnx2x_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index c26688d2f326..1ac4eb0d3413 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -486,8 +486,9 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev); /* setup_tc callback */ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc); -int __bnx2x_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc); int bnx2x_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index 156fb374522b..b98d9f33d9af 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7237,10 +7237,11 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) return 0; } -static int bnxt_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *ntc) +static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *ntc) { - if (ntc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index fdf220aa08d6..89d2b0cd9869 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2889,8 +2889,9 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate) return err; } -static int cxgb_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); @@ -2906,7 +2907,7 @@ static int cxgb_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, } if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) && - tc->type == TC_SETUP_CLSU32) { + type == TC_SETUP_CLSU32) { switch (tc->cls_u32->command) { case TC_CLSU32_NEW_KNODE: case TC_CLSU32_REPLACE_KNODE: diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index 550ea1ec7b6c..d86d766777c8 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -342,14 +342,15 @@ static void dpaa_get_stats64(struct net_device *net_dev, } } -static int dpaa_setup_tc(struct net_device *net_dev, u32 handle, - u32 chain_index, __be16 proto, struct tc_to_netdev *tc) +static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { struct dpaa_priv *priv = netdev_priv(net_dev); u8 num_tc; int i; - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index ad9481c7ceae..6bb1e35336cc 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1219,11 +1219,11 @@ static int hns3_setup_tc(struct net_device *netdev, u8 tc) return 0; } -static int hns3_nic_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 protocol, +static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc) { - if (handle != TC_H_ROOT || tc->type != TC_SETUP_MQPRIO) + if (handle != TC_H_ROOT || type != TC_SETUP_MQPRIO) return -EINVAL; return hns3_setup_tc(dev, tc->mqprio->num_tc); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 5e37387c7082..b30190639e78 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1265,10 +1265,11 @@ err_queueing_scheme: return err; } -static int __fm10k_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int __fm10k_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 4104944ea367..7d47a718f922 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5656,11 +5656,11 @@ exit: return ret; } -static int __i40e_setup_tc(struct net_device *netdev, u32 handle, - u32 chain_index, __be16 proto, +static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 091fcc7e6e43..d39db9711df6 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9226,8 +9226,9 @@ free_jump: return err; } -static int __ixgbe_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { struct ixgbe_adapter *adapter = netdev_priv(dev); @@ -9235,7 +9236,7 @@ static int __ixgbe_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, return -EOPNOTSUPP; if (TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS) && - tc->type == TC_SETUP_CLSU32) { + type == TC_SETUP_CLSU32) { switch (tc->cls_u32->command) { case TC_CLSU32_NEW_KNODE: case TC_CLSU32_REPLACE_KNODE: @@ -9255,7 +9256,7 @@ static int __ixgbe_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, } } - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 3a291fc1780a..5c33550765ed 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -130,11 +130,11 @@ out: return err; } -static int __mlx4_en_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, +static int __mlx4_en_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; if (tc->mqprio->num_tc && tc->mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 57f31fa478ce..4052e225f1dc 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3027,8 +3027,8 @@ out: return err; } -static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, +static int mlx5e_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct mlx5e_priv *priv = netdev_priv(dev); @@ -3039,7 +3039,7 @@ static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, if (chain_index) return -EOPNOTSUPP; - switch (tc->type) { + switch (type) { case TC_SETUP_CLSFLOWER: switch (tc->cls_flower->command) { case TC_CLSFLOWER_REPLACE: @@ -3054,7 +3054,7 @@ static int mlx5e_ndo_setup_tc(struct net_device *dev, u32 handle, } mqprio: - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index 45e60be9c277..d44049ed5371 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -651,7 +651,8 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, return 0; } -static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, u32 handle, +static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, + enum tc_setup_type type, u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { @@ -664,15 +665,15 @@ static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, u32 handle, struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct net_device *uplink_dev = mlx5_eswitch_get_uplink_netdev(esw); - return uplink_dev->netdev_ops->ndo_setup_tc(uplink_dev, handle, - chain_index, + return uplink_dev->netdev_ops->ndo_setup_tc(uplink_dev, type, + handle, chain_index, proto, tc); } if (chain_index) return -EOPNOTSUPP; - switch (tc->type) { + switch (type) { case TC_SETUP_CLSFLOWER: switch (tc->cls_flower->command) { case TC_CLSFLOWER_REPLACE: diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 66d511d45c25..155424266cbf 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1693,8 +1693,8 @@ static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, kfree(mall_tc_entry); } -static int mlxsw_sp_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 proto, +static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); @@ -1703,7 +1703,7 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, u32 handle, if (chain_index) return -EOPNOTSUPP; - switch (tc->type) { + switch (type) { case TC_SETUP_MATCHALL: switch (tc->cls_mall->command) { case TC_CLSMATCHALL_REPLACE: @@ -1733,9 +1733,9 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, u32 handle, default: return -EOPNOTSUPP; } + default: + return -EOPNOTSUPP; } - - return -EOPNOTSUPP; } static const struct net_device_ops mlxsw_sp_port_netdev_ops = { diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index afbdf5fd4e4f..788880808a6e 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -121,7 +121,8 @@ static void nfp_bpf_vnic_clean(struct nfp_app *app, struct nfp_net *nn) } static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc) + enum tc_setup_type type, u32 handle, __be16 proto, + struct tc_to_netdev *tc) { struct nfp_net *nn = netdev_priv(netdev); @@ -130,7 +131,7 @@ static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, if (proto != htons(ETH_P_ALL)) return -EOPNOTSUPP; - if (tc->type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) { + if (type == TC_SETUP_CLSBPF && nfp_net_ebpf_capable(nn)) { if (!nn->dp.bpf_offload_xdp) return nfp_net_bpf_offload(nn, tc->cls_bpf); else diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 9e64c048e83f..314e6e8ba649 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -135,7 +135,8 @@ int nfp_flower_metadata_init(struct nfp_app *app); void nfp_flower_metadata_cleanup(struct nfp_app *app); int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc); + enum tc_setup_type type, u32 handle, __be16 proto, + struct tc_to_netdev *tc); int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow, struct nfp_fl_key_ls *key_ls, struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 4ad10bd5e139..d045cf8c140a 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -385,7 +385,8 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, } int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc) + enum tc_setup_type type, u32 handle, __be16 proto, + struct tc_to_netdev *tc) { if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) return -EOPNOTSUPP; @@ -393,7 +394,7 @@ int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, if (!eth_proto_is_802_3(proto)) return -EOPNOTSUPP; - if (tc->type != TC_SETUP_CLSFLOWER) + if (type != TC_SETUP_CLSFLOWER) return -EINVAL; return nfp_flower_repr_offload(app, netdev, tc->cls_flower); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index 5d714e10d9a9..b3b03bb9d907 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -109,7 +109,8 @@ struct nfp_app_type { void (*ctrl_msg_rx)(struct nfp_app *app, struct sk_buff *skb); int (*setup_tc)(struct nfp_app *app, struct net_device *netdev, - u32 handle, __be16 proto, struct tc_to_netdev *tc); + enum tc_setup_type type, u32 handle, __be16 proto, + struct tc_to_netdev *tc); bool (*tc_busy)(struct nfp_app *app, struct nfp_net *nn); int (*xdp_offload)(struct nfp_app *app, struct nfp_net *nn, struct bpf_prog *prog); @@ -238,12 +239,13 @@ static inline bool nfp_app_tc_busy(struct nfp_app *app, struct nfp_net *nn) static inline int nfp_app_setup_tc(struct nfp_app *app, struct net_device *netdev, + enum tc_setup_type type, u32 handle, __be16 proto, struct tc_to_netdev *tc) { if (!app || !app->type->setup_tc) return -EOPNOTSUPP; - return app->type->setup_tc(app, netdev, handle, proto, tc); + return app->type->setup_tc(app, netdev, type, handle, proto, tc); } static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index d16a7b78ba9b..9d776f982352 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -88,8 +88,9 @@ const struct switchdev_ops nfp_port_switchdev_ops = { .switchdev_port_attr_get = nfp_port_attr_get, }; -int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { struct nfp_port *port; @@ -100,7 +101,7 @@ int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, if (!port) return -EOPNOTSUPP; - return nfp_app_setup_tc(port->app, netdev, handle, proto, tc); + return nfp_app_setup_tc(port->app, netdev, type, handle, proto, tc); } struct nfp_port * diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 56c76926c82a..239c5401000c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -109,8 +109,9 @@ struct nfp_port { extern const struct switchdev_ops nfp_port_switchdev_ops; -int nfp_port_setup_tc(struct net_device *netdev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc); struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); struct nfp_port * diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index fcea9371ab7f..e41a7179bc05 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -32,8 +32,9 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); -int efx_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc); unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); extern unsigned int efx_piobuf_size; extern bool efx_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/efx.h b/drivers/net/ethernet/sfc/falcon/efx.h index e5a7a40cc8b6..f3bc67ec1f30 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.h +++ b/drivers/net/ethernet/sfc/falcon/efx.h @@ -32,8 +32,9 @@ netdev_tx_t ef4_hard_start_xmit(struct sk_buff *skb, struct net_device *net_dev); netdev_tx_t ef4_enqueue_skb(struct ef4_tx_queue *tx_queue, struct sk_buff *skb); void ef4_xmit_done(struct ef4_tx_queue *tx_queue, unsigned int index); -int ef4_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc); +int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc); unsigned int ef4_tx_max_skb_descs(struct ef4_nic *efx); extern bool ef4_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index f1520a404ac6..6c4752694c1f 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -425,8 +425,9 @@ void ef4_init_tx_queue_core_txq(struct ef4_tx_queue *tx_queue) efx->n_tx_channels : 0)); } -int ef4_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *ntc) +int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *ntc) { struct ef4_nic *efx = netdev_priv(net_dev); struct ef4_channel *channel; @@ -434,7 +435,7 @@ int ef4_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, unsigned tc, num_tc; int rc; - if (ntc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; num_tc = ntc->mqprio->num_tc; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 02d41eb4a8e9..0c08c10d751c 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -653,8 +653,9 @@ void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue) efx->n_tx_channels : 0)); } -int efx_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *ntc) +int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *ntc) { struct efx_nic *efx = netdev_priv(net_dev); struct efx_channel *channel; @@ -662,7 +663,7 @@ int efx_setup_tc(struct net_device *net_dev, u32 handle, u32 chain_index, unsigned tc, num_tc; int rc; - if (ntc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; num_tc = ntc->mqprio->num_tc; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 9d52c3a78621..cb21742f6177 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1877,8 +1877,9 @@ static u16 netcp_select_queue(struct net_device *dev, struct sk_buff *skb, return 0; } -static int netcp_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) +static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 proto, + struct tc_to_netdev *tc) { u8 num_tc; int i; @@ -1886,7 +1887,7 @@ static int netcp_setup_tc(struct net_device *dev, u32 handle, u32 chain_index, /* setup tc must be called under rtnl lock */ ASSERT_RTNL(); - if (tc->type != TC_SETUP_MQPRIO) + if (type != TC_SETUP_MQPRIO) return -EINVAL; tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 3a3cdc1b1f31..e4238e540544 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -774,7 +774,7 @@ typedef u16 (*select_queue_fallback_t)(struct net_device *dev, /* These structures hold the attributes of qdisc and classifiers * that are being passed to the netdevice through the setup_tc op. */ -enum { +enum tc_setup_type { TC_SETUP_MQPRIO, TC_SETUP_CLSU32, TC_SETUP_CLSFLOWER, @@ -785,7 +785,6 @@ enum { struct tc_cls_u32_offload; struct tc_to_netdev { - unsigned int type; union { struct tc_cls_u32_offload *cls_u32; struct tc_cls_flower_offload *cls_flower; @@ -978,8 +977,9 @@ struct xfrmdev_ops { * with PF and querying it may introduce a theoretical security risk. * int (*ndo_set_vf_rss_query_en)(struct net_device *dev, int vf, bool setting); * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb); - * int (*ndo_setup_tc)(struct net_device *dev, u32 handle, u32 chain_index, - * __be16 protocol, struct tc_to_netdev *tc); + * int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, + * u32 handle, u32 chain_index, __be16 protocol, + * struct tc_to_netdev *tc); * Called to setup any 'tc' scheduler, classifier or action on @dev. * This is always called from the stack with the rtnl lock held and netif * tx queues stopped. This allows the netdevice to perform queue @@ -1227,6 +1227,7 @@ struct net_device_ops { struct net_device *dev, int vf, bool setting); int (*ndo_setup_tc)(struct net_device *dev, + enum tc_setup_type type, u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 75c5c5808220..b4b63c20ec80 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -863,8 +863,8 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev, kfree(mall_tc_entry); } -static int dsa_slave_setup_tc(struct net_device *dev, u32 handle, - u32 chain_index, __be16 protocol, +static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc) { bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); @@ -872,7 +872,7 @@ static int dsa_slave_setup_tc(struct net_device *dev, u32 handle, if (chain_index) return -EOPNOTSUPP; - switch (tc->type) { + switch (type) { case TC_SETUP_MATCHALL: switch (tc->cls_mall->command) { case TC_CLSMATCHALL_REPLACE: diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index cf248c3137ad..e2bf2753173d 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -151,7 +151,6 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, struct tc_to_netdev offload; int err; - offload.type = TC_SETUP_CLSBPF; offload.cls_bpf = &bpf_offload; bpf_offload.command = cmd; @@ -161,7 +160,8 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, bpf_offload.exts_integrated = prog->exts_integrated; bpf_offload.gen_flags = prog->gen_flags; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, + tp->q->handle, tp->chain->index, tp->protocol, &offload); diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 7ab524fc43f9..ddeed17d2024 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -236,11 +236,10 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) offload.prio = tp->prio; offload.cookie = (unsigned long)f; - tc->type = TC_SETUP_CLSFLOWER; tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->chain->index, - tp->protocol, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tp->q->handle, + tp->chain->index, tp->protocol, tc); } static int fl_hw_replace_filter(struct tcf_proto *tp, @@ -273,11 +272,11 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, offload.key = &f->mkey; offload.exts = &f->exts; - tc->type = TC_SETUP_CLSFLOWER; tc->cls_flower = &offload; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, - tp->chain->index, tp->protocol, tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, + tp->q->handle, tp->chain->index, + tp->protocol, tc); if (!err) f->flags |= TCA_CLS_FLAGS_IN_HW; @@ -300,10 +299,9 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) offload.cookie = (unsigned long)f; offload.exts = &f->exts; - tc->type = TC_SETUP_CLSFLOWER; tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, + dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, tp->q->handle, tp->chain->index, tp->protocol, tc); } diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index f35177b48373..6ffe0b82ab83 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -58,14 +58,13 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, struct tc_cls_matchall_offload mall_offload = {0}; int err; - offload.type = TC_SETUP_MATCHALL; offload.cls_mall = &mall_offload; offload.cls_mall->command = TC_CLSMATCHALL_REPLACE; offload.cls_mall->exts = &head->exts; offload.cls_mall->cookie = cookie; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, - tp->chain->index, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MATCHALL, + tp->q->handle, tp->chain->index, tp->protocol, &offload); if (!err) head->flags |= TCA_CLS_FLAGS_IN_HW; @@ -81,14 +80,13 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, struct tc_to_netdev offload; struct tc_cls_matchall_offload mall_offload = {0}; - offload.type = TC_SETUP_MATCHALL; offload.cls_mall = &mall_offload; offload.cls_mall->command = TC_CLSMATCHALL_DESTROY; offload.cls_mall->exts = NULL; offload.cls_mall->cookie = cookie; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, tp->chain->index, - tp->protocol, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MATCHALL, tp->q->handle, + tp->chain->index, tp->protocol, &offload); } static void mall_destroy(struct tcf_proto *tp) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 9fd243799fe7..d1bae4cc749f 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -434,15 +434,14 @@ static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle) struct tc_cls_u32_offload u32_offload = {0}; struct tc_to_netdev offload; - offload.type = TC_SETUP_CLSU32; offload.cls_u32 = &u32_offload; if (tc_should_offload(dev, tp, 0)) { offload.cls_u32->command = TC_CLSU32_DELETE_KNODE; offload.cls_u32->knode.handle = handle; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, - tp->chain->index, tp->protocol, - &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, + tp->q->handle, tp->chain->index, + tp->protocol, &offload); } } @@ -457,7 +456,6 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, if (!tc_should_offload(dev, tp, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; - offload.type = TC_SETUP_CLSU32; offload.cls_u32 = &u32_offload; offload.cls_u32->command = TC_CLSU32_NEW_HNODE; @@ -465,7 +463,7 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, offload.cls_u32->hnode.handle = h->handle; offload.cls_u32->hnode.prio = h->prio; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, tp->q->handle, tp->chain->index, tp->protocol, &offload); if (tc_skip_sw(flags)) @@ -480,7 +478,6 @@ static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) struct tc_cls_u32_offload u32_offload = {0}; struct tc_to_netdev offload; - offload.type = TC_SETUP_CLSU32; offload.cls_u32 = &u32_offload; if (tc_should_offload(dev, tp, 0)) { @@ -489,9 +486,9 @@ static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) offload.cls_u32->hnode.handle = h->handle; offload.cls_u32->hnode.prio = h->prio; - dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, - tp->chain->index, tp->protocol, - &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, + tp->q->handle, tp->chain->index, + tp->protocol, &offload); } } @@ -503,7 +500,6 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, struct tc_to_netdev offload; int err; - offload.type = TC_SETUP_CLSU32; offload.cls_u32 = &u32_offload; if (!tc_should_offload(dev, tp, flags)) @@ -524,7 +520,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, if (n->ht_down) offload.cls_u32->knode.link_handle = n->ht_down->handle; - err = dev->netdev_ops->ndo_setup_tc(dev, tp->q->handle, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, tp->q->handle, tp->chain->index, tp->protocol, &offload); diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index e0c02725cd48..329610ce4dfe 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -40,10 +40,10 @@ static void mqprio_destroy(struct Qdisc *sch) if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) { struct tc_mqprio_qopt offload = { 0 }; - struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO, - { .mqprio = &offload } }; + struct tc_to_netdev tc = { { .mqprio = &offload } }; - dev->netdev_ops->ndo_setup_tc(dev, sch->handle, 0, 0, &tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, + sch->handle, 0, 0, &tc); } else { netdev_set_num_tc(dev, 0); } @@ -149,11 +149,10 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) */ if (qopt->hw) { struct tc_mqprio_qopt offload = *qopt; - struct tc_to_netdev tc = { .type = TC_SETUP_MQPRIO, - { .mqprio = &offload } }; + struct tc_to_netdev tc = { { .mqprio = &offload } }; - err = dev->netdev_ops->ndo_setup_tc(dev, sch->handle, - 0, 0, &tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, + sch->handle, 0, 0, &tc); if (err) return err; -- cgit v1.2.3 From ade9b6588420b335851951702ab975c975b0c1b2 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:18 +0200 Subject: net: sched: rename TC_SETUP_MATCHALL to TC_SETUP_CLSMATCHALL In order to be aligned with the rest of the types, rename TC_SETUP_MATCHALL to TC_SETUP_CLSMATCHALL. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 2 +- include/linux/netdevice.h | 2 +- net/dsa/slave.c | 2 +- net/sched/cls_matchall.c | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 155424266cbf..6438c38e7a68 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1704,7 +1704,7 @@ static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, return -EOPNOTSUPP; switch (type) { - case TC_SETUP_MATCHALL: + case TC_SETUP_CLSMATCHALL: switch (tc->cls_mall->command) { case TC_CLSMATCHALL_REPLACE: return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index e4238e540544..f8051a36f900 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -778,7 +778,7 @@ enum tc_setup_type { TC_SETUP_MQPRIO, TC_SETUP_CLSU32, TC_SETUP_CLSFLOWER, - TC_SETUP_MATCHALL, + TC_SETUP_CLSMATCHALL, TC_SETUP_CLSBPF, }; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index b4b63c20ec80..453f6ddcd023 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -873,7 +873,7 @@ static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, return -EOPNOTSUPP; switch (type) { - case TC_SETUP_MATCHALL: + case TC_SETUP_CLSMATCHALL: switch (tc->cls_mall->command) { case TC_CLSMATCHALL_REPLACE: return dsa_slave_add_cls_matchall(dev, protocol, diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 6ffe0b82ab83..a8853ada22f6 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -63,7 +63,7 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, offload.cls_mall->exts = &head->exts; offload.cls_mall->cookie = cookie; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MATCHALL, + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, tp->q->handle, tp->chain->index, tp->protocol, &offload); if (!err) @@ -85,7 +85,7 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, offload.cls_mall->exts = NULL; offload.cls_mall->cookie = cookie; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MATCHALL, tp->q->handle, + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, tp->q->handle, tp->chain->index, tp->protocol, &offload); } -- cgit v1.2.3 From 3e0e82664322290a59189f7c2bcb39b0de932505 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:19 +0200 Subject: net: sched: make egress_dev flag part of flower offload struct Since this is specific to flower now, make it part of the flower offload struct. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 2 +- include/linux/netdevice.h | 1 - include/net/pkt_cls.h | 1 + net/sched/cls_flower.c | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index d44049ed5371..0e6bab182071 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -661,7 +661,7 @@ static int mlx5e_rep_ndo_setup_tc(struct net_device *dev, if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS)) return -EOPNOTSUPP; - if (tc->egress_dev) { + if (type == TC_SETUP_CLSFLOWER && tc->cls_flower->egress_dev) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; struct net_device *uplink_dev = mlx5_eswitch_get_uplink_netdev(esw); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index f8051a36f900..bd49dbaee84e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -792,7 +792,6 @@ struct tc_to_netdev { struct tc_cls_bpf_offload *cls_bpf; struct tc_mqprio_qopt *mqprio; }; - bool egress_dev; }; /* These structures hold the attributes of xdp state that are being passed diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index e0c54f111467..8213acdfdf5a 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -504,6 +504,7 @@ struct tc_cls_flower_offload { struct fl_flow_key *mask; struct fl_flow_key *key; struct tcf_exts *exts; + bool egress_dev; }; enum tc_matchall_command { diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index ddeed17d2024..52deeed2b7f5 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -259,7 +259,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, return tc_skip_sw(f->flags) ? -EINVAL : 0; } dev = f->hw_dev; - tc->egress_dev = true; + offload.egress_dev = true; } else { f->hw_dev = dev; } -- cgit v1.2.3 From 3fbae382f7dd81c4e43b76169e08cc0d440e760b Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:26 +0200 Subject: dsa: push cls_matchall setup_tc processing into a separate function Let dsa_slave_setup_tc be a splitter for specific setup_tc types and push out cls_matchall specific code into a separate function. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/slave.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 453f6ddcd023..e76d576b941d 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -863,26 +863,35 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev, kfree(mall_tc_entry); } -static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 protocol, - struct tc_to_netdev *tc) +static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, + u32 handle, u32 chain_index, + __be16 protocol, + struct tc_cls_matchall_offload *cls) { bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); if (chain_index) return -EOPNOTSUPP; + switch (cls->command) { + case TC_CLSMATCHALL_REPLACE: + return dsa_slave_add_cls_matchall(dev, protocol, cls, ingress); + case TC_CLSMATCHALL_DESTROY: + dsa_slave_del_cls_matchall(dev, cls); + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, + u32 handle, u32 chain_index, __be16 protocol, + struct tc_to_netdev *tc) +{ switch (type) { case TC_SETUP_CLSMATCHALL: - switch (tc->cls_mall->command) { - case TC_CLSMATCHALL_REPLACE: - return dsa_slave_add_cls_matchall(dev, protocol, - tc->cls_mall, - ingress); - case TC_CLSMATCHALL_DESTROY: - dsa_slave_del_cls_matchall(dev, tc->cls_mall); - return 0; - } + return dsa_slave_setup_tc_cls_matchall(dev, handle, chain_index, + protocol, tc->cls_mall); default: return -EOPNOTSUPP; } -- cgit v1.2.3 From 5fd9fc4e207dba0c05cafe78417952b4c4ca02dc Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:29 +0200 Subject: net: sched: push cls related args into cls_common structure As ndo_setup_tc is generic offload op for whole tc subsystem, does not really make sense to have cls-specific args. So move them under cls_common structurure which is embedded in all cls structs. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 1 - drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 1 - drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 1 - drivers/net/ethernet/broadcom/bnxt/bnxt.c | 1 - drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 14 +++++------- drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c | 7 +++--- drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h | 6 ++---- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 1 - .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 1 - drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 1 - drivers/net/ethernet/intel/i40e/i40e_main.c | 1 - drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 17 ++++++--------- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 1 - drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 11 ++++------ drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 15 +++++-------- drivers/net/ethernet/mellanox/mlx5/core/en_tc.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_tc.h | 2 +- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 25 ++++++++-------------- drivers/net/ethernet/mellanox/mlxsw/spectrum.h | 2 +- .../net/ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +- drivers/net/ethernet/netronome/nfp/bpf/main.c | 10 +++++---- drivers/net/ethernet/netronome/nfp/flower/main.h | 3 +-- .../net/ethernet/netronome/nfp/flower/offload.c | 12 ++++++----- drivers/net/ethernet/netronome/nfp/nfp_app.h | 6 ++---- drivers/net/ethernet/netronome/nfp/nfp_port.c | 6 +----- drivers/net/ethernet/netronome/nfp/nfp_port.h | 1 - drivers/net/ethernet/sfc/efx.h | 1 - drivers/net/ethernet/sfc/falcon/efx.h | 1 - drivers/net/ethernet/sfc/falcon/tx.c | 1 - drivers/net/ethernet/sfc/tx.c | 1 - drivers/net/ethernet/ti/netcp_core.c | 1 - include/linux/netdevice.h | 3 --- include/net/pkt_cls.h | 19 ++++++++++++++++ net/dsa/slave.c | 14 +++++------- net/sched/cls_bpf.c | 7 ++---- net/sched/cls_flower.c | 13 ++++++----- net/sched/cls_matchall.c | 8 +++---- net/sched/cls_u32.c | 20 +++++++---------- net/sched/sch_mqprio.c | 6 ++---- 39 files changed, 101 insertions(+), 144 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 6a6ea3bdd056..bbb7bfe0be7f 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1919,7 +1919,6 @@ static void xgbe_poll_controller(struct net_device *netdev) #endif /* End CONFIG_NET_POLL_CONTROLLER */ static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc_to_netdev) { struct xgbe_prv_data *pdata = netdev_priv(netdev); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 4395d1cac86f..257cf4be0162 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4285,7 +4285,6 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) } int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 1ac4eb0d3413..04eb95043023 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -487,7 +487,6 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev); /* setup_tc callback */ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc); int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc); int bnx2x_get_vf_config(struct net_device *dev, int vf, diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index b98d9f33d9af..1545b88c545d 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7238,7 +7238,6 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) } static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *ntc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 651229070113..13199317c8e0 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2890,27 +2890,24 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate) } static int cxgb_setup_tc_cls_u32(struct net_device *dev, - enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_cls_u32_offload *cls_u32) { - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - chain_index) + if (TC_H_MAJ(cls_u32->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_u32->common.chain_index) return -EOPNOTSUPP; switch (cls_u32->command) { case TC_CLSU32_NEW_KNODE: case TC_CLSU32_REPLACE_KNODE: - return cxgb4_config_knode(dev, proto, cls_u32); + return cxgb4_config_knode(dev, cls_u32); case TC_CLSU32_DELETE_KNODE: - return cxgb4_delete_knode(dev, proto, cls_u32); + return cxgb4_delete_knode(dev, cls_u32); default: return -EOPNOTSUPP; } } static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct port_info *pi = netdev2pinfo(dev); @@ -2925,8 +2922,7 @@ static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, switch (type) { case TC_SETUP_CLSU32: - return cxgb_setup_tc_cls_u32(dev, type, handle, chain_index, - proto, tc->cls_u32); + return cxgb_setup_tc_cls_u32(dev, tc->cls_u32); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c index 6f734c52ef25..48970ba08bdc 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.c @@ -146,11 +146,11 @@ static int fill_action_fields(struct adapter *adap, return 0; } -int cxgb4_config_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls) +int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) { const struct cxgb4_match_field *start, *link_start = NULL; struct adapter *adapter = netdev2adap(dev); + __be16 protocol = cls->common.protocol; struct ch_filter_specification fs; struct cxgb4_tc_u32_table *t; struct cxgb4_link *link; @@ -338,8 +338,7 @@ out: return ret; } -int cxgb4_delete_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls) +int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls) { struct adapter *adapter = netdev2adap(dev); unsigned int filter_id, max_tids, i, j; diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h index 021261a41c13..70a07b7cca56 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_tc_u32.h @@ -44,10 +44,8 @@ static inline bool can_tc_u32_offload(struct net_device *dev) return (dev->features & NETIF_F_HW_TC) && adap->tc_u32 ? true : false; } -int cxgb4_config_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls); -int cxgb4_delete_knode(struct net_device *dev, __be16 protocol, - struct tc_cls_u32_offload *cls); +int cxgb4_config_knode(struct net_device *dev, struct tc_cls_u32_offload *cls); +int cxgb4_delete_knode(struct net_device *dev, struct tc_cls_u32_offload *cls); void cxgb4_cleanup_tc_u32(struct adapter *adapter); struct cxgb4_tc_u32_table *cxgb4_init_tc_u32(struct adapter *adap); diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index d86d766777c8..3827608cec29 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -343,7 +343,6 @@ static void dpaa_get_stats64(struct net_device *net_dev, } static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct dpaa_priv *priv = netdev_priv(net_dev); diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index ef5795923b0c..dc64d751db24 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1220,7 +1220,6 @@ static int hns3_setup_tc(struct net_device *netdev, u8 tc) } static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index b30190639e78..71004b8eff95 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1266,7 +1266,6 @@ err_queueing_scheme: } static int __fm10k_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 7d47a718f922..97d8bb2e8320 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5657,7 +5657,6 @@ exit: } static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 35db198199b0..0a350314d76b 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -8851,7 +8851,6 @@ static int ixgbe_delete_clsu32(struct ixgbe_adapter *adapter, } static int ixgbe_configure_clsu32_add_hnode(struct ixgbe_adapter *adapter, - __be16 protocol, struct tc_cls_u32_offload *cls) { u32 uhtid = TC_U32_USERHTID(cls->hnode.handle); @@ -9037,9 +9036,9 @@ static int ixgbe_clsu32_build_input(struct ixgbe_fdir_filter *input, } static int ixgbe_configure_clsu32(struct ixgbe_adapter *adapter, - __be16 protocol, struct tc_cls_u32_offload *cls) { + __be16 protocol = cls->common.protocol; u32 loc = cls->knode.handle & 0xfffff; struct ixgbe_hw *hw = &adapter->hw; struct ixgbe_mat_field *field_ptr; @@ -9227,25 +9226,23 @@ free_jump: } static int ixgbe_setup_tc_cls_u32(struct net_device *dev, - u32 handle, u32 chain_index, __be16 proto, struct tc_cls_u32_offload *cls_u32) { struct ixgbe_adapter *adapter = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - chain_index) + if (TC_H_MAJ(cls_u32->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_u32->common.chain_index) return -EOPNOTSUPP; switch (cls_u32->command) { case TC_CLSU32_NEW_KNODE: case TC_CLSU32_REPLACE_KNODE: - return ixgbe_configure_clsu32(adapter, proto, cls_u32); + return ixgbe_configure_clsu32(adapter, cls_u32); case TC_CLSU32_DELETE_KNODE: return ixgbe_delete_clsu32(adapter, cls_u32); case TC_CLSU32_NEW_HNODE: case TC_CLSU32_REPLACE_HNODE: - return ixgbe_configure_clsu32_add_hnode(adapter, proto, - cls_u32); + return ixgbe_configure_clsu32_add_hnode(adapter, cls_u32); case TC_CLSU32_DELETE_HNODE: return ixgbe_configure_clsu32_del_hnode(adapter, cls_u32); default: @@ -9261,13 +9258,11 @@ static int ixgbe_setup_tc_mqprio(struct net_device *dev, } static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { switch (type) { case TC_SETUP_CLSU32: - return ixgbe_setup_tc_cls_u32(dev, handle, chain_index, proto, - tc->cls_u32); + return ixgbe_setup_tc_cls_u32(dev, tc->cls_u32); case TC_SETUP_MQPRIO: return ixgbe_setup_tc_mqprio(dev, tc->mqprio); default: diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 5c33550765ed..e81083e25ba6 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -131,7 +131,6 @@ out: } static int __mlx4_en_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { if (type != TC_SETUP_MQPRIO) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index adf35da74a85..15f2a942962a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3032,18 +3032,17 @@ out: } static int mlx5e_setup_tc_cls_flower(struct net_device *dev, - u32 handle, u32 chain_index, __be16 proto, struct tc_cls_flower_offload *cls_flower) { struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - chain_index) + if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_flower->common.chain_index) return -EOPNOTSUPP; switch (cls_flower->command) { case TC_CLSFLOWER_REPLACE: - return mlx5e_configure_flower(priv, proto, cls_flower); + return mlx5e_configure_flower(priv, cls_flower); case TC_CLSFLOWER_DESTROY: return mlx5e_delete_flower(priv, cls_flower); case TC_CLSFLOWER_STATS: @@ -3054,13 +3053,11 @@ static int mlx5e_setup_tc_cls_flower(struct net_device *dev, } static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { switch (type) { case TC_SETUP_CLSFLOWER: - return mlx5e_setup_tc_cls_flower(dev, handle, chain_index, - proto, tc->cls_flower); + return mlx5e_setup_tc_cls_flower(dev, tc->cls_flower); case TC_SETUP_MQPRIO: return mlx5e_setup_tc_mqprio(dev, tc->mqprio); default: diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index e6cc642f6d8c..e5cf2e7ae052 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -652,15 +652,13 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, } static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, - u32 handle, u32 chain_index, - __be16 proto, struct tc_to_netdev *tc) { struct tc_cls_flower_offload *cls_flower = tc->cls_flower; struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - chain_index) + if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_flower->common.chain_index) return -EOPNOTSUPP; if (cls_flower->egress_dev) { @@ -668,13 +666,12 @@ static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, dev = mlx5_eswitch_get_uplink_netdev(esw); return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, - handle, chain_index, - proto, tc); + tc); } switch (cls_flower->command) { case TC_CLSFLOWER_REPLACE: - return mlx5e_configure_flower(priv, proto, cls_flower); + return mlx5e_configure_flower(priv, cls_flower); case TC_CLSFLOWER_DESTROY: return mlx5e_delete_flower(priv, cls_flower); case TC_CLSFLOWER_STATS: @@ -685,13 +682,11 @@ static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, } static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { switch (type) { case TC_SETUP_CLSFLOWER: - return mlx5e_rep_setup_tc_cls_flower(dev, handle, chain_index, - proto, tc); + return mlx5e_rep_setup_tc_cls_flower(dev, tc); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 78f50d9f621d..3b10d3df7627 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1939,7 +1939,7 @@ static int parse_tc_fdb_actions(struct mlx5e_priv *priv, struct tcf_exts *exts, return err; } -int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, +int mlx5e_configure_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f) { struct mlx5_eswitch *esw = priv->mdev->priv.eswitch; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index ecbe30d808ae..5a0f4a487855 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -38,7 +38,7 @@ int mlx5e_tc_init(struct mlx5e_priv *priv); void mlx5e_tc_cleanup(struct mlx5e_priv *priv); -int mlx5e_configure_flower(struct mlx5e_priv *priv, __be16 protocol, +int mlx5e_configure_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f); int mlx5e_delete_flower(struct mlx5e_priv *priv, struct tc_cls_flower_offload *f); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index f333d086932d..1ca3204f5543 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1617,11 +1617,11 @@ mlxsw_sp_port_del_cls_matchall_sample(struct mlxsw_sp_port *mlxsw_sp_port) } static int mlxsw_sp_port_add_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, - __be16 protocol, struct tc_cls_matchall_offload *f, bool ingress) { struct mlxsw_sp_port_mall_tc_entry *mall_tc_entry; + __be16 protocol = f->common.protocol; const struct tc_action *a; LIST_HEAD(actions); int err; @@ -1694,18 +1694,16 @@ static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, - u32 handle, u32 chain_index, - __be16 proto, struct tc_cls_matchall_offload *f) { - bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress = TC_H_MAJ(f->common.handle) == TC_H_MAJ(TC_H_INGRESS); - if (chain_index) + if (f->common.chain_index) return -EOPNOTSUPP; switch (f->command) { case TC_CLSMATCHALL_REPLACE: - return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, proto, f, + return mlxsw_sp_port_add_cls_matchall(mlxsw_sp_port, f, ingress); case TC_CLSMATCHALL_DESTROY: mlxsw_sp_port_del_cls_matchall(mlxsw_sp_port, f); @@ -1717,18 +1715,16 @@ static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, static int mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, - u32 handle, u32 chain_index, __be16 proto, struct tc_cls_flower_offload *f) { - bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress = TC_H_MAJ(f->common.handle) == TC_H_MAJ(TC_H_INGRESS); - if (chain_index) + if (f->common.chain_index) return -EOPNOTSUPP; switch (f->command) { case TC_CLSFLOWER_REPLACE: - return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, - proto, f); + return mlxsw_sp_flower_replace(mlxsw_sp_port, ingress, f); case TC_CLSFLOWER_DESTROY: mlxsw_sp_flower_destroy(mlxsw_sp_port, ingress, f); return 0; @@ -1740,19 +1736,16 @@ mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); switch (type) { case TC_SETUP_CLSMATCHALL: - return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, handle, - chain_index, proto, + return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, tc->cls_mall); case TC_SETUP_CLSFLOWER: - return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, handle, - chain_index, proto, + return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, tc->cls_flower); default: return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h index e848f06e34e6..8452d1db2f3f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.h +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.h @@ -508,7 +508,7 @@ extern const struct mlxsw_sp_acl_ops mlxsw_sp_acl_tcam_ops; /* spectrum_flower.c */ int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, - __be16 protocol, struct tc_cls_flower_offload *f); + struct tc_cls_flower_offload *f); void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, struct tc_cls_flower_offload *f); int mlxsw_sp_flower_stats(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 9be48d2e43ca..021b6c0076c0 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -368,7 +368,7 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, } int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, - __be16 protocol, struct tc_cls_flower_offload *f) + struct tc_cls_flower_offload *f) { struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; struct net_device *dev = mlxsw_sp_port->dev; diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index d7975dcecb40..152a7abb58ed 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -121,20 +121,22 @@ static void nfp_bpf_vnic_clean(struct nfp_app *app, struct nfp_net *nn) } static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, u32 handle, __be16 proto, + enum tc_setup_type type, struct tc_to_netdev *tc) { + struct tc_cls_bpf_offload *cls_bpf = tc->cls_bpf; struct nfp_net *nn = netdev_priv(netdev); if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) || - TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - proto != htons(ETH_P_ALL)) + TC_H_MAJ(cls_bpf->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + cls_bpf->common.protocol != htons(ETH_P_ALL) || + cls_bpf->common.chain_index) return -EOPNOTSUPP; if (nn->dp.bpf_offload_xdp) return -EBUSY; - return nfp_net_bpf_offload(nn, tc->cls_bpf); + return nfp_net_bpf_offload(nn, cls_bpf); } static bool nfp_bpf_tc_busy(struct nfp_app *app, struct nfp_net *nn) diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index 314e6e8ba649..eb94d08e35cf 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -135,8 +135,7 @@ int nfp_flower_metadata_init(struct nfp_app *app); void nfp_flower_metadata_cleanup(struct nfp_app *app); int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, u32 handle, __be16 proto, - struct tc_to_netdev *tc); + enum tc_setup_type type, struct tc_to_netdev *tc); int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow, struct nfp_fl_key_ls *key_ls, struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 58af438a95c1..8197836c650d 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -385,13 +385,15 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, } int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, u32 handle, __be16 proto, - struct tc_to_netdev *tc) + enum tc_setup_type type, struct tc_to_netdev *tc) { + struct tc_cls_flower_offload *cls_flower = tc->cls_flower; + if (type != TC_SETUP_CLSFLOWER || - TC_H_MAJ(handle) != TC_H_MAJ(TC_H_INGRESS) || - !eth_proto_is_802_3(proto)) + TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + !eth_proto_is_802_3(cls_flower->common.protocol) || + cls_flower->common.chain_index) return -EOPNOTSUPP; - return nfp_flower_repr_offload(app, netdev, tc->cls_flower); + return nfp_flower_repr_offload(app, netdev, cls_flower); } diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index b3b03bb9d907..7a2f950b149c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -109,8 +109,7 @@ struct nfp_app_type { void (*ctrl_msg_rx)(struct nfp_app *app, struct sk_buff *skb); int (*setup_tc)(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, u32 handle, __be16 proto, - struct tc_to_netdev *tc); + enum tc_setup_type type, struct tc_to_netdev *tc); bool (*tc_busy)(struct nfp_app *app, struct nfp_net *nn); int (*xdp_offload)(struct nfp_app *app, struct nfp_net *nn, struct bpf_prog *prog); @@ -240,12 +239,11 @@ static inline bool nfp_app_tc_busy(struct nfp_app *app, struct nfp_net *nn) static inline int nfp_app_setup_tc(struct nfp_app *app, struct net_device *netdev, enum tc_setup_type type, - u32 handle, __be16 proto, struct tc_to_netdev *tc) { if (!app || !app->type->setup_tc) return -EOPNOTSUPP; - return app->type->setup_tc(app, netdev, type, handle, proto, tc); + return app->type->setup_tc(app, netdev, type, tc); } static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index 9d776f982352..e8abab2b912e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -89,19 +89,15 @@ const struct switchdev_ops nfp_port_switchdev_ops = { }; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { struct nfp_port *port; - if (chain_index) - return -EOPNOTSUPP; - port = nfp_port_from_netdev(netdev); if (!port) return -EOPNOTSUPP; - return nfp_app_setup_tc(port->app, netdev, type, handle, proto, tc); + return nfp_app_setup_tc(port->app, netdev, type, tc); } struct nfp_port * diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 239c5401000c..252f06d4307f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -110,7 +110,6 @@ struct nfp_port { extern const struct switchdev_ops nfp_port_switchdev_ops; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc); struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index e41a7179bc05..b0c6004db138 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -33,7 +33,6 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc); unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); extern unsigned int efx_piobuf_size; diff --git a/drivers/net/ethernet/sfc/falcon/efx.h b/drivers/net/ethernet/sfc/falcon/efx.h index f3bc67ec1f30..4497511fc914 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.h +++ b/drivers/net/ethernet/sfc/falcon/efx.h @@ -33,7 +33,6 @@ netdev_tx_t ef4_hard_start_xmit(struct sk_buff *skb, netdev_tx_t ef4_enqueue_skb(struct ef4_tx_queue *tx_queue, struct sk_buff *skb); void ef4_xmit_done(struct ef4_tx_queue *tx_queue, unsigned int index); int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc); unsigned int ef4_tx_max_skb_descs(struct ef4_nic *efx); extern bool ef4_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index 6c4752694c1f..447519ac3fa4 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -426,7 +426,6 @@ void ef4_init_tx_queue_core_txq(struct ef4_tx_queue *tx_queue) } int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *ntc) { struct ef4_nic *efx = netdev_priv(net_dev); diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 0c08c10d751c..d17af918ac50 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -654,7 +654,6 @@ void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue) } int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *ntc) { struct efx_nic *efx = netdev_priv(net_dev); diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index cb21742f6177..14f91b285f00 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1878,7 +1878,6 @@ static u16 netcp_select_queue(struct net_device *dev, struct sk_buff *skb, } static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 proto, struct tc_to_netdev *tc) { u8 num_tc; diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index bd49dbaee84e..6e2f7e38cf8e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -977,7 +977,6 @@ struct xfrmdev_ops { * int (*ndo_set_vf_rss_query_en)(struct net_device *dev, int vf, bool setting); * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb); * int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, - * u32 handle, u32 chain_index, __be16 protocol, * struct tc_to_netdev *tc); * Called to setup any 'tc' scheduler, classifier or action on @dev. * This is always called from the stack with the rtnl lock held and netif @@ -1227,8 +1226,6 @@ struct net_device_ops { int vf, bool setting); int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, - __be16 protocol, struct tc_to_netdev *tc); #if IS_ENABLED(CONFIG_FCOE) int (*ndo_fcoe_enable)(struct net_device *dev); diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 8213acdfdf5a..ffaddf72108e 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -405,6 +405,21 @@ tcf_match_indev(struct sk_buff *skb, int ifindex) } #endif /* CONFIG_NET_CLS_IND */ +struct tc_cls_common_offload { + u32 handle; + u32 chain_index; + __be16 protocol; +}; + +static inline void +tc_cls_common_offload_init(struct tc_cls_common_offload *cls_common, + const struct tcf_proto *tp) +{ + cls_common->handle = tp->q->handle; + cls_common->chain_index = tp->chain->index; + cls_common->protocol = tp->protocol; +} + struct tc_cls_u32_knode { struct tcf_exts *exts; struct tc_u32_sel *sel; @@ -431,6 +446,7 @@ enum tc_clsu32_command { }; struct tc_cls_u32_offload { + struct tc_cls_common_offload common; /* knode values */ enum tc_clsu32_command command; union { @@ -497,6 +513,7 @@ enum tc_fl_command { }; struct tc_cls_flower_offload { + struct tc_cls_common_offload common; enum tc_fl_command command; u32 prio; unsigned long cookie; @@ -513,6 +530,7 @@ enum tc_matchall_command { }; struct tc_cls_matchall_offload { + struct tc_cls_common_offload common; enum tc_matchall_command command; struct tcf_exts *exts; unsigned long cookie; @@ -526,6 +544,7 @@ enum tc_clsbpf_command { }; struct tc_cls_bpf_offload { + struct tc_cls_common_offload common; enum tc_clsbpf_command command; struct tcf_exts *exts; struct bpf_prog *prog; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index e76d576b941d..5e01e9271619 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -774,12 +774,12 @@ dsa_slave_mall_tc_entry_find(struct dsa_slave_priv *p, } static int dsa_slave_add_cls_matchall(struct net_device *dev, - __be16 protocol, struct tc_cls_matchall_offload *cls, bool ingress) { struct dsa_slave_priv *p = netdev_priv(dev); struct dsa_mall_tc_entry *mall_tc_entry; + __be16 protocol = cls->common.protocol; struct dsa_switch *ds = p->dp->ds; struct net *net = dev_net(dev); struct dsa_slave_priv *to_p; @@ -864,18 +864,16 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev, } static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, - u32 handle, u32 chain_index, - __be16 protocol, struct tc_cls_matchall_offload *cls) { - bool ingress = TC_H_MAJ(handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress = TC_H_MAJ(cls->common.handle) == TC_H_MAJ(TC_H_INGRESS); - if (chain_index) + if (cls->common.chain_index) return -EOPNOTSUPP; switch (cls->command) { case TC_CLSMATCHALL_REPLACE: - return dsa_slave_add_cls_matchall(dev, protocol, cls, ingress); + return dsa_slave_add_cls_matchall(dev, cls, ingress); case TC_CLSMATCHALL_DESTROY: dsa_slave_del_cls_matchall(dev, cls); return 0; @@ -885,13 +883,11 @@ static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, } static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, - u32 handle, u32 chain_index, __be16 protocol, struct tc_to_netdev *tc) { switch (type) { case TC_SETUP_CLSMATCHALL: - return dsa_slave_setup_tc_cls_matchall(dev, handle, chain_index, - protocol, tc->cls_mall); + return dsa_slave_setup_tc_cls_matchall(dev, tc->cls_mall); default: return -EOPNOTSUPP; } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index e2bf2753173d..dde8efdcee3b 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -153,6 +153,7 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, offload.cls_bpf = &bpf_offload; + tc_cls_common_offload_init(&bpf_offload.common, tp); bpf_offload.command = cmd; bpf_offload.exts = &prog->exts; bpf_offload.prog = prog->filter; @@ -160,11 +161,7 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, bpf_offload.exts_integrated = prog->exts_integrated; bpf_offload.gen_flags = prog->gen_flags; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, - tp->q->handle, - tp->chain->index, - tp->protocol, &offload); - + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, &offload); if (!err && (cmd == TC_CLSBPF_ADD || cmd == TC_CLSBPF_REPLACE)) prog->gen_flags |= TCA_CLS_FLAGS_IN_HW; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 52deeed2b7f5..1fdf2889ba9f 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -232,14 +232,14 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) if (!tc_can_offload(dev, tp)) return; + tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_DESTROY; offload.prio = tp->prio; offload.cookie = (unsigned long)f; tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tp->q->handle, - tp->chain->index, tp->protocol, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tc); } static int fl_hw_replace_filter(struct tcf_proto *tp, @@ -264,6 +264,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, f->hw_dev = dev; } + tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_REPLACE; offload.prio = tp->prio; offload.cookie = (unsigned long)f; @@ -274,9 +275,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, tc->cls_flower = &offload; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, - tp->q->handle, tp->chain->index, - tp->protocol, tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tc); if (!err) f->flags |= TCA_CLS_FLAGS_IN_HW; @@ -294,6 +293,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) if (!tc_can_offload(dev, tp)) return; + tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_STATS; offload.prio = tp->prio; offload.cookie = (unsigned long)f; @@ -301,8 +301,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) tc->cls_flower = &offload; - dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, tp->q->handle, - tp->chain->index, tp->protocol, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, tc); } static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index a8853ada22f6..174c700160ca 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -58,14 +58,14 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, struct tc_cls_matchall_offload mall_offload = {0}; int err; + tc_cls_common_offload_init(&mall_offload.common, tp); offload.cls_mall = &mall_offload; offload.cls_mall->command = TC_CLSMATCHALL_REPLACE; offload.cls_mall->exts = &head->exts; offload.cls_mall->cookie = cookie; err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, - tp->q->handle, tp->chain->index, - tp->protocol, &offload); + &offload); if (!err) head->flags |= TCA_CLS_FLAGS_IN_HW; @@ -80,13 +80,13 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, struct tc_to_netdev offload; struct tc_cls_matchall_offload mall_offload = {0}; + tc_cls_common_offload_init(&mall_offload.common, tp); offload.cls_mall = &mall_offload; offload.cls_mall->command = TC_CLSMATCHALL_DESTROY; offload.cls_mall->exts = NULL; offload.cls_mall->cookie = cookie; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, tp->q->handle, - tp->chain->index, tp->protocol, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, &offload); } static void mall_destroy(struct tcf_proto *tp) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index d1bae4cc749f..c0f59c471523 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -437,11 +437,10 @@ static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle) offload.cls_u32 = &u32_offload; if (tc_should_offload(dev, tp, 0)) { + tc_cls_common_offload_init(&u32_offload.common, tp); offload.cls_u32->command = TC_CLSU32_DELETE_KNODE; offload.cls_u32->knode.handle = handle; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, - tp->q->handle, tp->chain->index, - tp->protocol, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); } } @@ -458,14 +457,13 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, offload.cls_u32 = &u32_offload; + tc_cls_common_offload_init(&u32_offload.common, tp); offload.cls_u32->command = TC_CLSU32_NEW_HNODE; offload.cls_u32->hnode.divisor = h->divisor; offload.cls_u32->hnode.handle = h->handle; offload.cls_u32->hnode.prio = h->prio; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, tp->q->handle, - tp->chain->index, tp->protocol, - &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); if (tc_skip_sw(flags)) return err; @@ -481,14 +479,13 @@ static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) offload.cls_u32 = &u32_offload; if (tc_should_offload(dev, tp, 0)) { + tc_cls_common_offload_init(&u32_offload.common, tp); offload.cls_u32->command = TC_CLSU32_DELETE_HNODE; offload.cls_u32->hnode.divisor = h->divisor; offload.cls_u32->hnode.handle = h->handle; offload.cls_u32->hnode.prio = h->prio; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, - tp->q->handle, tp->chain->index, - tp->protocol, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); } } @@ -505,6 +502,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, if (!tc_should_offload(dev, tp, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; + tc_cls_common_offload_init(&u32_offload.common, tp); offload.cls_u32->command = TC_CLSU32_REPLACE_KNODE; offload.cls_u32->knode.handle = n->handle; offload.cls_u32->knode.fshift = n->fshift; @@ -520,9 +518,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, if (n->ht_down) offload.cls_u32->knode.link_handle = n->ht_down->handle; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, tp->q->handle, - tp->chain->index, tp->protocol, - &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); if (!err) n->flags |= TCA_CLS_FLAGS_IN_HW; diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 329610ce4dfe..09b577dde49c 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -42,8 +42,7 @@ static void mqprio_destroy(struct Qdisc *sch) struct tc_mqprio_qopt offload = { 0 }; struct tc_to_netdev tc = { { .mqprio = &offload } }; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, - sch->handle, 0, 0, &tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &tc); } else { netdev_set_num_tc(dev, 0); } @@ -151,8 +150,7 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) struct tc_mqprio_qopt offload = *qopt; struct tc_to_netdev tc = { { .mqprio = &offload } }; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, - sch->handle, 0, 0, &tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &tc); if (err) return err; -- cgit v1.2.3 From d7c1c8d2e53be974b5c72e31d7d35f6d9737fe84 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:30 +0200 Subject: net: sched: move prio into cls_common prio is not cls_flower specific, but it is meaningful for all classifiers. Seems that only mlxsw cares about the value. Obviously, cls offload in other drivers is broken. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c | 2 +- include/net/pkt_cls.h | 3 ++- net/sched/cls_flower.c | 3 --- 3 files changed, 3 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c index 021b6c0076c0..95428b41c50f 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c @@ -270,7 +270,7 @@ static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, return -EOPNOTSUPP; } - mlxsw_sp_acl_rulei_priority(rulei, f->prio); + mlxsw_sp_acl_rulei_priority(rulei, f->common.prio); if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) { struct flow_dissector_key_control *key = diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index ffaddf72108e..572083af02ac 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -409,6 +409,7 @@ struct tc_cls_common_offload { u32 handle; u32 chain_index; __be16 protocol; + u32 prio; }; static inline void @@ -418,6 +419,7 @@ tc_cls_common_offload_init(struct tc_cls_common_offload *cls_common, cls_common->handle = tp->q->handle; cls_common->chain_index = tp->chain->index; cls_common->protocol = tp->protocol; + cls_common->prio = tp->prio; } struct tc_cls_u32_knode { @@ -515,7 +517,6 @@ enum tc_fl_command { struct tc_cls_flower_offload { struct tc_cls_common_offload common; enum tc_fl_command command; - u32 prio; unsigned long cookie; struct flow_dissector *dissector; struct fl_flow_key *mask; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 1fdf2889ba9f..ccdf2f5014ca 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -234,7 +234,6 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_DESTROY; - offload.prio = tp->prio; offload.cookie = (unsigned long)f; tc->cls_flower = &offload; @@ -266,7 +265,6 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_REPLACE; - offload.prio = tp->prio; offload.cookie = (unsigned long)f; offload.dissector = dissector; offload.mask = mask; @@ -295,7 +293,6 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) tc_cls_common_offload_init(&offload.common, tp); offload.command = TC_CLSFLOWER_STATS; - offload.prio = tp->prio; offload.cookie = (unsigned long)f; offload.exts = &f->exts; -- cgit v1.2.3 From de4784ca030fed17d527dbb2bb4e21328b12de94 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Mon, 7 Aug 2017 10:15:32 +0200 Subject: net: sched: get rid of struct tc_to_netdev Get rid of struct tc_to_netdev which is now just unnecessary container and rather pass per-type structures down to drivers directly. Along with that, consolidate the naming of per-type structure variables in cls_*. Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- drivers/net/ethernet/amd/xgbe/xgbe-drv.c | 7 +- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c | 8 ++- drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h | 2 +- drivers/net/ethernet/broadcom/bnxt/bnxt.c | 8 ++- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 4 +- drivers/net/ethernet/freescale/dpaa/dpaa_eth.c | 7 +- .../net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c | 6 +- drivers/net/ethernet/intel/fm10k/fm10k_netdev.c | 8 ++- drivers/net/ethernet/intel/i40e/i40e_main.c | 8 ++- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 6 +- drivers/net/ethernet/mellanox/mlx4/en_netdev.c | 10 +-- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 6 +- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 12 ++-- drivers/net/ethernet/mellanox/mlx5/core/en_tc.h | 2 + drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 8 +-- drivers/net/ethernet/netronome/nfp/bpf/main.c | 5 +- drivers/net/ethernet/netronome/nfp/flower/main.h | 4 +- .../net/ethernet/netronome/nfp/flower/offload.c | 4 +- drivers/net/ethernet/netronome/nfp/nfp_app.h | 8 +-- drivers/net/ethernet/netronome/nfp/nfp_port.c | 4 +- drivers/net/ethernet/netronome/nfp/nfp_port.h | 3 +- drivers/net/ethernet/sfc/efx.h | 2 +- drivers/net/ethernet/sfc/falcon/efx.h | 2 +- drivers/net/ethernet/sfc/falcon/tx.c | 7 +- drivers/net/ethernet/sfc/tx.c | 7 +- drivers/net/ethernet/ti/netcp_core.c | 7 +- include/linux/netdevice.h | 19 +---- net/dsa/slave.c | 4 +- net/sched/cls_bpf.c | 21 +++--- net/sched/cls_flower.c | 54 ++++++-------- net/sched/cls_matchall.c | 27 +++---- net/sched/cls_u32.c | 83 ++++++++++------------ net/sched/sch_mqprio.c | 13 ++-- 33 files changed, 174 insertions(+), 202 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c index 37d3e5b65d94..2fd9b80b39b0 100644 --- a/drivers/net/ethernet/amd/xgbe/xgbe-drv.c +++ b/drivers/net/ethernet/amd/xgbe/xgbe-drv.c @@ -1919,16 +1919,17 @@ static void xgbe_poll_controller(struct net_device *netdev) #endif /* End CONFIG_NET_POLL_CONTROLLER */ static int xgbe_setup_tc(struct net_device *netdev, enum tc_setup_type type, - struct tc_to_netdev *tc_to_netdev) + void *type_data) { struct xgbe_prv_data *pdata = netdev_priv(netdev); + struct tc_mqprio_qopt *mqprio = type_data; u8 tc; if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc_to_netdev->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - tc = tc_to_netdev->mqprio->num_tc; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + tc = mqprio->num_tc; if (tc > pdata->hw_feat.tc_cnt) return -EINVAL; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index 8687afc24698..1216c1f1e052 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -4285,14 +4285,16 @@ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc) } int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return bnx2x_setup_tc(dev, tc->mqprio->num_tc); + return bnx2x_setup_tc(dev, mqprio->num_tc); } /* called with rtnl_lock */ diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h index 04eb95043023..a5265e1344f1 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.h @@ -487,7 +487,7 @@ netdev_tx_t bnx2x_start_xmit(struct sk_buff *skb, struct net_device *dev); /* setup_tc callback */ int bnx2x_setup_tc(struct net_device *dev, u8 num_tc); int __bnx2x_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); int bnx2x_get_vf_config(struct net_device *dev, int vf, struct ifla_vf_info *ivi); diff --git a/drivers/net/ethernet/broadcom/bnxt/bnxt.c b/drivers/net/ethernet/broadcom/bnxt/bnxt.c index a78f72a53042..6e14fc4fe2c8 100644 --- a/drivers/net/ethernet/broadcom/bnxt/bnxt.c +++ b/drivers/net/ethernet/broadcom/bnxt/bnxt.c @@ -7238,14 +7238,16 @@ int bnxt_setup_mq_tc(struct net_device *dev, u8 tc) } static int bnxt_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *ntc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return bnxt_setup_mq_tc(dev, ntc->mqprio->num_tc); + return bnxt_setup_mq_tc(dev, mqprio->num_tc); } #ifdef CONFIG_RFS_ACCEL diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index 13199317c8e0..d80b20d695e0 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2908,7 +2908,7 @@ static int cxgb_setup_tc_cls_u32(struct net_device *dev, } static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { struct port_info *pi = netdev2pinfo(dev); struct adapter *adap = netdev2adap(dev); @@ -2922,7 +2922,7 @@ static int cxgb_setup_tc(struct net_device *dev, enum tc_setup_type type, switch (type) { case TC_SETUP_CLSU32: - return cxgb_setup_tc_cls_u32(dev, tc->cls_u32); + return cxgb_setup_tc_cls_u32(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c index bfb44c95a7ec..733d54caabb6 100644 --- a/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c +++ b/drivers/net/ethernet/freescale/dpaa/dpaa_eth.c @@ -343,17 +343,18 @@ static void dpaa_get_stats64(struct net_device *net_dev, } static int dpaa_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { struct dpaa_priv *priv = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = type_data; u8 num_tc; int i; if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - num_tc = tc->mqprio->num_tc; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + num_tc = mqprio->num_tc; if (num_tc == priv->num_tc) return 0; diff --git a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c index aa43ebda9a00..069ae426aa24 100644 --- a/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c +++ b/drivers/net/ethernet/hisilicon/hns3/hns3pf/hns3_enet.c @@ -1220,12 +1220,14 @@ static int hns3_setup_tc(struct net_device *netdev, u8 tc) } static int hns3_nic_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - return hns3_setup_tc(dev, tc->mqprio->num_tc); + return hns3_setup_tc(dev, mqprio->num_tc); } static int hns3_vlan_rx_add_vid(struct net_device *netdev, diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c index 70888129200b..e69d49d91d67 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_netdev.c @@ -1266,14 +1266,16 @@ err_queueing_scheme: } static int __fm10k_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return fm10k_setup_tc(dev, tc->mqprio->num_tc); + return fm10k_setup_tc(dev, mqprio->num_tc); } static void fm10k_assign_l2_accel(struct fm10k_intfc *interface, diff --git a/drivers/net/ethernet/intel/i40e/i40e_main.c b/drivers/net/ethernet/intel/i40e/i40e_main.c index 1f4633830c79..a7e5a76703e7 100644 --- a/drivers/net/ethernet/intel/i40e/i40e_main.c +++ b/drivers/net/ethernet/intel/i40e/i40e_main.c @@ -5657,14 +5657,16 @@ exit: } static int __i40e_setup_tc(struct net_device *netdev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return i40e_setup_tc(netdev, tc->mqprio->num_tc); + return i40e_setup_tc(netdev, mqprio->num_tc); } /** diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 0a350314d76b..c6b132476de4 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9258,13 +9258,13 @@ static int ixgbe_setup_tc_mqprio(struct net_device *dev, } static int __ixgbe_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { switch (type) { case TC_SETUP_CLSU32: - return ixgbe_setup_tc_cls_u32(dev, tc->cls_u32); + return ixgbe_setup_tc_cls_u32(dev, type_data); case TC_SETUP_MQPRIO: - return ixgbe_setup_tc_mqprio(dev, tc->mqprio); + return ixgbe_setup_tc_mqprio(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c index 1667e86ac05d..6e67ca7aa7f5 100644 --- a/drivers/net/ethernet/mellanox/mlx4/en_netdev.c +++ b/drivers/net/ethernet/mellanox/mlx4/en_netdev.c @@ -131,17 +131,19 @@ out: } static int __mlx4_en_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; + if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - if (tc->mqprio->num_tc && tc->mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) + if (mqprio->num_tc && mqprio->num_tc != MLX4_EN_NUM_UP_HIGH) return -EINVAL; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - return mlx4_en_alloc_tx_queue_per_tc(dev, tc->mqprio->num_tc); + return mlx4_en_alloc_tx_queue_per_tc(dev, mqprio->num_tc); } #ifdef CONFIG_RFS_ACCEL diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 15f2a942962a..ae0916238b7b 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3053,13 +3053,13 @@ static int mlx5e_setup_tc_cls_flower(struct net_device *dev, } static int mlx5e_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { switch (type) { case TC_SETUP_CLSFLOWER: - return mlx5e_setup_tc_cls_flower(dev, tc->cls_flower); + return mlx5e_setup_tc_cls_flower(dev, type_data); case TC_SETUP_MQPRIO: - return mlx5e_setup_tc_mqprio(dev, tc->mqprio); + return mlx5e_setup_tc_mqprio(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index e5cf2e7ae052..3df994d1e173 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -651,10 +651,10 @@ static int mlx5e_rep_get_phys_port_name(struct net_device *dev, return 0; } -static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, - struct tc_to_netdev *tc) +static int +mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, + struct tc_cls_flower_offload *cls_flower) { - struct tc_cls_flower_offload *cls_flower = tc->cls_flower; struct mlx5e_priv *priv = netdev_priv(dev); if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || @@ -666,7 +666,7 @@ static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, dev = mlx5_eswitch_get_uplink_netdev(esw); return dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, - tc); + cls_flower); } switch (cls_flower->command) { @@ -682,11 +682,11 @@ static int mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, } static int mlx5e_rep_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { switch (type) { case TC_SETUP_CLSFLOWER: - return mlx5e_rep_setup_tc_cls_flower(dev, tc); + return mlx5e_rep_setup_tc_cls_flower(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h index 5a0f4a487855..2917d964ffc3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.h @@ -33,6 +33,8 @@ #ifndef __MLX5_EN_TC_H__ #define __MLX5_EN_TC_H__ +#include + #define MLX5E_TC_FLOW_ID_MASK 0x0000ffff int mlx5e_tc_init(struct mlx5e_priv *priv); diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index 1ca3204f5543..eb7c4549f464 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1736,17 +1736,15 @@ mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, } static int mlxsw_sp_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { struct mlxsw_sp_port *mlxsw_sp_port = netdev_priv(dev); switch (type) { case TC_SETUP_CLSMATCHALL: - return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, - tc->cls_mall); + return mlxsw_sp_setup_tc_cls_matchall(mlxsw_sp_port, type_data); case TC_SETUP_CLSFLOWER: - return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, - tc->cls_flower); + return mlxsw_sp_setup_tc_cls_flower(mlxsw_sp_port, type_data); default: return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index 152a7abb58ed..f981f60ec306 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -121,10 +121,9 @@ static void nfp_bpf_vnic_clean(struct nfp_app *app, struct nfp_net *nn) } static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, - struct tc_to_netdev *tc) + enum tc_setup_type type, void *type_data) { - struct tc_cls_bpf_offload *cls_bpf = tc->cls_bpf; + struct tc_cls_bpf_offload *cls_bpf = type_data; struct nfp_net *nn = netdev_priv(netdev); if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) || diff --git a/drivers/net/ethernet/netronome/nfp/flower/main.h b/drivers/net/ethernet/netronome/nfp/flower/main.h index eb94d08e35cf..71e4f4f4e9ba 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/main.h +++ b/drivers/net/ethernet/netronome/nfp/flower/main.h @@ -38,8 +38,8 @@ #include #include #include +#include -struct tc_to_netdev; struct net_device; struct nfp_app; @@ -135,7 +135,7 @@ int nfp_flower_metadata_init(struct nfp_app *app); void nfp_flower_metadata_cleanup(struct nfp_app *app); int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, struct tc_to_netdev *tc); + enum tc_setup_type type, void *type_data); int nfp_flower_compile_flow_match(struct tc_cls_flower_offload *flow, struct nfp_fl_key_ls *key_ls, struct net_device *netdev, diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 8197836c650d..01767c7376d5 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -385,9 +385,9 @@ nfp_flower_repr_offload(struct nfp_app *app, struct net_device *netdev, } int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, struct tc_to_netdev *tc) + enum tc_setup_type type, void *type_data) { - struct tc_cls_flower_offload *cls_flower = tc->cls_flower; + struct tc_cls_flower_offload *cls_flower = type_data; if (type != TC_SETUP_CLSFLOWER || TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || diff --git a/drivers/net/ethernet/netronome/nfp/nfp_app.h b/drivers/net/ethernet/netronome/nfp/nfp_app.h index 7a2f950b149c..f34e8778fae2 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_app.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_app.h @@ -42,7 +42,6 @@ struct bpf_prog; struct net_device; struct pci_dev; struct sk_buff; -struct tc_to_netdev; struct sk_buff; struct nfp_app; struct nfp_cpp; @@ -109,7 +108,7 @@ struct nfp_app_type { void (*ctrl_msg_rx)(struct nfp_app *app, struct sk_buff *skb); int (*setup_tc)(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, struct tc_to_netdev *tc); + enum tc_setup_type type, void *type_data); bool (*tc_busy)(struct nfp_app *app, struct nfp_net *nn); int (*xdp_offload)(struct nfp_app *app, struct nfp_net *nn, struct bpf_prog *prog); @@ -238,12 +237,11 @@ static inline bool nfp_app_tc_busy(struct nfp_app *app, struct nfp_net *nn) static inline int nfp_app_setup_tc(struct nfp_app *app, struct net_device *netdev, - enum tc_setup_type type, - struct tc_to_netdev *tc) + enum tc_setup_type type, void *type_data) { if (!app || !app->type->setup_tc) return -EOPNOTSUPP; - return app->type->setup_tc(app, netdev, type, tc); + return app->type->setup_tc(app, netdev, type, type_data); } static inline int nfp_app_xdp_offload(struct nfp_app *app, struct nfp_net *nn, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.c b/drivers/net/ethernet/netronome/nfp/nfp_port.c index e8abab2b912e..0cf65e57addb 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.c @@ -89,7 +89,7 @@ const struct switchdev_ops nfp_port_switchdev_ops = { }; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { struct nfp_port *port; @@ -97,7 +97,7 @@ int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, if (!port) return -EOPNOTSUPP; - return nfp_app_setup_tc(port->app, netdev, type, tc); + return nfp_app_setup_tc(port->app, netdev, type, type_data); } struct nfp_port * diff --git a/drivers/net/ethernet/netronome/nfp/nfp_port.h b/drivers/net/ethernet/netronome/nfp/nfp_port.h index 252f06d4307f..c88e376dcf0f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_port.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_port.h @@ -36,7 +36,6 @@ #include -struct tc_to_netdev; struct net_device; struct nfp_app; struct nfp_pf; @@ -110,7 +109,7 @@ struct nfp_port { extern const struct switchdev_ops nfp_port_switchdev_ops; int nfp_port_setup_tc(struct net_device *netdev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); struct nfp_port *nfp_port_from_netdev(struct net_device *netdev); struct nfp_port * diff --git a/drivers/net/ethernet/sfc/efx.h b/drivers/net/ethernet/sfc/efx.h index b0c6004db138..d407adf59610 100644 --- a/drivers/net/ethernet/sfc/efx.h +++ b/drivers/net/ethernet/sfc/efx.h @@ -33,7 +33,7 @@ netdev_tx_t efx_hard_start_xmit(struct sk_buff *skb, netdev_tx_t efx_enqueue_skb(struct efx_tx_queue *tx_queue, struct sk_buff *skb); void efx_xmit_done(struct efx_tx_queue *tx_queue, unsigned int index); int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); unsigned int efx_tx_max_skb_descs(struct efx_nic *efx); extern unsigned int efx_piobuf_size; extern bool efx_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/efx.h b/drivers/net/ethernet/sfc/falcon/efx.h index 4497511fc914..4f3bb30661ea 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.h +++ b/drivers/net/ethernet/sfc/falcon/efx.h @@ -33,7 +33,7 @@ netdev_tx_t ef4_hard_start_xmit(struct sk_buff *skb, netdev_tx_t ef4_enqueue_skb(struct ef4_tx_queue *tx_queue, struct sk_buff *skb); void ef4_xmit_done(struct ef4_tx_queue *tx_queue, unsigned int index); int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); unsigned int ef4_tx_max_skb_descs(struct ef4_nic *efx); extern bool ef4_separate_tx_channels; diff --git a/drivers/net/ethernet/sfc/falcon/tx.c b/drivers/net/ethernet/sfc/falcon/tx.c index 0f125e15143a..6a75f4140a4b 100644 --- a/drivers/net/ethernet/sfc/falcon/tx.c +++ b/drivers/net/ethernet/sfc/falcon/tx.c @@ -426,9 +426,10 @@ void ef4_init_tx_queue_core_txq(struct ef4_tx_queue *tx_queue) } int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *ntc) + void *type_data) { struct ef4_nic *efx = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = type_data; struct ef4_channel *channel; struct ef4_tx_queue *tx_queue; unsigned tc, num_tc; @@ -437,12 +438,12 @@ int ef4_setup_tc(struct net_device *net_dev, enum tc_setup_type type, if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - num_tc = ntc->mqprio->num_tc; + num_tc = mqprio->num_tc; if (ef4_nic_rev(efx) < EF4_REV_FALCON_B0 || num_tc > EF4_MAX_TX_TC) return -EINVAL; - ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; if (num_tc == net_dev->num_tc) return 0; diff --git a/drivers/net/ethernet/sfc/tx.c b/drivers/net/ethernet/sfc/tx.c index 53ba30c3eb7b..32bf1fecf864 100644 --- a/drivers/net/ethernet/sfc/tx.c +++ b/drivers/net/ethernet/sfc/tx.c @@ -654,9 +654,10 @@ void efx_init_tx_queue_core_txq(struct efx_tx_queue *tx_queue) } int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, - struct tc_to_netdev *ntc) + void *type_data) { struct efx_nic *efx = netdev_priv(net_dev); + struct tc_mqprio_qopt *mqprio = type_data; struct efx_channel *channel; struct efx_tx_queue *tx_queue; unsigned tc, num_tc; @@ -665,12 +666,12 @@ int efx_setup_tc(struct net_device *net_dev, enum tc_setup_type type, if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - num_tc = ntc->mqprio->num_tc; + num_tc = mqprio->num_tc; if (num_tc > EFX_MAX_TX_TC) return -EINVAL; - ntc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; if (num_tc == net_dev->num_tc) return 0; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index caba0abc0158..eb96a6913235 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -1878,8 +1878,9 @@ static u16 netcp_select_queue(struct net_device *dev, struct sk_buff *skb, } static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { + struct tc_mqprio_qopt *mqprio = type_data; u8 num_tc; int i; @@ -1889,8 +1890,8 @@ static int netcp_setup_tc(struct net_device *dev, enum tc_setup_type type, if (type != TC_SETUP_MQPRIO) return -EOPNOTSUPP; - tc->mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; - num_tc = tc->mqprio->num_tc; + mqprio->hw = TC_MQPRIO_HW_OFFLOAD_TCS; + num_tc = mqprio->num_tc; /* Sanity-check the number of traffic classes requested */ if ((dev->real_num_tx_queues <= 1) || diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 6e2f7e38cf8e..1d238d54c484 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -771,9 +771,6 @@ static inline bool netdev_phys_item_id_same(struct netdev_phys_item_id *a, typedef u16 (*select_queue_fallback_t)(struct net_device *dev, struct sk_buff *skb); -/* These structures hold the attributes of qdisc and classifiers - * that are being passed to the netdevice through the setup_tc op. - */ enum tc_setup_type { TC_SETUP_MQPRIO, TC_SETUP_CLSU32, @@ -782,18 +779,6 @@ enum tc_setup_type { TC_SETUP_CLSBPF, }; -struct tc_cls_u32_offload; - -struct tc_to_netdev { - union { - struct tc_cls_u32_offload *cls_u32; - struct tc_cls_flower_offload *cls_flower; - struct tc_cls_matchall_offload *cls_mall; - struct tc_cls_bpf_offload *cls_bpf; - struct tc_mqprio_qopt *mqprio; - }; -}; - /* These structures hold the attributes of xdp state that are being passed * to the netdevice through the xdp op. */ @@ -977,7 +962,7 @@ struct xfrmdev_ops { * int (*ndo_set_vf_rss_query_en)(struct net_device *dev, int vf, bool setting); * int (*ndo_get_vf_port)(struct net_device *dev, int vf, struct sk_buff *skb); * int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, - * struct tc_to_netdev *tc); + * void *type_data); * Called to setup any 'tc' scheduler, classifier or action on @dev. * This is always called from the stack with the rtnl lock held and netif * tx queues stopped. This allows the netdevice to perform queue @@ -1226,7 +1211,7 @@ struct net_device_ops { int vf, bool setting); int (*ndo_setup_tc)(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc); + void *type_data); #if IS_ENABLED(CONFIG_FCOE) int (*ndo_fcoe_enable)(struct net_device *dev); int (*ndo_fcoe_disable)(struct net_device *dev); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5e01e9271619..c6b5de2fe413 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -883,11 +883,11 @@ static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, } static int dsa_slave_setup_tc(struct net_device *dev, enum tc_setup_type type, - struct tc_to_netdev *tc) + void *type_data) { switch (type) { case TC_SETUP_CLSMATCHALL: - return dsa_slave_setup_tc_cls_matchall(dev, tc->cls_mall); + return dsa_slave_setup_tc_cls_matchall(dev, type_data); default: return -EOPNOTSUPP; } diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index dde8efdcee3b..2d4d06e41cd9 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -147,21 +147,18 @@ static int cls_bpf_offload_cmd(struct tcf_proto *tp, struct cls_bpf_prog *prog, enum tc_clsbpf_command cmd) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_bpf_offload bpf_offload = {}; - struct tc_to_netdev offload; + struct tc_cls_bpf_offload cls_bpf = {}; int err; - offload.cls_bpf = &bpf_offload; + tc_cls_common_offload_init(&cls_bpf.common, tp); + cls_bpf.command = cmd; + cls_bpf.exts = &prog->exts; + cls_bpf.prog = prog->filter; + cls_bpf.name = prog->bpf_name; + cls_bpf.exts_integrated = prog->exts_integrated; + cls_bpf.gen_flags = prog->gen_flags; - tc_cls_common_offload_init(&bpf_offload.common, tp); - bpf_offload.command = cmd; - bpf_offload.exts = &prog->exts; - bpf_offload.prog = prog->filter; - bpf_offload.name = prog->bpf_name; - bpf_offload.exts_integrated = prog->exts_integrated; - bpf_offload.gen_flags = prog->gen_flags; - - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSBPF, &cls_bpf); if (!err && (cmd == TC_CLSBPF_ADD || cmd == TC_CLSBPF_REPLACE)) prog->gen_flags |= TCA_CLS_FLAGS_IN_HW; diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index ccdf2f5014ca..1474bacf4df4 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -88,7 +88,6 @@ struct cls_fl_filter { u32 handle; u32 flags; struct rcu_head rcu; - struct tc_to_netdev tc; struct net_device *hw_dev; }; @@ -225,20 +224,17 @@ static void fl_destroy_filter(struct rcu_head *head) static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) { - struct tc_cls_flower_offload offload = {0}; + struct tc_cls_flower_offload cls_flower = {}; struct net_device *dev = f->hw_dev; - struct tc_to_netdev *tc = &f->tc; if (!tc_can_offload(dev, tp)) return; - tc_cls_common_offload_init(&offload.common, tp); - offload.command = TC_CLSFLOWER_DESTROY; - offload.cookie = (unsigned long)f; + tc_cls_common_offload_init(&cls_flower.common, tp); + cls_flower.command = TC_CLSFLOWER_DESTROY; + cls_flower.cookie = (unsigned long) f; - tc->cls_flower = &offload; - - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, &cls_flower); } static int fl_hw_replace_filter(struct tcf_proto *tp, @@ -247,8 +243,7 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, struct cls_fl_filter *f) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_flower_offload offload = {0}; - struct tc_to_netdev *tc = &f->tc; + struct tc_cls_flower_offload cls_flower = {}; int err; if (!tc_can_offload(dev, tp)) { @@ -258,22 +253,21 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, return tc_skip_sw(f->flags) ? -EINVAL : 0; } dev = f->hw_dev; - offload.egress_dev = true; + cls_flower.egress_dev = true; } else { f->hw_dev = dev; } - tc_cls_common_offload_init(&offload.common, tp); - offload.command = TC_CLSFLOWER_REPLACE; - offload.cookie = (unsigned long)f; - offload.dissector = dissector; - offload.mask = mask; - offload.key = &f->mkey; - offload.exts = &f->exts; - - tc->cls_flower = &offload; + tc_cls_common_offload_init(&cls_flower.common, tp); + cls_flower.command = TC_CLSFLOWER_REPLACE; + cls_flower.cookie = (unsigned long) f; + cls_flower.dissector = dissector; + cls_flower.mask = mask; + cls_flower.key = &f->mkey; + cls_flower.exts = &f->exts; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, + &cls_flower); if (!err) f->flags |= TCA_CLS_FLAGS_IN_HW; @@ -284,21 +278,19 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) { - struct tc_cls_flower_offload offload = {0}; + struct tc_cls_flower_offload cls_flower = {}; struct net_device *dev = f->hw_dev; - struct tc_to_netdev *tc = &f->tc; if (!tc_can_offload(dev, tp)) return; - tc_cls_common_offload_init(&offload.common, tp); - offload.command = TC_CLSFLOWER_STATS; - offload.cookie = (unsigned long)f; - offload.exts = &f->exts; - - tc->cls_flower = &offload; + tc_cls_common_offload_init(&cls_flower.common, tp); + cls_flower.command = TC_CLSFLOWER_STATS; + cls_flower.cookie = (unsigned long) f; + cls_flower.exts = &f->exts; - dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, + &cls_flower); } static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index 174c700160ca..c9f6500b8080 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -54,18 +54,16 @@ static int mall_replace_hw_filter(struct tcf_proto *tp, unsigned long cookie) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_to_netdev offload; - struct tc_cls_matchall_offload mall_offload = {0}; + struct tc_cls_matchall_offload cls_mall = {}; int err; - tc_cls_common_offload_init(&mall_offload.common, tp); - offload.cls_mall = &mall_offload; - offload.cls_mall->command = TC_CLSMATCHALL_REPLACE; - offload.cls_mall->exts = &head->exts; - offload.cls_mall->cookie = cookie; + tc_cls_common_offload_init(&cls_mall.common, tp); + cls_mall.command = TC_CLSMATCHALL_REPLACE; + cls_mall.exts = &head->exts; + cls_mall.cookie = cookie; err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, - &offload); + &cls_mall); if (!err) head->flags |= TCA_CLS_FLAGS_IN_HW; @@ -77,16 +75,13 @@ static void mall_destroy_hw_filter(struct tcf_proto *tp, unsigned long cookie) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_to_netdev offload; - struct tc_cls_matchall_offload mall_offload = {0}; + struct tc_cls_matchall_offload cls_mall = {}; - tc_cls_common_offload_init(&mall_offload.common, tp); - offload.cls_mall = &mall_offload; - offload.cls_mall->command = TC_CLSMATCHALL_DESTROY; - offload.cls_mall->exts = NULL; - offload.cls_mall->cookie = cookie; + tc_cls_common_offload_init(&cls_mall.common, tp); + cls_mall.command = TC_CLSMATCHALL_DESTROY; + cls_mall.cookie = cookie; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, &offload); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSMATCHALL, &cls_mall); } static void mall_destroy(struct tcf_proto *tp) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index c0f59c471523..4ed51d347d0a 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -431,39 +431,35 @@ static int u32_delete_key(struct tcf_proto *tp, struct tc_u_knode *key) static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_u32_offload u32_offload = {0}; - struct tc_to_netdev offload; + struct tc_cls_u32_offload cls_u32 = {}; - offload.cls_u32 = &u32_offload; + if (!tc_should_offload(dev, tp, 0)) + return; - if (tc_should_offload(dev, tp, 0)) { - tc_cls_common_offload_init(&u32_offload.common, tp); - offload.cls_u32->command = TC_CLSU32_DELETE_KNODE; - offload.cls_u32->knode.handle = handle; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); - } + tc_cls_common_offload_init(&cls_u32.common, tp); + cls_u32.command = TC_CLSU32_DELETE_KNODE; + cls_u32.knode.handle = handle; + + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32); } static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, u32 flags) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_u32_offload u32_offload = {0}; - struct tc_to_netdev offload; + struct tc_cls_u32_offload cls_u32 = {}; int err; if (!tc_should_offload(dev, tp, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; - offload.cls_u32 = &u32_offload; - - tc_cls_common_offload_init(&u32_offload.common, tp); - offload.cls_u32->command = TC_CLSU32_NEW_HNODE; - offload.cls_u32->hnode.divisor = h->divisor; - offload.cls_u32->hnode.handle = h->handle; - offload.cls_u32->hnode.prio = h->prio; + tc_cls_common_offload_init(&cls_u32.common, tp); + cls_u32.command = TC_CLSU32_NEW_HNODE; + cls_u32.hnode.divisor = h->divisor; + cls_u32.hnode.handle = h->handle; + cls_u32.hnode.prio = h->prio; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32); if (tc_skip_sw(flags)) return err; @@ -473,52 +469,47 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_u32_offload u32_offload = {0}; - struct tc_to_netdev offload; + struct tc_cls_u32_offload cls_u32 = {}; - offload.cls_u32 = &u32_offload; + if (!tc_should_offload(dev, tp, 0)) + return; - if (tc_should_offload(dev, tp, 0)) { - tc_cls_common_offload_init(&u32_offload.common, tp); - offload.cls_u32->command = TC_CLSU32_DELETE_HNODE; - offload.cls_u32->hnode.divisor = h->divisor; - offload.cls_u32->hnode.handle = h->handle; - offload.cls_u32->hnode.prio = h->prio; + tc_cls_common_offload_init(&cls_u32.common, tp); + cls_u32.command = TC_CLSU32_DELETE_HNODE; + cls_u32.hnode.divisor = h->divisor; + cls_u32.hnode.handle = h->handle; + cls_u32.hnode.prio = h->prio; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); - } + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32); } static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, u32 flags) { struct net_device *dev = tp->q->dev_queue->dev; - struct tc_cls_u32_offload u32_offload = {0}; - struct tc_to_netdev offload; + struct tc_cls_u32_offload cls_u32 = {}; int err; - offload.cls_u32 = &u32_offload; - if (!tc_should_offload(dev, tp, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; - tc_cls_common_offload_init(&u32_offload.common, tp); - offload.cls_u32->command = TC_CLSU32_REPLACE_KNODE; - offload.cls_u32->knode.handle = n->handle; - offload.cls_u32->knode.fshift = n->fshift; + tc_cls_common_offload_init(&cls_u32.common, tp); + cls_u32.command = TC_CLSU32_REPLACE_KNODE; + cls_u32.knode.handle = n->handle; + cls_u32.knode.fshift = n->fshift; #ifdef CONFIG_CLS_U32_MARK - offload.cls_u32->knode.val = n->val; - offload.cls_u32->knode.mask = n->mask; + cls_u32.knode.val = n->val; + cls_u32.knode.mask = n->mask; #else - offload.cls_u32->knode.val = 0; - offload.cls_u32->knode.mask = 0; + cls_u32.knode.val = 0; + cls_u32.knode.mask = 0; #endif - offload.cls_u32->knode.sel = &n->sel; - offload.cls_u32->knode.exts = &n->exts; + cls_u32.knode.sel = &n->sel; + cls_u32.knode.exts = &n->exts; if (n->ht_down) - offload.cls_u32->knode.link_handle = n->ht_down->handle; + cls_u32.knode.link_handle = n->ht_down->handle; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &offload); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSU32, &cls_u32); if (!err) n->flags |= TCA_CLS_FLAGS_IN_HW; diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 09b577dde49c..2165a05994b7 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -39,10 +39,9 @@ static void mqprio_destroy(struct Qdisc *sch) } if (priv->hw_offload && dev->netdev_ops->ndo_setup_tc) { - struct tc_mqprio_qopt offload = { 0 }; - struct tc_to_netdev tc = { { .mqprio = &offload } }; + struct tc_mqprio_qopt mqprio = {}; - dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &tc); + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &mqprio); } else { netdev_set_num_tc(dev, 0); } @@ -147,14 +146,14 @@ static int mqprio_init(struct Qdisc *sch, struct nlattr *opt) * supplied and verified mapping */ if (qopt->hw) { - struct tc_mqprio_qopt offload = *qopt; - struct tc_to_netdev tc = { { .mqprio = &offload } }; + struct tc_mqprio_qopt mqprio = *qopt; - err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, &tc); + err = dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_MQPRIO, + &mqprio); if (err) return err; - priv->hw_offload = offload.hw; + priv->hw_offload = mqprio.hw; } else { netdev_set_num_tc(dev, qopt->num_tc); for (i = 0; i < qopt->num_tc; i++) -- cgit v1.2.3 From fb74c27735f0a34e76dbf1972084e984ad2ea145 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 7 Aug 2017 08:44:16 -0700 Subject: net: ipv4: add second dif to udp socket lookups Add a second device index, sdif, to udp socket lookups. sdif is the index for ingress devices enslaved to an l3mdev. It allows the lookups to consider the enslaved device as well as the L3 domain when searching for a socket. Early demux lookups are handled in the next patch as part of INET_MATCH changes. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/net/ip.h | 10 +++++++++ include/net/udp.h | 2 +- net/ipv4/udp.c | 58 +++++++++++++++++++++++++++++++---------------------- net/ipv4/udp_diag.c | 6 +++--- 4 files changed, 48 insertions(+), 28 deletions(-) (limited to 'net') diff --git a/include/net/ip.h b/include/net/ip.h index 9e59dcf1787a..39db596eb89f 100644 --- a/include/net/ip.h +++ b/include/net/ip.h @@ -78,6 +78,16 @@ struct ipcm_cookie { #define IPCB(skb) ((struct inet_skb_parm*)((skb)->cb)) #define PKTINFO_SKB_CB(skb) ((struct in_pktinfo *)((skb)->cb)) +/* return enslaved device index if relevant */ +static inline int inet_sdif(struct sk_buff *skb) +{ +#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) + if (skb && ipv4_l3mdev_skb(IPCB(skb)->flags)) + return IPCB(skb)->iif; +#endif + return 0; +} + struct ip_ra_chain { struct ip_ra_chain __rcu *next; struct sock *sk; diff --git a/include/net/udp.h b/include/net/udp.h index cc8036987dcb..826c713d5a48 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -287,7 +287,7 @@ int udp_lib_setsockopt(struct sock *sk, int level, int optname, struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, __be32 daddr, __be16 dport, int dif); struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, - __be32 daddr, __be16 dport, int dif, + __be32 daddr, __be16 dport, int dif, int sdif, struct udp_table *tbl, struct sk_buff *skb); struct sock *udp4_lib_lookup_skb(struct sk_buff *skb, __be16 sport, __be16 dport); diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 38bca2c4897d..fe14429e4a6c 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -380,8 +380,8 @@ int udp_v4_get_port(struct sock *sk, unsigned short snum) static int compute_score(struct sock *sk, struct net *net, __be32 saddr, __be16 sport, - __be32 daddr, unsigned short hnum, int dif, - bool exact_dif) + __be32 daddr, unsigned short hnum, + int dif, int sdif, bool exact_dif) { int score; struct inet_sock *inet; @@ -413,10 +413,15 @@ static int compute_score(struct sock *sk, struct net *net, } if (sk->sk_bound_dev_if || exact_dif) { - if (sk->sk_bound_dev_if != dif) + bool dev_match = (sk->sk_bound_dev_if == dif || + sk->sk_bound_dev_if == sdif); + + if (exact_dif && !dev_match) return -1; - score += 4; + if (sk->sk_bound_dev_if && dev_match) + score += 4; } + if (sk->sk_incoming_cpu == raw_smp_processor_id()) score++; return score; @@ -436,10 +441,11 @@ static u32 udp_ehashfn(const struct net *net, const __be32 laddr, /* called with rcu_read_lock() */ static struct sock *udp4_lib_lookup2(struct net *net, - __be32 saddr, __be16 sport, - __be32 daddr, unsigned int hnum, int dif, bool exact_dif, - struct udp_hslot *hslot2, - struct sk_buff *skb) + __be32 saddr, __be16 sport, + __be32 daddr, unsigned int hnum, + int dif, int sdif, bool exact_dif, + struct udp_hslot *hslot2, + struct sk_buff *skb) { struct sock *sk, *result; int score, badness, matches = 0, reuseport = 0; @@ -449,7 +455,7 @@ static struct sock *udp4_lib_lookup2(struct net *net, badness = 0; udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { score = compute_score(sk, net, saddr, sport, - daddr, hnum, dif, exact_dif); + daddr, hnum, dif, sdif, exact_dif); if (score > badness) { reuseport = sk->sk_reuseport; if (reuseport) { @@ -477,8 +483,8 @@ static struct sock *udp4_lib_lookup2(struct net *net, * harder than this. -DaveM */ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, - __be16 sport, __be32 daddr, __be16 dport, - int dif, struct udp_table *udptable, struct sk_buff *skb) + __be16 sport, __be32 daddr, __be16 dport, int dif, + int sdif, struct udp_table *udptable, struct sk_buff *skb) { struct sock *sk, *result; unsigned short hnum = ntohs(dport); @@ -496,7 +502,7 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, goto begin; result = udp4_lib_lookup2(net, saddr, sport, - daddr, hnum, dif, + daddr, hnum, dif, sdif, exact_dif, hslot2, skb); if (!result) { unsigned int old_slot2 = slot2; @@ -511,7 +517,7 @@ struct sock *__udp4_lib_lookup(struct net *net, __be32 saddr, goto begin; result = udp4_lib_lookup2(net, saddr, sport, - daddr, hnum, dif, + daddr, hnum, dif, sdif, exact_dif, hslot2, skb); } return result; @@ -521,7 +527,7 @@ begin: badness = 0; sk_for_each_rcu(sk, &hslot->head) { score = compute_score(sk, net, saddr, sport, - daddr, hnum, dif, exact_dif); + daddr, hnum, dif, sdif, exact_dif); if (score > badness) { reuseport = sk->sk_reuseport; if (reuseport) { @@ -554,7 +560,7 @@ static inline struct sock *__udp4_lib_lookup_skb(struct sk_buff *skb, return __udp4_lib_lookup(dev_net(skb->dev), iph->saddr, sport, iph->daddr, dport, inet_iif(skb), - udptable, skb); + inet_sdif(skb), udptable, skb); } struct sock *udp4_lib_lookup_skb(struct sk_buff *skb, @@ -576,7 +582,7 @@ struct sock *udp4_lib_lookup(struct net *net, __be32 saddr, __be16 sport, struct sock *sk; sk = __udp4_lib_lookup(net, saddr, sport, daddr, dport, - dif, &udp_table, NULL); + dif, 0, &udp_table, NULL); if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) sk = NULL; return sk; @@ -587,7 +593,7 @@ EXPORT_SYMBOL_GPL(udp4_lib_lookup); static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk, __be16 loc_port, __be32 loc_addr, __be16 rmt_port, __be32 rmt_addr, - int dif, unsigned short hnum) + int dif, int sdif, unsigned short hnum) { struct inet_sock *inet = inet_sk(sk); @@ -597,7 +603,8 @@ static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk, (inet->inet_dport != rmt_port && inet->inet_dport) || (inet->inet_rcv_saddr && inet->inet_rcv_saddr != loc_addr) || ipv6_only_sock(sk) || - (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)) + (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif && + sk->sk_bound_dev_if != sdif)) return false; if (!ip_mc_sf_allow(sk, loc_addr, rmt_addr, dif)) return false; @@ -628,8 +635,8 @@ void __udp4_lib_err(struct sk_buff *skb, u32 info, struct udp_table *udptable) struct net *net = dev_net(skb->dev); sk = __udp4_lib_lookup(net, iph->daddr, uh->dest, - iph->saddr, uh->source, skb->dev->ifindex, udptable, - NULL); + iph->saddr, uh->source, skb->dev->ifindex, 0, + udptable, NULL); if (!sk) { __ICMP_INC_STATS(net, ICMP_MIB_INERRORS); return; /* No socket for error */ @@ -1953,6 +1960,7 @@ static int __udp4_lib_mcast_deliver(struct net *net, struct sk_buff *skb, unsigned int hash2 = 0, hash2_any = 0, use_hash2 = (hslot->count > 10); unsigned int offset = offsetof(typeof(*sk), sk_node); int dif = skb->dev->ifindex; + int sdif = inet_sdif(skb); struct hlist_node *node; struct sk_buff *nskb; @@ -1967,7 +1975,7 @@ start_lookup: sk_for_each_entry_offset_rcu(sk, node, &hslot->head, offset) { if (!__udp_is_mcast_sock(net, sk, uh->dest, daddr, - uh->source, saddr, dif, hnum)) + uh->source, saddr, dif, sdif, hnum)) continue; if (!first) { @@ -2157,7 +2165,7 @@ drop: static struct sock *__udp4_lib_mcast_demux_lookup(struct net *net, __be16 loc_port, __be32 loc_addr, __be16 rmt_port, __be32 rmt_addr, - int dif) + int dif, int sdif) { struct sock *sk, *result; unsigned short hnum = ntohs(loc_port); @@ -2171,7 +2179,7 @@ static struct sock *__udp4_lib_mcast_demux_lookup(struct net *net, result = NULL; sk_for_each_rcu(sk, &hslot->head) { if (__udp_is_mcast_sock(net, sk, loc_port, loc_addr, - rmt_port, rmt_addr, dif, hnum)) { + rmt_port, rmt_addr, dif, sdif, hnum)) { if (result) return NULL; result = sk; @@ -2216,6 +2224,7 @@ void udp_v4_early_demux(struct sk_buff *skb) struct sock *sk = NULL; struct dst_entry *dst; int dif = skb->dev->ifindex; + int sdif = inet_sdif(skb); int ours; /* validate the packet */ @@ -2241,7 +2250,8 @@ void udp_v4_early_demux(struct sk_buff *skb) } sk = __udp4_lib_mcast_demux_lookup(net, uh->dest, iph->daddr, - uh->source, iph->saddr, dif); + uh->source, iph->saddr, + dif, sdif); } else if (skb->pkt_type == PACKET_HOST) { sk = __udp4_lib_demux_lookup(net, uh->dest, iph->daddr, uh->source, iph->saddr, dif); diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 4515836d2a3a..1f07fe109535 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -45,7 +45,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, sk = __udp4_lib_lookup(net, req->id.idiag_src[0], req->id.idiag_sport, req->id.idiag_dst[0], req->id.idiag_dport, - req->id.idiag_if, tbl, NULL); + req->id.idiag_if, 0, tbl, NULL); #if IS_ENABLED(CONFIG_IPV6) else if (req->sdiag_family == AF_INET6) sk = __udp6_lib_lookup(net, @@ -182,7 +182,7 @@ static int __udp_diag_destroy(struct sk_buff *in_skb, sk = __udp4_lib_lookup(net, req->id.idiag_dst[0], req->id.idiag_dport, req->id.idiag_src[0], req->id.idiag_sport, - req->id.idiag_if, tbl, NULL); + req->id.idiag_if, 0, tbl, NULL); #if IS_ENABLED(CONFIG_IPV6) else if (req->sdiag_family == AF_INET6) { if (ipv6_addr_v4mapped((struct in6_addr *)req->id.idiag_dst) && @@ -190,7 +190,7 @@ static int __udp_diag_destroy(struct sk_buff *in_skb, sk = __udp4_lib_lookup(net, req->id.idiag_dst[3], req->id.idiag_dport, req->id.idiag_src[3], req->id.idiag_sport, - req->id.idiag_if, tbl, NULL); + req->id.idiag_if, 0, tbl, NULL); else sk = __udp6_lib_lookup(net, -- cgit v1.2.3 From 3fa6f616a7a4d0bdf4d877d530456d8a5c3b109b Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 7 Aug 2017 08:44:17 -0700 Subject: net: ipv4: add second dif to inet socket lookups Add a second device index, sdif, to inet socket lookups. sdif is the index for ingress devices enslaved to an l3mdev. It allows the lookups to consider the enslaved device as well as the L3 domain when searching for a socket. TCP moves the data in the cb. Prior to tcp_v4_rcv (e.g., early demux) the ingress index is obtained from IPCB using inet_sdif and after the cb move in tcp_v4_rcv the tcp_v4_sdif helper is used. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/net/inet_hashtables.h | 31 +++++++++++++++++-------------- include/net/tcp.h | 10 ++++++++++ net/dccp/ipv4.c | 4 ++-- net/ipv4/inet_hashtables.c | 27 +++++++++++++++++---------- net/ipv4/tcp_ipv4.c | 13 ++++++++----- net/ipv4/udp.c | 6 +++--- net/netfilter/xt_TPROXY.c | 2 +- 7 files changed, 58 insertions(+), 35 deletions(-) (limited to 'net') diff --git a/include/net/inet_hashtables.h b/include/net/inet_hashtables.h index 5026b1f08bb8..2dbbbff5e1e3 100644 --- a/include/net/inet_hashtables.h +++ b/include/net/inet_hashtables.h @@ -221,16 +221,16 @@ struct sock *__inet_lookup_listener(struct net *net, const __be32 saddr, const __be16 sport, const __be32 daddr, const unsigned short hnum, - const int dif); + const int dif, const int sdif); static inline struct sock *inet_lookup_listener(struct net *net, struct inet_hashinfo *hashinfo, struct sk_buff *skb, int doff, __be32 saddr, __be16 sport, - __be32 daddr, __be16 dport, int dif) + __be32 daddr, __be16 dport, int dif, int sdif) { return __inet_lookup_listener(net, hashinfo, skb, doff, saddr, sport, - daddr, ntohs(dport), dif); + daddr, ntohs(dport), dif, sdif); } /* Socket demux engine toys. */ @@ -262,22 +262,24 @@ static inline struct sock *inet_lookup_listener(struct net *net, (((__force __u64)(__be32)(__daddr)) << 32) | \ ((__force __u64)(__be32)(__saddr))) #endif /* __BIG_ENDIAN */ -#define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ +#define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif, __sdif) \ (((__sk)->sk_portpair == (__ports)) && \ ((__sk)->sk_addrpair == (__cookie)) && \ (!(__sk)->sk_bound_dev_if || \ - ((__sk)->sk_bound_dev_if == (__dif))) && \ + ((__sk)->sk_bound_dev_if == (__dif)) || \ + ((__sk)->sk_bound_dev_if == (__sdif))) && \ net_eq(sock_net(__sk), (__net))) #else /* 32-bit arch */ #define INET_ADDR_COOKIE(__name, __saddr, __daddr) \ const int __name __deprecated __attribute__((unused)) -#define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif) \ +#define INET_MATCH(__sk, __net, __cookie, __saddr, __daddr, __ports, __dif, __sdif) \ (((__sk)->sk_portpair == (__ports)) && \ ((__sk)->sk_daddr == (__saddr)) && \ ((__sk)->sk_rcv_saddr == (__daddr)) && \ (!(__sk)->sk_bound_dev_if || \ - ((__sk)->sk_bound_dev_if == (__dif))) && \ + ((__sk)->sk_bound_dev_if == (__dif)) || \ + ((__sk)->sk_bound_dev_if == (__sdif))) && \ net_eq(sock_net(__sk), (__net))) #endif /* 64-bit arch */ @@ -288,7 +290,7 @@ struct sock *__inet_lookup_established(struct net *net, struct inet_hashinfo *hashinfo, const __be32 saddr, const __be16 sport, const __be32 daddr, const u16 hnum, - const int dif); + const int dif, const int sdif); static inline struct sock * inet_lookup_established(struct net *net, struct inet_hashinfo *hashinfo, @@ -297,7 +299,7 @@ static inline struct sock * const int dif) { return __inet_lookup_established(net, hashinfo, saddr, sport, daddr, - ntohs(dport), dif); + ntohs(dport), dif, 0); } static inline struct sock *__inet_lookup(struct net *net, @@ -305,20 +307,20 @@ static inline struct sock *__inet_lookup(struct net *net, struct sk_buff *skb, int doff, const __be32 saddr, const __be16 sport, const __be32 daddr, const __be16 dport, - const int dif, + const int dif, const int sdif, bool *refcounted) { u16 hnum = ntohs(dport); struct sock *sk; sk = __inet_lookup_established(net, hashinfo, saddr, sport, - daddr, hnum, dif); + daddr, hnum, dif, sdif); *refcounted = true; if (sk) return sk; *refcounted = false; return __inet_lookup_listener(net, hashinfo, skb, doff, saddr, - sport, daddr, hnum, dif); + sport, daddr, hnum, dif, sdif); } static inline struct sock *inet_lookup(struct net *net, @@ -332,7 +334,7 @@ static inline struct sock *inet_lookup(struct net *net, bool refcounted; sk = __inet_lookup(net, hashinfo, skb, doff, saddr, sport, daddr, - dport, dif, &refcounted); + dport, dif, 0, &refcounted); if (sk && !refcounted && !refcount_inc_not_zero(&sk->sk_refcnt)) sk = NULL; @@ -344,6 +346,7 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo, int doff, const __be16 sport, const __be16 dport, + const int sdif, bool *refcounted) { struct sock *sk = skb_steal_sock(skb); @@ -355,7 +358,7 @@ static inline struct sock *__inet_lookup_skb(struct inet_hashinfo *hashinfo, return __inet_lookup(dev_net(skb_dst(skb)->dev), hashinfo, skb, doff, iph->saddr, sport, - iph->daddr, dport, inet_iif(skb), + iph->daddr, dport, inet_iif(skb), sdif, refcounted); } diff --git a/include/net/tcp.h b/include/net/tcp.h index 5173fecde495..2b89f1ab8552 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -840,6 +840,16 @@ static inline bool inet_exact_dif_match(struct net *net, struct sk_buff *skb) return false; } +/* TCP_SKB_CB reference means this can not be used from early demux */ +static inline int tcp_v4_sdif(struct sk_buff *skb) +{ +#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) + if (skb && ipv4_l3mdev_skb(TCP_SKB_CB(skb)->header.h4.flags)) + return TCP_SKB_CB(skb)->header.h4.iif; +#endif + return 0; +} + /* Due to TSO, an SKB can be composed of multiple actual * packets. To keep these tracked properly, we use this. */ diff --git a/net/dccp/ipv4.c b/net/dccp/ipv4.c index 1b202f16531f..001c08696334 100644 --- a/net/dccp/ipv4.c +++ b/net/dccp/ipv4.c @@ -256,7 +256,7 @@ static void dccp_v4_err(struct sk_buff *skb, u32 info) sk = __inet_lookup_established(net, &dccp_hashinfo, iph->daddr, dh->dccph_dport, iph->saddr, ntohs(dh->dccph_sport), - inet_iif(skb)); + inet_iif(skb), 0); if (!sk) { __ICMP_INC_STATS(net, ICMP_MIB_INERRORS); return; @@ -804,7 +804,7 @@ static int dccp_v4_rcv(struct sk_buff *skb) lookup: sk = __inet_lookup_skb(&dccp_hashinfo, skb, __dccp_hdr_len(dh), - dh->dccph_sport, dh->dccph_dport, &refcounted); + dh->dccph_sport, dh->dccph_dport, 0, &refcounted); if (!sk) { dccp_pr_debug("failed to look up flow ID in table and " "get corresponding socket\n"); diff --git a/net/ipv4/inet_hashtables.c b/net/ipv4/inet_hashtables.c index 2e3389d614d1..597bb4cfe805 100644 --- a/net/ipv4/inet_hashtables.c +++ b/net/ipv4/inet_hashtables.c @@ -170,7 +170,7 @@ EXPORT_SYMBOL_GPL(__inet_inherit_port); static inline int compute_score(struct sock *sk, struct net *net, const unsigned short hnum, const __be32 daddr, - const int dif, bool exact_dif) + const int dif, const int sdif, bool exact_dif) { int score = -1; struct inet_sock *inet = inet_sk(sk); @@ -185,9 +185,13 @@ static inline int compute_score(struct sock *sk, struct net *net, score += 4; } if (sk->sk_bound_dev_if || exact_dif) { - if (sk->sk_bound_dev_if != dif) + bool dev_match = (sk->sk_bound_dev_if == dif || + sk->sk_bound_dev_if == sdif); + + if (exact_dif && !dev_match) return -1; - score += 4; + if (sk->sk_bound_dev_if && dev_match) + score += 4; } if (sk->sk_incoming_cpu == raw_smp_processor_id()) score++; @@ -208,7 +212,7 @@ struct sock *__inet_lookup_listener(struct net *net, struct sk_buff *skb, int doff, const __be32 saddr, __be16 sport, const __be32 daddr, const unsigned short hnum, - const int dif) + const int dif, const int sdif) { unsigned int hash = inet_lhashfn(net, hnum); struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash]; @@ -218,7 +222,8 @@ struct sock *__inet_lookup_listener(struct net *net, u32 phash = 0; sk_for_each_rcu(sk, &ilb->head) { - score = compute_score(sk, net, hnum, daddr, dif, exact_dif); + score = compute_score(sk, net, hnum, daddr, + dif, sdif, exact_dif); if (score > hiscore) { reuseport = sk->sk_reuseport; if (reuseport) { @@ -268,7 +273,7 @@ struct sock *__inet_lookup_established(struct net *net, struct inet_hashinfo *hashinfo, const __be32 saddr, const __be16 sport, const __be32 daddr, const u16 hnum, - const int dif) + const int dif, const int sdif) { INET_ADDR_COOKIE(acookie, saddr, daddr); const __portpair ports = INET_COMBINED_PORTS(sport, hnum); @@ -286,11 +291,12 @@ begin: if (sk->sk_hash != hash) continue; if (likely(INET_MATCH(sk, net, acookie, - saddr, daddr, ports, dif))) { + saddr, daddr, ports, dif, sdif))) { if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt))) goto out; if (unlikely(!INET_MATCH(sk, net, acookie, - saddr, daddr, ports, dif))) { + saddr, daddr, ports, + dif, sdif))) { sock_gen_put(sk); goto begin; } @@ -321,9 +327,10 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row, __be32 daddr = inet->inet_rcv_saddr; __be32 saddr = inet->inet_daddr; int dif = sk->sk_bound_dev_if; + struct net *net = sock_net(sk); + int sdif = l3mdev_master_ifindex_by_index(net, dif); INET_ADDR_COOKIE(acookie, saddr, daddr); const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport); - struct net *net = sock_net(sk); unsigned int hash = inet_ehashfn(net, daddr, lport, saddr, inet->inet_dport); struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash); @@ -339,7 +346,7 @@ static int __inet_check_established(struct inet_timewait_death_row *death_row, continue; if (likely(INET_MATCH(sk2, net, acookie, - saddr, daddr, ports, dif))) { + saddr, daddr, ports, dif, sdif))) { if (sk2->sk_state == TCP_TIME_WAIT) { tw = inet_twsk(sk2); if (twsk_unique(sk, sk2, twp)) diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5f708c85110e..c8784ab37852 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -383,7 +383,7 @@ void tcp_v4_err(struct sk_buff *icmp_skb, u32 info) sk = __inet_lookup_established(net, &tcp_hashinfo, iph->daddr, th->dest, iph->saddr, ntohs(th->source), - inet_iif(icmp_skb)); + inet_iif(icmp_skb), 0); if (!sk) { __ICMP_INC_STATS(net, ICMP_MIB_INERRORS); return; @@ -659,7 +659,8 @@ static void tcp_v4_send_reset(const struct sock *sk, struct sk_buff *skb) sk1 = __inet_lookup_listener(net, &tcp_hashinfo, NULL, 0, ip_hdr(skb)->saddr, th->source, ip_hdr(skb)->daddr, - ntohs(th->source), inet_iif(skb)); + ntohs(th->source), inet_iif(skb), + tcp_v4_sdif(skb)); /* don't send rst if it can't find key */ if (!sk1) goto out; @@ -1523,7 +1524,7 @@ void tcp_v4_early_demux(struct sk_buff *skb) sk = __inet_lookup_established(dev_net(skb->dev), &tcp_hashinfo, iph->saddr, th->source, iph->daddr, ntohs(th->dest), - skb->skb_iif); + skb->skb_iif, inet_sdif(skb)); if (sk) { skb->sk = sk; skb->destructor = sock_edemux; @@ -1588,6 +1589,7 @@ EXPORT_SYMBOL(tcp_filter); int tcp_v4_rcv(struct sk_buff *skb) { struct net *net = dev_net(skb->dev); + int sdif = inet_sdif(skb); const struct iphdr *iph; const struct tcphdr *th; bool refcounted; @@ -1638,7 +1640,7 @@ int tcp_v4_rcv(struct sk_buff *skb) lookup: sk = __inet_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th), th->source, - th->dest, &refcounted); + th->dest, sdif, &refcounted); if (!sk) goto no_tcp_socket; @@ -1766,7 +1768,8 @@ do_time_wait: __tcp_hdrlen(th), iph->saddr, th->source, iph->daddr, th->dest, - inet_iif(skb)); + inet_iif(skb), + sdif); if (sk2) { inet_twsk_deschedule_put(inet_twsk(sk)); sk = sk2; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index fe14429e4a6c..99f25bfec606 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -2196,7 +2196,7 @@ static struct sock *__udp4_lib_mcast_demux_lookup(struct net *net, static struct sock *__udp4_lib_demux_lookup(struct net *net, __be16 loc_port, __be32 loc_addr, __be16 rmt_port, __be32 rmt_addr, - int dif) + int dif, int sdif) { unsigned short hnum = ntohs(loc_port); unsigned int hash2 = udp4_portaddr_hash(net, loc_addr, hnum); @@ -2208,7 +2208,7 @@ static struct sock *__udp4_lib_demux_lookup(struct net *net, udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { if (INET_MATCH(sk, net, acookie, rmt_addr, - loc_addr, ports, dif)) + loc_addr, ports, dif, sdif)) return sk; /* Only check first socket in chain */ break; @@ -2254,7 +2254,7 @@ void udp_v4_early_demux(struct sk_buff *skb) dif, sdif); } else if (skb->pkt_type == PACKET_HOST) { sk = __udp4_lib_demux_lookup(net, uh->dest, iph->daddr, - uh->source, iph->saddr, dif); + uh->source, iph->saddr, dif, sdif); } if (!sk || !refcount_inc_not_zero(&sk->sk_refcnt)) diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index d767e35fff6b..94fb0fd0c667 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -125,7 +125,7 @@ nf_tproxy_get_sock_v4(struct net *net, struct sk_buff *skb, void *hp, __tcp_hdrlen(tcph), saddr, sport, daddr, dport, - in->ifindex); + in->ifindex, 0); if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) sk = NULL; -- cgit v1.2.3 From 67359930e185c491b47cb958d5f1d6c1af4598a2 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 7 Aug 2017 08:44:18 -0700 Subject: net: ipv4: add second dif to raw socket lookups Add a second device index, sdif, to raw socket lookups. sdif is the index for ingress devices enslaved to an l3mdev. It allows the lookups to consider the enslaved device as well as the L3 domain when searching for a socket. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/net/raw.h | 2 +- net/ipv4/raw.c | 16 +++++++++++----- net/ipv4/raw_diag.c | 2 +- 3 files changed, 13 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/raw.h b/include/net/raw.h index 57c33dd22ec4..99d26d0c4a19 100644 --- a/include/net/raw.h +++ b/include/net/raw.h @@ -26,7 +26,7 @@ extern struct proto raw_prot; extern struct raw_hashinfo raw_v4_hashinfo; struct sock *__raw_v4_lookup(struct net *net, struct sock *sk, unsigned short num, __be32 raddr, - __be32 laddr, int dif); + __be32 laddr, int dif, int sdif); int raw_abort(struct sock *sk, int err); void raw_icmp_error(struct sk_buff *, int, u32); diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index b0bb5d0a30bd..2726aecf224b 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -122,7 +122,8 @@ void raw_unhash_sk(struct sock *sk) EXPORT_SYMBOL_GPL(raw_unhash_sk); struct sock *__raw_v4_lookup(struct net *net, struct sock *sk, - unsigned short num, __be32 raddr, __be32 laddr, int dif) + unsigned short num, __be32 raddr, __be32 laddr, + int dif, int sdif) { sk_for_each_from(sk) { struct inet_sock *inet = inet_sk(sk); @@ -130,7 +131,8 @@ struct sock *__raw_v4_lookup(struct net *net, struct sock *sk, if (net_eq(sock_net(sk), net) && inet->inet_num == num && !(inet->inet_daddr && inet->inet_daddr != raddr) && !(inet->inet_rcv_saddr && inet->inet_rcv_saddr != laddr) && - !(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif)) + !(sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif && + sk->sk_bound_dev_if != sdif)) goto found; /* gotcha */ } sk = NULL; @@ -171,6 +173,7 @@ static int icmp_filter(const struct sock *sk, const struct sk_buff *skb) */ static int raw_v4_input(struct sk_buff *skb, const struct iphdr *iph, int hash) { + int sdif = inet_sdif(skb); struct sock *sk; struct hlist_head *head; int delivered = 0; @@ -184,7 +187,7 @@ static int raw_v4_input(struct sk_buff *skb, const struct iphdr *iph, int hash) net = dev_net(skb->dev); sk = __raw_v4_lookup(net, __sk_head(head), iph->protocol, iph->saddr, iph->daddr, - skb->dev->ifindex); + skb->dev->ifindex, sdif); while (sk) { delivered = 1; @@ -199,7 +202,7 @@ static int raw_v4_input(struct sk_buff *skb, const struct iphdr *iph, int hash) } sk = __raw_v4_lookup(net, sk_next(sk), iph->protocol, iph->saddr, iph->daddr, - skb->dev->ifindex); + skb->dev->ifindex, sdif); } out: read_unlock(&raw_v4_hashinfo.lock); @@ -297,12 +300,15 @@ void raw_icmp_error(struct sk_buff *skb, int protocol, u32 info) read_lock(&raw_v4_hashinfo.lock); raw_sk = sk_head(&raw_v4_hashinfo.ht[hash]); if (raw_sk) { + int dif = skb->dev->ifindex; + int sdif = inet_sdif(skb); + iph = (const struct iphdr *)skb->data; net = dev_net(skb->dev); while ((raw_sk = __raw_v4_lookup(net, raw_sk, protocol, iph->daddr, iph->saddr, - skb->dev->ifindex)) != NULL) { + dif, sdif)) != NULL) { raw_err(raw_sk, skb, info); raw_sk = sk_next(raw_sk); iph = (const struct iphdr *)skb->data; diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c index e1a51ca68d23..c600d3c71d4d 100644 --- a/net/ipv4/raw_diag.c +++ b/net/ipv4/raw_diag.c @@ -46,7 +46,7 @@ static struct sock *raw_lookup(struct net *net, struct sock *from, sk = __raw_v4_lookup(net, from, r->sdiag_raw_protocol, r->id.idiag_dst[0], r->id.idiag_src[0], - r->id.idiag_if); + r->id.idiag_if, 0); #if IS_ENABLED(CONFIG_IPV6) else sk = __raw_v6_lookup(net, from, r->sdiag_raw_protocol, -- cgit v1.2.3 From 60d9b03141243589dacd3136f3fcb4e6976df954 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 7 Aug 2017 08:44:19 -0700 Subject: net: ipv4: add second dif to multicast source filter Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/linux/igmp.h | 3 ++- net/ipv4/igmp.c | 6 ++++-- net/ipv4/raw.c | 2 +- net/ipv4/udp.c | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/linux/igmp.h b/include/linux/igmp.h index 97caf1821de8..f8231854b5d6 100644 --- a/include/linux/igmp.h +++ b/include/linux/igmp.h @@ -118,7 +118,8 @@ extern int ip_mc_msfget(struct sock *sk, struct ip_msfilter *msf, struct ip_msfilter __user *optval, int __user *optlen); extern int ip_mc_gsfget(struct sock *sk, struct group_filter *gsf, struct group_filter __user *optval, int __user *optlen); -extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt, int dif); +extern int ip_mc_sf_allow(struct sock *sk, __be32 local, __be32 rmt, + int dif, int sdif); extern void ip_mc_init_dev(struct in_device *); extern void ip_mc_destroy_dev(struct in_device *); extern void ip_mc_up(struct in_device *); diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 28f14afd0dd3..5bc8570c2ec3 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -2549,7 +2549,8 @@ done: /* * check if a multicast source filter allows delivery for a given */ -int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) +int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, + int dif, int sdif) { struct inet_sock *inet = inet_sk(sk); struct ip_mc_socklist *pmc; @@ -2564,7 +2565,8 @@ int ip_mc_sf_allow(struct sock *sk, __be32 loc_addr, __be32 rmt_addr, int dif) rcu_read_lock(); for_each_pmc_rcu(inet, pmc) { if (pmc->multi.imr_multiaddr.s_addr == loc_addr && - pmc->multi.imr_ifindex == dif) + (pmc->multi.imr_ifindex == dif || + (sdif && pmc->multi.imr_ifindex == sdif))) break; } ret = inet->mc_all; diff --git a/net/ipv4/raw.c b/net/ipv4/raw.c index 2726aecf224b..33b70bfd1122 100644 --- a/net/ipv4/raw.c +++ b/net/ipv4/raw.c @@ -193,7 +193,7 @@ static int raw_v4_input(struct sk_buff *skb, const struct iphdr *iph, int hash) delivered = 1; if ((iph->protocol != IPPROTO_ICMP || !icmp_filter(sk, skb)) && ip_mc_sf_allow(sk, iph->daddr, iph->saddr, - skb->dev->ifindex)) { + skb->dev->ifindex, sdif)) { struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC); /* Not releasing hash table! */ diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 99f25bfec606..cac59d7420cd 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -606,7 +606,7 @@ static inline bool __udp_is_mcast_sock(struct net *net, struct sock *sk, (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif && sk->sk_bound_dev_if != sdif)) return false; - if (!ip_mc_sf_allow(sk, loc_addr, rmt_addr, dif)) + if (!ip_mc_sf_allow(sk, loc_addr, rmt_addr, dif, sdif)) return false; return true; } -- cgit v1.2.3 From 1801b570dd2ae50b90231f283e79a9a94fbe7875 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 7 Aug 2017 08:44:20 -0700 Subject: net: ipv6: add second dif to udp socket lookups Add a second device index, sdif, to udp socket lookups. sdif is the index for ingress devices enslaved to an l3mdev. It allows the lookups to consider the enslaved device as well as the L3 domain when searching for a socket. Early demux lookups are handled in the next patch as part of INET_MATCH changes. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/linux/ipv6.h | 10 ++++++++++ include/net/udp.h | 2 +- net/ipv4/udp_diag.c | 4 ++-- net/ipv6/udp.c | 40 ++++++++++++++++++++++------------------ 4 files changed, 35 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/include/linux/ipv6.h b/include/linux/ipv6.h index 474d6bbc158c..ac2da4e11d5e 100644 --- a/include/linux/ipv6.h +++ b/include/linux/ipv6.h @@ -158,6 +158,16 @@ static inline bool inet6_is_jumbogram(const struct sk_buff *skb) return !!(IP6CB(skb)->flags & IP6SKB_JUMBOGRAM); } +/* can not be used in TCP layer after tcp_v6_fill_cb */ +static inline int inet6_sdif(const struct sk_buff *skb) +{ +#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) + if (skb && ipv6_l3mdev_skb(IP6CB(skb)->flags)) + return IP6CB(skb)->iif; +#endif + return 0; +} + /* can not be used in TCP layer after tcp_v6_fill_cb */ static inline bool inet6_exact_dif_match(struct net *net, struct sk_buff *skb) { diff --git a/include/net/udp.h b/include/net/udp.h index 826c713d5a48..20dcdca4e85c 100644 --- a/include/net/udp.h +++ b/include/net/udp.h @@ -298,7 +298,7 @@ struct sock *udp6_lib_lookup(struct net *net, struct sock *__udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport, const struct in6_addr *daddr, __be16 dport, - int dif, struct udp_table *tbl, + int dif, int sdif, struct udp_table *tbl, struct sk_buff *skb); struct sock *udp6_lib_lookup_skb(struct sk_buff *skb, __be16 sport, __be16 dport); diff --git a/net/ipv4/udp_diag.c b/net/ipv4/udp_diag.c index 1f07fe109535..d0390d844ac8 100644 --- a/net/ipv4/udp_diag.c +++ b/net/ipv4/udp_diag.c @@ -53,7 +53,7 @@ static int udp_dump_one(struct udp_table *tbl, struct sk_buff *in_skb, req->id.idiag_sport, (struct in6_addr *)req->id.idiag_dst, req->id.idiag_dport, - req->id.idiag_if, tbl, NULL); + req->id.idiag_if, 0, tbl, NULL); #endif if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) sk = NULL; @@ -198,7 +198,7 @@ static int __udp_diag_destroy(struct sk_buff *in_skb, req->id.idiag_dport, (struct in6_addr *)req->id.idiag_src, req->id.idiag_sport, - req->id.idiag_if, tbl, NULL); + req->id.idiag_if, 0, tbl, NULL); } #endif else { diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 578142b7ca3e..d96a877798a7 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -129,7 +129,7 @@ static void udp_v6_rehash(struct sock *sk) static int compute_score(struct sock *sk, struct net *net, const struct in6_addr *saddr, __be16 sport, const struct in6_addr *daddr, unsigned short hnum, - int dif, bool exact_dif) + int dif, int sdif, bool exact_dif) { int score; struct inet_sock *inet; @@ -161,9 +161,13 @@ static int compute_score(struct sock *sk, struct net *net, } if (sk->sk_bound_dev_if || exact_dif) { - if (sk->sk_bound_dev_if != dif) + bool dev_match = (sk->sk_bound_dev_if == dif || + sk->sk_bound_dev_if == sdif); + + if (exact_dif && !dev_match) return -1; - score++; + if (sk->sk_bound_dev_if && dev_match) + score++; } if (sk->sk_incoming_cpu == raw_smp_processor_id()) @@ -175,9 +179,9 @@ static int compute_score(struct sock *sk, struct net *net, /* called with rcu_read_lock() */ static struct sock *udp6_lib_lookup2(struct net *net, const struct in6_addr *saddr, __be16 sport, - const struct in6_addr *daddr, unsigned int hnum, int dif, - bool exact_dif, struct udp_hslot *hslot2, - struct sk_buff *skb) + const struct in6_addr *daddr, unsigned int hnum, + int dif, int sdif, bool exact_dif, + struct udp_hslot *hslot2, struct sk_buff *skb) { struct sock *sk, *result; int score, badness, matches = 0, reuseport = 0; @@ -187,7 +191,7 @@ static struct sock *udp6_lib_lookup2(struct net *net, badness = -1; udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { score = compute_score(sk, net, saddr, sport, - daddr, hnum, dif, exact_dif); + daddr, hnum, dif, sdif, exact_dif); if (score > badness) { reuseport = sk->sk_reuseport; if (reuseport) { @@ -214,10 +218,10 @@ static struct sock *udp6_lib_lookup2(struct net *net, /* rcu_read_lock() must be held */ struct sock *__udp6_lib_lookup(struct net *net, - const struct in6_addr *saddr, __be16 sport, - const struct in6_addr *daddr, __be16 dport, - int dif, struct udp_table *udptable, - struct sk_buff *skb) + const struct in6_addr *saddr, __be16 sport, + const struct in6_addr *daddr, __be16 dport, + int dif, int sdif, struct udp_table *udptable, + struct sk_buff *skb) { struct sock *sk, *result; unsigned short hnum = ntohs(dport); @@ -235,7 +239,7 @@ struct sock *__udp6_lib_lookup(struct net *net, goto begin; result = udp6_lib_lookup2(net, saddr, sport, - daddr, hnum, dif, exact_dif, + daddr, hnum, dif, sdif, exact_dif, hslot2, skb); if (!result) { unsigned int old_slot2 = slot2; @@ -250,7 +254,7 @@ struct sock *__udp6_lib_lookup(struct net *net, goto begin; result = udp6_lib_lookup2(net, saddr, sport, - daddr, hnum, dif, + daddr, hnum, dif, sdif, exact_dif, hslot2, skb); } @@ -261,7 +265,7 @@ begin: badness = -1; sk_for_each_rcu(sk, &hslot->head) { score = compute_score(sk, net, saddr, sport, daddr, hnum, dif, - exact_dif); + sdif, exact_dif); if (score > badness) { reuseport = sk->sk_reuseport; if (reuseport) { @@ -294,7 +298,7 @@ static struct sock *__udp6_lib_lookup_skb(struct sk_buff *skb, return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport, &iph->daddr, dport, inet6_iif(skb), - udptable, skb); + inet6_sdif(skb), udptable, skb); } struct sock *udp6_lib_lookup_skb(struct sk_buff *skb, @@ -304,7 +308,7 @@ struct sock *udp6_lib_lookup_skb(struct sk_buff *skb, return __udp6_lib_lookup(dev_net(skb->dev), &iph->saddr, sport, &iph->daddr, dport, inet6_iif(skb), - &udp_table, skb); + inet6_sdif(skb), &udp_table, skb); } EXPORT_SYMBOL_GPL(udp6_lib_lookup_skb); @@ -320,7 +324,7 @@ struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be struct sock *sk; sk = __udp6_lib_lookup(net, saddr, sport, daddr, dport, - dif, &udp_table, NULL); + dif, 0, &udp_table, NULL); if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) sk = NULL; return sk; @@ -501,7 +505,7 @@ void __udp6_lib_err(struct sk_buff *skb, struct inet6_skb_parm *opt, struct net *net = dev_net(skb->dev); sk = __udp6_lib_lookup(net, daddr, uh->dest, saddr, uh->source, - inet6_iif(skb), udptable, skb); + inet6_iif(skb), 0, udptable, skb); if (!sk) { __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), ICMP6_MIB_INERRORS); -- cgit v1.2.3 From 4297a0ef085729af98adab9131d128c576ed3044 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 7 Aug 2017 08:44:21 -0700 Subject: net: ipv6: add second dif to inet6 socket lookups Add a second device index, sdif, to inet6 socket lookups. sdif is the index for ingress devices enslaved to an l3mdev. It allows the lookups to consider the enslaved device as well as the L3 domain when searching for a socket. TCP moves the data in the cb. Prior to tcp_v4_rcv (e.g., early demux) the ingress index is obtained from IPCB using inet_sdif and after tcp_v4_rcv tcp_v4_sdif is used. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/net/inet6_hashtables.h | 22 +++++++++++++--------- include/net/tcp.h | 10 ++++++++++ net/dccp/ipv6.c | 4 ++-- net/ipv6/inet6_hashtables.c | 28 +++++++++++++++++----------- net/ipv6/tcp_ipv6.c | 13 ++++++++----- net/ipv6/udp.c | 7 ++++--- net/netfilter/xt_TPROXY.c | 4 ++-- 7 files changed, 56 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/include/net/inet6_hashtables.h b/include/net/inet6_hashtables.h index b87becacd9d3..6e91e38a31da 100644 --- a/include/net/inet6_hashtables.h +++ b/include/net/inet6_hashtables.h @@ -49,7 +49,8 @@ struct sock *__inet6_lookup_established(struct net *net, const struct in6_addr *saddr, const __be16 sport, const struct in6_addr *daddr, - const u16 hnum, const int dif); + const u16 hnum, const int dif, + const int sdif); struct sock *inet6_lookup_listener(struct net *net, struct inet_hashinfo *hashinfo, @@ -57,7 +58,8 @@ struct sock *inet6_lookup_listener(struct net *net, const struct in6_addr *saddr, const __be16 sport, const struct in6_addr *daddr, - const unsigned short hnum, const int dif); + const unsigned short hnum, + const int dif, const int sdif); static inline struct sock *__inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo, @@ -66,24 +68,25 @@ static inline struct sock *__inet6_lookup(struct net *net, const __be16 sport, const struct in6_addr *daddr, const u16 hnum, - const int dif, + const int dif, const int sdif, bool *refcounted) { struct sock *sk = __inet6_lookup_established(net, hashinfo, saddr, - sport, daddr, hnum, dif); + sport, daddr, hnum, + dif, sdif); *refcounted = true; if (sk) return sk; *refcounted = false; return inet6_lookup_listener(net, hashinfo, skb, doff, saddr, sport, - daddr, hnum, dif); + daddr, hnum, dif, sdif); } static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo, struct sk_buff *skb, int doff, const __be16 sport, const __be16 dport, - int iif, + int iif, int sdif, bool *refcounted) { struct sock *sk = skb_steal_sock(skb); @@ -95,7 +98,7 @@ static inline struct sock *__inet6_lookup_skb(struct inet_hashinfo *hashinfo, return __inet6_lookup(dev_net(skb_dst(skb)->dev), hashinfo, skb, doff, &ipv6_hdr(skb)->saddr, sport, &ipv6_hdr(skb)->daddr, ntohs(dport), - iif, refcounted); + iif, sdif, refcounted); } struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo, @@ -107,13 +110,14 @@ struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo, int inet6_hash(struct sock *sk); #endif /* IS_ENABLED(CONFIG_IPV6) */ -#define INET6_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif) \ +#define INET6_MATCH(__sk, __net, __saddr, __daddr, __ports, __dif, __sdif) \ (((__sk)->sk_portpair == (__ports)) && \ ((__sk)->sk_family == AF_INET6) && \ ipv6_addr_equal(&(__sk)->sk_v6_daddr, (__saddr)) && \ ipv6_addr_equal(&(__sk)->sk_v6_rcv_saddr, (__daddr)) && \ (!(__sk)->sk_bound_dev_if || \ - ((__sk)->sk_bound_dev_if == (__dif))) && \ + ((__sk)->sk_bound_dev_if == (__dif)) || \ + ((__sk)->sk_bound_dev_if == (__sdif))) && \ net_eq(sock_net(__sk), (__net))) #endif /* _INET6_HASHTABLES_H */ diff --git a/include/net/tcp.h b/include/net/tcp.h index 2b89f1ab8552..999f3efe572b 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -827,6 +827,16 @@ static inline int tcp_v6_iif(const struct sk_buff *skb) return l3_slave ? skb->skb_iif : TCP_SKB_CB(skb)->header.h6.iif; } + +/* TCP_SKB_CB reference means this can not be used from early demux */ +static inline int tcp_v6_sdif(const struct sk_buff *skb) +{ +#if IS_ENABLED(CONFIG_NET_L3_MASTER_DEV) + if (skb && ipv6_l3mdev_skb(TCP_SKB_CB(skb)->header.h6.flags)) + return TCP_SKB_CB(skb)->header.h6.iif; +#endif + return 0; +} #endif /* TCP_SKB_CB reference means this can not be used from early demux */ diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 1b58eac8aad3..47a7b59b355e 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -89,7 +89,7 @@ static void dccp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, sk = __inet6_lookup_established(net, &dccp_hashinfo, &hdr->daddr, dh->dccph_dport, &hdr->saddr, ntohs(dh->dccph_sport), - inet6_iif(skb)); + inet6_iif(skb), 0); if (!sk) { __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), @@ -687,7 +687,7 @@ static int dccp_v6_rcv(struct sk_buff *skb) lookup: sk = __inet6_lookup_skb(&dccp_hashinfo, skb, __dccp_hdr_len(dh), dh->dccph_sport, dh->dccph_dport, - inet6_iif(skb), &refcounted); + inet6_iif(skb), 0, &refcounted); if (!sk) { dccp_pr_debug("failed to look up flow ID in table and " "get corresponding socket\n"); diff --git a/net/ipv6/inet6_hashtables.c b/net/ipv6/inet6_hashtables.c index b13b8f93079d..b01858f5deb1 100644 --- a/net/ipv6/inet6_hashtables.c +++ b/net/ipv6/inet6_hashtables.c @@ -56,7 +56,7 @@ struct sock *__inet6_lookup_established(struct net *net, const __be16 sport, const struct in6_addr *daddr, const u16 hnum, - const int dif) + const int dif, const int sdif) { struct sock *sk; const struct hlist_nulls_node *node; @@ -73,12 +73,12 @@ begin: sk_nulls_for_each_rcu(sk, node, &head->chain) { if (sk->sk_hash != hash) continue; - if (!INET6_MATCH(sk, net, saddr, daddr, ports, dif)) + if (!INET6_MATCH(sk, net, saddr, daddr, ports, dif, sdif)) continue; if (unlikely(!refcount_inc_not_zero(&sk->sk_refcnt))) goto out; - if (unlikely(!INET6_MATCH(sk, net, saddr, daddr, ports, dif))) { + if (unlikely(!INET6_MATCH(sk, net, saddr, daddr, ports, dif, sdif))) { sock_gen_put(sk); goto begin; } @@ -96,7 +96,7 @@ EXPORT_SYMBOL(__inet6_lookup_established); static inline int compute_score(struct sock *sk, struct net *net, const unsigned short hnum, const struct in6_addr *daddr, - const int dif, bool exact_dif) + const int dif, const int sdif, bool exact_dif) { int score = -1; @@ -110,9 +110,13 @@ static inline int compute_score(struct sock *sk, struct net *net, score++; } if (sk->sk_bound_dev_if || exact_dif) { - if (sk->sk_bound_dev_if != dif) + bool dev_match = (sk->sk_bound_dev_if == dif || + sk->sk_bound_dev_if == sdif); + + if (exact_dif && !dev_match) return -1; - score++; + if (sk->sk_bound_dev_if && dev_match) + score++; } if (sk->sk_incoming_cpu == raw_smp_processor_id()) score++; @@ -126,7 +130,7 @@ struct sock *inet6_lookup_listener(struct net *net, struct sk_buff *skb, int doff, const struct in6_addr *saddr, const __be16 sport, const struct in6_addr *daddr, - const unsigned short hnum, const int dif) + const unsigned short hnum, const int dif, const int sdif) { unsigned int hash = inet_lhashfn(net, hnum); struct inet_listen_hashbucket *ilb = &hashinfo->listening_hash[hash]; @@ -136,7 +140,7 @@ struct sock *inet6_lookup_listener(struct net *net, u32 phash = 0; sk_for_each(sk, &ilb->head) { - score = compute_score(sk, net, hnum, daddr, dif, exact_dif); + score = compute_score(sk, net, hnum, daddr, dif, sdif, exact_dif); if (score > hiscore) { reuseport = sk->sk_reuseport; if (reuseport) { @@ -171,7 +175,7 @@ struct sock *inet6_lookup(struct net *net, struct inet_hashinfo *hashinfo, bool refcounted; sk = __inet6_lookup(net, hashinfo, skb, doff, saddr, sport, daddr, - ntohs(dport), dif, &refcounted); + ntohs(dport), dif, 0, &refcounted); if (sk && !refcounted && !refcount_inc_not_zero(&sk->sk_refcnt)) sk = NULL; return sk; @@ -187,8 +191,9 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row, const struct in6_addr *daddr = &sk->sk_v6_rcv_saddr; const struct in6_addr *saddr = &sk->sk_v6_daddr; const int dif = sk->sk_bound_dev_if; - const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport); struct net *net = sock_net(sk); + const int sdif = l3mdev_master_ifindex_by_index(net, dif); + const __portpair ports = INET_COMBINED_PORTS(inet->inet_dport, lport); const unsigned int hash = inet6_ehashfn(net, daddr, lport, saddr, inet->inet_dport); struct inet_ehash_bucket *head = inet_ehash_bucket(hinfo, hash); @@ -203,7 +208,8 @@ static int __inet6_check_established(struct inet_timewait_death_row *death_row, if (sk2->sk_hash != hash) continue; - if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports, dif))) { + if (likely(INET6_MATCH(sk2, net, saddr, daddr, ports, + dif, sdif))) { if (sk2->sk_state == TCP_TIME_WAIT) { tw = inet_twsk(sk2); if (twsk_unique(sk, sk2, twp)) diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index ced5dcf37465..f776ec4ecf6d 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -350,7 +350,7 @@ static void tcp_v6_err(struct sk_buff *skb, struct inet6_skb_parm *opt, sk = __inet6_lookup_established(net, &tcp_hashinfo, &hdr->daddr, th->dest, &hdr->saddr, ntohs(th->source), - skb->dev->ifindex); + skb->dev->ifindex, inet6_sdif(skb)); if (!sk) { __ICMP6_INC_STATS(net, __in6_dev_get(skb->dev), @@ -918,7 +918,8 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb) &tcp_hashinfo, NULL, 0, &ipv6h->saddr, th->source, &ipv6h->daddr, - ntohs(th->source), tcp_v6_iif(skb)); + ntohs(th->source), tcp_v6_iif(skb), + tcp_v6_sdif(skb)); if (!sk1) goto out; @@ -1397,6 +1398,7 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr, static int tcp_v6_rcv(struct sk_buff *skb) { + int sdif = inet6_sdif(skb); const struct tcphdr *th; const struct ipv6hdr *hdr; bool refcounted; @@ -1430,7 +1432,7 @@ static int tcp_v6_rcv(struct sk_buff *skb) lookup: sk = __inet6_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th), - th->source, th->dest, inet6_iif(skb), + th->source, th->dest, inet6_iif(skb), sdif, &refcounted); if (!sk) goto no_tcp_socket; @@ -1563,7 +1565,8 @@ do_time_wait: skb, __tcp_hdrlen(th), &ipv6_hdr(skb)->saddr, th->source, &ipv6_hdr(skb)->daddr, - ntohs(th->dest), tcp_v6_iif(skb)); + ntohs(th->dest), tcp_v6_iif(skb), + sdif); if (sk2) { struct inet_timewait_sock *tw = inet_twsk(sk); inet_twsk_deschedule_put(tw); @@ -1610,7 +1613,7 @@ static void tcp_v6_early_demux(struct sk_buff *skb) sk = __inet6_lookup_established(dev_net(skb->dev), &tcp_hashinfo, &hdr->saddr, th->source, &hdr->daddr, ntohs(th->dest), - inet6_iif(skb)); + inet6_iif(skb), inet6_sdif(skb)); if (sk) { skb->sk = sk; skb->destructor = sock_edemux; diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index d96a877798a7..19afcaf4a22e 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -897,7 +897,7 @@ discard: static struct sock *__udp6_lib_demux_lookup(struct net *net, __be16 loc_port, const struct in6_addr *loc_addr, __be16 rmt_port, const struct in6_addr *rmt_addr, - int dif) + int dif, int sdif) { unsigned short hnum = ntohs(loc_port); unsigned int hash2 = udp6_portaddr_hash(net, loc_addr, hnum); @@ -908,7 +908,7 @@ static struct sock *__udp6_lib_demux_lookup(struct net *net, udp_portaddr_for_each_entry_rcu(sk, &hslot2->head) { if (sk->sk_state == TCP_ESTABLISHED && - INET6_MATCH(sk, net, rmt_addr, loc_addr, ports, dif)) + INET6_MATCH(sk, net, rmt_addr, loc_addr, ports, dif, sdif)) return sk; /* Only check first socket in chain */ break; @@ -923,6 +923,7 @@ static void udp_v6_early_demux(struct sk_buff *skb) struct sock *sk; struct dst_entry *dst; int dif = skb->dev->ifindex; + int sdif = inet6_sdif(skb); if (!pskb_may_pull(skb, skb_transport_offset(skb) + sizeof(struct udphdr))) @@ -934,7 +935,7 @@ static void udp_v6_early_demux(struct sk_buff *skb) sk = __udp6_lib_demux_lookup(net, uh->dest, &ipv6_hdr(skb)->daddr, uh->source, &ipv6_hdr(skb)->saddr, - dif); + dif, sdif); else return; diff --git a/net/netfilter/xt_TPROXY.c b/net/netfilter/xt_TPROXY.c index 94fb0fd0c667..ade4c10c28c6 100644 --- a/net/netfilter/xt_TPROXY.c +++ b/net/netfilter/xt_TPROXY.c @@ -195,7 +195,7 @@ nf_tproxy_get_sock_v6(struct net *net, struct sk_buff *skb, int thoff, void *hp, thoff + __tcp_hdrlen(tcph), saddr, sport, daddr, ntohs(dport), - in->ifindex); + in->ifindex, 0); if (sk && !refcount_inc_not_zero(&sk->sk_refcnt)) sk = NULL; @@ -208,7 +208,7 @@ nf_tproxy_get_sock_v6(struct net *net, struct sk_buff *skb, int thoff, void *hp, case NFT_LOOKUP_ESTABLISHED: sk = __inet6_lookup_established(net, &tcp_hashinfo, saddr, sport, daddr, ntohs(dport), - in->ifindex); + in->ifindex, 0); break; default: BUG(); -- cgit v1.2.3 From 5108ab4bf446fa9ad2c71f5fc1d839067b72636f Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 7 Aug 2017 08:44:22 -0700 Subject: net: ipv6: add second dif to raw socket lookups Add a second device index, sdif, to raw socket lookups. sdif is the index for ingress devices enslaved to an l3mdev. It allows the lookups to consider the enslaved device as well as the L3 domain when searching for a socket. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- include/net/rawv6.h | 2 +- net/ipv4/raw_diag.c | 2 +- net/ipv6/raw.c | 13 ++++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/rawv6.h b/include/net/rawv6.h index cbe4e9de1894..4addc5c988e0 100644 --- a/include/net/rawv6.h +++ b/include/net/rawv6.h @@ -6,7 +6,7 @@ extern struct raw_hashinfo raw_v6_hashinfo; struct sock *__raw_v6_lookup(struct net *net, struct sock *sk, unsigned short num, const struct in6_addr *loc_addr, - const struct in6_addr *rmt_addr, int dif); + const struct in6_addr *rmt_addr, int dif, int sdif); int raw_abort(struct sock *sk, int err); diff --git a/net/ipv4/raw_diag.c b/net/ipv4/raw_diag.c index c600d3c71d4d..c200065ef9a5 100644 --- a/net/ipv4/raw_diag.c +++ b/net/ipv4/raw_diag.c @@ -52,7 +52,7 @@ static struct sock *raw_lookup(struct net *net, struct sock *from, sk = __raw_v6_lookup(net, from, r->sdiag_raw_protocol, (const struct in6_addr *)r->id.idiag_src, (const struct in6_addr *)r->id.idiag_dst, - r->id.idiag_if); + r->id.idiag_if, 0); #endif return sk; } diff --git a/net/ipv6/raw.c b/net/ipv6/raw.c index 60be012fe708..e4462b0ff801 100644 --- a/net/ipv6/raw.c +++ b/net/ipv6/raw.c @@ -72,7 +72,7 @@ EXPORT_SYMBOL_GPL(raw_v6_hashinfo); struct sock *__raw_v6_lookup(struct net *net, struct sock *sk, unsigned short num, const struct in6_addr *loc_addr, - const struct in6_addr *rmt_addr, int dif) + const struct in6_addr *rmt_addr, int dif, int sdif) { bool is_multicast = ipv6_addr_is_multicast(loc_addr); @@ -86,7 +86,9 @@ struct sock *__raw_v6_lookup(struct net *net, struct sock *sk, !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) continue; - if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != dif) + if (sk->sk_bound_dev_if && + sk->sk_bound_dev_if != dif && + sk->sk_bound_dev_if != sdif) continue; if (!ipv6_addr_any(&sk->sk_v6_rcv_saddr)) { @@ -178,7 +180,8 @@ static bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) goto out; net = dev_net(skb->dev); - sk = __raw_v6_lookup(net, sk, nexthdr, daddr, saddr, inet6_iif(skb)); + sk = __raw_v6_lookup(net, sk, nexthdr, daddr, saddr, + inet6_iif(skb), inet6_sdif(skb)); while (sk) { int filtered; @@ -222,7 +225,7 @@ static bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr) } } sk = __raw_v6_lookup(net, sk_next(sk), nexthdr, daddr, saddr, - inet6_iif(skb)); + inet6_iif(skb), inet6_sdif(skb)); } out: read_unlock(&raw_v6_hashinfo.lock); @@ -378,7 +381,7 @@ void raw6_icmp_error(struct sk_buff *skb, int nexthdr, net = dev_net(skb->dev); while ((sk = __raw_v6_lookup(net, sk, nexthdr, saddr, daddr, - inet6_iif(skb)))) { + inet6_iif(skb), inet6_iif(skb)))) { rawv6_err(sk, skb, NULL, type, code, inner_offset, info); sk = sk_next(sk); -- cgit v1.2.3 From 08bd10ffb44d11000c6b2781e8d9066567e50e27 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Fri, 4 Aug 2017 18:19:18 -0700 Subject: lwtunnel: replace EXPORT_SYMBOL with EXPORT_SYMBOL_GPL Signed-off-by: Roopa Prabhu Signed-off-by: David S. Miller --- net/core/lwtunnel.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index d9cb3532f1dd..435f35f9a61c 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -65,7 +65,7 @@ struct lwtunnel_state *lwtunnel_state_alloc(int encap_len) return lws; } -EXPORT_SYMBOL(lwtunnel_state_alloc); +EXPORT_SYMBOL_GPL(lwtunnel_state_alloc); static const struct lwtunnel_encap_ops __rcu * lwtun_encaps[LWTUNNEL_ENCAP_MAX + 1] __read_mostly; @@ -80,7 +80,7 @@ int lwtunnel_encap_add_ops(const struct lwtunnel_encap_ops *ops, &lwtun_encaps[num], NULL, ops) ? 0 : -1; } -EXPORT_SYMBOL(lwtunnel_encap_add_ops); +EXPORT_SYMBOL_GPL(lwtunnel_encap_add_ops); int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops, unsigned int encap_type) @@ -99,7 +99,7 @@ int lwtunnel_encap_del_ops(const struct lwtunnel_encap_ops *ops, return ret; } -EXPORT_SYMBOL(lwtunnel_encap_del_ops); +EXPORT_SYMBOL_GPL(lwtunnel_encap_del_ops); int lwtunnel_build_state(u16 encap_type, struct nlattr *encap, unsigned int family, @@ -138,7 +138,7 @@ int lwtunnel_build_state(u16 encap_type, return ret; } -EXPORT_SYMBOL(lwtunnel_build_state); +EXPORT_SYMBOL_GPL(lwtunnel_build_state); int lwtunnel_valid_encap_type(u16 encap_type, struct netlink_ext_ack *extack) { @@ -175,7 +175,7 @@ int lwtunnel_valid_encap_type(u16 encap_type, struct netlink_ext_ack *extack) return ret; } -EXPORT_SYMBOL(lwtunnel_valid_encap_type); +EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type); int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining, struct netlink_ext_ack *extack) @@ -205,7 +205,7 @@ int lwtunnel_valid_encap_type_attr(struct nlattr *attr, int remaining, return 0; } -EXPORT_SYMBOL(lwtunnel_valid_encap_type_attr); +EXPORT_SYMBOL_GPL(lwtunnel_valid_encap_type_attr); void lwtstate_free(struct lwtunnel_state *lws) { @@ -219,7 +219,7 @@ void lwtstate_free(struct lwtunnel_state *lws) } module_put(ops->owner); } -EXPORT_SYMBOL(lwtstate_free); +EXPORT_SYMBOL_GPL(lwtstate_free); int lwtunnel_fill_encap(struct sk_buff *skb, struct lwtunnel_state *lwtstate) { @@ -259,7 +259,7 @@ nla_put_failure: return (ret == -EOPNOTSUPP ? 0 : ret); } -EXPORT_SYMBOL(lwtunnel_fill_encap); +EXPORT_SYMBOL_GPL(lwtunnel_fill_encap); int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate) { @@ -281,7 +281,7 @@ int lwtunnel_get_encap_size(struct lwtunnel_state *lwtstate) return ret; } -EXPORT_SYMBOL(lwtunnel_get_encap_size); +EXPORT_SYMBOL_GPL(lwtunnel_get_encap_size); int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b) { @@ -309,7 +309,7 @@ int lwtunnel_cmp_encap(struct lwtunnel_state *a, struct lwtunnel_state *b) return ret; } -EXPORT_SYMBOL(lwtunnel_cmp_encap); +EXPORT_SYMBOL_GPL(lwtunnel_cmp_encap); int lwtunnel_output(struct net *net, struct sock *sk, struct sk_buff *skb) { @@ -343,7 +343,7 @@ drop: return ret; } -EXPORT_SYMBOL(lwtunnel_output); +EXPORT_SYMBOL_GPL(lwtunnel_output); int lwtunnel_xmit(struct sk_buff *skb) { @@ -378,7 +378,7 @@ drop: return ret; } -EXPORT_SYMBOL(lwtunnel_xmit); +EXPORT_SYMBOL_GPL(lwtunnel_xmit); int lwtunnel_input(struct sk_buff *skb) { @@ -412,4 +412,4 @@ drop: return ret; } -EXPORT_SYMBOL(lwtunnel_input); +EXPORT_SYMBOL_GPL(lwtunnel_input); -- cgit v1.2.3 From 54df2cf819a23dba4bb2c4134ed62659a7d324f5 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 4 Aug 2017 21:31:42 -0700 Subject: net_sched: refactor notification code for RTM_DELTFILTER It is confusing to use 'unsigned long fh' as both a handle and a pointer, especially commit 9ee7837449b3 ("net sched filters: fix notification of filter delete with proper handle"). This patch introduces tfilter_del_notify() so that we can pass it as a pointer as before, and we don't need to check RTM_DELTFILTER in tcf_fill_node() any more. This prepares for the next patch. Cc: Jamal Hadi Salim Cc: Jiri Pirko Signed-off-by: Cong Wang Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/cls_api.c | 44 +++++++++++++++++++++++++++++++++++++++----- 1 file changed, 39 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index e655221c654e..afd099727aea 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -104,6 +104,10 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct tcf_proto *tp, unsigned long fh, int event, bool unicast); +static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, + struct nlmsghdr *n, struct tcf_proto *tp, + unsigned long fh, bool unicast, bool *last); + static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct tcf_chain *chain, int event) @@ -595,11 +599,10 @@ replay: } break; case RTM_DELTFILTER: - err = tp->ops->delete(tp, fh, &last); + err = tfilter_del_notify(net, skb, n, tp, fh, false, + &last); if (err) goto errout; - tfilter_notify(net, skb, n, tp, t->tcm_handle, - RTM_DELTFILTER, false); if (last) { tcf_chain_tp_remove(chain, &chain_info, tp); tcf_proto_destroy(tp); @@ -659,9 +662,9 @@ static int tcf_fill_node(struct net *net, struct sk_buff *skb, goto nla_put_failure; if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index)) goto nla_put_failure; - tcm->tcm_handle = fh; - if (RTM_DELTFILTER != event) { + if (!fh) { tcm->tcm_handle = 0; + } else { if (tp->ops->dump && tp->ops->dump(net, tp, fh, skb, tcm) < 0) goto nla_put_failure; } @@ -698,6 +701,37 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, n->nlmsg_flags & NLM_F_ECHO); } +static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, + struct nlmsghdr *n, struct tcf_proto *tp, + unsigned long fh, bool unicast, bool *last) +{ + struct sk_buff *skb; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + int err; + + skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + if (tcf_fill_node(net, skb, tp, fh, portid, n->nlmsg_seq, + n->nlmsg_flags, RTM_DELTFILTER) <= 0) { + kfree_skb(skb); + return -EINVAL; + } + + err = tp->ops->delete(tp, fh, last); + if (err) { + kfree_skb(skb); + return err; + } + + if (unicast) + return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); + + return rtnetlink_send(skb, net, portid, RTNLGRP_TC, + n->nlmsg_flags & NLM_F_ECHO); +} + struct tcf_dump_args { struct tcf_walker w; struct sk_buff *skb; -- cgit v1.2.3 From 8113c095672f6504b23eba6edf4a57b5f7f744af Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Fri, 4 Aug 2017 21:31:43 -0700 Subject: net_sched: use void pointer for filter handle Now we use 'unsigned long fh' as a pointer in every place, it is safe to convert it to a void pointer now. This gets rid of many casts to pointer. Cc: Jamal Hadi Salim Cc: Jiri Pirko Signed-off-by: Cong Wang Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 2 +- include/net/sch_generic.h | 8 ++++---- net/sched/cls_api.c | 17 ++++++++--------- net/sched/cls_basic.c | 22 ++++++++++------------ net/sched/cls_bpf.c | 27 ++++++++++++--------------- net/sched/cls_cgroup.c | 12 ++++++------ net/sched/cls_flow.c | 24 ++++++++++++------------ net/sched/cls_flower.c | 22 +++++++++++----------- net/sched/cls_fw.c | 26 +++++++++++++------------- net/sched/cls_matchall.c | 16 ++++++++-------- net/sched/cls_route.c | 26 +++++++++++++------------- net/sched/cls_rsvp.h | 24 ++++++++++++------------ net/sched/cls_tcindex.c | 36 ++++++++++++++++-------------------- net/sched/cls_u32.c | 30 +++++++++++++++--------------- 14 files changed, 141 insertions(+), 151 deletions(-) (limited to 'net') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index 572083af02ac..0f78e6560b2d 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -11,7 +11,7 @@ struct tcf_walker { int stop; int skip; int count; - int (*fn)(struct tcf_proto *, unsigned long node, struct tcf_walker *); + int (*fn)(struct tcf_proto *, void *node, struct tcf_walker *); }; int register_tcf_proto_ops(struct tcf_proto_ops *ops); diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 1c123e2b2415..e79f5ad1c5f3 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -213,16 +213,16 @@ struct tcf_proto_ops { int (*init)(struct tcf_proto*); void (*destroy)(struct tcf_proto*); - unsigned long (*get)(struct tcf_proto*, u32 handle); + void* (*get)(struct tcf_proto*, u32 handle); int (*change)(struct net *net, struct sk_buff *, struct tcf_proto*, unsigned long, u32 handle, struct nlattr **, - unsigned long *, bool); - int (*delete)(struct tcf_proto*, unsigned long, bool*); + void **, bool); + int (*delete)(struct tcf_proto*, void *, bool*); void (*walk)(struct tcf_proto*, struct tcf_walker *arg); /* rtnetlink specific */ - int (*dump)(struct net*, struct tcf_proto*, unsigned long, + int (*dump)(struct net*, struct tcf_proto*, void *, struct sk_buff *skb, struct tcmsg*); struct module *owner; diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index afd099727aea..668afb6e9885 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -102,11 +102,11 @@ EXPORT_SYMBOL(unregister_tcf_proto_ops); static int tfilter_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct tcf_proto *tp, - unsigned long fh, int event, bool unicast); + void *fh, int event, bool unicast); static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct tcf_proto *tp, - unsigned long fh, bool unicast, bool *last); + void *fh, bool unicast, bool *last); static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, @@ -432,7 +432,7 @@ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, struct tcf_proto *tp; const struct Qdisc_class_ops *cops; unsigned long cl; - unsigned long fh; + void *fh; int err; int tp_created; @@ -571,7 +571,7 @@ replay: fh = tp->ops->get(tp, t->tcm_handle); - if (fh == 0) { + if (!fh) { if (n->nlmsg_type == RTM_DELTFILTER && t->tcm_handle == 0) { tcf_chain_tp_remove(chain, &chain_info, tp); tfilter_notify(net, skb, n, tp, fh, @@ -641,7 +641,7 @@ errout: } static int tcf_fill_node(struct net *net, struct sk_buff *skb, - struct tcf_proto *tp, unsigned long fh, u32 portid, + struct tcf_proto *tp, void *fh, u32 portid, u32 seq, u16 flags, int event) { struct tcmsg *tcm; @@ -679,7 +679,7 @@ nla_put_failure: static int tfilter_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct tcf_proto *tp, - unsigned long fh, int event, bool unicast) + void *fh, int event, bool unicast) { struct sk_buff *skb; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; @@ -703,7 +703,7 @@ static int tfilter_notify(struct net *net, struct sk_buff *oskb, static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, struct nlmsghdr *n, struct tcf_proto *tp, - unsigned long fh, bool unicast, bool *last) + void *fh, bool unicast, bool *last) { struct sk_buff *skb; u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; @@ -738,8 +738,7 @@ struct tcf_dump_args { struct netlink_callback *cb; }; -static int tcf_node_dump(struct tcf_proto *tp, unsigned long n, - struct tcf_walker *arg) +static int tcf_node_dump(struct tcf_proto *tp, void *n, struct tcf_walker *arg) { struct tcf_dump_args *a = (void *)arg; struct net *net = sock_net(a->skb->sk); diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 7c7a82138f76..73cc7f167a38 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -56,20 +56,18 @@ static int basic_classify(struct sk_buff *skb, const struct tcf_proto *tp, return -1; } -static unsigned long basic_get(struct tcf_proto *tp, u32 handle) +static void *basic_get(struct tcf_proto *tp, u32 handle) { - unsigned long l = 0UL; struct basic_head *head = rtnl_dereference(tp->root); struct basic_filter *f; list_for_each_entry(f, &head->flist, link) { if (f->handle == handle) { - l = (unsigned long) f; - break; + return f; } } - return l; + return NULL; } static int basic_init(struct tcf_proto *tp) @@ -106,10 +104,10 @@ static void basic_destroy(struct tcf_proto *tp) kfree_rcu(head, rcu); } -static int basic_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int basic_delete(struct tcf_proto *tp, void *arg, bool *last) { struct basic_head *head = rtnl_dereference(tp->root); - struct basic_filter *f = (struct basic_filter *) arg; + struct basic_filter *f = arg; list_del_rcu(&f->link); tcf_unbind_filter(tp, &f->res); @@ -149,7 +147,7 @@ static int basic_set_parms(struct net *net, struct tcf_proto *tp, static int basic_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, - struct nlattr **tca, unsigned long *arg, bool ovr) + struct nlattr **tca, void **arg, bool ovr) { int err; struct basic_head *head = rtnl_dereference(tp->root); @@ -202,7 +200,7 @@ static int basic_change(struct net *net, struct sk_buff *in_skb, if (err < 0) goto errout; - *arg = (unsigned long)fnew; + *arg = fnew; if (fold) { list_replace_rcu(&fold->link, &fnew->link); @@ -228,7 +226,7 @@ static void basic_walk(struct tcf_proto *tp, struct tcf_walker *arg) if (arg->count < arg->skip) goto skip; - if (arg->fn(tp, (unsigned long) f, arg) < 0) { + if (arg->fn(tp, f, arg) < 0) { arg->stop = 1; break; } @@ -237,10 +235,10 @@ skip: } } -static int basic_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { - struct basic_filter *f = (struct basic_filter *) fh; + struct basic_filter *f = fh; struct nlattr *nest; if (f == NULL) diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 2d4d06e41cd9..db17b68df94e 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -270,11 +270,11 @@ static void __cls_bpf_delete(struct tcf_proto *tp, struct cls_bpf_prog *prog) call_rcu(&prog->rcu, cls_bpf_delete_prog_rcu); } -static int cls_bpf_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int cls_bpf_delete(struct tcf_proto *tp, void *arg, bool *last) { struct cls_bpf_head *head = rtnl_dereference(tp->root); - __cls_bpf_delete(tp, (struct cls_bpf_prog *) arg); + __cls_bpf_delete(tp, arg); *last = list_empty(&head->plist); return 0; } @@ -290,20 +290,17 @@ static void cls_bpf_destroy(struct tcf_proto *tp) kfree_rcu(head, rcu); } -static unsigned long cls_bpf_get(struct tcf_proto *tp, u32 handle) +static void *cls_bpf_get(struct tcf_proto *tp, u32 handle) { struct cls_bpf_head *head = rtnl_dereference(tp->root); struct cls_bpf_prog *prog; - unsigned long ret = 0UL; list_for_each_entry(prog, &head->plist, link) { - if (prog->handle == handle) { - ret = (unsigned long) prog; - break; - } + if (prog->handle == handle) + return prog; } - return ret; + return NULL; } static int cls_bpf_prog_from_ops(struct nlattr **tb, struct cls_bpf_prog *prog) @@ -448,10 +445,10 @@ static u32 cls_bpf_grab_new_handle(struct tcf_proto *tp, static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg, bool ovr) + void **arg, bool ovr) { struct cls_bpf_head *head = rtnl_dereference(tp->root); - struct cls_bpf_prog *oldprog = (struct cls_bpf_prog *) *arg; + struct cls_bpf_prog *oldprog = *arg; struct nlattr *tb[TCA_BPF_MAX + 1]; struct cls_bpf_prog *prog; int ret; @@ -509,7 +506,7 @@ static int cls_bpf_change(struct net *net, struct sk_buff *in_skb, list_add_rcu(&prog->link, &head->plist); } - *arg = (unsigned long) prog; + *arg = prog; return 0; errout: @@ -557,10 +554,10 @@ static int cls_bpf_dump_ebpf_info(const struct cls_bpf_prog *prog, return 0; } -static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int cls_bpf_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *tm) { - struct cls_bpf_prog *prog = (struct cls_bpf_prog *) fh; + struct cls_bpf_prog *prog = fh; struct nlattr *nest; u32 bpf_flags = 0; int ret; @@ -618,7 +615,7 @@ static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg) list_for_each_entry(prog, &head->plist, link) { if (arg->count < arg->skip) goto skip; - if (arg->fn(tp, (unsigned long) prog, arg) < 0) { + if (arg->fn(tp, prog, arg) < 0) { arg->stop = 1; break; } diff --git a/net/sched/cls_cgroup.c b/net/sched/cls_cgroup.c index df7a582775df..d48452f87975 100644 --- a/net/sched/cls_cgroup.c +++ b/net/sched/cls_cgroup.c @@ -43,9 +43,9 @@ static int cls_cgroup_classify(struct sk_buff *skb, const struct tcf_proto *tp, return tcf_exts_exec(skb, &head->exts, res); } -static unsigned long cls_cgroup_get(struct tcf_proto *tp, u32 handle) +static void *cls_cgroup_get(struct tcf_proto *tp, u32 handle) { - return 0UL; + return NULL; } static int cls_cgroup_init(struct tcf_proto *tp) @@ -71,7 +71,7 @@ static void cls_cgroup_destroy_rcu(struct rcu_head *root) static int cls_cgroup_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg, bool ovr) + void **arg, bool ovr) { struct nlattr *tb[TCA_CGROUP_MAX + 1]; struct cls_cgroup_head *head = rtnl_dereference(tp->root); @@ -128,7 +128,7 @@ static void cls_cgroup_destroy(struct tcf_proto *tp) call_rcu(&head->rcu, cls_cgroup_destroy_rcu); } -static int cls_cgroup_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int cls_cgroup_delete(struct tcf_proto *tp, void *arg, bool *last) { return -EOPNOTSUPP; } @@ -140,7 +140,7 @@ static void cls_cgroup_walk(struct tcf_proto *tp, struct tcf_walker *arg) if (arg->count < arg->skip) goto skip; - if (arg->fn(tp, (unsigned long) head, arg) < 0) { + if (arg->fn(tp, head, arg) < 0) { arg->stop = 1; return; } @@ -148,7 +148,7 @@ skip: arg->count++; } -static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int cls_cgroup_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { struct cls_cgroup_head *head = rtnl_dereference(tp->root); diff --git a/net/sched/cls_flow.c b/net/sched/cls_flow.c index 55e281b20140..2a3a60ec5b86 100644 --- a/net/sched/cls_flow.c +++ b/net/sched/cls_flow.c @@ -382,7 +382,7 @@ static void flow_destroy_filter(struct rcu_head *head) static int flow_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg, bool ovr) + void **arg, bool ovr) { struct flow_head *head = rtnl_dereference(tp->root); struct flow_filter *fold, *fnew; @@ -439,7 +439,7 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, if (err < 0) goto err2; - fold = (struct flow_filter *)*arg; + fold = *arg; if (fold) { err = -EINVAL; if (fold->handle != handle && handle) @@ -532,12 +532,12 @@ static int flow_change(struct net *net, struct sk_buff *in_skb, if (perturb_period) mod_timer(&fnew->perturb_timer, jiffies + perturb_period); - if (*arg == 0) + if (!*arg) list_add_tail_rcu(&fnew->list, &head->filters); else list_replace_rcu(&fold->list, &fnew->list); - *arg = (unsigned long)fnew; + *arg = fnew; if (fold) call_rcu(&fold->rcu, flow_destroy_filter); @@ -551,10 +551,10 @@ err1: return err; } -static int flow_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int flow_delete(struct tcf_proto *tp, void *arg, bool *last) { struct flow_head *head = rtnl_dereference(tp->root); - struct flow_filter *f = (struct flow_filter *)arg; + struct flow_filter *f = arg; list_del_rcu(&f->list); call_rcu(&f->rcu, flow_destroy_filter); @@ -586,21 +586,21 @@ static void flow_destroy(struct tcf_proto *tp) kfree_rcu(head, rcu); } -static unsigned long flow_get(struct tcf_proto *tp, u32 handle) +static void *flow_get(struct tcf_proto *tp, u32 handle) { struct flow_head *head = rtnl_dereference(tp->root); struct flow_filter *f; list_for_each_entry(f, &head->filters, list) if (f->handle == handle) - return (unsigned long)f; - return 0; + return f; + return NULL; } -static int flow_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int flow_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { - struct flow_filter *f = (struct flow_filter *)fh; + struct flow_filter *f = fh; struct nlattr *nest; if (f == NULL) @@ -666,7 +666,7 @@ static void flow_walk(struct tcf_proto *tp, struct tcf_walker *arg) list_for_each_entry(f, &head->filters, list) { if (arg->count < arg->skip) goto skip; - if (arg->fn(tp, (unsigned long)f, arg) < 0) { + if (arg->fn(tp, f, arg) < 0) { arg->stop = 1; break; } diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 1474bacf4df4..d2551a03c542 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -332,15 +332,15 @@ static void fl_destroy(struct tcf_proto *tp) call_rcu(&head->rcu, fl_destroy_rcu); } -static unsigned long fl_get(struct tcf_proto *tp, u32 handle) +static void *fl_get(struct tcf_proto *tp, u32 handle) { struct cls_fl_head *head = rtnl_dereference(tp->root); struct cls_fl_filter *f; list_for_each_entry(f, &head->filters, list) if (f->handle == handle) - return (unsigned long) f; - return 0; + return f; + return NULL; } static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { @@ -883,10 +883,10 @@ static u32 fl_grab_new_handle(struct tcf_proto *tp, static int fl_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg, bool ovr) + void **arg, bool ovr) { struct cls_fl_head *head = rtnl_dereference(tp->root); - struct cls_fl_filter *fold = (struct cls_fl_filter *) *arg; + struct cls_fl_filter *fold = *arg; struct cls_fl_filter *fnew; struct nlattr **tb; struct fl_flow_mask mask = {}; @@ -977,7 +977,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, fl_hw_destroy_filter(tp, fold); } - *arg = (unsigned long) fnew; + *arg = fnew; if (fold) { list_replace_rcu(&fold->list, &fnew->list); @@ -998,10 +998,10 @@ errout_tb: return err; } -static int fl_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int fl_delete(struct tcf_proto *tp, void *arg, bool *last) { struct cls_fl_head *head = rtnl_dereference(tp->root); - struct cls_fl_filter *f = (struct cls_fl_filter *) arg; + struct cls_fl_filter *f = arg; if (!tc_skip_sw(f->flags)) rhashtable_remove_fast(&head->ht, &f->ht_node, @@ -1019,7 +1019,7 @@ static void fl_walk(struct tcf_proto *tp, struct tcf_walker *arg) list_for_each_entry_rcu(f, &head->filters, list) { if (arg->count < arg->skip) goto skip; - if (arg->fn(tp, (unsigned long) f, arg) < 0) { + if (arg->fn(tp, f, arg) < 0) { arg->stop = 1; break; } @@ -1154,11 +1154,11 @@ static int fl_dump_key_flags(struct sk_buff *skb, u32 flags_key, u32 flags_mask) return nla_put(skb, TCA_FLOWER_KEY_FLAGS_MASK, 4, &_mask); } -static int fl_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int fl_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { struct cls_fl_head *head = rtnl_dereference(tp->root); - struct cls_fl_filter *f = (struct cls_fl_filter *) fh; + struct cls_fl_filter *f = fh; struct nlattr *nest; struct fl_flow_key *key, *mask; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 11f178f1b2be..192255ec50bd 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -95,20 +95,20 @@ static int fw_classify(struct sk_buff *skb, const struct tcf_proto *tp, return -1; } -static unsigned long fw_get(struct tcf_proto *tp, u32 handle) +static void *fw_get(struct tcf_proto *tp, u32 handle) { struct fw_head *head = rtnl_dereference(tp->root); struct fw_filter *f; if (head == NULL) - return 0; + return NULL; f = rtnl_dereference(head->ht[fw_hash(handle)]); for (; f; f = rtnl_dereference(f->next)) { if (f->id == handle) - return (unsigned long)f; + return f; } - return 0; + return NULL; } static int fw_init(struct tcf_proto *tp) @@ -147,10 +147,10 @@ static void fw_destroy(struct tcf_proto *tp) kfree_rcu(head, rcu); } -static int fw_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int fw_delete(struct tcf_proto *tp, void *arg, bool *last) { struct fw_head *head = rtnl_dereference(tp->root); - struct fw_filter *f = (struct fw_filter *)arg; + struct fw_filter *f = arg; struct fw_filter __rcu **fp; struct fw_filter *pfp; int ret = -EINVAL; @@ -230,11 +230,11 @@ static int fw_set_parms(struct net *net, struct tcf_proto *tp, static int fw_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, - u32 handle, struct nlattr **tca, unsigned long *arg, + u32 handle, struct nlattr **tca, void **arg, bool ovr) { struct fw_head *head = rtnl_dereference(tp->root); - struct fw_filter *f = (struct fw_filter *) *arg; + struct fw_filter *f = *arg; struct nlattr *opt = tca[TCA_OPTIONS]; struct nlattr *tb[TCA_FW_MAX + 1]; int err; @@ -288,7 +288,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, tcf_unbind_filter(tp, &f->res); call_rcu(&f->rcu, fw_delete_filter); - *arg = (unsigned long)fnew; + *arg = fnew; return err; } @@ -325,7 +325,7 @@ static int fw_change(struct net *net, struct sk_buff *in_skb, RCU_INIT_POINTER(f->next, head->ht[fw_hash(handle)]); rcu_assign_pointer(head->ht[fw_hash(handle)], f); - *arg = (unsigned long)f; + *arg = f; return 0; errout: @@ -354,7 +354,7 @@ static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) arg->count++; continue; } - if (arg->fn(tp, (unsigned long)f, arg) < 0) { + if (arg->fn(tp, f, arg) < 0) { arg->stop = 1; return; } @@ -363,11 +363,11 @@ static void fw_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } -static int fw_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int fw_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { struct fw_head *head = rtnl_dereference(tp->root); - struct fw_filter *f = (struct fw_filter *)fh; + struct fw_filter *f = fh; struct nlattr *nest; if (f == NULL) diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index c9f6500b8080..d44e26fdae84 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -98,9 +98,9 @@ static void mall_destroy(struct tcf_proto *tp) call_rcu(&head->rcu, mall_destroy_rcu); } -static unsigned long mall_get(struct tcf_proto *tp, u32 handle) +static void *mall_get(struct tcf_proto *tp, u32 handle) { - return 0UL; + return NULL; } static const struct nla_policy mall_policy[TCA_MATCHALL_MAX + 1] = { @@ -129,7 +129,7 @@ static int mall_set_parms(struct net *net, struct tcf_proto *tp, static int mall_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg, bool ovr) + void **arg, bool ovr) { struct cls_mall_head *head = rtnl_dereference(tp->root); struct net_device *dev = tp->q->dev_queue->dev; @@ -185,7 +185,7 @@ static int mall_change(struct net *net, struct sk_buff *in_skb, if (!tc_in_hw(new->flags)) new->flags |= TCA_CLS_FLAGS_NOT_IN_HW; - *arg = (unsigned long) head; + *arg = head; rcu_assign_pointer(tp->root, new); return 0; @@ -197,7 +197,7 @@ err_exts_init: return err; } -static int mall_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int mall_delete(struct tcf_proto *tp, void *arg, bool *last) { return -EOPNOTSUPP; } @@ -208,16 +208,16 @@ static void mall_walk(struct tcf_proto *tp, struct tcf_walker *arg) if (arg->count < arg->skip) goto skip; - if (arg->fn(tp, (unsigned long) head, arg) < 0) + if (arg->fn(tp, head, arg) < 0) arg->stop = 1; skip: arg->count++; } -static int mall_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int mall_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { - struct cls_mall_head *head = (struct cls_mall_head *) fh; + struct cls_mall_head *head = fh; struct nlattr *nest; if (!head) diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index f1e7d7850b44..3b70982394ce 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -216,7 +216,7 @@ static inline u32 from_hash(u32 id) return 16 + (id & 0xF); } -static unsigned long route4_get(struct tcf_proto *tp, u32 handle) +static void *route4_get(struct tcf_proto *tp, u32 handle) { struct route4_head *head = rtnl_dereference(tp->root); struct route4_bucket *b; @@ -225,11 +225,11 @@ static unsigned long route4_get(struct tcf_proto *tp, u32 handle) h1 = to_hash(handle); if (h1 > 256) - return 0; + return NULL; h2 = from_hash(handle >> 16); if (h2 > 32) - return 0; + return NULL; b = rtnl_dereference(head->table[h1]); if (b) { @@ -237,9 +237,9 @@ static unsigned long route4_get(struct tcf_proto *tp, u32 handle) f; f = rtnl_dereference(f->next)) if (f->handle == handle) - return (unsigned long)f; + return f; } - return 0; + return NULL; } static int route4_init(struct tcf_proto *tp) @@ -294,10 +294,10 @@ static void route4_destroy(struct tcf_proto *tp) kfree_rcu(head, rcu); } -static int route4_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int route4_delete(struct tcf_proto *tp, void *arg, bool *last) { struct route4_head *head = rtnl_dereference(tp->root); - struct route4_filter *f = (struct route4_filter *)arg; + struct route4_filter *f = arg; struct route4_filter __rcu **fp; struct route4_filter *nf; struct route4_bucket *b; @@ -448,7 +448,7 @@ static int route4_set_parms(struct net *net, struct tcf_proto *tp, static int route4_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, - struct nlattr **tca, unsigned long *arg, bool ovr) + struct nlattr **tca, void **arg, bool ovr) { struct route4_head *head = rtnl_dereference(tp->root); struct route4_filter __rcu **fp; @@ -467,7 +467,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, if (err < 0) return err; - fold = (struct route4_filter *)*arg; + fold = *arg; if (fold && handle && fold->handle != handle) return -EINVAL; @@ -525,7 +525,7 @@ static int route4_change(struct net *net, struct sk_buff *in_skb, } route4_reset_fastmap(head); - *arg = (unsigned long)f; + *arg = f; if (fold) { tcf_unbind_filter(tp, &fold->res); call_rcu(&fold->rcu, route4_delete_filter); @@ -564,7 +564,7 @@ static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg) arg->count++; continue; } - if (arg->fn(tp, (unsigned long)f, arg) < 0) { + if (arg->fn(tp, f, arg) < 0) { arg->stop = 1; return; } @@ -575,10 +575,10 @@ static void route4_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } -static int route4_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int route4_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { - struct route4_filter *f = (struct route4_filter *)fh; + struct route4_filter *f = fh; struct nlattr *nest; u32 id; diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 4adb67a73491..26203ff817f3 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -248,7 +248,7 @@ static void rsvp_replace(struct tcf_proto *tp, struct rsvp_filter *n, u32 h) BUG_ON(1); } -static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle) +static void *rsvp_get(struct tcf_proto *tp, u32 handle) { struct rsvp_head *head = rtnl_dereference(tp->root); struct rsvp_session *s; @@ -257,17 +257,17 @@ static unsigned long rsvp_get(struct tcf_proto *tp, u32 handle) unsigned int h2 = (handle >> 8) & 0xFF; if (h2 > 16) - return 0; + return NULL; for (s = rtnl_dereference(head->ht[h1]); s; s = rtnl_dereference(s->next)) { for (f = rtnl_dereference(s->ht[h2]); f; f = rtnl_dereference(f->next)) { if (f->handle == handle) - return (unsigned long)f; + return f; } } - return 0; + return NULL; } static int rsvp_init(struct tcf_proto *tp) @@ -328,10 +328,10 @@ static void rsvp_destroy(struct tcf_proto *tp) kfree_rcu(data, rcu); } -static int rsvp_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int rsvp_delete(struct tcf_proto *tp, void *arg, bool *last) { struct rsvp_head *head = rtnl_dereference(tp->root); - struct rsvp_filter *nfp, *f = (struct rsvp_filter *)arg; + struct rsvp_filter *nfp, *f = arg; struct rsvp_filter __rcu **fp; unsigned int h = f->handle; struct rsvp_session __rcu **sp; @@ -464,7 +464,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, - unsigned long *arg, bool ovr) + void **arg, bool ovr) { struct rsvp_head *data = rtnl_dereference(tp->root); struct rsvp_filter *f, *nfp; @@ -493,7 +493,7 @@ static int rsvp_change(struct net *net, struct sk_buff *in_skb, if (err < 0) goto errout2; - f = (struct rsvp_filter *)*arg; + f = *arg; if (f) { /* Node exists: adjust only classid */ struct rsvp_filter *n; @@ -604,7 +604,7 @@ insert: RCU_INIT_POINTER(f->next, nfp); rcu_assign_pointer(*fp, f); - *arg = (unsigned long)f; + *arg = f; return 0; } } @@ -663,7 +663,7 @@ static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg) arg->count++; continue; } - if (arg->fn(tp, (unsigned long)f, arg) < 0) { + if (arg->fn(tp, f, arg) < 0) { arg->stop = 1; return; } @@ -674,10 +674,10 @@ static void rsvp_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } -static int rsvp_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int rsvp_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { - struct rsvp_filter *f = (struct rsvp_filter *)fh; + struct rsvp_filter *f = fh; struct rsvp_session *s; struct nlattr *nest; struct tc_rsvp_pinfo pinfo; diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index d69f828f3fed..fb281b9b2c52 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -104,16 +104,16 @@ static int tcindex_classify(struct sk_buff *skb, const struct tcf_proto *tp, } -static unsigned long tcindex_get(struct tcf_proto *tp, u32 handle) +static void *tcindex_get(struct tcf_proto *tp, u32 handle) { struct tcindex_data *p = rtnl_dereference(tp->root); struct tcindex_filter_result *r; pr_debug("tcindex_get(tp %p,handle 0x%08x)\n", tp, handle); if (p->perfect && handle >= p->alloc_hash) - return 0; + return NULL; r = tcindex_lookup(p, handle); - return r && tcindex_filter_is_set(r) ? (unsigned long) r : 0UL; + return r && tcindex_filter_is_set(r) ? r : NULL; } static int tcindex_init(struct tcf_proto *tp) @@ -150,14 +150,14 @@ static void tcindex_destroy_fexts(struct rcu_head *head) kfree(f); } -static int tcindex_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int tcindex_delete(struct tcf_proto *tp, void *arg, bool *last) { struct tcindex_data *p = rtnl_dereference(tp->root); - struct tcindex_filter_result *r = (struct tcindex_filter_result *) arg; + struct tcindex_filter_result *r = arg; struct tcindex_filter __rcu **walk; struct tcindex_filter *f = NULL; - pr_debug("tcindex_delete(tp %p,arg 0x%lx),p %p\n", tp, arg, p); + pr_debug("tcindex_delete(tp %p,arg %p),p %p\n", tp, arg, p); if (p->perfect) { if (!r->res.class) return -ENOENT; @@ -192,8 +192,7 @@ found: } static int tcindex_destroy_element(struct tcf_proto *tp, - unsigned long arg, - struct tcf_walker *walker) + void *arg, struct tcf_walker *walker) { bool last; @@ -471,17 +470,17 @@ errout: static int tcindex_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, - struct nlattr **tca, unsigned long *arg, bool ovr) + struct nlattr **tca, void **arg, bool ovr) { struct nlattr *opt = tca[TCA_OPTIONS]; struct nlattr *tb[TCA_TCINDEX_MAX + 1]; struct tcindex_data *p = rtnl_dereference(tp->root); - struct tcindex_filter_result *r = (struct tcindex_filter_result *) *arg; + struct tcindex_filter_result *r = *arg; int err; pr_debug("tcindex_change(tp %p,handle 0x%08x,tca %p,arg %p),opt %p," - "p %p,r %p,*arg 0x%lx\n", - tp, handle, tca, arg, opt, p, r, arg ? *arg : 0L); + "p %p,r %p,*arg %p\n", + tp, handle, tca, arg, opt, p, r, arg ? *arg : NULL); if (!opt) return 0; @@ -506,9 +505,7 @@ static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker) if (!p->perfect[i].res.class) continue; if (walker->count >= walker->skip) { - if (walker->fn(tp, - (unsigned long) (p->perfect+i), walker) - < 0) { + if (walker->fn(tp, p->perfect + i, walker) < 0) { walker->stop = 1; return; } @@ -522,8 +519,7 @@ static void tcindex_walk(struct tcf_proto *tp, struct tcf_walker *walker) for (f = rtnl_dereference(p->h[i]); f; f = next) { next = rtnl_dereference(f->next); if (walker->count >= walker->skip) { - if (walker->fn(tp, (unsigned long) &f->result, - walker) < 0) { + if (walker->fn(tp, &f->result, walker) < 0) { walker->stop = 1; return; } @@ -548,14 +544,14 @@ static void tcindex_destroy(struct tcf_proto *tp) } -static int tcindex_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int tcindex_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { struct tcindex_data *p = rtnl_dereference(tp->root); - struct tcindex_filter_result *r = (struct tcindex_filter_result *) fh; + struct tcindex_filter_result *r = fh; struct nlattr *nest; - pr_debug("tcindex_dump(tp %p,fh 0x%lx,skb %p,t %p),p %p,r %p\n", + pr_debug("tcindex_dump(tp %p,fh %p,skb %p,t %p),p %p,r %p\n", tp, fh, skb, t, p, r); pr_debug("p->perfect %p p->h %p\n", p->perfect, p->h); diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 4ed51d347d0a..5a3f78181526 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -289,7 +289,7 @@ out: } -static unsigned long u32_get(struct tcf_proto *tp, u32 handle) +static void *u32_get(struct tcf_proto *tp, u32 handle) { struct tc_u_hnode *ht; struct tc_u_common *tp_c = tp->data; @@ -300,12 +300,12 @@ static unsigned long u32_get(struct tcf_proto *tp, u32 handle) ht = u32_lookup_ht(tp_c, TC_U32_HTID(handle)); if (!ht) - return 0; + return NULL; if (TC_U32_KEY(handle) == 0) - return (unsigned long)ht; + return ht; - return (unsigned long)u32_lookup_key(ht, handle); + return u32_lookup_key(ht, handle); } static u32 gen_new_htid(struct tc_u_common *tp_c) @@ -605,9 +605,9 @@ static void u32_destroy(struct tcf_proto *tp) tp->data = NULL; } -static int u32_delete(struct tcf_proto *tp, unsigned long arg, bool *last) +static int u32_delete(struct tcf_proto *tp, void *arg, bool *last) { - struct tc_u_hnode *ht = (struct tc_u_hnode *)arg; + struct tc_u_hnode *ht = arg; struct tc_u_hnode *root_ht = rtnl_dereference(tp->root); struct tc_u_common *tp_c = tp->data; int ret = 0; @@ -831,7 +831,7 @@ static struct tc_u_knode *u32_init_knode(struct tcf_proto *tp, static int u32_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, - struct nlattr **tca, unsigned long *arg, bool ovr) + struct nlattr **tca, void **arg, bool ovr) { struct tc_u_common *tp_c = tp->data; struct tc_u_hnode *ht; @@ -858,7 +858,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, return -EINVAL; } - n = (struct tc_u_knode *)*arg; + n = *arg; if (n) { struct tc_u_knode *new; @@ -925,7 +925,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, RCU_INIT_POINTER(ht->next, tp_c->hlist); rcu_assign_pointer(tp_c->hlist, ht); - *arg = (unsigned long)ht; + *arg = ht; return 0; } @@ -1020,7 +1020,7 @@ static int u32_change(struct net *net, struct sk_buff *in_skb, RCU_INIT_POINTER(n->next, pins); rcu_assign_pointer(*ins, n); - *arg = (unsigned long)n; + *arg = n; return 0; } @@ -1054,7 +1054,7 @@ static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg) if (ht->prio != tp->prio) continue; if (arg->count >= arg->skip) { - if (arg->fn(tp, (unsigned long)ht, arg) < 0) { + if (arg->fn(tp, ht, arg) < 0) { arg->stop = 1; return; } @@ -1068,7 +1068,7 @@ static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg) arg->count++; continue; } - if (arg->fn(tp, (unsigned long)n, arg) < 0) { + if (arg->fn(tp, n, arg) < 0) { arg->stop = 1; return; } @@ -1078,10 +1078,10 @@ static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } -static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, +static int u32_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { - struct tc_u_knode *n = (struct tc_u_knode *)fh; + struct tc_u_knode *n = fh; struct tc_u_hnode *ht_up, *ht_down; struct nlattr *nest; @@ -1095,7 +1095,7 @@ static int u32_dump(struct net *net, struct tcf_proto *tp, unsigned long fh, goto nla_put_failure; if (TC_U32_KEY(n->handle) == 0) { - struct tc_u_hnode *ht = (struct tc_u_hnode *)fh; + struct tc_u_hnode *ht = fh; u32 divisor = ht->divisor + 1; if (nla_put_u32(skb, TCA_U32_DIVISOR, divisor)) -- cgit v1.2.3 From 925615ceda94532ba612e641549c7b1e41ebff10 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Sat, 5 Aug 2017 12:38:24 +0200 Subject: ipv6: sr: allow SRH insertion with arbitrary segments_left value The seg6_validate_srh() function only allows SRHs whose active segment is the first segment of the path. However, an application may insert an SRH whose active segment is not the first one. Such an application might be for example an SR-aware Virtual Network Function. This patch enables to insert SRHs with an arbitrary active segment. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/exthdrs.c | 4 ++-- net/ipv6/seg6.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/exthdrs.c b/net/ipv6/exthdrs.c index 3cec529c6113..95516138e861 100644 --- a/net/ipv6/exthdrs.c +++ b/net/ipv6/exthdrs.c @@ -882,7 +882,7 @@ static void ipv6_push_rthdr4(struct sk_buff *skb, u8 *proto, (hops - 1) * sizeof(struct in6_addr)); sr_phdr->segments[0] = **addr_p; - *addr_p = &sr_ihdr->segments[hops - 1]; + *addr_p = &sr_ihdr->segments[sr_ihdr->segments_left]; #ifdef CONFIG_IPV6_SEG6_HMAC if (sr_has_hmac(sr_phdr)) { @@ -1174,7 +1174,7 @@ struct in6_addr *fl6_update_dst(struct flowi6 *fl6, { struct ipv6_sr_hdr *srh = (struct ipv6_sr_hdr *)opt->srcrt; - fl6->daddr = srh->segments[srh->first_segment]; + fl6->daddr = srh->segments[srh->segments_left]; break; } default: diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index 15fba55e3da8..81c2339b3285 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -40,7 +40,7 @@ bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len) if (((srh->hdrlen + 1) << 3) != len) return false; - if (srh->segments_left != srh->first_segment) + if (srh->segments_left > srh->first_segment) return false; tlv_offset = sizeof(*srh) + ((srh->first_segment + 1) << 4); -- cgit v1.2.3 From b04c80d3a7e228cfb832cdb1c9ce8151f174669c Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Sat, 5 Aug 2017 12:38:25 +0200 Subject: ipv6: sr: export SRH insertion functions This patch exports the seg6_do_srh_encap() and seg6_do_srh_inline() functions. It also removes the CONFIG_IPV6_SEG6_INLINE knob that enabled the compilation of seg6_do_srh_inline(). This function is now built-in. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/net/seg6.h | 2 ++ net/ipv6/Kconfig | 12 ------------ net/ipv6/seg6_iptunnel.c | 12 ++++-------- 3 files changed, 6 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/include/net/seg6.h b/include/net/seg6.h index 4e0357517d79..a32abb040e1d 100644 --- a/include/net/seg6.h +++ b/include/net/seg6.h @@ -58,5 +58,7 @@ extern int seg6_iptunnel_init(void); extern void seg6_iptunnel_exit(void); extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len); +extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh); +extern int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh); #endif diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 48c452959d2c..50181a96923e 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -315,18 +315,6 @@ config IPV6_SEG6_LWTUNNEL If unsure, say N. -config IPV6_SEG6_INLINE - bool "IPv6: direct Segment Routing Header insertion " - depends on IPV6_SEG6_LWTUNNEL - ---help--- - Support for direct insertion of the Segment Routing Header, - also known as inline mode. Be aware that direct insertion of - extension headers (as opposed to encapsulation) may break - multiple mechanisms such as PMTUD or IPSec AH. Use this feature - only if you know exactly what you are doing. - - If unsure, say N. - config IPV6_SEG6_HMAC bool "IPv6: Segment Routing HMAC support" depends on IPV6 diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 264d772d3c7d..501233040570 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -91,7 +91,7 @@ static void set_tun_src(struct net *net, struct net_device *dev, } /* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */ -static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) +int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) { struct net *net = dev_net(skb_dst(skb)->dev); struct ipv6hdr *hdr, *inner_hdr; @@ -141,10 +141,10 @@ static int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) return 0; } +EXPORT_SYMBOL_GPL(seg6_do_srh_encap); /* insert an SRH within an IPv6 packet, just after the IPv6 header */ -#ifdef CONFIG_IPV6_SEG6_INLINE -static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) +int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) { struct ipv6hdr *hdr, *oldhdr; struct ipv6_sr_hdr *isrh; @@ -193,7 +193,7 @@ static int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) return 0; } -#endif +EXPORT_SYMBOL_GPL(seg6_do_srh_inline); static int seg6_do_srh(struct sk_buff *skb) { @@ -209,12 +209,10 @@ static int seg6_do_srh(struct sk_buff *skb) } switch (tinfo->mode) { -#ifdef CONFIG_IPV6_SEG6_INLINE case SEG6_IPTUN_MODE_INLINE: err = seg6_do_srh_inline(skb, tinfo->srh); skb_reset_inner_headers(skb); break; -#endif case SEG6_IPTUN_MODE_ENCAP: err = seg6_do_srh_encap(skb, tinfo->srh); break; @@ -357,10 +355,8 @@ static int seg6_build_state(struct nlattr *nla, return -EINVAL; switch (tuninfo->mode) { -#ifdef CONFIG_IPV6_SEG6_INLINE case SEG6_IPTUN_MODE_INLINE: break; -#endif case SEG6_IPTUN_MODE_ENCAP: break; default: -- cgit v1.2.3 From d1df6fd8a1d22d37cffa0075ab8ad423ce656777 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Sat, 5 Aug 2017 12:38:26 +0200 Subject: ipv6: sr: define core operations for seg6local lightweight tunnel This patch implements a new type of lightweight tunnel named seg6local. A seg6local lwt is defined by a type of action and a set of parameters. The action represents the operation to perform on the packets matching the lwt's route, and is not necessarily an encapsulation. The set of parameters are arguments for the processing function. Each action is defined in a struct seg6_action_desc within seg6_action_table[]. This structure contains the action, mandatory attributes, the processing function, and a static headroom size required by the action. The mandatory attributes are encoded as a bitmask field. The static headroom is set to a non-zero value when the processing function always add a constant number of bytes to the skb (e.g. the header size for encapsulations). To facilitate rtnetlink-related operations such as parsing, fill_encap, and cmp_encap, each type of action parameter is associated to three function pointers, in seg6_action_params[]. All actions defined in seg6_local.h are detailed in [1]. [1] https://tools.ietf.org/html/draft-filsfils-spring-srv6-network-programming-01 Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/linux/seg6_local.h | 6 + include/net/seg6.h | 2 + include/uapi/linux/lwtunnel.h | 1 + include/uapi/linux/seg6_local.h | 68 +++++++++ net/core/lwtunnel.c | 2 + net/ipv6/Kconfig | 3 +- net/ipv6/Makefile | 2 +- net/ipv6/seg6.c | 5 + net/ipv6/seg6_local.c | 320 ++++++++++++++++++++++++++++++++++++++++ 9 files changed, 407 insertions(+), 2 deletions(-) create mode 100644 include/linux/seg6_local.h create mode 100644 include/uapi/linux/seg6_local.h create mode 100644 net/ipv6/seg6_local.c (limited to 'net') diff --git a/include/linux/seg6_local.h b/include/linux/seg6_local.h new file mode 100644 index 000000000000..ee63e76fe0c7 --- /dev/null +++ b/include/linux/seg6_local.h @@ -0,0 +1,6 @@ +#ifndef _LINUX_SEG6_LOCAL_H +#define _LINUX_SEG6_LOCAL_H + +#include + +#endif diff --git a/include/net/seg6.h b/include/net/seg6.h index a32abb040e1d..5379f550f521 100644 --- a/include/net/seg6.h +++ b/include/net/seg6.h @@ -56,6 +56,8 @@ extern int seg6_init(void); extern void seg6_exit(void); extern int seg6_iptunnel_init(void); extern void seg6_iptunnel_exit(void); +extern int seg6_local_init(void); +extern void seg6_local_exit(void); extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len); extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh); diff --git a/include/uapi/linux/lwtunnel.h b/include/uapi/linux/lwtunnel.h index 92724cba1eba..7fdd19ca7511 100644 --- a/include/uapi/linux/lwtunnel.h +++ b/include/uapi/linux/lwtunnel.h @@ -11,6 +11,7 @@ enum lwtunnel_encap_types { LWTUNNEL_ENCAP_IP6, LWTUNNEL_ENCAP_SEG6, LWTUNNEL_ENCAP_BPF, + LWTUNNEL_ENCAP_SEG6_LOCAL, __LWTUNNEL_ENCAP_MAX, }; diff --git a/include/uapi/linux/seg6_local.h b/include/uapi/linux/seg6_local.h new file mode 100644 index 000000000000..ef2d8c3e76c1 --- /dev/null +++ b/include/uapi/linux/seg6_local.h @@ -0,0 +1,68 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#ifndef _UAPI_LINUX_SEG6_LOCAL_H +#define _UAPI_LINUX_SEG6_LOCAL_H + +#include + +enum { + SEG6_LOCAL_UNSPEC, + SEG6_LOCAL_ACTION, + SEG6_LOCAL_SRH, + SEG6_LOCAL_TABLE, + SEG6_LOCAL_NH4, + SEG6_LOCAL_NH6, + SEG6_LOCAL_IIF, + SEG6_LOCAL_OIF, + __SEG6_LOCAL_MAX, +}; +#define SEG6_LOCAL_MAX (__SEG6_LOCAL_MAX - 1) + +enum { + SEG6_LOCAL_ACTION_UNSPEC = 0, + /* node segment */ + SEG6_LOCAL_ACTION_END = 1, + /* adjacency segment (IPv6 cross-connect) */ + SEG6_LOCAL_ACTION_END_X = 2, + /* lookup of next seg NH in table */ + SEG6_LOCAL_ACTION_END_T = 3, + /* decap and L2 cross-connect */ + SEG6_LOCAL_ACTION_END_DX2 = 4, + /* decap and IPv6 cross-connect */ + SEG6_LOCAL_ACTION_END_DX6 = 5, + /* decap and IPv4 cross-connect */ + SEG6_LOCAL_ACTION_END_DX4 = 6, + /* decap and lookup of DA in v6 table */ + SEG6_LOCAL_ACTION_END_DT6 = 7, + /* decap and lookup of DA in v4 table */ + SEG6_LOCAL_ACTION_END_DT4 = 8, + /* binding segment with insertion */ + SEG6_LOCAL_ACTION_END_B6 = 9, + /* binding segment with encapsulation */ + SEG6_LOCAL_ACTION_END_B6_ENCAP = 10, + /* binding segment with MPLS encap */ + SEG6_LOCAL_ACTION_END_BM = 11, + /* lookup last seg in table */ + SEG6_LOCAL_ACTION_END_S = 12, + /* forward to SR-unaware VNF with static proxy */ + SEG6_LOCAL_ACTION_END_AS = 13, + /* forward to SR-unaware VNF with masquerading */ + SEG6_LOCAL_ACTION_END_AM = 14, + + __SEG6_LOCAL_ACTION_MAX, +}; + +#define SEG6_LOCAL_ACTION_MAX (__SEG6_LOCAL_ACTION_MAX - 1) + +#endif diff --git a/net/core/lwtunnel.c b/net/core/lwtunnel.c index 435f35f9a61c..0b171756453c 100644 --- a/net/core/lwtunnel.c +++ b/net/core/lwtunnel.c @@ -44,6 +44,8 @@ static const char *lwtunnel_encap_str(enum lwtunnel_encap_types encap_type) return "SEG6"; case LWTUNNEL_ENCAP_BPF: return "BPF"; + case LWTUNNEL_ENCAP_SEG6_LOCAL: + return "SEG6LOCAL"; case LWTUNNEL_ENCAP_IP6: case LWTUNNEL_ENCAP_IP: case LWTUNNEL_ENCAP_NONE: diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 50181a96923e..0d722396dce6 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -311,7 +311,8 @@ config IPV6_SEG6_LWTUNNEL ---help--- Support for encapsulation of packets within an outer IPv6 header and a Segment Routing Header using the lightweight - tunnels mechanism. + tunnels mechanism. Also enable support for advanced local + processing of SRv6 packets based on their active segment. If unsure, say N. diff --git a/net/ipv6/Makefile b/net/ipv6/Makefile index f8b24c2e0d77..10e342363793 100644 --- a/net/ipv6/Makefile +++ b/net/ipv6/Makefile @@ -23,7 +23,7 @@ ipv6-$(CONFIG_IPV6_MULTIPLE_TABLES) += fib6_rules.o ipv6-$(CONFIG_PROC_FS) += proc.o ipv6-$(CONFIG_SYN_COOKIES) += syncookies.o ipv6-$(CONFIG_NETLABEL) += calipso.o -ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o +ipv6-$(CONFIG_IPV6_SEG6_LWTUNNEL) += seg6_iptunnel.o seg6_local.o ipv6-$(CONFIG_IPV6_SEG6_HMAC) += seg6_hmac.o ipv6-objs += $(ipv6-y) diff --git a/net/ipv6/seg6.c b/net/ipv6/seg6.c index 81c2339b3285..c81407770956 100644 --- a/net/ipv6/seg6.c +++ b/net/ipv6/seg6.c @@ -456,6 +456,10 @@ int __init seg6_init(void) err = seg6_iptunnel_init(); if (err) goto out_unregister_pernet; + + err = seg6_local_init(); + if (err) + goto out_unregister_pernet; #endif #ifdef CONFIG_IPV6_SEG6_HMAC @@ -471,6 +475,7 @@ out: #ifdef CONFIG_IPV6_SEG6_HMAC out_unregister_iptun: #ifdef CONFIG_IPV6_SEG6_LWTUNNEL + seg6_local_exit(); seg6_iptunnel_exit(); #endif #endif diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c new file mode 100644 index 000000000000..53615d7e0723 --- /dev/null +++ b/net/ipv6/seg6_local.c @@ -0,0 +1,320 @@ +/* + * SR-IPv6 implementation + * + * Author: + * David Lebrun + * + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CONFIG_IPV6_SEG6_HMAC +#include +#endif + +struct seg6_local_lwt; + +struct seg6_action_desc { + int action; + unsigned long attrs; + int (*input)(struct sk_buff *skb, struct seg6_local_lwt *slwt); + int static_headroom; +}; + +struct seg6_local_lwt { + int action; + struct ipv6_sr_hdr *srh; + int table; + struct in_addr nh4; + struct in6_addr nh6; + int iif; + int oif; + + int headroom; + struct seg6_action_desc *desc; +}; + +static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) +{ + return (struct seg6_local_lwt *)lwt->data; +} + +static struct seg6_action_desc seg6_action_table[] = { + { + .action = SEG6_LOCAL_ACTION_END, + .attrs = 0, + }, +}; + +static struct seg6_action_desc *__get_action_desc(int action) +{ + struct seg6_action_desc *desc; + int i, count; + + count = sizeof(seg6_action_table) / sizeof(struct seg6_action_desc); + for (i = 0; i < count; i++) { + desc = &seg6_action_table[i]; + if (desc->action == action) + return desc; + } + + return NULL; +} + +static int seg6_local_input(struct sk_buff *skb) +{ + struct dst_entry *orig_dst = skb_dst(skb); + struct seg6_action_desc *desc; + struct seg6_local_lwt *slwt; + + slwt = seg6_local_lwtunnel(orig_dst->lwtstate); + desc = slwt->desc; + + return desc->input(skb, slwt); +} + +static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { + [SEG6_LOCAL_ACTION] = { .type = NLA_U32 }, + [SEG6_LOCAL_SRH] = { .type = NLA_BINARY }, + [SEG6_LOCAL_TABLE] = { .type = NLA_U32 }, + [SEG6_LOCAL_NH4] = { .type = NLA_BINARY, + .len = sizeof(struct in_addr) }, + [SEG6_LOCAL_NH6] = { .type = NLA_BINARY, + .len = sizeof(struct in6_addr) }, + [SEG6_LOCAL_IIF] = { .type = NLA_U32 }, + [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, +}; + +struct seg6_action_param { + int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); + int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); + int (*cmp)(struct seg6_local_lwt *a, struct seg6_local_lwt *b); +}; + +static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { + [SEG6_LOCAL_SRH] = { .parse = NULL, + .put = NULL, + .cmp = NULL }, + + [SEG6_LOCAL_TABLE] = { .parse = NULL, + .put = NULL, + .cmp = NULL }, + + [SEG6_LOCAL_NH4] = { .parse = NULL, + .put = NULL, + .cmp = NULL }, + + [SEG6_LOCAL_NH6] = { .parse = NULL, + .put = NULL, + .cmp = NULL }, + + [SEG6_LOCAL_IIF] = { .parse = NULL, + .put = NULL, + .cmp = NULL }, + + [SEG6_LOCAL_OIF] = { .parse = NULL, + .put = NULL, + .cmp = NULL }, +}; + +static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) +{ + struct seg6_action_param *param; + struct seg6_action_desc *desc; + int i, err; + + desc = __get_action_desc(slwt->action); + if (!desc) + return -EINVAL; + + if (!desc->input) + return -EOPNOTSUPP; + + slwt->desc = desc; + slwt->headroom += desc->static_headroom; + + for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { + if (desc->attrs & (1 << i)) { + if (!attrs[i]) + return -EINVAL; + + param = &seg6_action_params[i]; + + err = param->parse(attrs, slwt); + if (err < 0) + return err; + } + } + + return 0; +} + +static int seg6_local_build_state(struct nlattr *nla, unsigned int family, + const void *cfg, struct lwtunnel_state **ts, + struct netlink_ext_ack *extack) +{ + struct nlattr *tb[SEG6_LOCAL_MAX + 1]; + struct lwtunnel_state *newts; + struct seg6_local_lwt *slwt; + int err; + + err = nla_parse_nested(tb, SEG6_LOCAL_MAX, nla, seg6_local_policy, + extack); + + if (err < 0) + return err; + + if (!tb[SEG6_LOCAL_ACTION]) + return -EINVAL; + + newts = lwtunnel_state_alloc(sizeof(*slwt)); + if (!newts) + return -ENOMEM; + + slwt = seg6_local_lwtunnel(newts); + slwt->action = nla_get_u32(tb[SEG6_LOCAL_ACTION]); + + err = parse_nla_action(tb, slwt); + if (err < 0) + goto out_free; + + newts->type = LWTUNNEL_ENCAP_SEG6_LOCAL; + newts->flags = LWTUNNEL_STATE_INPUT_REDIRECT; + newts->headroom = slwt->headroom; + + *ts = newts; + + return 0; + +out_free: + kfree(slwt->srh); + kfree(newts); + return err; +} + +static void seg6_local_destroy_state(struct lwtunnel_state *lwt) +{ + struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); + + kfree(slwt->srh); +} + +static int seg6_local_fill_encap(struct sk_buff *skb, + struct lwtunnel_state *lwt) +{ + struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); + struct seg6_action_param *param; + int i, err; + + if (nla_put_u32(skb, SEG6_LOCAL_ACTION, slwt->action)) + return -EMSGSIZE; + + for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { + if (slwt->desc->attrs & (1 << i)) { + param = &seg6_action_params[i]; + err = param->put(skb, slwt); + if (err < 0) + return err; + } + } + + return 0; +} + +static int seg6_local_get_encap_size(struct lwtunnel_state *lwt) +{ + struct seg6_local_lwt *slwt = seg6_local_lwtunnel(lwt); + unsigned long attrs; + int nlsize; + + nlsize = nla_total_size(4); /* action */ + + attrs = slwt->desc->attrs; + + if (attrs & (1 << SEG6_LOCAL_SRH)) + nlsize += nla_total_size((slwt->srh->hdrlen + 1) << 3); + + if (attrs & (1 << SEG6_LOCAL_TABLE)) + nlsize += nla_total_size(4); + + if (attrs & (1 << SEG6_LOCAL_NH4)) + nlsize += nla_total_size(4); + + if (attrs & (1 << SEG6_LOCAL_NH6)) + nlsize += nla_total_size(16); + + if (attrs & (1 << SEG6_LOCAL_IIF)) + nlsize += nla_total_size(4); + + if (attrs & (1 << SEG6_LOCAL_OIF)) + nlsize += nla_total_size(4); + + return nlsize; +} + +static int seg6_local_cmp_encap(struct lwtunnel_state *a, + struct lwtunnel_state *b) +{ + struct seg6_local_lwt *slwt_a, *slwt_b; + struct seg6_action_param *param; + int i; + + slwt_a = seg6_local_lwtunnel(a); + slwt_b = seg6_local_lwtunnel(b); + + if (slwt_a->action != slwt_b->action) + return 1; + + if (slwt_a->desc->attrs != slwt_b->desc->attrs) + return 1; + + for (i = 0; i < SEG6_LOCAL_MAX + 1; i++) { + if (slwt_a->desc->attrs & (1 << i)) { + param = &seg6_action_params[i]; + if (param->cmp(slwt_a, slwt_b)) + return 1; + } + } + + return 0; +} + +static const struct lwtunnel_encap_ops seg6_local_ops = { + .build_state = seg6_local_build_state, + .destroy_state = seg6_local_destroy_state, + .input = seg6_local_input, + .fill_encap = seg6_local_fill_encap, + .get_encap_size = seg6_local_get_encap_size, + .cmp_encap = seg6_local_cmp_encap, + .owner = THIS_MODULE, +}; + +int __init seg6_local_init(void) +{ + return lwtunnel_encap_add_ops(&seg6_local_ops, + LWTUNNEL_ENCAP_SEG6_LOCAL); +} + +void seg6_local_exit(void) +{ + lwtunnel_encap_del_ops(&seg6_local_ops, LWTUNNEL_ENCAP_SEG6_LOCAL); +} -- cgit v1.2.3 From 2d9cc60aee6bfbd72a06516615af9cde9cb6189a Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Sat, 5 Aug 2017 12:38:27 +0200 Subject: ipv6: sr: add rtnetlink functions for seg6local action parameters This patch adds the necessary functions to parse, fill, and compare seg6local rtnetlink attributes, for all defined action parameters. - The SRH parameter defines an SRH to be inserted or encapsulated. - The TABLE parameter defines the table to use for the route lookup of the next segment or the inner decapsulated packet. - The NH4 parameter defines the IPv4 next-hop for an inner decapsulated IPv4 packet. - The NH6 parameter defines the IPv6 next-hop for the next segment or for an inner decapsulated IPv6 packet - The IIF parameter defines an ingress interface index. - The OIF parameter defines an egress interface index. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/seg6_local.c | 211 +++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 193 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 53615d7e0723..ab1fc1b17ddf 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -104,6 +104,181 @@ static const struct nla_policy seg6_local_policy[SEG6_LOCAL_MAX + 1] = { [SEG6_LOCAL_OIF] = { .type = NLA_U32 }, }; +static int parse_nla_srh(struct nlattr **attrs, struct seg6_local_lwt *slwt) +{ + struct ipv6_sr_hdr *srh; + int len; + + srh = nla_data(attrs[SEG6_LOCAL_SRH]); + len = nla_len(attrs[SEG6_LOCAL_SRH]); + + /* SRH must contain at least one segment */ + if (len < sizeof(*srh) + sizeof(struct in6_addr)) + return -EINVAL; + + if (!seg6_validate_srh(srh, len)) + return -EINVAL; + + slwt->srh = kmalloc(len, GFP_KERNEL); + if (!slwt->srh) + return -ENOMEM; + + memcpy(slwt->srh, srh, len); + + slwt->headroom += len; + + return 0; +} + +static int put_nla_srh(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + struct ipv6_sr_hdr *srh; + struct nlattr *nla; + int len; + + srh = slwt->srh; + len = (srh->hdrlen + 1) << 3; + + nla = nla_reserve(skb, SEG6_LOCAL_SRH, len); + if (!nla) + return -EMSGSIZE; + + memcpy(nla_data(nla), srh, len); + + return 0; +} + +static int cmp_nla_srh(struct seg6_local_lwt *a, struct seg6_local_lwt *b) +{ + int len = (a->srh->hdrlen + 1) << 3; + + if (len != ((b->srh->hdrlen + 1) << 3)) + return 1; + + return memcmp(a->srh, b->srh, len); +} + +static int parse_nla_table(struct nlattr **attrs, struct seg6_local_lwt *slwt) +{ + slwt->table = nla_get_u32(attrs[SEG6_LOCAL_TABLE]); + + return 0; +} + +static int put_nla_table(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + if (nla_put_u32(skb, SEG6_LOCAL_TABLE, slwt->table)) + return -EMSGSIZE; + + return 0; +} + +static int cmp_nla_table(struct seg6_local_lwt *a, struct seg6_local_lwt *b) +{ + if (a->table != b->table) + return 1; + + return 0; +} + +static int parse_nla_nh4(struct nlattr **attrs, struct seg6_local_lwt *slwt) +{ + memcpy(&slwt->nh4, nla_data(attrs[SEG6_LOCAL_NH4]), + sizeof(struct in_addr)); + + return 0; +} + +static int put_nla_nh4(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + struct nlattr *nla; + + nla = nla_reserve(skb, SEG6_LOCAL_NH4, sizeof(struct in_addr)); + if (!nla) + return -EMSGSIZE; + + memcpy(nla_data(nla), &slwt->nh4, sizeof(struct in_addr)); + + return 0; +} + +static int cmp_nla_nh4(struct seg6_local_lwt *a, struct seg6_local_lwt *b) +{ + return memcmp(&a->nh4, &b->nh4, sizeof(struct in_addr)); +} + +static int parse_nla_nh6(struct nlattr **attrs, struct seg6_local_lwt *slwt) +{ + memcpy(&slwt->nh6, nla_data(attrs[SEG6_LOCAL_NH6]), + sizeof(struct in6_addr)); + + return 0; +} + +static int put_nla_nh6(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + struct nlattr *nla; + + nla = nla_reserve(skb, SEG6_LOCAL_NH6, sizeof(struct in6_addr)); + if (!nla) + return -EMSGSIZE; + + memcpy(nla_data(nla), &slwt->nh6, sizeof(struct in6_addr)); + + return 0; +} + +static int cmp_nla_nh6(struct seg6_local_lwt *a, struct seg6_local_lwt *b) +{ + return memcmp(&a->nh6, &b->nh6, sizeof(struct in6_addr)); +} + +static int parse_nla_iif(struct nlattr **attrs, struct seg6_local_lwt *slwt) +{ + slwt->iif = nla_get_u32(attrs[SEG6_LOCAL_IIF]); + + return 0; +} + +static int put_nla_iif(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + if (nla_put_u32(skb, SEG6_LOCAL_IIF, slwt->iif)) + return -EMSGSIZE; + + return 0; +} + +static int cmp_nla_iif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) +{ + if (a->iif != b->iif) + return 1; + + return 0; +} + +static int parse_nla_oif(struct nlattr **attrs, struct seg6_local_lwt *slwt) +{ + slwt->oif = nla_get_u32(attrs[SEG6_LOCAL_OIF]); + + return 0; +} + +static int put_nla_oif(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + if (nla_put_u32(skb, SEG6_LOCAL_OIF, slwt->oif)) + return -EMSGSIZE; + + return 0; +} + +static int cmp_nla_oif(struct seg6_local_lwt *a, struct seg6_local_lwt *b) +{ + if (a->oif != b->oif) + return 1; + + return 0; +} + struct seg6_action_param { int (*parse)(struct nlattr **attrs, struct seg6_local_lwt *slwt); int (*put)(struct sk_buff *skb, struct seg6_local_lwt *slwt); @@ -111,29 +286,29 @@ struct seg6_action_param { }; static struct seg6_action_param seg6_action_params[SEG6_LOCAL_MAX + 1] = { - [SEG6_LOCAL_SRH] = { .parse = NULL, - .put = NULL, - .cmp = NULL }, + [SEG6_LOCAL_SRH] = { .parse = parse_nla_srh, + .put = put_nla_srh, + .cmp = cmp_nla_srh }, - [SEG6_LOCAL_TABLE] = { .parse = NULL, - .put = NULL, - .cmp = NULL }, + [SEG6_LOCAL_TABLE] = { .parse = parse_nla_table, + .put = put_nla_table, + .cmp = cmp_nla_table }, - [SEG6_LOCAL_NH4] = { .parse = NULL, - .put = NULL, - .cmp = NULL }, + [SEG6_LOCAL_NH4] = { .parse = parse_nla_nh4, + .put = put_nla_nh4, + .cmp = cmp_nla_nh4 }, - [SEG6_LOCAL_NH6] = { .parse = NULL, - .put = NULL, - .cmp = NULL }, + [SEG6_LOCAL_NH6] = { .parse = parse_nla_nh6, + .put = put_nla_nh6, + .cmp = cmp_nla_nh6 }, - [SEG6_LOCAL_IIF] = { .parse = NULL, - .put = NULL, - .cmp = NULL }, + [SEG6_LOCAL_IIF] = { .parse = parse_nla_iif, + .put = put_nla_iif, + .cmp = cmp_nla_iif }, - [SEG6_LOCAL_OIF] = { .parse = NULL, - .put = NULL, - .cmp = NULL }, + [SEG6_LOCAL_OIF] = { .parse = parse_nla_oif, + .put = put_nla_oif, + .cmp = cmp_nla_oif }, }; static int parse_nla_action(struct nlattr **attrs, struct seg6_local_lwt *slwt) -- cgit v1.2.3 From 140f04c33bbcf89440dcaf8bef04918abff5a52d Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Sat, 5 Aug 2017 12:39:48 +0200 Subject: ipv6: sr: implement several seg6local actions This patch implements the following seg6local actions. - SEG6_LOCAL_ACTION_END: regular SRH processing. The DA of the packet is updated to the next segment and forwarded accordingly. - SEG6_LOCAL_ACTION_END_X: same as above, except that the packet is forwarded to the specified IPv6 next-hop. - SEG6_LOCAL_ACTION_END_DX6: decapsulate the packet and forward to inner IPv6 packet to the specified IPv6 next-hop. - SEG6_LOCAL_ACTION_END_B6: insert the specified SRH directly after the IPv6 header of the packet. - SEG6_LOCAL_ACTION_END_B6_ENCAP: encapsulate the packet within an outer IPv6 header, containing the specified SRH. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/seg6_local.c | 271 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) (limited to 'net') diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index ab1fc1b17ddf..147680e7a00c 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -58,11 +58,282 @@ static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) return (struct seg6_local_lwt *)lwt->data; } +static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb) +{ + struct ipv6_sr_hdr *srh; + struct ipv6hdr *hdr; + int len; + + hdr = ipv6_hdr(skb); + if (hdr->nexthdr != IPPROTO_ROUTING) + return NULL; + + srh = (struct ipv6_sr_hdr *)(hdr + 1); + len = (srh->hdrlen + 1) << 3; + + if (!pskb_may_pull(skb, sizeof(*hdr) + len)) + return NULL; + + if (!seg6_validate_srh(srh, len)) + return NULL; + + return srh; +} + +static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) +{ + struct ipv6_sr_hdr *srh; + + srh = get_srh(skb); + if (!srh) + return NULL; + + if (srh->segments_left == 0) + return NULL; + +#ifdef CONFIG_IPV6_SEG6_HMAC + if (!seg6_hmac_validate_skb(skb)) + return NULL; +#endif + + return srh; +} + +/* regular endpoint function */ +static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + struct ipv6_sr_hdr *srh; + struct in6_addr *addr; + + srh = get_and_validate_srh(skb); + if (!srh) + goto drop; + + srh->segments_left--; + addr = srh->segments + srh->segments_left; + + ipv6_hdr(skb)->daddr = *addr; + + skb_dst_drop(skb); + ip6_route_input(skb); + + return dst_input(skb); + +drop: + kfree_skb(skb); + return -EINVAL; +} + +/* regular endpoint, and forward to specified nexthop */ +static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + struct net *net = dev_net(skb->dev); + struct ipv6_sr_hdr *srh; + struct dst_entry *dst; + struct in6_addr *addr; + struct ipv6hdr *hdr; + struct flowi6 fl6; + int flags; + + srh = get_and_validate_srh(skb); + if (!srh) + goto drop; + + srh->segments_left--; + addr = srh->segments + srh->segments_left; + + hdr = ipv6_hdr(skb); + hdr->daddr = *addr; + + skb_dst_drop(skb); + + fl6.flowi6_iif = skb->dev->ifindex; + fl6.daddr = slwt->nh6; + fl6.saddr = hdr->saddr; + fl6.flowlabel = ip6_flowinfo(hdr); + fl6.flowi6_mark = skb->mark; + fl6.flowi6_proto = hdr->nexthdr; + + flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_IFACE | + RT6_LOOKUP_F_REACHABLE; + + dst = ip6_route_input_lookup(net, skb->dev, &fl6, flags); + if (dst->dev->flags & IFF_LOOPBACK) + goto drop; + + skb_dst_set(skb, dst); + + return dst_input(skb); + +drop: + kfree_skb(skb); + return -EINVAL; +} + +/* decapsulate and forward to specified nexthop */ +static int input_action_end_dx6(struct sk_buff *skb, + struct seg6_local_lwt *slwt) +{ + struct net *net = dev_net(skb->dev); + struct ipv6hdr *inner_hdr; + struct ipv6_sr_hdr *srh; + struct dst_entry *dst; + unsigned int off = 0; + struct flowi6 fl6; + bool use_nh; + int flags; + + /* this function accepts IPv6 encapsulated packets, with either + * an SRH with SL=0, or no SRH. + */ + + srh = get_srh(skb); + if (srh && srh->segments_left > 0) + goto drop; + +#ifdef CONFIG_IPV6_SEG6_HMAC + if (srh && !seg6_hmac_validate_skb(skb)) + goto drop; +#endif + + if (ipv6_find_hdr(skb, &off, IPPROTO_IPV6, NULL, NULL) < 0) + goto drop; + + if (!pskb_pull(skb, off)) + goto drop; + + skb_postpull_rcsum(skb, skb_network_header(skb), off); + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb->encapsulation = 0; + + inner_hdr = ipv6_hdr(skb); + + /* The inner packet is not associated to any local interface, + * so we do not call netif_rx(). + * + * If slwt->nh6 is set to ::, then lookup the nexthop for the + * inner packet's DA. Otherwise, use the specified nexthop. + */ + + use_nh = !ipv6_addr_any(&slwt->nh6); + + skb_dst_drop(skb); + + fl6.flowi6_iif = skb->dev->ifindex; + fl6.daddr = use_nh ? slwt->nh6 : inner_hdr->daddr; + fl6.saddr = inner_hdr->saddr; + fl6.flowlabel = ip6_flowinfo(inner_hdr); + fl6.flowi6_mark = skb->mark; + fl6.flowi6_proto = inner_hdr->nexthdr; + + flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_REACHABLE; + if (use_nh) + flags |= RT6_LOOKUP_F_IFACE; + + dst = ip6_route_input_lookup(net, skb->dev, &fl6, flags); + if (dst->dev->flags & IFF_LOOPBACK) + goto drop; + + skb_dst_set(skb, dst); + + return dst_input(skb); +drop: + kfree_skb(skb); + return -EINVAL; +} + +/* push an SRH on top of the current one */ +static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + struct ipv6_sr_hdr *srh; + int err = -EINVAL; + + srh = get_and_validate_srh(skb); + if (!srh) + goto drop; + + err = seg6_do_srh_inline(skb, slwt->srh); + if (err) + goto drop; + + ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); + + skb_dst_drop(skb); + ip6_route_input(skb); + + return dst_input(skb); + +drop: + kfree_skb(skb); + return err; +} + +/* encapsulate within an outer IPv6 header and a specified SRH */ +static int input_action_end_b6_encap(struct sk_buff *skb, + struct seg6_local_lwt *slwt) +{ + struct ipv6_sr_hdr *srh; + struct in6_addr *addr; + int err = -EINVAL; + + srh = get_and_validate_srh(skb); + if (!srh) + goto drop; + + srh->segments_left--; + addr = srh->segments + srh->segments_left; + ipv6_hdr(skb)->daddr = *addr; + + skb_reset_inner_headers(skb); + skb->encapsulation = 1; + + err = seg6_do_srh_encap(skb, slwt->srh); + if (err) + goto drop; + + ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); + skb_set_transport_header(skb, sizeof(struct ipv6hdr)); + + skb_dst_drop(skb); + ip6_route_input(skb); + + return dst_input(skb); + +drop: + kfree_skb(skb); + return err; +} + static struct seg6_action_desc seg6_action_table[] = { { .action = SEG6_LOCAL_ACTION_END, .attrs = 0, + .input = input_action_end, + }, + { + .action = SEG6_LOCAL_ACTION_END_X, + .attrs = (1 << SEG6_LOCAL_NH6), + .input = input_action_end_x, }, + { + .action = SEG6_LOCAL_ACTION_END_DX6, + .attrs = (1 << SEG6_LOCAL_NH6), + .input = input_action_end_dx6, + }, + { + .action = SEG6_LOCAL_ACTION_END_B6, + .attrs = (1 << SEG6_LOCAL_SRH), + .input = input_action_end_b6, + }, + { + .action = SEG6_LOCAL_ACTION_END_B6_ENCAP, + .attrs = (1 << SEG6_LOCAL_SRH), + .input = input_action_end_b6_encap, + .static_headroom = sizeof(struct ipv6hdr), + } }; static struct seg6_action_desc *__get_action_desc(int action) -- cgit v1.2.3 From 206e41fe7f441542e8b92b314cfcdab21c709233 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 5 Aug 2017 16:20:17 -0400 Subject: net: dsa: remove useless argument in legacy setup dsa_switch_alloc() already assigns ds-dev, which can be used in dsa_switch_setup_one and dsa_cpu_dsa_setups instead of requiring an additional struct device argument. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/legacy.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 1d7a3282f2a7..fa162030a69c 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -78,7 +78,7 @@ dsa_switch_probe(struct device *parent, struct device *host_dev, int sw_addr, } /* basic switch operations **************************************************/ -static int dsa_cpu_dsa_setups(struct dsa_switch *ds, struct device *dev) +static int dsa_cpu_dsa_setups(struct dsa_switch *ds) { struct dsa_port *dport; int ret, port; @@ -88,15 +88,15 @@ static int dsa_cpu_dsa_setups(struct dsa_switch *ds, struct device *dev) continue; dport = &ds->ports[port]; - ret = dsa_cpu_dsa_setup(ds, dev, dport, port); + ret = dsa_cpu_dsa_setup(ds, ds->dev, dport, port); if (ret) return ret; } return 0; } -static int dsa_switch_setup_one(struct dsa_switch *ds, struct net_device *master, - struct device *parent) +static int dsa_switch_setup_one(struct dsa_switch *ds, + struct net_device *master) { const struct dsa_switch_ops *ops = ds->ops; struct dsa_switch_tree *dst = ds->dst; @@ -176,7 +176,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct net_device *master } if (!ds->slave_mii_bus && ops->phy_read) { - ds->slave_mii_bus = devm_mdiobus_alloc(parent); + ds->slave_mii_bus = devm_mdiobus_alloc(ds->dev); if (!ds->slave_mii_bus) return -ENOMEM; dsa_slave_mii_bus_init(ds); @@ -196,14 +196,14 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, struct net_device *master if (!(ds->enabled_port_mask & (1 << i))) continue; - ret = dsa_slave_create(ds, parent, i, cd->port_names[i]); + ret = dsa_slave_create(ds, ds->dev, i, cd->port_names[i]); if (ret < 0) netdev_err(master, "[%d]: can't create dsa slave device for port %d(%s): %d\n", index, i, cd->port_names[i], ret); } /* Perform configuration of the CPU and DSA ports */ - ret = dsa_cpu_dsa_setups(ds, parent); + ret = dsa_cpu_dsa_setups(ds); if (ret < 0) netdev_err(master, "[%d] : can't configure CPU and DSA ports\n", index); @@ -252,7 +252,7 @@ dsa_switch_setup(struct dsa_switch_tree *dst, struct net_device *master, ds->ops = ops; ds->priv = priv; - ret = dsa_switch_setup_one(ds, master, parent); + ret = dsa_switch_setup_one(ds, master); if (ret) return ERR_PTR(ret); -- cgit v1.2.3 From 47d0dcc35412feb354aace4c5dcc07c03cb4ccb0 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 5 Aug 2017 16:20:18 -0400 Subject: net: dsa: remove useless args of dsa_cpu_dsa_setup dsa_cpu_dsa_setup currently takes 4 arguments but they are all available from the dsa_port argument. Remove all others. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa.c | 10 +++++----- net/dsa/dsa2.c | 4 ++-- net/dsa/dsa_priv.h | 3 +-- net/dsa/legacy.c | 4 +--- 4 files changed, 9 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index a91e520e735f..4118848b8e58 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -67,17 +67,17 @@ const struct dsa_device_ops *dsa_device_ops[DSA_TAG_LAST] = { [DSA_TAG_PROTO_NONE] = &none_ops, }; -int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct device *dev, - struct dsa_port *dport, int port) +int dsa_cpu_dsa_setup(struct dsa_port *port) { - struct device_node *port_dn = dport->dn; + struct device_node *port_dn = port->dn; + struct dsa_switch *ds = port->ds; struct phy_device *phydev; int ret, mode; if (of_phy_is_fixed_link(port_dn)) { ret = of_phy_register_fixed_link(port_dn); if (ret) { - dev_err(dev, "failed to register fixed PHY\n"); + dev_err(ds->dev, "failed to register fixed PHY\n"); return ret; } phydev = of_phy_find_device(port_dn); @@ -90,7 +90,7 @@ int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct device *dev, genphy_config_init(phydev); genphy_read_status(phydev); if (ds->ops->adjust_link) - ds->ops->adjust_link(ds, port, phydev); + ds->ops->adjust_link(ds, port->index, phydev); put_device(&phydev->mdio.dev); } diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index c442051d5a55..2a0120493cf1 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -219,7 +219,7 @@ static int dsa_dsa_port_apply(struct dsa_port *port) struct dsa_switch *ds = port->ds; int err; - err = dsa_cpu_dsa_setup(ds, ds->dev, port, port->index); + err = dsa_cpu_dsa_setup(port); if (err) { dev_warn(ds->dev, "Failed to setup dsa port %d: %d\n", port->index, err); @@ -243,7 +243,7 @@ static int dsa_cpu_port_apply(struct dsa_port *port) struct dsa_switch *ds = port->ds; int err; - err = dsa_cpu_dsa_setup(ds, ds->dev, port, port->index); + err = dsa_cpu_dsa_setup(port); if (err) { dev_warn(ds->dev, "Failed to setup cpu port %d: %d\n", port->index, err); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 306cff229def..2873d912b12e 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -101,8 +101,7 @@ struct dsa_slave_priv { }; /* dsa.c */ -int dsa_cpu_dsa_setup(struct dsa_switch *ds, struct device *dev, - struct dsa_port *dport, int port); +int dsa_cpu_dsa_setup(struct dsa_port *port); void dsa_cpu_dsa_destroy(struct dsa_port *dport); const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol); int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index fa162030a69c..903e8d70c792 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -80,15 +80,13 @@ dsa_switch_probe(struct device *parent, struct device *host_dev, int sw_addr, /* basic switch operations **************************************************/ static int dsa_cpu_dsa_setups(struct dsa_switch *ds) { - struct dsa_port *dport; int ret, port; for (port = 0; port < ds->num_ports; port++) { if (!(dsa_is_cpu_port(ds, port) || dsa_is_dsa_port(ds, port))) continue; - dport = &ds->ports[port]; - ret = dsa_cpu_dsa_setup(ds, ds->dev, dport, port); + ret = dsa_cpu_dsa_setup(&ds->ports[port]); if (ret) return ret; } -- cgit v1.2.3 From 4cfbf09cf97f265c82f48ca2bd7ea704b6a21a90 Mon Sep 17 00:00:00 2001 From: Vivien Didelot Date: Sat, 5 Aug 2017 16:20:19 -0400 Subject: net: dsa: remove useless args of dsa_slave_create dsa_slave_create currently takes 4 arguments while it only needs the related dsa_port and its name. Remove all other arguments. Signed-off-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa2.c | 2 +- net/dsa/dsa_priv.h | 3 +-- net/dsa/legacy.c | 2 +- net/dsa/slave.c | 14 +++++++------- 4 files changed, 10 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/dsa/dsa2.c b/net/dsa/dsa2.c index 2a0120493cf1..cceaa4dd9f53 100644 --- a/net/dsa/dsa2.c +++ b/net/dsa/dsa2.c @@ -275,7 +275,7 @@ static int dsa_user_port_apply(struct dsa_port *port) if (!name) name = "eth%d"; - err = dsa_slave_create(ds, ds->dev, port->index, name); + err = dsa_slave_create(port, name); if (err) { dev_warn(ds->dev, "Failed to create slave %d: %d\n", port->index, err); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 2873d912b12e..945ded148c9c 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -148,8 +148,7 @@ int dsa_port_vlan_dump(struct dsa_port *dp, extern const struct dsa_device_ops notag_netdev_ops; void dsa_slave_mii_bus_init(struct dsa_switch *ds); void dsa_cpu_port_ethtool_init(struct ethtool_ops *ops); -int dsa_slave_create(struct dsa_switch *ds, struct device *parent, - int port, const char *name); +int dsa_slave_create(struct dsa_port *port, const char *name); void dsa_slave_destroy(struct net_device *slave_dev); int dsa_slave_suspend(struct net_device *slave_dev); int dsa_slave_resume(struct net_device *slave_dev); diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 903e8d70c792..612acf16d573 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -194,7 +194,7 @@ static int dsa_switch_setup_one(struct dsa_switch *ds, if (!(ds->enabled_port_mask & (1 << i))) continue; - ret = dsa_slave_create(ds, ds->dev, i, cd->port_names[i]); + ret = dsa_slave_create(&ds->ports[i], cd->port_names[i]); if (ret < 0) netdev_err(master, "[%d]: can't create dsa slave device for port %d(%s): %d\n", index, i, cd->port_names[i], ret); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index c6b5de2fe413..3b36c47472c6 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1195,9 +1195,9 @@ int dsa_slave_resume(struct net_device *slave_dev) return 0; } -int dsa_slave_create(struct dsa_switch *ds, struct device *parent, - int port, const char *name) +int dsa_slave_create(struct dsa_port *port, const char *name) { + struct dsa_switch *ds = port->ds; struct dsa_switch_tree *dst = ds->dst; struct net_device *master; struct net_device *slave_dev; @@ -1227,8 +1227,8 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, netdev_for_each_tx_queue(slave_dev, dsa_slave_set_lockdep_class_one, NULL); - SET_NETDEV_DEV(slave_dev, parent); - slave_dev->dev.of_node = ds->ports[port].dn; + SET_NETDEV_DEV(slave_dev, port->ds->dev); + slave_dev->dev.of_node = port->dn; slave_dev->vlan_features = master->vlan_features; p = netdev_priv(slave_dev); @@ -1237,7 +1237,7 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, free_netdev(slave_dev); return -ENOMEM; } - p->dp = &ds->ports[port]; + p->dp = port; INIT_LIST_HEAD(&p->mall_tc_list); p->xmit = dst->tag_ops->xmit; @@ -1245,12 +1245,12 @@ int dsa_slave_create(struct dsa_switch *ds, struct device *parent, p->old_link = -1; p->old_duplex = -1; - ds->ports[port].netdev = slave_dev; + port->netdev = slave_dev; ret = register_netdev(slave_dev); if (ret) { netdev_err(master, "error %d registering interface %s\n", ret, slave_dev->name); - ds->ports[port].netdev = NULL; + port->netdev = NULL; free_percpu(p->stats64); free_netdev(slave_dev); return ret; -- cgit v1.2.3 From 13ead5c4f2e3b649156c74892f3cf4b62c8e3d0c Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sun, 6 Aug 2017 10:19:07 +0200 Subject: xfrm: check that cached bundle is still valid Quoting Ilan Tayari: 1. Set up a host-to-host IPSec tunnel (or transport, doesn't matter) 2. Ping over IPSec, or do something to populate the pcpu cache 3. Join a MC group, then leave MC group 4. Try to ping again using same CPU as before -> traffic doesn't egress the machine at all Ilan debugged the problem down to the fact that one of the path dsts devices point to lo due to earlier dst_dev_put(). In this case, dst is marked as DEAD and we cannot reuse the bundle. The cache only asserted that the requested policy and that of the cached bundle match, but its not enough - also verify the path is still valid. Fixes: ec30d78c14a813 ("xfrm: add xdst pcpu cache") Reported-by: Ayham Masood Tested-by: Ilan Tayari Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/xfrm/xfrm_policy.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 06c3bf7ab86b..8da428f56aec 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -1818,7 +1818,8 @@ xfrm_resolve_and_create_bundle(struct xfrm_policy **pols, int num_pols, xdst->num_pols == num_pols && !xfrm_pol_dead(xdst) && memcmp(xdst->pols, pols, - sizeof(struct xfrm_policy *) * num_pols) == 0) { + sizeof(struct xfrm_policy *) * num_pols) == 0 && + xfrm_bundle_ok(xdst)) { dst_hold(&xdst->u.dst); return xdst; } -- cgit v1.2.3 From 6c2c1dcb185f1e44e1c895781dbaba40195234f9 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:39 +0300 Subject: net: dsa: Change DSA slave FDB API to be switchdev independent In order to support FDB add/del to be on a notifier chain the slave API need to be changed to be switchdev independent. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 12 +++++------- drivers/net/dsa/b53/b53_priv.h | 8 +++----- drivers/net/dsa/microchip/ksz_common.c | 34 ++++++++++++++++------------------ drivers/net/dsa/mt7530.c | 14 ++++++-------- drivers/net/dsa/mv88e6xxx/chip.c | 12 +++++------- drivers/net/dsa/qca8k.c | 15 ++++++--------- include/net/dsa.h | 8 +++----- net/dsa/switch.c | 8 +++++--- 8 files changed, 49 insertions(+), 62 deletions(-) (limited to 'net') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 7f36d3e3c98b..53361796607a 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1214,8 +1214,7 @@ static int b53_arl_op(struct b53_device *dev, int op, int port, } int b53_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct b53_device *priv = ds->priv; @@ -1230,22 +1229,21 @@ int b53_fdb_prepare(struct dsa_switch *ds, int port, EXPORT_SYMBOL(b53_fdb_prepare); void b53_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct b53_device *priv = ds->priv; - if (b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, true)) + if (b53_arl_op(priv, 0, port, addr, vid, true)) pr_err("%s: failed to add MAC address\n", __func__); } EXPORT_SYMBOL(b53_fdb_add); int b53_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct b53_device *priv = ds->priv; - return b53_arl_op(priv, 0, port, fdb->addr, fdb->vid, false); + return b53_arl_op(priv, 0, port, addr, vid, false); } EXPORT_SYMBOL(b53_fdb_del); diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index 155a9c48c317..d417bcaec71d 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -397,13 +397,11 @@ int b53_vlan_dump(struct dsa_switch *ds, int port, struct switchdev_obj_port_vlan *vlan, switchdev_obj_dump_cb_t *cb); int b53_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const unsigned char *addr, u16 vid); void b53_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const unsigned char *addr, u16 vid); int b53_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb); + const unsigned char *addr, u16 vid); int b53_fdb_dump(struct dsa_switch *ds, int port, struct switchdev_obj_port_fdb *fdb, switchdev_obj_dump_cb_t *cb); diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index b313ecdf2919..db828080ee93 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -679,8 +679,7 @@ static int ksz_port_vlan_dump(struct dsa_switch *ds, int port, } static int ksz_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { /* nothing needed */ @@ -707,8 +706,7 @@ struct alu_struct { }; static void ksz_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct ksz_device *dev = ds->priv; u32 alu_table[4]; @@ -717,12 +715,12 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port, mutex_lock(&dev->alu_mutex); /* find any entry with mac & vid */ - data = fdb->vid << ALU_FID_INDEX_S; - data |= ((fdb->addr[0] << 8) | fdb->addr[1]); + data = vid << ALU_FID_INDEX_S; + data |= ((addr[0] << 8) | addr[1]); ksz_write32(dev, REG_SW_ALU_INDEX_0, data); - data = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16)); - data |= ((fdb->addr[4] << 8) | fdb->addr[5]); + data = ((addr[2] << 24) | (addr[3] << 16)); + data |= ((addr[4] << 8) | addr[5]); ksz_write32(dev, REG_SW_ALU_INDEX_1, data); /* start read operation */ @@ -740,12 +738,12 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port, /* update ALU entry */ alu_table[0] = ALU_V_STATIC_VALID; alu_table[1] |= BIT(port); - if (fdb->vid) + if (vid) alu_table[1] |= ALU_V_USE_FID; - alu_table[2] = (fdb->vid << ALU_V_FID_S); - alu_table[2] |= ((fdb->addr[0] << 8) | fdb->addr[1]); - alu_table[3] = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16)); - alu_table[3] |= ((fdb->addr[4] << 8) | fdb->addr[5]); + alu_table[2] = (vid << ALU_V_FID_S); + alu_table[2] |= ((addr[0] << 8) | addr[1]); + alu_table[3] = ((addr[2] << 24) | (addr[3] << 16)); + alu_table[3] |= ((addr[4] << 8) | addr[5]); write_table(ds, alu_table); @@ -760,7 +758,7 @@ exit: } static int ksz_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct ksz_device *dev = ds->priv; u32 alu_table[4]; @@ -770,12 +768,12 @@ static int ksz_port_fdb_del(struct dsa_switch *ds, int port, mutex_lock(&dev->alu_mutex); /* read any entry with mac & vid */ - data = fdb->vid << ALU_FID_INDEX_S; - data |= ((fdb->addr[0] << 8) | fdb->addr[1]); + data = vid << ALU_FID_INDEX_S; + data |= ((addr[0] << 8) | addr[1]); ksz_write32(dev, REG_SW_ALU_INDEX_0, data); - data = ((fdb->addr[2] << 24) | (fdb->addr[3] << 16)); - data |= ((fdb->addr[4] << 8) | fdb->addr[5]); + data = ((addr[2] << 24) | (addr[3] << 16)); + data |= ((addr[4] << 8) | addr[5]); ksz_write32(dev, REG_SW_ALU_INDEX_1, data); /* start read operation */ diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 1e46418a3b74..430e3ab65a49 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -802,8 +802,7 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, static int mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct mt7530_priv *priv = ds->priv; int ret; @@ -813,7 +812,7 @@ mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, * is called while the entry is still available. */ mutex_lock(&priv->reg_mutex); - mt7530_fdb_write(priv, fdb->vid, 0, fdb->addr, -1, STATIC_ENT); + mt7530_fdb_write(priv, vid, 0, addr, -1, STATIC_ENT); ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); mutex_unlock(&priv->reg_mutex); @@ -822,28 +821,27 @@ mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, static void mt7530_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct mt7530_priv *priv = ds->priv; u8 port_mask = BIT(port); mutex_lock(&priv->reg_mutex); - mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_ENT); + mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_ENT); mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); mutex_unlock(&priv->reg_mutex); } static int mt7530_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct mt7530_priv *priv = ds->priv; int ret; u8 port_mask = BIT(port); mutex_lock(&priv->reg_mutex); - mt7530_fdb_write(priv, fdb->vid, port_mask, fdb->addr, -1, STATIC_EMP); + mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_EMP); ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); mutex_unlock(&priv->reg_mutex); diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 521738c4cd17..823697526db4 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1407,8 +1407,7 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, } static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { /* We don't need any dynamic resource from the kernel (yet), * so skip the prepare phase. @@ -1417,13 +1416,12 @@ static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, } static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct mv88e6xxx_chip *chip = ds->priv; mutex_lock(&chip->reg_lock); - if (mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, + if (mv88e6xxx_port_db_load_purge(chip, port, addr, vid, MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC)) dev_err(ds->dev, "p%d: failed to load unicast MAC address\n", port); @@ -1431,13 +1429,13 @@ static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, } static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct mv88e6xxx_chip *chip = ds->priv; int err; mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_db_load_purge(chip, port, fdb->addr, fdb->vid, + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, MV88E6XXX_G1_ATU_DATA_STATE_UNUSED); mutex_unlock(&chip->reg_lock); diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 36c169b0c705..2fb5df9dbd64 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -778,8 +778,7 @@ qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, static int qca8k_port_fdb_prepare(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; @@ -788,33 +787,31 @@ qca8k_port_fdb_prepare(struct dsa_switch *ds, int port, * when port_fdb_add is called an entry is still available. Otherwise * the last free entry might have been used up by auto learning */ - return qca8k_port_fdb_insert(priv, fdb->addr, 0, fdb->vid); + return qca8k_port_fdb_insert(priv, addr, 0, vid); } static void qca8k_port_fdb_add(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const unsigned char *addr, u16 vid) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u16 port_mask = BIT(port); /* Update the FDB entry adding the port_mask */ - qca8k_port_fdb_insert(priv, fdb->addr, port_mask, fdb->vid); + qca8k_port_fdb_insert(priv, addr, port_mask, vid); } static int qca8k_port_fdb_del(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb) + const unsigned char *addr, u16 vid) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u16 port_mask = BIT(port); - u16 vid = fdb->vid; if (!vid) vid = 1; - return qca8k_fdb_del(priv, fdb->addr, port_mask, vid); + return qca8k_fdb_del(priv, addr, port_mask, vid); } static int diff --git a/include/net/dsa.h b/include/net/dsa.h index 0b1a0622b33c..ba11005dac8c 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -386,13 +386,11 @@ struct dsa_switch_ops { * Forwarding database */ int (*port_fdb_prepare)(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const unsigned char *addr, u16 vid); void (*port_fdb_add)(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const unsigned char *addr, u16 vid); int (*port_fdb_del)(struct dsa_switch *ds, int port, - const struct switchdev_obj_port_fdb *fdb); + const unsigned char *addr, u16 vid); int (*port_fdb_dump)(struct dsa_switch *ds, int port, struct switchdev_obj_port_fdb *fdb, switchdev_obj_dump_cb_t *cb); diff --git a/net/dsa/switch.c b/net/dsa/switch.c index 97e2e9c8cf3f..a9edfbad3889 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -94,10 +94,11 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds, if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add) return -EOPNOTSUPP; - return ds->ops->port_fdb_prepare(ds, info->port, fdb, trans); + return ds->ops->port_fdb_prepare(ds, info->port, fdb->addr, + fdb->vid); } - ds->ops->port_fdb_add(ds, info->port, fdb, trans); + ds->ops->port_fdb_add(ds, info->port, fdb->addr, fdb->vid); return 0; } @@ -114,7 +115,8 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds, if (!ds->ops->port_fdb_del) return -EOPNOTSUPP; - return ds->ops->port_fdb_del(ds, info->port, fdb); + return ds->ops->port_fdb_del(ds, info->port, fdb->addr, + fdb->vid); } static int dsa_switch_mdb_add(struct dsa_switch *ds, -- cgit v1.2.3 From 1b6dd556c3045ca5fa31cc1e98a4a43afa680e1e Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:40 +0300 Subject: net: dsa: Remove prepare phase for FDB The prepare phase for FDB add is unneeded because most of DSA devices can have failures during bus transactions (SPI, I2C, etc.), thus, the prepare phase cannot guarantee success of the commit stage. The support for learning FDB through notification chain, which will be introduced in the following patches, will provide the ability to notify back the bridge about successful offload. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 17 +++-------------- drivers/net/dsa/b53/b53_priv.h | 6 ++---- drivers/net/dsa/bcm_sf2.c | 1 - drivers/net/dsa/microchip/ksz_common.c | 24 ++++++++++-------------- drivers/net/dsa/mt7530.c | 25 ++++--------------------- drivers/net/dsa/mv88e6xxx/chip.c | 23 +++++++---------------- drivers/net/dsa/qca8k.c | 18 +----------------- include/net/dsa.h | 4 +--- net/dsa/dsa_priv.h | 4 +--- net/dsa/port.c | 4 +--- net/dsa/slave.c | 4 +++- net/dsa/switch.c | 14 +++----------- 12 files changed, 36 insertions(+), 108 deletions(-) (limited to 'net') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 53361796607a..3cf4f0a04453 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1213,8 +1213,8 @@ static int b53_arl_op(struct b53_device *dev, int op, int port, return b53_arl_rw_op(dev, 0); } -int b53_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +int b53_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) { struct b53_device *priv = ds->priv; @@ -1224,17 +1224,7 @@ int b53_fdb_prepare(struct dsa_switch *ds, int port, if (is5325(priv) || is5365(priv)) return -EOPNOTSUPP; - return 0; -} -EXPORT_SYMBOL(b53_fdb_prepare); - -void b53_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - struct b53_device *priv = ds->priv; - - if (b53_arl_op(priv, 0, port, addr, vid, true)) - pr_err("%s: failed to add MAC address\n", __func__); + return b53_arl_op(priv, 0, port, addr, vid, true); } EXPORT_SYMBOL(b53_fdb_add); @@ -1563,7 +1553,6 @@ static const struct dsa_switch_ops b53_switch_ops = { .port_vlan_add = b53_vlan_add, .port_vlan_del = b53_vlan_del, .port_vlan_dump = b53_vlan_dump, - .port_fdb_prepare = b53_fdb_prepare, .port_fdb_dump = b53_fdb_dump, .port_fdb_add = b53_fdb_add, .port_fdb_del = b53_fdb_del, diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index d417bcaec71d..f29c892efa6b 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -396,10 +396,8 @@ int b53_vlan_del(struct dsa_switch *ds, int port, int b53_vlan_dump(struct dsa_switch *ds, int port, struct switchdev_obj_port_vlan *vlan, switchdev_obj_dump_cb_t *cb); -int b53_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); -void b53_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); +int b53_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid); int b53_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int b53_fdb_dump(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/bcm_sf2.c b/drivers/net/dsa/bcm_sf2.c index 558667c814c9..1907b27297c3 100644 --- a/drivers/net/dsa/bcm_sf2.c +++ b/drivers/net/dsa/bcm_sf2.c @@ -1022,7 +1022,6 @@ static const struct dsa_switch_ops bcm_sf2_ops = { .port_vlan_add = b53_vlan_add, .port_vlan_del = b53_vlan_del, .port_vlan_dump = b53_vlan_dump, - .port_fdb_prepare = b53_fdb_prepare, .port_fdb_dump = b53_fdb_dump, .port_fdb_add = b53_fdb_add, .port_fdb_del = b53_fdb_del, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index db828080ee93..b55f3649ff93 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -678,14 +678,6 @@ static int ksz_port_vlan_dump(struct dsa_switch *ds, int port, return err; } -static int ksz_port_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - /* nothing needed */ - - return 0; -} - struct alu_struct { /* entry 1 */ u8 is_static:1; @@ -705,12 +697,13 @@ struct alu_struct { u8 mac[ETH_ALEN]; }; -static void ksz_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +static int ksz_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) { struct ksz_device *dev = ds->priv; u32 alu_table[4]; u32 data; + int ret = 0; mutex_lock(&dev->alu_mutex); @@ -727,7 +720,8 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port, ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_READ | ALU_START); /* wait to be finished */ - if (wait_alu_ready(dev, ALU_START, 1000) < 0) { + ret = wait_alu_ready(dev, ALU_START, 1000); + if (ret < 0) { dev_dbg(dev->dev, "Failed to read ALU\n"); goto exit; } @@ -750,11 +744,14 @@ static void ksz_port_fdb_add(struct dsa_switch *ds, int port, ksz_write32(dev, REG_SW_ALU_CTRL__4, ALU_WRITE | ALU_START); /* wait to be finished */ - if (wait_alu_ready(dev, ALU_START, 1000) < 0) - dev_dbg(dev->dev, "Failed to read ALU\n"); + ret = wait_alu_ready(dev, ALU_START, 1000); + if (ret < 0) + dev_dbg(dev->dev, "Failed to write ALU\n"); exit: mutex_unlock(&dev->alu_mutex); + + return ret; } static int ksz_port_fdb_del(struct dsa_switch *ds, int port, @@ -1128,7 +1125,6 @@ static const struct dsa_switch_ops ksz_switch_ops = { .port_vlan_add = ksz_port_vlan_add, .port_vlan_del = ksz_port_vlan_del, .port_vlan_dump = ksz_port_vlan_dump, - .port_fdb_prepare = ksz_port_fdb_prepare, .port_fdb_dump = ksz_port_fdb_dump, .port_fdb_add = ksz_port_fdb_add, .port_fdb_del = ksz_port_fdb_del, diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index 430e3ab65a49..f92aae8947e6 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -801,35 +801,19 @@ mt7530_port_bridge_leave(struct dsa_switch *ds, int port, } static int -mt7530_port_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - struct mt7530_priv *priv = ds->priv; - int ret; - - /* Because auto-learned entrie shares the same FDB table. - * an entry is reserved with no port_mask to make sure fdb_add - * is called while the entry is still available. - */ - mutex_lock(&priv->reg_mutex); - mt7530_fdb_write(priv, vid, 0, addr, -1, STATIC_ENT); - ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); - mutex_unlock(&priv->reg_mutex); - - return ret; -} - -static void mt7530_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { struct mt7530_priv *priv = ds->priv; + int ret; u8 port_mask = BIT(port); mutex_lock(&priv->reg_mutex); mt7530_fdb_write(priv, vid, port_mask, addr, -1, STATIC_ENT); - mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); + ret = mt7530_fdb_cmd(priv, MT7530_FDB_WRITE, 0); mutex_unlock(&priv->reg_mutex); + + return ret; } static int @@ -1013,7 +997,6 @@ static struct dsa_switch_ops mt7530_switch_ops = { .port_stp_state_set = mt7530_stp_state_set, .port_bridge_join = mt7530_port_bridge_join, .port_bridge_leave = mt7530_port_bridge_leave, - .port_fdb_prepare = mt7530_port_fdb_prepare, .port_fdb_add = mt7530_port_fdb_add, .port_fdb_del = mt7530_port_fdb_del, .port_fdb_dump = mt7530_port_fdb_dump, diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 823697526db4..68cc1f6fb75e 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1406,26 +1406,18 @@ static int mv88e6xxx_port_db_load_purge(struct mv88e6xxx_chip *chip, int port, return mv88e6xxx_g1_atu_loadpurge(chip, vlan.fid, &entry); } -static int mv88e6xxx_port_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - /* We don't need any dynamic resource from the kernel (yet), - * so skip the prepare phase. - */ - return 0; -} - -static void mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) +static int mv88e6xxx_port_fdb_add(struct dsa_switch *ds, int port, + const unsigned char *addr, u16 vid) { struct mv88e6xxx_chip *chip = ds->priv; + int err; mutex_lock(&chip->reg_lock); - if (mv88e6xxx_port_db_load_purge(chip, port, addr, vid, - MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC)) - dev_err(ds->dev, "p%d: failed to load unicast MAC address\n", - port); + err = mv88e6xxx_port_db_load_purge(chip, port, addr, vid, + MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC); mutex_unlock(&chip->reg_lock); + + return err; } static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, @@ -3905,7 +3897,6 @@ static const struct dsa_switch_ops mv88e6xxx_switch_ops = { .port_vlan_add = mv88e6xxx_port_vlan_add, .port_vlan_del = mv88e6xxx_port_vlan_del, .port_vlan_dump = mv88e6xxx_port_vlan_dump, - .port_fdb_prepare = mv88e6xxx_port_fdb_prepare, .port_fdb_add = mv88e6xxx_port_fdb_add, .port_fdb_del = mv88e6xxx_port_fdb_del, .port_fdb_dump = mv88e6xxx_port_fdb_dump, diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index 2fb5df9dbd64..f8ef823349bc 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -777,28 +777,13 @@ qca8k_port_fdb_insert(struct qca8k_priv *priv, const u8 *addr, } static int -qca8k_port_fdb_prepare(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid) -{ - struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; - - /* The FDB table for static and auto learned entries is the same. We - * need to reserve an entry with no port_mask set to make sure that - * when port_fdb_add is called an entry is still available. Otherwise - * the last free entry might have been used up by auto learning - */ - return qca8k_port_fdb_insert(priv, addr, 0, vid); -} - -static void qca8k_port_fdb_add(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; u16 port_mask = BIT(port); - /* Update the FDB entry adding the port_mask */ - qca8k_port_fdb_insert(priv, addr, port_mask, vid); + return qca8k_port_fdb_insert(priv, addr, port_mask, vid); } static int @@ -866,7 +851,6 @@ static const struct dsa_switch_ops qca8k_switch_ops = { .port_stp_state_set = qca8k_port_stp_state_set, .port_bridge_join = qca8k_port_bridge_join, .port_bridge_leave = qca8k_port_bridge_leave, - .port_fdb_prepare = qca8k_port_fdb_prepare, .port_fdb_add = qca8k_port_fdb_add, .port_fdb_del = qca8k_port_fdb_del, .port_fdb_dump = qca8k_port_fdb_dump, diff --git a/include/net/dsa.h b/include/net/dsa.h index ba11005dac8c..446fc438cb80 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -385,9 +385,7 @@ struct dsa_switch_ops { /* * Forwarding database */ - int (*port_fdb_prepare)(struct dsa_switch *ds, int port, - const unsigned char *addr, u16 vid); - void (*port_fdb_add)(struct dsa_switch *ds, int port, + int (*port_fdb_add)(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int (*port_fdb_del)(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 945ded148c9c..04cd711ed2fd 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -44,7 +44,6 @@ struct dsa_notifier_bridge_info { /* DSA_NOTIFIER_FDB_* */ struct dsa_notifier_fdb_info { const struct switchdev_obj_port_fdb *fdb; - struct switchdev_trans *trans; int sw_index; int port; }; @@ -122,8 +121,7 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, struct switchdev_trans *trans); int dsa_port_fdb_add(struct dsa_port *dp, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans); + const struct switchdev_obj_port_fdb *fdb); int dsa_port_fdb_del(struct dsa_port *dp, const struct switchdev_obj_port_fdb *fdb); int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb, diff --git a/net/dsa/port.c b/net/dsa/port.c index efc3bce3a89d..bd271b9cc1f2 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -147,13 +147,11 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, } int dsa_port_fdb_add(struct dsa_port *dp, - const struct switchdev_obj_port_fdb *fdb, - struct switchdev_trans *trans) + const struct switchdev_obj_port_fdb *fdb) { struct dsa_notifier_fdb_info info = { .sw_index = dp->ds->index, .port = dp->index, - .trans = trans, .fdb = fdb, }; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 3b36c47472c6..bb7ab26ef768 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -251,7 +251,9 @@ static int dsa_slave_port_obj_add(struct net_device *dev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_FDB: - err = dsa_port_fdb_add(dp, SWITCHDEV_OBJ_PORT_FDB(obj), trans); + if (switchdev_trans_ph_prepare(trans)) + return 0; + err = dsa_port_fdb_add(dp, SWITCHDEV_OBJ_PORT_FDB(obj)); break; case SWITCHDEV_OBJ_ID_PORT_MDB: err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans); diff --git a/net/dsa/switch.c b/net/dsa/switch.c index a9edfbad3889..eb20e0fee0e1 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -84,23 +84,15 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds, struct dsa_notifier_fdb_info *info) { const struct switchdev_obj_port_fdb *fdb = info->fdb; - struct switchdev_trans *trans = info->trans; /* Do not care yet about other switch chips of the fabric */ if (ds->index != info->sw_index) return 0; - if (switchdev_trans_ph_prepare(trans)) { - if (!ds->ops->port_fdb_prepare || !ds->ops->port_fdb_add) - return -EOPNOTSUPP; - - return ds->ops->port_fdb_prepare(ds, info->port, fdb->addr, - fdb->vid); - } - - ds->ops->port_fdb_add(ds, info->port, fdb->addr, fdb->vid); + if (!ds->ops->port_fdb_add) + return -EOPNOTSUPP; - return 0; + return ds->ops->port_fdb_add(ds, info->port, fdb->addr, fdb->vid); } static int dsa_switch_fdb_del(struct dsa_switch *ds, -- cgit v1.2.3 From 2acf4e6a890b0228ed19b228063d69666f61ee19 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:41 +0300 Subject: net: dsa: Remove switchdev dependency from DSA switch notifier chain Currently, the switchdev objects are embedded inside the DSA notifier info. This patch removes this dependency. This is done as a preparation stage before adding support for learning FDB through the switchdev notification chain. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Florian Fainelli Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- net/dsa/dsa_priv.h | 11 ++++++----- net/dsa/port.c | 15 +++++++++------ net/dsa/slave.c | 6 ++++-- net/dsa/switch.c | 11 ++++------- 4 files changed, 23 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 04cd711ed2fd..c0ee6a7694f8 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -43,9 +43,10 @@ struct dsa_notifier_bridge_info { /* DSA_NOTIFIER_FDB_* */ struct dsa_notifier_fdb_info { - const struct switchdev_obj_port_fdb *fdb; int sw_index; int port; + const unsigned char *addr; + u16 vid; }; /* DSA_NOTIFIER_MDB_* */ @@ -120,10 +121,10 @@ int dsa_port_vlan_filtering(struct dsa_port *dp, bool vlan_filtering, struct switchdev_trans *trans); int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, struct switchdev_trans *trans); -int dsa_port_fdb_add(struct dsa_port *dp, - const struct switchdev_obj_port_fdb *fdb); -int dsa_port_fdb_del(struct dsa_port *dp, - const struct switchdev_obj_port_fdb *fdb); +int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, + u16 vid); +int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, + u16 vid); int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb, switchdev_obj_dump_cb_t *cb); int dsa_port_mdb_add(struct dsa_port *dp, diff --git a/net/dsa/port.c b/net/dsa/port.c index bd271b9cc1f2..86e0585215bf 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -146,25 +146,28 @@ int dsa_port_ageing_time(struct dsa_port *dp, clock_t ageing_clock, return dsa_port_notify(dp, DSA_NOTIFIER_AGEING_TIME, &info); } -int dsa_port_fdb_add(struct dsa_port *dp, - const struct switchdev_obj_port_fdb *fdb) +int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, + u16 vid) { struct dsa_notifier_fdb_info info = { .sw_index = dp->ds->index, .port = dp->index, - .fdb = fdb, + .addr = addr, + .vid = vid, }; return dsa_port_notify(dp, DSA_NOTIFIER_FDB_ADD, &info); } -int dsa_port_fdb_del(struct dsa_port *dp, - const struct switchdev_obj_port_fdb *fdb) +int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, + u16 vid) { struct dsa_notifier_fdb_info info = { .sw_index = dp->ds->index, .port = dp->index, - .fdb = fdb, + .addr = addr, + .vid = vid, + }; return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index bb7ab26ef768..6a1d4d6d212b 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -253,7 +253,8 @@ static int dsa_slave_port_obj_add(struct net_device *dev, case SWITCHDEV_OBJ_ID_PORT_FDB: if (switchdev_trans_ph_prepare(trans)) return 0; - err = dsa_port_fdb_add(dp, SWITCHDEV_OBJ_PORT_FDB(obj)); + err = dsa_port_fdb_add(dp, SWITCHDEV_OBJ_PORT_FDB(obj)->addr, + SWITCHDEV_OBJ_PORT_FDB(obj)->vid); break; case SWITCHDEV_OBJ_ID_PORT_MDB: err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans); @@ -279,7 +280,8 @@ static int dsa_slave_port_obj_del(struct net_device *dev, switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_FDB: - err = dsa_port_fdb_del(dp, SWITCHDEV_OBJ_PORT_FDB(obj)); + err = dsa_port_fdb_del(dp, SWITCHDEV_OBJ_PORT_FDB(obj)->addr, + SWITCHDEV_OBJ_PORT_FDB(obj)->vid); break; case SWITCHDEV_OBJ_ID_PORT_MDB: err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); diff --git a/net/dsa/switch.c b/net/dsa/switch.c index eb20e0fee0e1..e6c06aa349a6 100644 --- a/net/dsa/switch.c +++ b/net/dsa/switch.c @@ -83,8 +83,6 @@ static int dsa_switch_bridge_leave(struct dsa_switch *ds, static int dsa_switch_fdb_add(struct dsa_switch *ds, struct dsa_notifier_fdb_info *info) { - const struct switchdev_obj_port_fdb *fdb = info->fdb; - /* Do not care yet about other switch chips of the fabric */ if (ds->index != info->sw_index) return 0; @@ -92,14 +90,13 @@ static int dsa_switch_fdb_add(struct dsa_switch *ds, if (!ds->ops->port_fdb_add) return -EOPNOTSUPP; - return ds->ops->port_fdb_add(ds, info->port, fdb->addr, fdb->vid); + return ds->ops->port_fdb_add(ds, info->port, info->addr, + info->vid); } static int dsa_switch_fdb_del(struct dsa_switch *ds, struct dsa_notifier_fdb_info *info) { - const struct switchdev_obj_port_fdb *fdb = info->fdb; - /* Do not care yet about other switch chips of the fabric */ if (ds->index != info->sw_index) return 0; @@ -107,8 +104,8 @@ static int dsa_switch_fdb_del(struct dsa_switch *ds, if (!ds->ops->port_fdb_del) return -EOPNOTSUPP; - return ds->ops->port_fdb_del(ds, info->port, fdb->addr, - fdb->vid); + return ds->ops->port_fdb_del(ds, info->port, info->addr, + info->vid); } static int dsa_switch_mdb_add(struct dsa_switch *ds, -- cgit v1.2.3 From c9eb3e0f870105242a15a5e628ed202cf32afe0d Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:42 +0300 Subject: net: dsa: Add support for learning FDB through notification Add support for learning FDB through notification. The driver defers the hardware update via ordered work queue. In case of a successful FDB add a notification is sent back to bridge. In case of hw FDB del failure the static FDB will be deleted from the bridge, thus, the interface is moved to down state in order to indicate inconsistent situation. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa.c | 13 ++++++ net/dsa/dsa_priv.h | 1 + net/dsa/slave.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 139 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 4118848b8e58..99e38af85fc5 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -282,10 +282,22 @@ static struct packet_type dsa_pack_type __read_mostly = { .func = dsa_switch_rcv, }; +static struct workqueue_struct *dsa_owq; + +bool dsa_schedule_work(struct work_struct *work) +{ + return queue_work(dsa_owq, work); +} + static int __init dsa_init_module(void) { int rc; + dsa_owq = alloc_ordered_workqueue("dsa_ordered", + WQ_MEM_RECLAIM); + if (!dsa_owq) + return -ENOMEM; + rc = dsa_slave_register_notifier(); if (rc) return rc; @@ -305,6 +317,7 @@ static void __exit dsa_cleanup_module(void) dsa_slave_unregister_notifier(); dev_remove_pack(&dsa_pack_type); dsa_legacy_unregister(); + destroy_workqueue(dsa_owq); } module_exit(dsa_cleanup_module); diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index c0ee6a7694f8..fe90e6475297 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -106,6 +106,7 @@ void dsa_cpu_dsa_destroy(struct dsa_port *dport); const struct dsa_device_ops *dsa_resolve_tag_protocol(int tag_protocol); int dsa_cpu_port_ethtool_setup(struct dsa_port *cpu_dp); void dsa_cpu_port_ethtool_restore(struct dsa_port *cpu_dp); +bool dsa_schedule_work(struct work_struct *work); /* legacy.c */ int dsa_legacy_register(void); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 6a1d4d6d212b..064f833a0422 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1332,19 +1332,142 @@ static int dsa_slave_netdevice_event(struct notifier_block *nb, return NOTIFY_DONE; } +struct dsa_switchdev_event_work { + struct work_struct work; + struct switchdev_notifier_fdb_info fdb_info; + struct net_device *dev; + unsigned long event; +}; + +static void dsa_slave_switchdev_event_work(struct work_struct *work) +{ + struct dsa_switchdev_event_work *switchdev_work = + container_of(work, struct dsa_switchdev_event_work, work); + struct net_device *dev = switchdev_work->dev; + struct switchdev_notifier_fdb_info *fdb_info; + struct dsa_slave_priv *p = netdev_priv(dev); + int err; + + rtnl_lock(); + switch (switchdev_work->event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: + fdb_info = &switchdev_work->fdb_info; + err = dsa_port_fdb_add(p->dp, fdb_info->addr, fdb_info->vid); + if (err) { + netdev_dbg(dev, "fdb add failed err=%d\n", err); + break; + } + call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev, + &fdb_info->info); + break; + + case SWITCHDEV_FDB_DEL_TO_DEVICE: + fdb_info = &switchdev_work->fdb_info; + err = dsa_port_fdb_del(p->dp, fdb_info->addr, fdb_info->vid); + if (err) { + netdev_dbg(dev, "fdb del failed err=%d\n", err); + dev_close(dev); + } + break; + } + rtnl_unlock(); + + kfree(switchdev_work->fdb_info.addr); + kfree(switchdev_work); + dev_put(dev); +} + +static int +dsa_slave_switchdev_fdb_work_init(struct dsa_switchdev_event_work * + switchdev_work, + const struct switchdev_notifier_fdb_info * + fdb_info) +{ + memcpy(&switchdev_work->fdb_info, fdb_info, + sizeof(switchdev_work->fdb_info)); + switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC); + if (!switchdev_work->fdb_info.addr) + return -ENOMEM; + ether_addr_copy((u8 *)switchdev_work->fdb_info.addr, + fdb_info->addr); + return 0; +} + +/* Called under rcu_read_lock() */ +static int dsa_slave_switchdev_event(struct notifier_block *unused, + unsigned long event, void *ptr) +{ + struct net_device *dev = switchdev_notifier_info_to_dev(ptr); + struct dsa_switchdev_event_work *switchdev_work; + + if (!dsa_slave_dev_check(dev)) + return NOTIFY_DONE; + + switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC); + if (!switchdev_work) + return NOTIFY_BAD; + + INIT_WORK(&switchdev_work->work, + dsa_slave_switchdev_event_work); + switchdev_work->dev = dev; + switchdev_work->event = event; + + switch (event) { + case SWITCHDEV_FDB_ADD_TO_DEVICE: /* fall through */ + case SWITCHDEV_FDB_DEL_TO_DEVICE: + if (dsa_slave_switchdev_fdb_work_init(switchdev_work, + ptr)) + goto err_fdb_work_init; + dev_hold(dev); + break; + default: + kfree(switchdev_work); + return NOTIFY_DONE; + } + + dsa_schedule_work(&switchdev_work->work); + return NOTIFY_OK; + +err_fdb_work_init: + kfree(switchdev_work); + return NOTIFY_BAD; +} + static struct notifier_block dsa_slave_nb __read_mostly = { - .notifier_call = dsa_slave_netdevice_event, + .notifier_call = dsa_slave_netdevice_event, +}; + +static struct notifier_block dsa_slave_switchdev_notifier = { + .notifier_call = dsa_slave_switchdev_event, }; int dsa_slave_register_notifier(void) { - return register_netdevice_notifier(&dsa_slave_nb); + int err; + + err = register_netdevice_notifier(&dsa_slave_nb); + if (err) + return err; + + err = register_switchdev_notifier(&dsa_slave_switchdev_notifier); + if (err) + goto err_switchdev_nb; + + return 0; + +err_switchdev_nb: + unregister_netdevice_notifier(&dsa_slave_nb); + return err; } void dsa_slave_unregister_notifier(void) { int err; + err = unregister_switchdev_notifier(&dsa_slave_switchdev_notifier); + if (err) + pr_err("DSA: failed to unregister switchdev notifier (%d)\n", err); + err = unregister_netdevice_notifier(&dsa_slave_nb); if (err) pr_err("DSA: failed to unregister slave notifier (%d)\n", err); -- cgit v1.2.3 From 37b8da1a3c68501c2fba94951f8f59280c7a9752 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:43 +0300 Subject: net: dsa: Move FDB add/del implementation inside DSA Currently DSA uses switchdev's implementation of FDB add/del ndos. This patch moves the implementation inside DSA in order to support the legacy way for static FDB configuration. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/dsa_priv.h | 7 +++++++ net/dsa/legacy.c | 22 ++++++++++++++++++++++ net/dsa/slave.c | 14 ++------------ 3 files changed, 31 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index fe90e6475297..5d4e668b9471 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -111,6 +111,13 @@ bool dsa_schedule_work(struct work_struct *work); /* legacy.c */ int dsa_legacy_register(void); void dsa_legacy_unregister(void); +int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid, + u16 flags); +int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid); /* port.c */ int dsa_port_set_state(struct dsa_port *dp, u8 state, diff --git a/net/dsa/legacy.c b/net/dsa/legacy.c index 612acf16d573..91e6f7981d39 100644 --- a/net/dsa/legacy.c +++ b/net/dsa/legacy.c @@ -739,6 +739,28 @@ static int dsa_resume(struct device *d) } #endif +/* legacy way, bypassing the bridge *****************************************/ +int dsa_legacy_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid, + u16 flags) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_port *dp = p->dp; + + return dsa_port_fdb_add(dp, addr, vid); +} + +int dsa_legacy_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], + struct net_device *dev, + const unsigned char *addr, u16 vid) +{ + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_port *dp = p->dp; + + return dsa_port_fdb_del(dp, addr, vid); +} + static SIMPLE_DEV_PM_OPS(dsa_pm_ops, dsa_suspend, dsa_resume); static const struct of_device_id dsa_of_match_table[] = { diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 064f833a0422..84a8694c69d0 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -250,12 +250,6 @@ static int dsa_slave_port_obj_add(struct net_device *dev, */ switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_FDB: - if (switchdev_trans_ph_prepare(trans)) - return 0; - err = dsa_port_fdb_add(dp, SWITCHDEV_OBJ_PORT_FDB(obj)->addr, - SWITCHDEV_OBJ_PORT_FDB(obj)->vid); - break; case SWITCHDEV_OBJ_ID_PORT_MDB: err = dsa_port_mdb_add(dp, SWITCHDEV_OBJ_PORT_MDB(obj), trans); break; @@ -279,10 +273,6 @@ static int dsa_slave_port_obj_del(struct net_device *dev, int err; switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = dsa_port_fdb_del(dp, SWITCHDEV_OBJ_PORT_FDB(obj)->addr, - SWITCHDEV_OBJ_PORT_FDB(obj)->vid); - break; case SWITCHDEV_OBJ_ID_PORT_MDB: err = dsa_port_mdb_del(dp, SWITCHDEV_OBJ_PORT_MDB(obj)); break; @@ -985,8 +975,8 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_change_rx_flags = dsa_slave_change_rx_flags, .ndo_set_rx_mode = dsa_slave_set_rx_mode, .ndo_set_mac_address = dsa_slave_set_mac_address, - .ndo_fdb_add = switchdev_port_fdb_add, - .ndo_fdb_del = switchdev_port_fdb_del, + .ndo_fdb_add = dsa_legacy_fdb_add, + .ndo_fdb_del = dsa_legacy_fdb_del, .ndo_fdb_dump = switchdev_port_fdb_dump, .ndo_do_ioctl = dsa_slave_ioctl, .ndo_get_iflink = dsa_slave_get_iflink, -- cgit v1.2.3 From c9e2105e295ebd123f9cded311f3e5678218f3f4 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:44 +0300 Subject: net: dsa: Add support for querying supported bridge flags The DSA drivers do not support bridge flags offload. Yet, this attribute should be added in order for the bridge to fail when one tries set a flag on the port, as explained in commit dc0ecabd6231 ("net: switchdev: Add support for querying supported bridge flags by hardware"). Signed-off-by: Arkadi Sharshevsky Reviewed-by: Vivien Didelot Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/slave.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'net') diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 84a8694c69d0..e9c1d8c5de0f 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -324,6 +324,9 @@ static int dsa_slave_port_attr_get(struct net_device *dev, attr->u.ppid.id_len = sizeof(ds->index); memcpy(&attr->u.ppid.id, &ds->index, attr->u.ppid.id_len); break; + case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT: + attr->u.brport_flags_support = 0; + break; default: return -EOPNOTSUPP; } -- cgit v1.2.3 From c069fcd82c571953b8aaf68769afe9ccb1aa7a9f Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:46 +0300 Subject: net: dsa: Remove support for bypass bridge port attributes/vlan set The bridge port attributes/vlan for DSA devices should be set only from bridge code. Furthermore, The vlans are synced totally with the bridge so there is no need for special dump support. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 4 ---- net/dsa/dsa_priv.h | 4 ---- net/dsa/port.c | 12 ------------ net/dsa/slave.c | 6 ------ 4 files changed, 26 deletions(-) (limited to 'net') diff --git a/include/net/dsa.h b/include/net/dsa.h index 446fc438cb80..d7b9bdd0e768 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -378,10 +378,6 @@ struct dsa_switch_ops { struct switchdev_trans *trans); int (*port_vlan_del)(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan); - int (*port_vlan_dump)(struct dsa_switch *ds, int port, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb); - /* * Forwarding database */ diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 5d4e668b9471..7f297bcefa0c 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -147,10 +147,6 @@ int dsa_port_vlan_add(struct dsa_port *dp, struct switchdev_trans *trans); int dsa_port_vlan_del(struct dsa_port *dp, const struct switchdev_obj_port_vlan *vlan); -int dsa_port_vlan_dump(struct dsa_port *dp, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb); - /* slave.c */ extern const struct dsa_device_ops notag_netdev_ops; void dsa_slave_mii_bus_init(struct dsa_switch *ds); diff --git a/net/dsa/port.c b/net/dsa/port.c index 86e0585215bf..ce1921663cdd 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -246,15 +246,3 @@ int dsa_port_vlan_del(struct dsa_port *dp, return dsa_port_notify(dp, DSA_NOTIFIER_VLAN_DEL, &info); } - -int dsa_port_vlan_dump(struct dsa_port *dp, - struct switchdev_obj_port_vlan *vlan, - switchdev_obj_dump_cb_t *cb) -{ - struct dsa_switch *ds = dp->ds; - - if (ds->ops->port_vlan_dump) - return ds->ops->port_vlan_dump(ds, dp->index, vlan, cb); - - return -EOPNOTSUPP; -} diff --git a/net/dsa/slave.c b/net/dsa/slave.c index e9c1d8c5de0f..ccf670679343 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -302,9 +302,6 @@ static int dsa_slave_port_obj_dump(struct net_device *dev, case SWITCHDEV_OBJ_ID_PORT_MDB: err = dsa_port_mdb_dump(dp, SWITCHDEV_OBJ_PORT_MDB(obj), cb); break; - case SWITCHDEV_OBJ_ID_PORT_VLAN: - err = dsa_port_vlan_dump(dp, SWITCHDEV_OBJ_PORT_VLAN(obj), cb); - break; default: err = -EOPNOTSUPP; break; @@ -988,9 +985,6 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_netpoll_cleanup = dsa_slave_netpoll_cleanup, .ndo_poll_controller = dsa_slave_poll_controller, #endif - .ndo_bridge_getlink = switchdev_port_bridge_getlink, - .ndo_bridge_setlink = switchdev_port_bridge_setlink, - .ndo_bridge_dellink = switchdev_port_bridge_dellink, .ndo_get_phys_port_name = dsa_slave_get_phys_port_name, .ndo_setup_tc = dsa_slave_setup_tc, .ndo_get_stats64 = dsa_slave_get_stats64, -- cgit v1.2.3 From dc0cbff3ff9fe331160c2be2b3f47564e247137d Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:48 +0300 Subject: net: dsa: Remove redundant MDB dump support Currently the MDB HW database is synced with the bridge's one, thus, There is no need to support special dump functionality. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 4 ---- net/dsa/dsa_priv.h | 2 -- net/dsa/port.c | 11 ----------- net/dsa/slave.c | 3 --- 4 files changed, 20 deletions(-) (limited to 'net') diff --git a/include/net/dsa.h b/include/net/dsa.h index d7b9bdd0e768..4ef185997b6f 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -400,10 +400,6 @@ struct dsa_switch_ops { struct switchdev_trans *trans); int (*port_mdb_del)(struct dsa_switch *ds, int port, const struct switchdev_obj_port_mdb *mdb); - int (*port_mdb_dump)(struct dsa_switch *ds, int port, - struct switchdev_obj_port_mdb *mdb, - switchdev_obj_dump_cb_t *cb); - /* * RXNFC */ diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 7f297bcefa0c..9c890de6e4dd 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -140,8 +140,6 @@ int dsa_port_mdb_add(struct dsa_port *dp, struct switchdev_trans *trans); int dsa_port_mdb_del(struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb); -int dsa_port_mdb_dump(struct dsa_port *dp, struct switchdev_obj_port_mdb *mdb, - switchdev_obj_dump_cb_t *cb); int dsa_port_vlan_add(struct dsa_port *dp, const struct switchdev_obj_port_vlan *vlan, struct switchdev_trans *trans); diff --git a/net/dsa/port.c b/net/dsa/port.c index ce1921663cdd..73787828953a 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -210,17 +210,6 @@ int dsa_port_mdb_del(struct dsa_port *dp, return dsa_port_notify(dp, DSA_NOTIFIER_MDB_DEL, &info); } -int dsa_port_mdb_dump(struct dsa_port *dp, struct switchdev_obj_port_mdb *mdb, - switchdev_obj_dump_cb_t *cb) -{ - struct dsa_switch *ds = dp->ds; - - if (ds->ops->port_mdb_dump) - return ds->ops->port_mdb_dump(ds, dp->index, mdb, cb); - - return -EOPNOTSUPP; -} - int dsa_port_vlan_add(struct dsa_port *dp, const struct switchdev_obj_port_vlan *vlan, struct switchdev_trans *trans) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index ccf670679343..5807c905fd1d 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -299,9 +299,6 @@ static int dsa_slave_port_obj_dump(struct net_device *dev, case SWITCHDEV_OBJ_ID_PORT_FDB: err = dsa_port_fdb_dump(dp, SWITCHDEV_OBJ_PORT_FDB(obj), cb); break; - case SWITCHDEV_OBJ_ID_PORT_MDB: - err = dsa_port_mdb_dump(dp, SWITCHDEV_OBJ_PORT_MDB(obj), cb); - break; default: err = -EOPNOTSUPP; break; -- cgit v1.2.3 From 2bedde1abbef5eec211308f0293dd7681b0513ec Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:49 +0300 Subject: net: dsa: Move FDB dump implementation inside DSA >From all switchdev devices only DSA requires special FDB dump. This is due to lack of ability for syncing the hardware learned FDBs with the bridge. Due to this it is removed from switchdev and moved inside DSA. Signed-off-by: Arkadi Sharshevsky Signed-off-by: David S. Miller --- drivers/net/dsa/b53/b53_common.c | 16 ++---- drivers/net/dsa/b53/b53_priv.h | 3 +- drivers/net/dsa/microchip/ksz_common.c | 20 ++----- drivers/net/dsa/mt7530.c | 10 +--- drivers/net/dsa/mv88e6xxx/chip.c | 38 ++++--------- drivers/net/dsa/qca8k.c | 15 ++--- include/net/dsa.h | 5 +- include/net/switchdev.h | 12 ---- net/dsa/dsa_priv.h | 2 - net/dsa/port.c | 11 ---- net/dsa/slave.c | 100 +++++++++++++++++++++++++-------- net/switchdev/switchdev.c | 84 --------------------------- 12 files changed, 112 insertions(+), 204 deletions(-) (limited to 'net') diff --git a/drivers/net/dsa/b53/b53_common.c b/drivers/net/dsa/b53/b53_common.c index 0176d8087344..274f3679f33d 100644 --- a/drivers/net/dsa/b53/b53_common.c +++ b/drivers/net/dsa/b53/b53_common.c @@ -1227,8 +1227,7 @@ static void b53_arl_search_rd(struct b53_device *dev, u8 idx, } static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { if (!ent->is_valid) return 0; @@ -1236,16 +1235,11 @@ static int b53_fdb_copy(int port, const struct b53_arl_entry *ent, if (port != ent->port) return 0; - ether_addr_copy(fdb->addr, ent->mac); - fdb->vid = ent->vid; - fdb->ndm_state = ent->is_static ? NUD_NOARP : NUD_REACHABLE; - - return cb(&fdb->obj); + return cb(ent->mac, ent->vid, ent->is_static, data); } int b53_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct b53_device *priv = ds->priv; struct b53_arl_entry results[2]; @@ -1263,13 +1257,13 @@ int b53_fdb_dump(struct dsa_switch *ds, int port, return ret; b53_arl_search_rd(priv, 0, &results[0]); - ret = b53_fdb_copy(port, &results[0], fdb, cb); + ret = b53_fdb_copy(port, &results[0], cb, data); if (ret) return ret; if (priv->num_arl_entries > 2) { b53_arl_search_rd(priv, 1, &results[1]); - ret = b53_fdb_copy(port, &results[1], fdb, cb); + ret = b53_fdb_copy(port, &results[1], cb, data); if (ret) return ret; diff --git a/drivers/net/dsa/b53/b53_priv.h b/drivers/net/dsa/b53/b53_priv.h index af5d6c166bff..01bd8cbe9a3f 100644 --- a/drivers/net/dsa/b53/b53_priv.h +++ b/drivers/net/dsa/b53/b53_priv.h @@ -398,8 +398,7 @@ int b53_fdb_add(struct dsa_switch *ds, int port, int b53_fdb_del(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int b53_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb); + dsa_fdb_dump_cb_t *cb, void *data); int b53_mirror_add(struct dsa_switch *ds, int port, struct dsa_mall_mirror_tc_entry *mirror, bool ingress); void b53_mirror_del(struct dsa_switch *ds, int port, diff --git a/drivers/net/dsa/microchip/ksz_common.c b/drivers/net/dsa/microchip/ksz_common.c index 4de9d90a4bb3..56cd6d365352 100644 --- a/drivers/net/dsa/microchip/ksz_common.c +++ b/drivers/net/dsa/microchip/ksz_common.c @@ -805,12 +805,11 @@ static void convert_alu(struct alu_struct *alu, u32 *alu_table) } static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct ksz_device *dev = ds->priv; int ret = 0; - u32 data; + u32 ksz_data; u32 alu_table[4]; struct alu_struct alu; int timeout; @@ -823,8 +822,8 @@ static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, do { timeout = 1000; do { - ksz_read32(dev, REG_SW_ALU_CTRL__4, &data); - if ((data & ALU_VALID) || !(data & ALU_START)) + ksz_read32(dev, REG_SW_ALU_CTRL__4, &ksz_data); + if ((ksz_data & ALU_VALID) || !(ksz_data & ALU_START)) break; usleep_range(1, 10); } while (timeout-- > 0); @@ -841,18 +840,11 @@ static int ksz_port_fdb_dump(struct dsa_switch *ds, int port, convert_alu(&alu, alu_table); if (alu.port_forward & BIT(port)) { - fdb->vid = alu.fid; - if (alu.is_static) - fdb->ndm_state = NUD_NOARP; - else - fdb->ndm_state = NUD_REACHABLE; - ether_addr_copy(fdb->addr, alu.mac); - - ret = cb(&fdb->obj); + ret = cb(alu.mac, alu.fid, alu.is_static, data); if (ret) goto exit; } - } while (data & ALU_START); + } while (ksz_data & ALU_START); exit: diff --git a/drivers/net/dsa/mt7530.c b/drivers/net/dsa/mt7530.c index f92aae8947e6..12700710f26d 100644 --- a/drivers/net/dsa/mt7530.c +++ b/drivers/net/dsa/mt7530.c @@ -834,8 +834,7 @@ mt7530_port_fdb_del(struct dsa_switch *ds, int port, static int mt7530_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct mt7530_priv *priv = ds->priv; struct mt7530_fdb _fdb = { 0 }; @@ -853,11 +852,8 @@ mt7530_port_fdb_dump(struct dsa_switch *ds, int port, if (rsp & ATC_SRCH_HIT) { mt7530_fdb_read(priv, &_fdb); if (_fdb.port_mask & BIT(port)) { - ether_addr_copy(fdb->addr, _fdb.mac); - fdb->vid = _fdb.vid; - fdb->ndm_state = _fdb.noarp ? - NUD_NOARP : NUD_REACHABLE; - ret = cb(&fdb->obj); + ret = cb(_fdb.mac, _fdb.vid, _fdb.noarp, + data); if (ret < 0) break; } diff --git a/drivers/net/dsa/mv88e6xxx/chip.c b/drivers/net/dsa/mv88e6xxx/chip.c index 1f5c202b974d..918d8f0fe091 100644 --- a/drivers/net/dsa/mv88e6xxx/chip.c +++ b/drivers/net/dsa/mv88e6xxx/chip.c @@ -1381,10 +1381,10 @@ static int mv88e6xxx_port_fdb_del(struct dsa_switch *ds, int port, static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, u16 fid, u16 vid, int port, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct mv88e6xxx_atu_entry addr; + bool is_static; int err; addr.state = MV88E6XXX_G1_ATU_DATA_STATE_UNUSED; @@ -1401,24 +1401,12 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, if (addr.trunk || (addr.portvec & BIT(port)) == 0) continue; - if (obj->id == SWITCHDEV_OBJ_ID_PORT_FDB) { - struct switchdev_obj_port_fdb *fdb; - - if (!is_unicast_ether_addr(addr.mac)) - continue; - - fdb = SWITCHDEV_OBJ_PORT_FDB(obj); - fdb->vid = vid; - ether_addr_copy(fdb->addr, addr.mac); - if (addr.state == MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC) - fdb->ndm_state = NUD_NOARP; - else - fdb->ndm_state = NUD_REACHABLE; - } else { - return -EOPNOTSUPP; - } + if (!is_unicast_ether_addr(addr.mac)) + continue; - err = cb(obj); + is_static = (addr.state == + MV88E6XXX_G1_ATU_DATA_STATE_UC_STATIC); + err = cb(addr.mac, vid, is_static, data); if (err) return err; } while (!is_broadcast_ether_addr(addr.mac)); @@ -1427,8 +1415,7 @@ static int mv88e6xxx_port_db_dump_fid(struct mv88e6xxx_chip *chip, } static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct mv88e6xxx_vtu_entry vlan = { .vid = chip->info->max_vid, @@ -1441,7 +1428,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, if (err) return err; - err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, obj, cb); + err = mv88e6xxx_port_db_dump_fid(chip, fid, 0, port, cb, data); if (err) return err; @@ -1455,7 +1442,7 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, break; err = mv88e6xxx_port_db_dump_fid(chip, vlan.fid, vlan.vid, port, - obj, cb); + cb, data); if (err) return err; } while (vlan.vid < chip->info->max_vid); @@ -1464,14 +1451,13 @@ static int mv88e6xxx_port_db_dump(struct mv88e6xxx_chip *chip, int port, } static int mv88e6xxx_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct mv88e6xxx_chip *chip = ds->priv; int err; mutex_lock(&chip->reg_lock); - err = mv88e6xxx_port_db_dump(chip, port, &fdb->obj, cb); + err = mv88e6xxx_port_db_dump(chip, port, cb, data); mutex_unlock(&chip->reg_lock); return err; diff --git a/drivers/net/dsa/qca8k.c b/drivers/net/dsa/qca8k.c index f8ef823349bc..17977f06cb98 100644 --- a/drivers/net/dsa/qca8k.c +++ b/drivers/net/dsa/qca8k.c @@ -801,27 +801,20 @@ qca8k_port_fdb_del(struct dsa_switch *ds, int port, static int qca8k_port_fdb_dump(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) + dsa_fdb_dump_cb_t *cb, void *data) { struct qca8k_priv *priv = (struct qca8k_priv *)ds->priv; struct qca8k_fdb _fdb = { 0 }; int cnt = QCA8K_NUM_FDB_RECORDS; + bool is_static; int ret = 0; mutex_lock(&priv->reg_mutex); while (cnt-- && !qca8k_fdb_next(priv, &_fdb, port)) { if (!_fdb.aging) break; - - ether_addr_copy(fdb->addr, _fdb.mac); - fdb->vid = _fdb.vid; - if (_fdb.aging == QCA8K_ATU_STATUS_STATIC) - fdb->ndm_state = NUD_NOARP; - else - fdb->ndm_state = NUD_REACHABLE; - - ret = cb(&fdb->obj); + is_static = (_fdb.aging == QCA8K_ATU_STATUS_STATIC); + ret = cb(_fdb.mac, _fdb.vid, is_static, data); if (ret) break; } diff --git a/include/net/dsa.h b/include/net/dsa.h index 4ef185997b6f..a4f66dbb4b7c 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -272,6 +272,8 @@ static inline u8 dsa_upstream_port(struct dsa_switch *ds) return ds->rtable[dst->cpu_dp->ds->index]; } +typedef int dsa_fdb_dump_cb_t(const unsigned char *addr, u16 vid, + bool is_static, void *data); struct dsa_switch_ops { /* * Legacy probing. @@ -386,8 +388,7 @@ struct dsa_switch_ops { int (*port_fdb_del)(struct dsa_switch *ds, int port, const unsigned char *addr, u16 vid); int (*port_fdb_dump)(struct dsa_switch *ds, int port, - struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb); + dsa_fdb_dump_cb_t *cb, void *data); /* * Multicast database diff --git a/include/net/switchdev.h b/include/net/switchdev.h index 8ae9e3b6392e..d2637a685ca6 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -208,9 +208,6 @@ int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], struct net_device *dev, const unsigned char *addr, u16 vid); -int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct net_device *dev, - struct net_device *filter_dev, int *idx); void switchdev_port_fwd_mark_set(struct net_device *dev, struct net_device *group_dev, bool joining); @@ -309,15 +306,6 @@ static inline int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], return -EOPNOTSUPP; } -static inline int switchdev_port_fdb_dump(struct sk_buff *skb, - struct netlink_callback *cb, - struct net_device *dev, - struct net_device *filter_dev, - int *idx) -{ - return *idx; -} - static inline bool switchdev_port_same_parent_id(struct net_device *a, struct net_device *b) { diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 9c890de6e4dd..1debf9c42fc4 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -133,8 +133,6 @@ int dsa_port_fdb_add(struct dsa_port *dp, const unsigned char *addr, u16 vid); int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, u16 vid); -int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb); int dsa_port_mdb_add(struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb, struct switchdev_trans *trans); diff --git a/net/dsa/port.c b/net/dsa/port.c index 73787828953a..659676ba3f8b 100644 --- a/net/dsa/port.c +++ b/net/dsa/port.c @@ -173,17 +173,6 @@ int dsa_port_fdb_del(struct dsa_port *dp, const unsigned char *addr, return dsa_port_notify(dp, DSA_NOTIFIER_FDB_DEL, &info); } -int dsa_port_fdb_dump(struct dsa_port *dp, struct switchdev_obj_port_fdb *fdb, - switchdev_obj_dump_cb_t *cb) -{ - struct dsa_switch *ds = dp->ds; - - if (ds->ops->port_fdb_dump) - return ds->ops->port_fdb_dump(ds, dp->index, fdb, cb); - - return -EOPNOTSUPP; -} - int dsa_port_mdb_add(struct dsa_port *dp, const struct switchdev_obj_port_mdb *mdb, struct switchdev_trans *trans) diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 5807c905fd1d..8c79011c5a83 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -199,6 +199,83 @@ out: return 0; } +struct dsa_slave_dump_ctx { + struct net_device *dev; + struct sk_buff *skb; + struct netlink_callback *cb; + int idx; +}; + +static int +dsa_slave_port_fdb_do_dump(const unsigned char *addr, u16 vid, + bool is_static, void *data) +{ + struct dsa_slave_dump_ctx *dump = data; + u32 portid = NETLINK_CB(dump->cb->skb).portid; + u32 seq = dump->cb->nlh->nlmsg_seq; + struct nlmsghdr *nlh; + struct ndmsg *ndm; + + if (dump->idx < dump->cb->args[2]) + goto skip; + + nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, + sizeof(*ndm), NLM_F_MULTI); + if (!nlh) + return -EMSGSIZE; + + ndm = nlmsg_data(nlh); + ndm->ndm_family = AF_BRIDGE; + ndm->ndm_pad1 = 0; + ndm->ndm_pad2 = 0; + ndm->ndm_flags = NTF_SELF; + ndm->ndm_type = 0; + ndm->ndm_ifindex = dump->dev->ifindex; + ndm->ndm_state = is_static ? NUD_NOARP : NUD_REACHABLE; + + if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, addr)) + goto nla_put_failure; + + if (vid && nla_put_u16(dump->skb, NDA_VLAN, vid)) + goto nla_put_failure; + + nlmsg_end(dump->skb, nlh); + +skip: + dump->idx++; + return 0; + +nla_put_failure: + nlmsg_cancel(dump->skb, nlh); + return -EMSGSIZE; +} + +static int +dsa_slave_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, + struct net_device *dev, struct net_device *filter_dev, + int *idx) +{ + struct dsa_slave_dump_ctx dump = { + .dev = dev, + .skb = skb, + .cb = cb, + .idx = *idx, + }; + struct dsa_slave_priv *p = netdev_priv(dev); + struct dsa_port *dp = p->dp; + struct dsa_switch *ds = dp->ds; + int err; + + if (!ds->ops->port_fdb_dump) + return -EOPNOTSUPP; + + err = ds->ops->port_fdb_dump(ds, dp->index, + dsa_slave_port_fdb_do_dump, + &dump); + *idx = dump.idx; + return err; +} + static int dsa_slave_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd) { struct dsa_slave_priv *p = netdev_priv(dev); @@ -287,26 +364,6 @@ static int dsa_slave_port_obj_del(struct net_device *dev, return err; } -static int dsa_slave_port_obj_dump(struct net_device *dev, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) -{ - struct dsa_slave_priv *p = netdev_priv(dev); - struct dsa_port *dp = p->dp; - int err; - - switch (obj->id) { - case SWITCHDEV_OBJ_ID_PORT_FDB: - err = dsa_port_fdb_dump(dp, SWITCHDEV_OBJ_PORT_FDB(obj), cb); - break; - default: - err = -EOPNOTSUPP; - break; - } - - return err; -} - static int dsa_slave_port_attr_get(struct net_device *dev, struct switchdev_attr *attr) { @@ -974,7 +1031,7 @@ static const struct net_device_ops dsa_slave_netdev_ops = { .ndo_set_mac_address = dsa_slave_set_mac_address, .ndo_fdb_add = dsa_legacy_fdb_add, .ndo_fdb_del = dsa_legacy_fdb_del, - .ndo_fdb_dump = switchdev_port_fdb_dump, + .ndo_fdb_dump = dsa_slave_fdb_dump, .ndo_do_ioctl = dsa_slave_ioctl, .ndo_get_iflink = dsa_slave_get_iflink, #ifdef CONFIG_NET_POLL_CONTROLLER @@ -992,7 +1049,6 @@ static const struct switchdev_ops dsa_slave_switchdev_ops = { .switchdev_port_attr_set = dsa_slave_port_attr_set, .switchdev_port_obj_add = dsa_slave_port_obj_add, .switchdev_port_obj_del = dsa_slave_port_obj_del, - .switchdev_port_obj_dump = dsa_slave_port_obj_dump, }; static struct device_type dsa_type = { diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 25dc67ef9d37..3d32981b9aa1 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -1009,90 +1009,6 @@ int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], } EXPORT_SYMBOL_GPL(switchdev_port_fdb_del); -struct switchdev_fdb_dump { - struct switchdev_obj_port_fdb fdb; - struct net_device *dev; - struct sk_buff *skb; - struct netlink_callback *cb; - int idx; -}; - -static int switchdev_port_fdb_dump_cb(struct switchdev_obj *obj) -{ - struct switchdev_obj_port_fdb *fdb = SWITCHDEV_OBJ_PORT_FDB(obj); - struct switchdev_fdb_dump *dump = - container_of(fdb, struct switchdev_fdb_dump, fdb); - u32 portid = NETLINK_CB(dump->cb->skb).portid; - u32 seq = dump->cb->nlh->nlmsg_seq; - struct nlmsghdr *nlh; - struct ndmsg *ndm; - - if (dump->idx < dump->cb->args[2]) - goto skip; - - nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH, - sizeof(*ndm), NLM_F_MULTI); - if (!nlh) - return -EMSGSIZE; - - ndm = nlmsg_data(nlh); - ndm->ndm_family = AF_BRIDGE; - ndm->ndm_pad1 = 0; - ndm->ndm_pad2 = 0; - ndm->ndm_flags = NTF_SELF; - ndm->ndm_type = 0; - ndm->ndm_ifindex = dump->dev->ifindex; - ndm->ndm_state = fdb->ndm_state; - - if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, fdb->addr)) - goto nla_put_failure; - - if (fdb->vid && nla_put_u16(dump->skb, NDA_VLAN, fdb->vid)) - goto nla_put_failure; - - nlmsg_end(dump->skb, nlh); - -skip: - dump->idx++; - return 0; - -nla_put_failure: - nlmsg_cancel(dump->skb, nlh); - return -EMSGSIZE; -} - -/** - * switchdev_port_fdb_dump - Dump port FDB (MAC/VLAN) entries - * - * @skb: netlink skb - * @cb: netlink callback - * @dev: port device - * @filter_dev: filter device - * @idx: - * - * Dump FDB entries from switch device. - */ -int switchdev_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb, - struct net_device *dev, - struct net_device *filter_dev, int *idx) -{ - struct switchdev_fdb_dump dump = { - .fdb.obj.orig_dev = dev, - .fdb.obj.id = SWITCHDEV_OBJ_ID_PORT_FDB, - .dev = dev, - .skb = skb, - .cb = cb, - .idx = *idx, - }; - int err; - - err = switchdev_port_obj_dump(dev, &dump.fdb.obj, - switchdev_port_fdb_dump_cb); - *idx = dump.idx; - return err; -} -EXPORT_SYMBOL_GPL(switchdev_port_fdb_dump); - bool switchdev_port_same_parent_id(struct net_device *a, struct net_device *b) { -- cgit v1.2.3 From 3a83c2a7a518d4044f7bb73b5b05b59be618e547 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:50 +0300 Subject: net: bridge: Remove FDB deletion through switchdev object At this point no driver supports FDB add/del through switchdev object but rather via notification chain, thus, it is removed. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Vivien Didelot Reviewed-by: Ivan Vecera Acked-by: Jiri Pirko Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- net/bridge/br_fdb.c | 18 ------------------ 1 file changed, 18 deletions(-) (limited to 'net') diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index a5e4a736a984..a79b648aac88 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -169,29 +169,11 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr) } } -static void fdb_del_external_learn(struct net_bridge_fdb_entry *f) -{ - struct switchdev_obj_port_fdb fdb = { - .obj = { - .orig_dev = f->dst->dev, - .id = SWITCHDEV_OBJ_ID_PORT_FDB, - .flags = SWITCHDEV_F_DEFER, - }, - .vid = f->vlan_id, - }; - - ether_addr_copy(fdb.addr, f->addr.addr); - switchdev_port_obj_del(f->dst->dev, &fdb.obj); -} - static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) { if (f->is_static) fdb_del_hw_addr(br, f->addr.addr); - if (f->added_by_external_learn) - fdb_del_external_learn(f); - hlist_del_init_rcu(&f->hlist); fdb_notify(br, f, RTM_DELNEIGH); call_rcu(&f->rcu, fdb_rcu_free); -- cgit v1.2.3 From 29ab586c3d83f81c435e269cace9a1619afb5bbd Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Sun, 6 Aug 2017 16:15:51 +0300 Subject: net: switchdev: Remove bridge bypass support from switchdev Currently the bridge port flags, vlans, FDBs and MDBs can be offloaded through the bridge code, making the switchdev's SELF bridge bypass implementation to be redundant. This implies several changes: - No need for dump infra in switchdev, DSA's special case is handled privately. - Remove obj_dump from switchdev_ops. - FDBs are removed from obj_add/del routines, due to the fact that they are offloaded through the bridge notification chain. - The switchdev_port_bridge_xx() and switchdev_port_fdb_xx() functions can be removed. Signed-off-by: Arkadi Sharshevsky Reviewed-by: Vivien Didelot Acked-by: Jiri Pirko Reviewed-by: Ivan Vecera Reviewed-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/switchdev.h | 75 -------- net/switchdev/switchdev.c | 435 ---------------------------------------------- 2 files changed, 510 deletions(-) (limited to 'net') diff --git a/include/net/switchdev.h b/include/net/switchdev.h index d2637a685ca6..d767b7991887 100644 --- a/include/net/switchdev.h +++ b/include/net/switchdev.h @@ -74,7 +74,6 @@ struct switchdev_attr { enum switchdev_obj_id { SWITCHDEV_OBJ_ID_UNDEFINED, SWITCHDEV_OBJ_ID_PORT_VLAN, - SWITCHDEV_OBJ_ID_PORT_FDB, SWITCHDEV_OBJ_ID_PORT_MDB, }; @@ -97,17 +96,6 @@ struct switchdev_obj_port_vlan { #define SWITCHDEV_OBJ_PORT_VLAN(obj) \ container_of(obj, struct switchdev_obj_port_vlan, obj) -/* SWITCHDEV_OBJ_ID_PORT_FDB */ -struct switchdev_obj_port_fdb { - struct switchdev_obj obj; - unsigned char addr[ETH_ALEN]; - u16 vid; - u16 ndm_state; -}; - -#define SWITCHDEV_OBJ_PORT_FDB(obj) \ - container_of(obj, struct switchdev_obj_port_fdb, obj) - /* SWITCHDEV_OBJ_ID_PORT_MDB */ struct switchdev_obj_port_mdb { struct switchdev_obj obj; @@ -135,8 +123,6 @@ typedef int switchdev_obj_dump_cb_t(struct switchdev_obj *obj); * @switchdev_port_obj_add: Add an object to port (see switchdev_obj_*). * * @switchdev_port_obj_del: Delete an object from port (see switchdev_obj_*). - * - * @switchdev_port_obj_dump: Dump port objects (see switchdev_obj_*). */ struct switchdev_ops { int (*switchdev_port_attr_get)(struct net_device *dev, @@ -149,9 +135,6 @@ struct switchdev_ops { struct switchdev_trans *trans); int (*switchdev_port_obj_del)(struct net_device *dev, const struct switchdev_obj *obj); - int (*switchdev_port_obj_dump)(struct net_device *dev, - struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb); }; enum switchdev_notifier_type { @@ -189,25 +172,10 @@ int switchdev_port_obj_add(struct net_device *dev, const struct switchdev_obj *obj); int switchdev_port_obj_del(struct net_device *dev, const struct switchdev_obj *obj); -int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb); int register_switchdev_notifier(struct notifier_block *nb); int unregister_switchdev_notifier(struct notifier_block *nb); int call_switchdev_notifiers(unsigned long val, struct net_device *dev, struct switchdev_notifier_info *info); -int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, - struct net_device *dev, u32 filter_mask, - int nlflags); -int switchdev_port_bridge_setlink(struct net_device *dev, - struct nlmsghdr *nlh, u16 flags); -int switchdev_port_bridge_dellink(struct net_device *dev, - struct nlmsghdr *nlh, u16 flags); -int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, const unsigned char *addr, - u16 vid, u16 nlm_flags); -int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, const unsigned char *addr, - u16 vid); void switchdev_port_fwd_mark_set(struct net_device *dev, struct net_device *group_dev, bool joining); @@ -246,13 +214,6 @@ static inline int switchdev_port_obj_del(struct net_device *dev, return -EOPNOTSUPP; } -static inline int switchdev_port_obj_dump(struct net_device *dev, - const struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) -{ - return -EOPNOTSUPP; -} - static inline int register_switchdev_notifier(struct notifier_block *nb) { return 0; @@ -270,42 +231,6 @@ static inline int call_switchdev_notifiers(unsigned long val, return NOTIFY_DONE; } -static inline int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, - u32 seq, struct net_device *dev, - u32 filter_mask, int nlflags) -{ - return -EOPNOTSUPP; -} - -static inline int switchdev_port_bridge_setlink(struct net_device *dev, - struct nlmsghdr *nlh, - u16 flags) -{ - return -EOPNOTSUPP; -} - -static inline int switchdev_port_bridge_dellink(struct net_device *dev, - struct nlmsghdr *nlh, - u16 flags) -{ - return -EOPNOTSUPP; -} - -static inline int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, - const unsigned char *addr, - u16 vid, u16 nlm_flags) -{ - return -EOPNOTSUPP; -} - -static inline int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, - const unsigned char *addr, u16 vid) -{ - return -EOPNOTSUPP; -} - static inline bool switchdev_port_same_parent_id(struct net_device *a, struct net_device *b) { diff --git a/net/switchdev/switchdev.c b/net/switchdev/switchdev.c index 3d32981b9aa1..0531b41d1f2d 100644 --- a/net/switchdev/switchdev.c +++ b/net/switchdev/switchdev.c @@ -343,8 +343,6 @@ static size_t switchdev_obj_size(const struct switchdev_obj *obj) switch (obj->id) { case SWITCHDEV_OBJ_ID_PORT_VLAN: return sizeof(struct switchdev_obj_port_vlan); - case SWITCHDEV_OBJ_ID_PORT_FDB: - return sizeof(struct switchdev_obj_port_fdb); case SWITCHDEV_OBJ_ID_PORT_MDB: return sizeof(struct switchdev_obj_port_mdb); default: @@ -534,43 +532,6 @@ int switchdev_port_obj_del(struct net_device *dev, } EXPORT_SYMBOL_GPL(switchdev_port_obj_del); -/** - * switchdev_port_obj_dump - Dump port objects - * - * @dev: port device - * @id: object ID - * @obj: object to dump - * @cb: function to call with a filled object - * - * rtnl_lock must be held. - */ -int switchdev_port_obj_dump(struct net_device *dev, struct switchdev_obj *obj, - switchdev_obj_dump_cb_t *cb) -{ - const struct switchdev_ops *ops = dev->switchdev_ops; - struct net_device *lower_dev; - struct list_head *iter; - int err = -EOPNOTSUPP; - - ASSERT_RTNL(); - - if (ops && ops->switchdev_port_obj_dump) - return ops->switchdev_port_obj_dump(dev, obj, cb); - - /* Switch device port(s) may be stacked under - * bond/team/vlan dev, so recurse down to dump objects on - * first port at bottom of stack. - */ - - netdev_for_each_lower_dev(dev, lower_dev, iter) { - err = switchdev_port_obj_dump(lower_dev, obj, cb); - break; - } - - return err; -} -EXPORT_SYMBOL_GPL(switchdev_port_obj_dump); - static ATOMIC_NOTIFIER_HEAD(switchdev_notif_chain); /** @@ -613,402 +574,6 @@ int call_switchdev_notifiers(unsigned long val, struct net_device *dev, } EXPORT_SYMBOL_GPL(call_switchdev_notifiers); -struct switchdev_vlan_dump { - struct switchdev_obj_port_vlan vlan; - struct sk_buff *skb; - u32 filter_mask; - u16 flags; - u16 begin; - u16 end; -}; - -static int switchdev_port_vlan_dump_put(struct switchdev_vlan_dump *dump) -{ - struct bridge_vlan_info vinfo; - - vinfo.flags = dump->flags; - - if (dump->begin == 0 && dump->end == 0) { - return 0; - } else if (dump->begin == dump->end) { - vinfo.vid = dump->begin; - if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO, - sizeof(vinfo), &vinfo)) - return -EMSGSIZE; - } else { - vinfo.vid = dump->begin; - vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_BEGIN; - if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO, - sizeof(vinfo), &vinfo)) - return -EMSGSIZE; - vinfo.vid = dump->end; - vinfo.flags &= ~BRIDGE_VLAN_INFO_RANGE_BEGIN; - vinfo.flags |= BRIDGE_VLAN_INFO_RANGE_END; - if (nla_put(dump->skb, IFLA_BRIDGE_VLAN_INFO, - sizeof(vinfo), &vinfo)) - return -EMSGSIZE; - } - - return 0; -} - -static int switchdev_port_vlan_dump_cb(struct switchdev_obj *obj) -{ - struct switchdev_obj_port_vlan *vlan = SWITCHDEV_OBJ_PORT_VLAN(obj); - struct switchdev_vlan_dump *dump = - container_of(vlan, struct switchdev_vlan_dump, vlan); - int err = 0; - - if (vlan->vid_begin > vlan->vid_end) - return -EINVAL; - - if (dump->filter_mask & RTEXT_FILTER_BRVLAN) { - dump->flags = vlan->flags; - for (dump->begin = dump->end = vlan->vid_begin; - dump->begin <= vlan->vid_end; - dump->begin++, dump->end++) { - err = switchdev_port_vlan_dump_put(dump); - if (err) - return err; - } - } else if (dump->filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) { - if (dump->begin > vlan->vid_begin && - dump->begin >= vlan->vid_end) { - if ((dump->begin - 1) == vlan->vid_end && - dump->flags == vlan->flags) { - /* prepend */ - dump->begin = vlan->vid_begin; - } else { - err = switchdev_port_vlan_dump_put(dump); - dump->flags = vlan->flags; - dump->begin = vlan->vid_begin; - dump->end = vlan->vid_end; - } - } else if (dump->end <= vlan->vid_begin && - dump->end < vlan->vid_end) { - if ((dump->end + 1) == vlan->vid_begin && - dump->flags == vlan->flags) { - /* append */ - dump->end = vlan->vid_end; - } else { - err = switchdev_port_vlan_dump_put(dump); - dump->flags = vlan->flags; - dump->begin = vlan->vid_begin; - dump->end = vlan->vid_end; - } - } else { - err = -EINVAL; - } - } - - return err; -} - -static int switchdev_port_vlan_fill(struct sk_buff *skb, struct net_device *dev, - u32 filter_mask) -{ - struct switchdev_vlan_dump dump = { - .vlan.obj.orig_dev = dev, - .vlan.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, - .skb = skb, - .filter_mask = filter_mask, - }; - int err = 0; - - if ((filter_mask & RTEXT_FILTER_BRVLAN) || - (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED)) { - err = switchdev_port_obj_dump(dev, &dump.vlan.obj, - switchdev_port_vlan_dump_cb); - if (err) - goto err_out; - if (filter_mask & RTEXT_FILTER_BRVLAN_COMPRESSED) - /* last one */ - err = switchdev_port_vlan_dump_put(&dump); - } - -err_out: - return err == -EOPNOTSUPP ? 0 : err; -} - -/** - * switchdev_port_bridge_getlink - Get bridge port attributes - * - * @dev: port device - * - * Called for SELF on rtnl_bridge_getlink to get bridge port - * attributes. - */ -int switchdev_port_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, - struct net_device *dev, u32 filter_mask, - int nlflags) -{ - struct switchdev_attr attr = { - .orig_dev = dev, - .id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS, - }; - u16 mode = BRIDGE_MODE_UNDEF; - u32 mask = BR_LEARNING | BR_LEARNING_SYNC | BR_FLOOD; - int err; - - if (!netif_is_bridge_port(dev)) - return -EOPNOTSUPP; - - err = switchdev_port_attr_get(dev, &attr); - if (err && err != -EOPNOTSUPP) - return err; - - return ndo_dflt_bridge_getlink(skb, pid, seq, dev, mode, - attr.u.brport_flags, mask, nlflags, - filter_mask, switchdev_port_vlan_fill); -} -EXPORT_SYMBOL_GPL(switchdev_port_bridge_getlink); - -static int switchdev_port_br_setflag(struct net_device *dev, - struct nlattr *nlattr, - unsigned long brport_flag) -{ - struct switchdev_attr attr = { - .orig_dev = dev, - .id = SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS, - }; - u8 flag = nla_get_u8(nlattr); - int err; - - err = switchdev_port_attr_get(dev, &attr); - if (err) - return err; - - if (flag) - attr.u.brport_flags |= brport_flag; - else - attr.u.brport_flags &= ~brport_flag; - - return switchdev_port_attr_set(dev, &attr); -} - -static const struct nla_policy -switchdev_port_bridge_policy[IFLA_BRPORT_MAX + 1] = { - [IFLA_BRPORT_STATE] = { .type = NLA_U8 }, - [IFLA_BRPORT_COST] = { .type = NLA_U32 }, - [IFLA_BRPORT_PRIORITY] = { .type = NLA_U16 }, - [IFLA_BRPORT_MODE] = { .type = NLA_U8 }, - [IFLA_BRPORT_GUARD] = { .type = NLA_U8 }, - [IFLA_BRPORT_PROTECT] = { .type = NLA_U8 }, - [IFLA_BRPORT_FAST_LEAVE] = { .type = NLA_U8 }, - [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, - [IFLA_BRPORT_LEARNING_SYNC] = { .type = NLA_U8 }, - [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, -}; - -static int switchdev_port_br_setlink_protinfo(struct net_device *dev, - struct nlattr *protinfo) -{ - struct nlattr *attr; - int rem; - int err; - - err = nla_validate_nested(protinfo, IFLA_BRPORT_MAX, - switchdev_port_bridge_policy, NULL); - if (err) - return err; - - nla_for_each_nested(attr, protinfo, rem) { - switch (nla_type(attr)) { - case IFLA_BRPORT_LEARNING: - err = switchdev_port_br_setflag(dev, attr, - BR_LEARNING); - break; - case IFLA_BRPORT_LEARNING_SYNC: - err = switchdev_port_br_setflag(dev, attr, - BR_LEARNING_SYNC); - break; - case IFLA_BRPORT_UNICAST_FLOOD: - err = switchdev_port_br_setflag(dev, attr, BR_FLOOD); - break; - default: - err = -EOPNOTSUPP; - break; - } - if (err) - return err; - } - - return 0; -} - -static int switchdev_port_br_afspec(struct net_device *dev, - struct nlattr *afspec, - int (*f)(struct net_device *dev, - const struct switchdev_obj *obj)) -{ - struct nlattr *attr; - struct bridge_vlan_info *vinfo; - struct switchdev_obj_port_vlan vlan = { - .obj.orig_dev = dev, - .obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN, - }; - int rem; - int err; - - nla_for_each_nested(attr, afspec, rem) { - if (nla_type(attr) != IFLA_BRIDGE_VLAN_INFO) - continue; - if (nla_len(attr) != sizeof(struct bridge_vlan_info)) - return -EINVAL; - vinfo = nla_data(attr); - if (!vinfo->vid || vinfo->vid >= VLAN_VID_MASK) - return -EINVAL; - vlan.flags = vinfo->flags; - if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_BEGIN) { - if (vlan.vid_begin) - return -EINVAL; - vlan.vid_begin = vinfo->vid; - /* don't allow range of pvids */ - if (vlan.flags & BRIDGE_VLAN_INFO_PVID) - return -EINVAL; - } else if (vinfo->flags & BRIDGE_VLAN_INFO_RANGE_END) { - if (!vlan.vid_begin) - return -EINVAL; - vlan.vid_end = vinfo->vid; - if (vlan.vid_end <= vlan.vid_begin) - return -EINVAL; - err = f(dev, &vlan.obj); - if (err) - return err; - vlan.vid_begin = 0; - } else { - if (vlan.vid_begin) - return -EINVAL; - vlan.vid_begin = vinfo->vid; - vlan.vid_end = vinfo->vid; - err = f(dev, &vlan.obj); - if (err) - return err; - vlan.vid_begin = 0; - } - } - - return 0; -} - -/** - * switchdev_port_bridge_setlink - Set bridge port attributes - * - * @dev: port device - * @nlh: netlink header - * @flags: netlink flags - * - * Called for SELF on rtnl_bridge_setlink to set bridge port - * attributes. - */ -int switchdev_port_bridge_setlink(struct net_device *dev, - struct nlmsghdr *nlh, u16 flags) -{ - struct nlattr *protinfo; - struct nlattr *afspec; - int err = 0; - - if (!netif_is_bridge_port(dev)) - return -EOPNOTSUPP; - - protinfo = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), - IFLA_PROTINFO); - if (protinfo) { - err = switchdev_port_br_setlink_protinfo(dev, protinfo); - if (err) - return err; - } - - afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), - IFLA_AF_SPEC); - if (afspec) - err = switchdev_port_br_afspec(dev, afspec, - switchdev_port_obj_add); - - return err; -} -EXPORT_SYMBOL_GPL(switchdev_port_bridge_setlink); - -/** - * switchdev_port_bridge_dellink - Set bridge port attributes - * - * @dev: port device - * @nlh: netlink header - * @flags: netlink flags - * - * Called for SELF on rtnl_bridge_dellink to set bridge port - * attributes. - */ -int switchdev_port_bridge_dellink(struct net_device *dev, - struct nlmsghdr *nlh, u16 flags) -{ - struct nlattr *afspec; - - if (!netif_is_bridge_port(dev)) - return -EOPNOTSUPP; - - afspec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), - IFLA_AF_SPEC); - if (afspec) - return switchdev_port_br_afspec(dev, afspec, - switchdev_port_obj_del); - - return 0; -} -EXPORT_SYMBOL_GPL(switchdev_port_bridge_dellink); - -/** - * switchdev_port_fdb_add - Add FDB (MAC/VLAN) entry to port - * - * @ndmsg: netlink hdr - * @nlattr: netlink attributes - * @dev: port device - * @addr: MAC address to add - * @vid: VLAN to add - * - * Add FDB entry to switch device. - */ -int switchdev_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, const unsigned char *addr, - u16 vid, u16 nlm_flags) -{ - struct switchdev_obj_port_fdb fdb = { - .obj.orig_dev = dev, - .obj.id = SWITCHDEV_OBJ_ID_PORT_FDB, - .vid = vid, - }; - - ether_addr_copy(fdb.addr, addr); - return switchdev_port_obj_add(dev, &fdb.obj); -} -EXPORT_SYMBOL_GPL(switchdev_port_fdb_add); - -/** - * switchdev_port_fdb_del - Delete FDB (MAC/VLAN) entry from port - * - * @ndmsg: netlink hdr - * @nlattr: netlink attributes - * @dev: port device - * @addr: MAC address to delete - * @vid: VLAN to delete - * - * Delete FDB entry from switch device. - */ -int switchdev_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[], - struct net_device *dev, const unsigned char *addr, - u16 vid) -{ - struct switchdev_obj_port_fdb fdb = { - .obj.orig_dev = dev, - .obj.id = SWITCHDEV_OBJ_ID_PORT_FDB, - .vid = vid, - }; - - ether_addr_copy(fdb.addr, addr); - return switchdev_port_obj_del(dev, &fdb.obj); -} -EXPORT_SYMBOL_GPL(switchdev_port_fdb_del); - bool switchdev_port_same_parent_id(struct net_device *a, struct net_device *b) { -- cgit v1.2.3 From 198ec9ae052922d67491ab3cc3fd704580e0daf4 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Tue, 8 Aug 2017 15:48:40 +0200 Subject: Bluetooth: document config options Kernel config options should include useful help text; I had to look up the terms on wikipedia. Signed-off-by: Pavel Machek Signed-off-by: Marcel Holtmann --- net/bluetooth/Kconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'net') diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index 68f951b3e85a..db82a40875e8 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -45,6 +45,11 @@ config BT_BREDR bool "Bluetooth Classic (BR/EDR) features" depends on BT default y + help + Bluetooth Classic includes support for Basic Rate (BR) + available with Bluetooth version 1.0b or later and support + for Enhanced Data Rate (EDR) available with Bluetooth + version 2.0 or later. source "net/bluetooth/rfcomm/Kconfig" @@ -58,11 +63,18 @@ config BT_HS bool "Bluetooth High Speed (HS) features" depends on BT_BREDR default y + help + Bluetooth High Speed includes support for off-loading + Bluetooth connections via 802.11 (wifi) physical layer + available with Bluetooth version 3.0 or later. config BT_LE bool "Bluetooth Low Energy (LE) features" depends on BT default y + help + Bluetooth Low Energy includes support low-energy physical + layer available with Bluetooth version 4.0 or later. config BT_6LOWPAN tristate "Bluetooth 6LoWPAN support" -- cgit v1.2.3 From 274cdb46e98b965449c68d912bcc45899ba34593 Mon Sep 17 00:00:00 2001 From: Egil Hjelmeland Date: Tue, 8 Aug 2017 00:22:21 +0200 Subject: net: dsa: lan9303: Only allocate 3 ports Save 2628 bytes on arm eabi by allocate only the required 3 ports. Now that ds->num_ports is correct: In net/dsa/tag_lan9303.c eliminate duplicate LAN9303_MAX_PORTS, use ds->num_ports. (Matching the pattern of other net/dsa/tag_xxx.c files.) Signed-off-by: Egil Hjelmeland Reviewed-by: Florian Fainelli Reviewed-by: Vivien Didelot Signed-off-by: David S. Miller --- drivers/net/dsa/lan9303-core.c | 2 +- net/dsa/tag_lan9303.c | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/drivers/net/dsa/lan9303-core.c b/drivers/net/dsa/lan9303-core.c index 15befd155251..46fc1d5d3c9e 100644 --- a/drivers/net/dsa/lan9303-core.c +++ b/drivers/net/dsa/lan9303-core.c @@ -811,7 +811,7 @@ static struct dsa_switch_ops lan9303_switch_ops = { static int lan9303_register_switch(struct lan9303 *chip) { - chip->ds = dsa_switch_alloc(chip->dev, DSA_MAX_PORTS); + chip->ds = dsa_switch_alloc(chip->dev, LAN9303_NUM_PORTS); if (!chip->ds) return -ENOMEM; diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c index 247774d149f9..e23e7635fa00 100644 --- a/net/dsa/tag_lan9303.c +++ b/net/dsa/tag_lan9303.c @@ -39,7 +39,6 @@ */ #define LAN9303_TAG_LEN 4 -#define LAN9303_MAX_PORTS 3 static struct sk_buff *lan9303_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -104,7 +103,7 @@ static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev, source_port = ntohs(lan9303_tag[1]) & 0x3; - if (source_port >= LAN9303_MAX_PORTS) { + if (source_port >= ds->num_ports) { dev_warn_ratelimited(&dev->dev, "Dropping packet due to invalid source port\n"); return NULL; } -- cgit v1.2.3 From 7120371c8ef1bca709308f6e41130b6fd87b7d15 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Mon, 7 Aug 2017 15:26:50 -0700 Subject: net_sched: get rid of some forward declarations If we move up tcf_fill_node() we can get rid of these forward declarations. Also, move down tfilter_notify_chain() to group them together. Reported-by: Jamal Hadi Salim Cc: Jamal Hadi Salim Signed-off-by: Cong Wang Acked-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_api.c | 214 +++++++++++++++++++++++++--------------------------- 1 file changed, 103 insertions(+), 111 deletions(-) (limited to 'net') diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 668afb6e9885..8d1157aebaf7 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -100,25 +100,6 @@ int unregister_tcf_proto_ops(struct tcf_proto_ops *ops) } EXPORT_SYMBOL(unregister_tcf_proto_ops); -static int tfilter_notify(struct net *net, struct sk_buff *oskb, - struct nlmsghdr *n, struct tcf_proto *tp, - void *fh, int event, bool unicast); - -static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, - struct nlmsghdr *n, struct tcf_proto *tp, - void *fh, bool unicast, bool *last); - -static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, - struct nlmsghdr *n, - struct tcf_chain *chain, int event) -{ - struct tcf_proto *tp; - - for (tp = rtnl_dereference(chain->filter_chain); - tp; tp = rtnl_dereference(tp->next)) - tfilter_notify(net, oskb, n, tp, 0, event, false); -} - /* Select new prio value from the range, managed by kernel. */ static inline u32 tcf_auto_prio(struct tcf_proto *tp) @@ -411,6 +392,109 @@ static struct tcf_proto *tcf_chain_tp_find(struct tcf_chain *chain, return tp; } +static int tcf_fill_node(struct net *net, struct sk_buff *skb, + struct tcf_proto *tp, void *fh, u32 portid, + u32 seq, u16 flags, int event) +{ + struct tcmsg *tcm; + struct nlmsghdr *nlh; + unsigned char *b = skb_tail_pointer(skb); + + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); + if (!nlh) + goto out_nlmsg_trim; + tcm = nlmsg_data(nlh); + tcm->tcm_family = AF_UNSPEC; + tcm->tcm__pad1 = 0; + tcm->tcm__pad2 = 0; + tcm->tcm_ifindex = qdisc_dev(tp->q)->ifindex; + tcm->tcm_parent = tp->classid; + tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); + if (nla_put_string(skb, TCA_KIND, tp->ops->kind)) + goto nla_put_failure; + if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index)) + goto nla_put_failure; + if (!fh) { + tcm->tcm_handle = 0; + } else { + if (tp->ops->dump && tp->ops->dump(net, tp, fh, skb, tcm) < 0) + goto nla_put_failure; + } + nlh->nlmsg_len = skb_tail_pointer(skb) - b; + return skb->len; + +out_nlmsg_trim: +nla_put_failure: + nlmsg_trim(skb, b); + return -1; +} + +static int tfilter_notify(struct net *net, struct sk_buff *oskb, + struct nlmsghdr *n, struct tcf_proto *tp, + void *fh, int event, bool unicast) +{ + struct sk_buff *skb; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + + skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + if (tcf_fill_node(net, skb, tp, fh, portid, n->nlmsg_seq, + n->nlmsg_flags, event) <= 0) { + kfree_skb(skb); + return -EINVAL; + } + + if (unicast) + return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); + + return rtnetlink_send(skb, net, portid, RTNLGRP_TC, + n->nlmsg_flags & NLM_F_ECHO); +} + +static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, + struct nlmsghdr *n, struct tcf_proto *tp, + void *fh, bool unicast, bool *last) +{ + struct sk_buff *skb; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + int err; + + skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + if (tcf_fill_node(net, skb, tp, fh, portid, n->nlmsg_seq, + n->nlmsg_flags, RTM_DELTFILTER) <= 0) { + kfree_skb(skb); + return -EINVAL; + } + + err = tp->ops->delete(tp, fh, last); + if (err) { + kfree_skb(skb); + return err; + } + + if (unicast) + return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); + + return rtnetlink_send(skb, net, portid, RTNLGRP_TC, + n->nlmsg_flags & NLM_F_ECHO); +} + +static void tfilter_notify_chain(struct net *net, struct sk_buff *oskb, + struct nlmsghdr *n, + struct tcf_chain *chain, int event) +{ + struct tcf_proto *tp; + + for (tp = rtnl_dereference(chain->filter_chain); + tp; tp = rtnl_dereference(tp->next)) + tfilter_notify(net, oskb, n, tp, 0, event, false); +} + /* Add/change/delete/get a filter node */ static int tc_ctl_tfilter(struct sk_buff *skb, struct nlmsghdr *n, @@ -640,98 +724,6 @@ errout: return err; } -static int tcf_fill_node(struct net *net, struct sk_buff *skb, - struct tcf_proto *tp, void *fh, u32 portid, - u32 seq, u16 flags, int event) -{ - struct tcmsg *tcm; - struct nlmsghdr *nlh; - unsigned char *b = skb_tail_pointer(skb); - - nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); - if (!nlh) - goto out_nlmsg_trim; - tcm = nlmsg_data(nlh); - tcm->tcm_family = AF_UNSPEC; - tcm->tcm__pad1 = 0; - tcm->tcm__pad2 = 0; - tcm->tcm_ifindex = qdisc_dev(tp->q)->ifindex; - tcm->tcm_parent = tp->classid; - tcm->tcm_info = TC_H_MAKE(tp->prio, tp->protocol); - if (nla_put_string(skb, TCA_KIND, tp->ops->kind)) - goto nla_put_failure; - if (nla_put_u32(skb, TCA_CHAIN, tp->chain->index)) - goto nla_put_failure; - if (!fh) { - tcm->tcm_handle = 0; - } else { - if (tp->ops->dump && tp->ops->dump(net, tp, fh, skb, tcm) < 0) - goto nla_put_failure; - } - nlh->nlmsg_len = skb_tail_pointer(skb) - b; - return skb->len; - -out_nlmsg_trim: -nla_put_failure: - nlmsg_trim(skb, b); - return -1; -} - -static int tfilter_notify(struct net *net, struct sk_buff *oskb, - struct nlmsghdr *n, struct tcf_proto *tp, - void *fh, int event, bool unicast) -{ - struct sk_buff *skb; - u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; - - skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb) - return -ENOBUFS; - - if (tcf_fill_node(net, skb, tp, fh, portid, n->nlmsg_seq, - n->nlmsg_flags, event) <= 0) { - kfree_skb(skb); - return -EINVAL; - } - - if (unicast) - return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); - - return rtnetlink_send(skb, net, portid, RTNLGRP_TC, - n->nlmsg_flags & NLM_F_ECHO); -} - -static int tfilter_del_notify(struct net *net, struct sk_buff *oskb, - struct nlmsghdr *n, struct tcf_proto *tp, - void *fh, bool unicast, bool *last) -{ - struct sk_buff *skb; - u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; - int err; - - skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb) - return -ENOBUFS; - - if (tcf_fill_node(net, skb, tp, fh, portid, n->nlmsg_seq, - n->nlmsg_flags, RTM_DELTFILTER) <= 0) { - kfree_skb(skb); - return -EINVAL; - } - - err = tp->ops->delete(tp, fh, last); - if (err) { - kfree_skb(skb); - return err; - } - - if (unicast) - return netlink_unicast(net->rtnl, skb, portid, MSG_DONTWAIT); - - return rtnetlink_send(skb, net, portid, RTNLGRP_TC, - n->nlmsg_flags & NLM_F_ECHO); -} - struct tcf_dump_args { struct tcf_walker w; struct sk_buff *skb; -- cgit v1.2.3 From feca7d8c135bc1527b244fe817b8b6498066ccec Mon Sep 17 00:00:00 2001 From: Vincent Bernat Date: Tue, 8 Aug 2017 20:23:49 +0200 Subject: net: ipv6: avoid overhead when no custom FIB rules are installed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit If the user hasn't installed any custom rules, don't go through the whole FIB rules layer. This is pretty similar to f4530fa574df (ipv4: Avoid overhead when no custom FIB rules are installed). Using a micro-benchmark module [1], timing ip6_route_output() with get_cycles(), with 40,000 routes in the main routing table, before this patch: min=606 max=12911 count=627 average=1959 95th=4903 90th=3747 50th=1602 mad=821 table=254 avgdepth=21.8 maxdepth=39 value │ ┊ count 600 │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 199 880 │▒▒▒░░░░░░░░░░░░░░░░ 43 1160 │▒▒▒░░░░░░░░░░░░░░░░░░░░ 48 1440 │▒▒▒░░░░░░░░░░░░░░░░░░░░░░░ 43 1720 │▒▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░ 59 2000 │▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 50 2280 │▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 26 2560 │▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 31 2840 │▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 28 3120 │▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 17 3400 │▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 17 3680 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 8 3960 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 11 4240 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 6 4520 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 6 4800 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 9 After: min=544 max=11687 count=627 average=1776 95th=4546 90th=3585 50th=1227 mad=565 table=254 avgdepth=21.8 maxdepth=39 value │ ┊ count 540 │▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒ 201 800 │▒▒▒▒▒░░░░░░░░░░░░░░░░ 63 1060 │▒▒▒▒▒░░░░░░░░░░░░░░░░░░░░░ 68 1320 │▒▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░ 39 1580 │▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 32 1840 │▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 32 2100 │▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 34 2360 │▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 33 2620 │▒▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 26 2880 │▒░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 22 3140 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 9 3400 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 8 3660 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 9 3920 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 8 4180 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 8 4440 │░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 8 At the frequency of the host during the bench (~ 3.7 GHz), this is about a 100 ns difference on the median value. A next step would be to collapse local and main tables, as in 0ddcf43d5d4a (ipv4: FIB Local/MAIN table collapse). [1]: https://github.com/vincentbernat/network-lab/blob/master/lab-routes-ipv6/kbench_mod.c Signed-off-by: Vincent Bernat Reviewed-by: Jiri Pirko Acked-by: David Ahern Signed-off-by: David S. Miller --- include/net/netns/ipv6.h | 1 + net/ipv6/fib6_rules.c | 40 +++++++++++++++++++++++++++------------- net/ipv6/route.c | 1 + 3 files changed, 29 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index abdf3b40303b..0e50bf3ed097 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -65,6 +65,7 @@ struct netns_ipv6 { unsigned int ip6_rt_gc_expire; unsigned long ip6_rt_last_gc; #ifdef CONFIG_IPV6_MULTIPLE_TABLES + bool fib6_has_custom_rules; struct rt6_info *ip6_prohibit_entry; struct rt6_info *ip6_blk_hole_entry; struct fib6_table *fib6_local_tbl; diff --git a/net/ipv6/fib6_rules.c b/net/ipv6/fib6_rules.c index 2f29e4e33bd3..b240f24a6e52 100644 --- a/net/ipv6/fib6_rules.c +++ b/net/ipv6/fib6_rules.c @@ -63,19 +63,32 @@ unsigned int fib6_rules_seq_read(struct net *net) struct dst_entry *fib6_rule_lookup(struct net *net, struct flowi6 *fl6, int flags, pol_lookup_t lookup) { - struct fib_lookup_arg arg = { - .lookup_ptr = lookup, - .flags = FIB_LOOKUP_NOREF, - }; - - /* update flow if oif or iif point to device enslaved to l3mdev */ - l3mdev_update_flow(net, flowi6_to_flowi(fl6)); - - fib_rules_lookup(net->ipv6.fib6_rules_ops, - flowi6_to_flowi(fl6), flags, &arg); - - if (arg.result) - return arg.result; + if (net->ipv6.fib6_has_custom_rules) { + struct fib_lookup_arg arg = { + .lookup_ptr = lookup, + .flags = FIB_LOOKUP_NOREF, + }; + + /* update flow if oif or iif point to device enslaved to l3mdev */ + l3mdev_update_flow(net, flowi6_to_flowi(fl6)); + + fib_rules_lookup(net->ipv6.fib6_rules_ops, + flowi6_to_flowi(fl6), flags, &arg); + + if (arg.result) + return arg.result; + } else { + struct rt6_info *rt; + + rt = lookup(net, net->ipv6.fib6_local_tbl, fl6, flags); + if (rt != net->ipv6.ip6_null_entry && rt->dst.error != -EAGAIN) + return &rt->dst; + ip6_rt_put(rt); + rt = lookup(net, net->ipv6.fib6_main_tbl, fl6, flags); + if (rt->dst.error != -EAGAIN) + return &rt->dst; + ip6_rt_put(rt); + } dst_hold(&net->ipv6.ip6_null_entry->dst); return &net->ipv6.ip6_null_entry->dst; @@ -245,6 +258,7 @@ static int fib6_rule_configure(struct fib_rule *rule, struct sk_buff *skb, rule6->dst.plen = frh->dst_len; rule6->tclass = frh->tos; + net->ipv6.fib6_has_custom_rules = true; err = 0; errout: return err; diff --git a/net/ipv6/route.c b/net/ipv6/route.c index aba07fce67fb..7ecbe5eb19f8 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3934,6 +3934,7 @@ static int __net_init ip6_route_net_init(struct net *net) ip6_template_metrics, true); #ifdef CONFIG_IPV6_MULTIPLE_TABLES + net->ipv6.fib6_has_custom_rules = false; net->ipv6.ip6_prohibit_entry = kmemdup(&ip6_prohibit_entry_template, sizeof(*net->ipv6.ip6_prohibit_entry), GFP_KERNEL); -- cgit v1.2.3 From ccaffff182027078e9443d912b5af461850965f4 Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Wed, 9 Aug 2017 19:09:43 -0400 Subject: sock: fix zerocopy panic in mem accounting Only call mm_unaccount_pinned_pages when releasing a struct ubuf_info that has initialized its field uarg->mmp. Before this patch, a vhost-net with experimental_zcopytx can crash in mm_unaccount_pinned_pages sock_zerocopy_put skb_zcopy_clear skb_release_data Only sock_zerocopy_alloc initializes this field. Move the unaccount call from generic sock_zerocopy_put to its specific callback sock_zerocopy_callback. Fixes: a91dbff551a6 ("sock: ulimit on MSG_ZEROCOPY pages") Reported-by: David Ahern Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- net/core/skbuff.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 42b62c716a33..cb123590c674 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -1044,6 +1044,8 @@ void sock_zerocopy_callback(struct ubuf_info *uarg, bool success) u32 lo, hi; u16 len; + mm_unaccount_pinned_pages(&uarg->mmp); + /* if !len, there was only 1 call, and it was aborted * so do not queue a completion notification */ @@ -1084,8 +1086,6 @@ EXPORT_SYMBOL_GPL(sock_zerocopy_callback); void sock_zerocopy_put(struct ubuf_info *uarg) { if (uarg && atomic_dec_and_test(&uarg->refcnt)) { - mm_unaccount_pinned_pages(&uarg->mmp); - if (uarg->callback) uarg->callback(uarg, uarg->zerocopy); else -- cgit v1.2.3 From 92b31a9af73b3a3fc801899335d6c47966351830 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 10 Aug 2017 01:39:55 +0200 Subject: bpf: add BPF_J{LT,LE,SLT,SLE} instructions Currently, eBPF only understands BPF_JGT (>), BPF_JGE (>=), BPF_JSGT (s>), BPF_JSGE (s>=) instructions, this means that particularly *JLT/*JLE counterparts involving immediates need to be rewritten from e.g. X < [IMM] by swapping arguments into [IMM] > X, meaning the immediate first is required to be loaded into a register Y := [IMM], such that then we can compare with Y > X. Note that the destination operand is always required to be a register. This has the downside of having unnecessarily increased register pressure, meaning complex program would need to spill other registers temporarily to stack in order to obtain an unused register for the [IMM]. Loading to registers will thus also affect state pruning since we need to account for that register use and potentially those registers that had to be spilled/filled again. As a consequence slightly more stack space might have been used due to spilling, and BPF programs are a bit longer due to extra code involving the register load and potentially required spill/fills. Thus, add BPF_JLT (<), BPF_JLE (<=), BPF_JSLT (s<), BPF_JSLE (s<=) counterparts to the eBPF instruction set. Modifying LLVM to remove the NegateCC() workaround in a PoC patch at [1] and allowing it to also emit the new instructions resulted in cilium's BPF programs that are injected into the fast-path to have a reduced program length in the range of 2-3% (e.g. accumulated main and tail call sections from one of the object file reduced from 4864 to 4729 insns), reduced complexity in the range of 10-30% (e.g. accumulated sections reduced in one of the cases from 116432 to 88428 insns), and reduced stack usage in the range of 1-5% (e.g. accumulated sections from one of the object files reduced from 824 to 784b). The modification for LLVM will be incorporated in a backwards compatible way. Plan is for LLVM to have i) a target specific option to offer a possibility to explicitly enable the extension by the user (as we have with -m target specific extensions today for various CPU insns), and ii) have the kernel checked for presence of the extensions and enable them transparently when the user is selecting more aggressive options such as -march=native in a bpf target context. (Other frontends generating BPF byte code, e.g. ply can probe the kernel directly for its code generation.) [1] https://github.com/borkmann/llvm/tree/bpf-insns Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- Documentation/networking/filter.txt | 4 + include/uapi/linux/bpf.h | 5 + kernel/bpf/core.c | 60 ++++++ lib/test_bpf.c | 364 ++++++++++++++++++++++++++++++++++++ net/core/filter.c | 21 ++- tools/include/uapi/linux/bpf.h | 5 + 6 files changed, 455 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/Documentation/networking/filter.txt b/Documentation/networking/filter.txt index d0fdba7d66e2..6a0df8df6c43 100644 --- a/Documentation/networking/filter.txt +++ b/Documentation/networking/filter.txt @@ -906,6 +906,10 @@ If BPF_CLASS(code) == BPF_JMP, BPF_OP(code) is one of: BPF_JSGE 0x70 /* eBPF only: signed '>=' */ BPF_CALL 0x80 /* eBPF only: function call */ BPF_EXIT 0x90 /* eBPF only: function return */ + BPF_JLT 0xa0 /* eBPF only: unsigned '<' */ + BPF_JLE 0xb0 /* eBPF only: unsigned '<=' */ + BPF_JSLT 0xc0 /* eBPF only: signed '<' */ + BPF_JSLE 0xd0 /* eBPF only: signed '<=' */ So BPF_ADD | BPF_X | BPF_ALU means 32-bit addition in both classic BPF and eBPF. There are only two registers in classic BPF, so it means A += X. diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 1d06be1569b1..91da8371a2d0 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -30,9 +30,14 @@ #define BPF_FROM_LE BPF_TO_LE #define BPF_FROM_BE BPF_TO_BE +/* jmp encodings */ #define BPF_JNE 0x50 /* jump != */ +#define BPF_JLT 0xa0 /* LT is unsigned, '<' */ +#define BPF_JLE 0xb0 /* LE is unsigned, '<=' */ #define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */ #define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */ +#define BPF_JSLT 0xc0 /* SLT is signed, '<' */ +#define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ diff --git a/kernel/bpf/core.c b/kernel/bpf/core.c index ad5f55922a13..c69e7f5bfde7 100644 --- a/kernel/bpf/core.c +++ b/kernel/bpf/core.c @@ -595,9 +595,13 @@ static int bpf_jit_blind_insn(const struct bpf_insn *from, case BPF_JMP | BPF_JEQ | BPF_K: case BPF_JMP | BPF_JNE | BPF_K: case BPF_JMP | BPF_JGT | BPF_K: + case BPF_JMP | BPF_JLT | BPF_K: case BPF_JMP | BPF_JGE | BPF_K: + case BPF_JMP | BPF_JLE | BPF_K: case BPF_JMP | BPF_JSGT | BPF_K: + case BPF_JMP | BPF_JSLT | BPF_K: case BPF_JMP | BPF_JSGE | BPF_K: + case BPF_JMP | BPF_JSLE | BPF_K: case BPF_JMP | BPF_JSET | BPF_K: /* Accommodate for extra offset in case of a backjump. */ off = from->off; @@ -833,12 +837,20 @@ static unsigned int ___bpf_prog_run(u64 *regs, const struct bpf_insn *insn, [BPF_JMP | BPF_JNE | BPF_K] = &&JMP_JNE_K, [BPF_JMP | BPF_JGT | BPF_X] = &&JMP_JGT_X, [BPF_JMP | BPF_JGT | BPF_K] = &&JMP_JGT_K, + [BPF_JMP | BPF_JLT | BPF_X] = &&JMP_JLT_X, + [BPF_JMP | BPF_JLT | BPF_K] = &&JMP_JLT_K, [BPF_JMP | BPF_JGE | BPF_X] = &&JMP_JGE_X, [BPF_JMP | BPF_JGE | BPF_K] = &&JMP_JGE_K, + [BPF_JMP | BPF_JLE | BPF_X] = &&JMP_JLE_X, + [BPF_JMP | BPF_JLE | BPF_K] = &&JMP_JLE_K, [BPF_JMP | BPF_JSGT | BPF_X] = &&JMP_JSGT_X, [BPF_JMP | BPF_JSGT | BPF_K] = &&JMP_JSGT_K, + [BPF_JMP | BPF_JSLT | BPF_X] = &&JMP_JSLT_X, + [BPF_JMP | BPF_JSLT | BPF_K] = &&JMP_JSLT_K, [BPF_JMP | BPF_JSGE | BPF_X] = &&JMP_JSGE_X, [BPF_JMP | BPF_JSGE | BPF_K] = &&JMP_JSGE_K, + [BPF_JMP | BPF_JSLE | BPF_X] = &&JMP_JSLE_X, + [BPF_JMP | BPF_JSLE | BPF_K] = &&JMP_JSLE_K, [BPF_JMP | BPF_JSET | BPF_X] = &&JMP_JSET_X, [BPF_JMP | BPF_JSET | BPF_K] = &&JMP_JSET_K, /* Program return */ @@ -1073,6 +1085,18 @@ out: CONT_JMP; } CONT; + JMP_JLT_X: + if (DST < SRC) { + insn += insn->off; + CONT_JMP; + } + CONT; + JMP_JLT_K: + if (DST < IMM) { + insn += insn->off; + CONT_JMP; + } + CONT; JMP_JGE_X: if (DST >= SRC) { insn += insn->off; @@ -1085,6 +1109,18 @@ out: CONT_JMP; } CONT; + JMP_JLE_X: + if (DST <= SRC) { + insn += insn->off; + CONT_JMP; + } + CONT; + JMP_JLE_K: + if (DST <= IMM) { + insn += insn->off; + CONT_JMP; + } + CONT; JMP_JSGT_X: if (((s64) DST) > ((s64) SRC)) { insn += insn->off; @@ -1097,6 +1133,18 @@ out: CONT_JMP; } CONT; + JMP_JSLT_X: + if (((s64) DST) < ((s64) SRC)) { + insn += insn->off; + CONT_JMP; + } + CONT; + JMP_JSLT_K: + if (((s64) DST) < ((s64) IMM)) { + insn += insn->off; + CONT_JMP; + } + CONT; JMP_JSGE_X: if (((s64) DST) >= ((s64) SRC)) { insn += insn->off; @@ -1109,6 +1157,18 @@ out: CONT_JMP; } CONT; + JMP_JSLE_X: + if (((s64) DST) <= ((s64) SRC)) { + insn += insn->off; + CONT_JMP; + } + CONT; + JMP_JSLE_K: + if (((s64) DST) <= ((s64) IMM)) { + insn += insn->off; + CONT_JMP; + } + CONT; JMP_JSET_X: if (DST & SRC) { insn += insn->off; diff --git a/lib/test_bpf.c b/lib/test_bpf.c index d9d5a410955c..aa8812ae6776 100644 --- a/lib/test_bpf.c +++ b/lib/test_bpf.c @@ -951,6 +951,32 @@ static struct bpf_test tests[] = { { 4, 4, 4, 3, 3 }, { { 2, 0 }, { 3, 1 }, { 4, MAX_K } }, }, + { + "JGE (jt 0), test 1", + .u.insns = { + BPF_STMT(BPF_LDX | BPF_LEN, 0), + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), + BPF_JUMP(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 1), + BPF_STMT(BPF_RET | BPF_K, 1), + BPF_STMT(BPF_RET | BPF_K, MAX_K) + }, + CLASSIC, + { 4, 4, 4, 3, 3 }, + { { 2, 0 }, { 3, 1 }, { 4, 1 } }, + }, + { + "JGE (jt 0), test 2", + .u.insns = { + BPF_STMT(BPF_LDX | BPF_LEN, 0), + BPF_STMT(BPF_LD | BPF_B | BPF_ABS, 2), + BPF_JUMP(BPF_JMP | BPF_JGE | BPF_X, 0, 0, 1), + BPF_STMT(BPF_RET | BPF_K, 1), + BPF_STMT(BPF_RET | BPF_K, MAX_K) + }, + CLASSIC, + { 4, 4, 5, 3, 3 }, + { { 4, 1 }, { 5, 1 }, { 6, MAX_K } }, + }, { "JGE", .u.insns = { @@ -4492,6 +4518,35 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + /* BPF_JMP | BPF_JSLT | BPF_K */ + { + "JMP_JSLT_K: Signed jump: if (-2 < -1) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 0xfffffffffffffffeLL), + BPF_JMP_IMM(BPF_JSLT, R1, -1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JSLT_K: Signed jump: if (-1 < -1) return 0", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_LD_IMM64(R1, 0xffffffffffffffffLL), + BPF_JMP_IMM(BPF_JSLT, R1, -1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JSGT | BPF_K */ { "JMP_JSGT_K: Signed jump: if (-1 > -2) return 1", @@ -4521,6 +4576,73 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + /* BPF_JMP | BPF_JSLE | BPF_K */ + { + "JMP_JSLE_K: Signed jump: if (-2 <= -1) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 0xfffffffffffffffeLL), + BPF_JMP_IMM(BPF_JSLE, R1, -1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JSLE_K: Signed jump: if (-1 <= -1) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 0xffffffffffffffffLL), + BPF_JMP_IMM(BPF_JSLE, R1, -1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JSLE_K: Signed jump: value walk 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_JMP_IMM(BPF_JSLE, R1, 0, 6), + BPF_ALU64_IMM(BPF_SUB, R1, 1), + BPF_JMP_IMM(BPF_JSLE, R1, 0, 4), + BPF_ALU64_IMM(BPF_SUB, R1, 1), + BPF_JMP_IMM(BPF_JSLE, R1, 0, 2), + BPF_ALU64_IMM(BPF_SUB, R1, 1), + BPF_JMP_IMM(BPF_JSLE, R1, 0, 1), + BPF_EXIT_INSN(), /* bad exit */ + BPF_ALU32_IMM(BPF_MOV, R0, 1), /* good exit */ + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JSLE_K: Signed jump: value walk 2", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_JMP_IMM(BPF_JSLE, R1, 0, 4), + BPF_ALU64_IMM(BPF_SUB, R1, 2), + BPF_JMP_IMM(BPF_JSLE, R1, 0, 2), + BPF_ALU64_IMM(BPF_SUB, R1, 2), + BPF_JMP_IMM(BPF_JSLE, R1, 0, 1), + BPF_EXIT_INSN(), /* bad exit */ + BPF_ALU32_IMM(BPF_MOV, R0, 1), /* good exit */ + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JSGE | BPF_K */ { "JMP_JSGE_K: Signed jump: if (-1 >= -2) return 1", @@ -4617,6 +4739,35 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + /* BPF_JMP | BPF_JLT | BPF_K */ + { + "JMP_JLT_K: if (2 < 3) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 2), + BPF_JMP_IMM(BPF_JLT, R1, 3, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JGT_K: Unsigned jump: if (1 < -1) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 1), + BPF_JMP_IMM(BPF_JLT, R1, -1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JGE | BPF_K */ { "JMP_JGE_K: if (3 >= 2) return 1", @@ -4632,6 +4783,21 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + /* BPF_JMP | BPF_JLE | BPF_K */ + { + "JMP_JLE_K: if (2 <= 3) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 2), + BPF_JMP_IMM(BPF_JLE, R1, 3, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JGT | BPF_K jump backwards */ { "JMP_JGT_K: if (3 > 2) return 1 (jump backwards)", @@ -4662,6 +4828,36 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + /* BPF_JMP | BPF_JLT | BPF_K jump backwards */ + { + "JMP_JGT_K: if (2 < 3) return 1 (jump backwards)", + .u.insns_int = { + BPF_JMP_IMM(BPF_JA, 0, 0, 2), /* goto start */ + BPF_ALU32_IMM(BPF_MOV, R0, 1), /* out: */ + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 0), /* start: */ + BPF_LD_IMM64(R1, 2), /* note: this takes 2 insns */ + BPF_JMP_IMM(BPF_JLT, R1, 3, -6), /* goto out */ + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JLE_K: if (3 <= 3) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_JMP_IMM(BPF_JLE, R1, 3, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JNE | BPF_K */ { "JMP_JNE_K: if (3 != 2) return 1", @@ -4752,6 +4948,37 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + /* BPF_JMP | BPF_JSLT | BPF_X */ + { + "JMP_JSLT_X: Signed jump: if (-2 < -1) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, -1), + BPF_LD_IMM64(R2, -2), + BPF_JMP_REG(BPF_JSLT, R2, R1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JSLT_X: Signed jump: if (-1 < -1) return 0", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_LD_IMM64(R1, -1), + BPF_LD_IMM64(R2, -1), + BPF_JMP_REG(BPF_JSLT, R1, R2, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JSGE | BPF_X */ { "JMP_JSGE_X: Signed jump: if (-1 >= -2) return 1", @@ -4783,6 +5010,37 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + /* BPF_JMP | BPF_JSLE | BPF_X */ + { + "JMP_JSLE_X: Signed jump: if (-2 <= -1) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, -1), + BPF_LD_IMM64(R2, -2), + BPF_JMP_REG(BPF_JSLE, R2, R1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JSLE_X: Signed jump: if (-1 <= -1) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, -1), + BPF_LD_IMM64(R2, -1), + BPF_JMP_REG(BPF_JSLE, R1, R2, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JGT | BPF_X */ { "JMP_JGT_X: if (3 > 2) return 1", @@ -4814,6 +5072,37 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + /* BPF_JMP | BPF_JLT | BPF_X */ + { + "JMP_JLT_X: if (2 < 3) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_LD_IMM64(R2, 2), + BPF_JMP_REG(BPF_JLT, R2, R1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JLT_X: Unsigned jump: if (1 < -1) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, -1), + BPF_LD_IMM64(R2, 1), + BPF_JMP_REG(BPF_JLT, R2, R1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JGE | BPF_X */ { "JMP_JGE_X: if (3 >= 2) return 1", @@ -4845,6 +5134,37 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + /* BPF_JMP | BPF_JLE | BPF_X */ + { + "JMP_JLE_X: if (2 <= 3) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_LD_IMM64(R2, 2), + BPF_JMP_REG(BPF_JLE, R2, R1, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, + { + "JMP_JLE_X: if (3 <= 3) return 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_LD_IMM64(R2, 3), + BPF_JMP_REG(BPF_JLE, R1, R2, 1), + BPF_EXIT_INSN(), + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, { /* Mainly testing JIT + imm64 here. */ "JMP_JGE_X: ldimm64 test 1", @@ -4890,6 +5210,50 @@ static struct bpf_test tests[] = { { }, { { 0, 1 } }, }, + { + "JMP_JLE_X: ldimm64 test 1", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_LD_IMM64(R2, 2), + BPF_JMP_REG(BPF_JLE, R2, R1, 2), + BPF_LD_IMM64(R0, 0xffffffffffffffffULL), + BPF_LD_IMM64(R0, 0xeeeeeeeeeeeeeeeeULL), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0xeeeeeeeeU } }, + }, + { + "JMP_JLE_X: ldimm64 test 2", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 0), + BPF_LD_IMM64(R1, 3), + BPF_LD_IMM64(R2, 2), + BPF_JMP_REG(BPF_JLE, R2, R1, 0), + BPF_LD_IMM64(R0, 0xffffffffffffffffULL), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 0xffffffffU } }, + }, + { + "JMP_JLE_X: ldimm64 test 3", + .u.insns_int = { + BPF_ALU32_IMM(BPF_MOV, R0, 1), + BPF_LD_IMM64(R1, 3), + BPF_LD_IMM64(R2, 2), + BPF_JMP_REG(BPF_JLE, R2, R1, 4), + BPF_LD_IMM64(R0, 0xffffffffffffffffULL), + BPF_LD_IMM64(R0, 0xeeeeeeeeeeeeeeeeULL), + BPF_EXIT_INSN(), + }, + INTERNAL, + { }, + { { 0, 1 } }, + }, /* BPF_JMP | BPF_JNE | BPF_X */ { "JMP_JNE_X: if (3 != 2) return 1", diff --git a/net/core/filter.c b/net/core/filter.c index 78d00933dbe7..5afe3ac191ec 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -514,14 +514,27 @@ do_pass: break; } - /* Convert JEQ into JNE when 'jump_true' is next insn. */ - if (fp->jt == 0 && BPF_OP(fp->code) == BPF_JEQ) { - insn->code = BPF_JMP | BPF_JNE | bpf_src; + /* Convert some jumps when 'jump_true' is next insn. */ + if (fp->jt == 0) { + switch (BPF_OP(fp->code)) { + case BPF_JEQ: + insn->code = BPF_JMP | BPF_JNE | bpf_src; + break; + case BPF_JGT: + insn->code = BPF_JMP | BPF_JLE | bpf_src; + break; + case BPF_JGE: + insn->code = BPF_JMP | BPF_JLT | bpf_src; + break; + default: + goto jmp_rest; + } + target = i + fp->jf + 1; BPF_EMIT_JMP; break; } - +jmp_rest: /* Other jumps are mapped into two insns: Jxx and JA. */ target = i + fp->jt + 1; insn->code = BPF_JMP | BPF_OP(fp->code) | bpf_src; diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h index 8d9bfcca3fe4..bf3b2e230455 100644 --- a/tools/include/uapi/linux/bpf.h +++ b/tools/include/uapi/linux/bpf.h @@ -30,9 +30,14 @@ #define BPF_FROM_LE BPF_TO_LE #define BPF_FROM_BE BPF_TO_BE +/* jmp encodings */ #define BPF_JNE 0x50 /* jump != */ +#define BPF_JLT 0xa0 /* LT is unsigned, '<' */ +#define BPF_JLE 0xb0 /* LE is unsigned, '<=' */ #define BPF_JSGT 0x60 /* SGT is signed '>', GT in x86 */ #define BPF_JSGE 0x70 /* SGE is signed '>=', GE in x86 */ +#define BPF_JSLT 0xc0 /* SLT is signed, '<' */ +#define BPF_JSLE 0xd0 /* SLE is signed, '<=' */ #define BPF_CALL 0x80 /* function call */ #define BPF_EXIT 0x90 /* function return */ -- cgit v1.2.3 From e1fa6d216dd134c7fca6dc5f0a10553c36503901 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 9 Aug 2017 20:41:47 +0200 Subject: rtnetlink: call rtnl_calcit directly There is only a single place in the kernel that regisers the "calcit" callback (to determine min allocation for dumps). This is in rtnetlink.c for PF_UNSPEC RTM_GETLINK. The function that checks for calcit presence at run time will first check the requested family (which will always fail for !PF_UNSPEC as no callsite registers this), then falls back to checking PF_UNSPEC. Therefore we can just check if type is RTM_GETLINK and then do a direct call. Because of fallback to PF_UNSPEC all RTM_GETLINK types used this regardless of family. This has the advantage that we don't need to allocate space for the function pointer for all the other families. A followup patch will drop the calcit function pointer from the rtnl_link callback structure. Signed-off-by: Florian Westphal Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 29 ++++------------------------- 1 file changed, 4 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9201e3621351..8c9d34deea7d 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -62,7 +62,6 @@ struct rtnl_link { rtnl_doit_func doit; rtnl_dumpit_func dumpit; - rtnl_calcit_func calcit; }; static DEFINE_MUTEX(rtnl_mutex); @@ -173,21 +172,6 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) return tab[msgindex].dumpit; } -static rtnl_calcit_func rtnl_get_calcit(int protocol, int msgindex) -{ - struct rtnl_link *tab; - - if (protocol <= RTNL_FAMILY_MAX) - tab = rtnl_msg_handlers[protocol]; - else - tab = NULL; - - if (tab == NULL || tab[msgindex].calcit == NULL) - tab = rtnl_msg_handlers[PF_UNSPEC]; - - return tab[msgindex].calcit; -} - /** * __rtnl_register - Register a rtnetlink message type * @protocol: Protocol family or PF_UNSPEC @@ -231,9 +215,6 @@ int __rtnl_register(int protocol, int msgtype, if (dumpit) tab[msgindex].dumpit = dumpit; - if (calcit) - tab[msgindex].calcit = calcit; - return 0; } EXPORT_SYMBOL_GPL(__rtnl_register); @@ -277,7 +258,6 @@ int rtnl_unregister(int protocol, int msgtype) rtnl_msg_handlers[protocol][msgindex].doit = NULL; rtnl_msg_handlers[protocol][msgindex].dumpit = NULL; - rtnl_msg_handlers[protocol][msgindex].calcit = NULL; return 0; } @@ -4187,15 +4167,14 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { struct sock *rtnl; rtnl_dumpit_func dumpit; - rtnl_calcit_func calcit; u16 min_dump_alloc = 0; dumpit = rtnl_get_dumpit(family, type); if (dumpit == NULL) return -EOPNOTSUPP; - calcit = rtnl_get_calcit(family, type); - if (calcit) - min_dump_alloc = calcit(skb, nlh); + + if (type == RTM_GETLINK) + min_dump_alloc = rtnl_calcit(skb, nlh); __rtnl_unlock(); rtnl = net->rtnl; @@ -4300,7 +4279,7 @@ void __init rtnetlink_init(void) register_netdevice_notifier(&rtnetlink_dev_notifier); rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, - rtnl_dump_ifinfo, rtnl_calcit); + rtnl_dump_ifinfo, NULL); rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL); rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, NULL); rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, NULL); -- cgit v1.2.3 From b97bac64a589d0158cf866e8995e831030f68f4f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 9 Aug 2017 20:41:48 +0200 Subject: rtnetlink: make rtnl_register accept a flags parameter This change allows us to later indicate to rtnetlink core that certain doit functions should be called without acquiring rtnl_mutex. This change should have no effect, we simply replace the last (now unused) calcit argument with the new flag. Signed-off-by: Florian Westphal Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/rtnetlink.h | 5 ++--- net/bridge/br_mdb.c | 6 +++--- net/can/gw.c | 6 +++--- net/core/fib_rules.c | 6 +++--- net/core/neighbour.c | 10 +++++----- net/core/net_namespace.c | 4 ++-- net/core/rtnetlink.c | 36 ++++++++++++++++++------------------ net/dcb/dcbnl.c | 4 ++-- net/decnet/dn_dev.c | 6 +++--- net/decnet/dn_fib.c | 4 ++-- net/decnet/dn_route.c | 4 ++-- net/ipv4/devinet.c | 8 ++++---- net/ipv4/fib_frontend.c | 6 +++--- net/ipv4/ipmr.c | 8 ++++---- net/ipv4/route.c | 2 +- net/ipv6/addrconf.c | 14 +++++++------- net/ipv6/addrlabel.c | 6 +++--- net/ipv6/ip6_fib.c | 2 +- net/ipv6/ip6mr.c | 2 +- net/ipv6/route.c | 6 +++--- net/mpls/af_mpls.c | 8 ++++---- net/phonet/pn_netlink.c | 12 ++++++------ net/qrtr/qrtr.c | 2 +- net/sched/act_api.c | 6 +++--- net/sched/cls_api.c | 6 +++--- net/sched/sch_api.c | 12 ++++++------ 26 files changed, 95 insertions(+), 96 deletions(-) (limited to 'net') diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index abe6b733d473..ac32460a0adb 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -7,12 +7,11 @@ typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *, struct netlink_ext_ack *); typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *); -typedef u16 (*rtnl_calcit_func)(struct sk_buff *, struct nlmsghdr *); int __rtnl_register(int protocol, int msgtype, - rtnl_doit_func, rtnl_dumpit_func, rtnl_calcit_func); + rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); void rtnl_register(int protocol, int msgtype, - rtnl_doit_func, rtnl_dumpit_func, rtnl_calcit_func); + rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); int rtnl_unregister(int protocol, int msgtype); void rtnl_unregister_all(int protocol); diff --git a/net/bridge/br_mdb.c b/net/bridge/br_mdb.c index a0b11e7d67d9..ca01def49af0 100644 --- a/net/bridge/br_mdb.c +++ b/net/bridge/br_mdb.c @@ -713,9 +713,9 @@ static int br_mdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void br_mdb_init(void) { - rtnl_register(PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, NULL); - rtnl_register(PF_BRIDGE, RTM_NEWMDB, br_mdb_add, NULL, NULL); - rtnl_register(PF_BRIDGE, RTM_DELMDB, br_mdb_del, NULL, NULL); + rtnl_register(PF_BRIDGE, RTM_GETMDB, NULL, br_mdb_dump, 0); + rtnl_register(PF_BRIDGE, RTM_NEWMDB, br_mdb_add, NULL, 0); + rtnl_register(PF_BRIDGE, RTM_DELMDB, br_mdb_del, NULL, 0); } void br_mdb_uninit(void) diff --git a/net/can/gw.c b/net/can/gw.c index 29748d844c3f..73a02af4b5d7 100644 --- a/net/can/gw.c +++ b/net/can/gw.c @@ -1031,15 +1031,15 @@ static __init int cgw_module_init(void) notifier.notifier_call = cgw_notifier; register_netdevice_notifier(¬ifier); - if (__rtnl_register(PF_CAN, RTM_GETROUTE, NULL, cgw_dump_jobs, NULL)) { + if (__rtnl_register(PF_CAN, RTM_GETROUTE, NULL, cgw_dump_jobs, 0)) { unregister_netdevice_notifier(¬ifier); kmem_cache_destroy(cgw_cache); return -ENOBUFS; } /* Only the first call to __rtnl_register can fail */ - __rtnl_register(PF_CAN, RTM_NEWROUTE, cgw_create_job, NULL, NULL); - __rtnl_register(PF_CAN, RTM_DELROUTE, cgw_remove_job, NULL, NULL); + __rtnl_register(PF_CAN, RTM_NEWROUTE, cgw_create_job, NULL, 0); + __rtnl_register(PF_CAN, RTM_DELROUTE, cgw_remove_job, NULL, 0); return 0; } diff --git a/net/core/fib_rules.c b/net/core/fib_rules.c index fc0b65093417..9a6d97c1d810 100644 --- a/net/core/fib_rules.c +++ b/net/core/fib_rules.c @@ -1026,9 +1026,9 @@ static struct pernet_operations fib_rules_net_ops = { static int __init fib_rules_init(void) { int err; - rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWRULE, fib_nl_newrule, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_DELRULE, fib_nl_delrule, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_GETRULE, NULL, fib_nl_dumprule, 0); err = register_pernet_subsys(&fib_rules_net_ops); if (err < 0) diff --git a/net/core/neighbour.c b/net/core/neighbour.c index d0713627deb6..16a1a4c4eb57 100644 --- a/net/core/neighbour.c +++ b/net/core/neighbour.c @@ -3261,13 +3261,13 @@ EXPORT_SYMBOL(neigh_sysctl_unregister); static int __init neigh_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWNEIGH, neigh_add, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_DELNEIGH, neigh_delete, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_GETNEIGH, NULL, neigh_dump_info, 0); rtnl_register(PF_UNSPEC, RTM_GETNEIGHTBL, NULL, neightbl_dump_info, - NULL); - rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, NULL); + 0); + rtnl_register(PF_UNSPEC, RTM_SETNEIGHTBL, neightbl_set, NULL, 0); return 0; } diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index 8726d051f31d..a7f06d706aa0 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -855,9 +855,9 @@ static int __init net_ns_init(void) register_pernet_subsys(&net_ns_ops); - rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL, 0); rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, rtnl_net_dumpid, - NULL); + 0); return 0; } diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 8c9d34deea7d..67607c540c03 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -178,7 +178,7 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) * @msgtype: rtnetlink message type * @doit: Function pointer called for each request message * @dumpit: Function pointer called for each dump request (NLM_F_DUMP) message - * @calcit: Function pointer to calc size of dump message + * @flags: rtnl_link_flags to modifiy behaviour of doit/dumpit functions * * Registers the specified function pointers (at least one of them has * to be non-NULL) to be called whenever a request message for the @@ -192,7 +192,7 @@ static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) */ int __rtnl_register(int protocol, int msgtype, rtnl_doit_func doit, rtnl_dumpit_func dumpit, - rtnl_calcit_func calcit) + unsigned int flags) { struct rtnl_link *tab; int msgindex; @@ -230,9 +230,9 @@ EXPORT_SYMBOL_GPL(__rtnl_register); */ void rtnl_register(int protocol, int msgtype, rtnl_doit_func doit, rtnl_dumpit_func dumpit, - rtnl_calcit_func calcit) + unsigned int flags) { - if (__rtnl_register(protocol, msgtype, doit, dumpit, calcit) < 0) + if (__rtnl_register(protocol, msgtype, doit, dumpit, flags) < 0) panic("Unable to register rtnetlink message handler, " "protocol = %d, message type = %d\n", protocol, msgtype); @@ -4279,23 +4279,23 @@ void __init rtnetlink_init(void) register_netdevice_notifier(&rtnetlink_dev_notifier); rtnl_register(PF_UNSPEC, RTM_GETLINK, rtnl_getlink, - rtnl_dump_ifinfo, NULL); - rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, NULL); + rtnl_dump_ifinfo, 0); + rtnl_register(PF_UNSPEC, RTM_SETLINK, rtnl_setlink, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_NEWLINK, rtnl_newlink, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_DELLINK, rtnl_dellink, NULL, 0); - rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, NULL); - rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, NULL); - rtnl_register(PF_UNSPEC, RTM_GETNETCONF, NULL, rtnl_dump_all, NULL); + rtnl_register(PF_UNSPEC, RTM_GETADDR, NULL, rtnl_dump_all, 0); + rtnl_register(PF_UNSPEC, RTM_GETROUTE, NULL, rtnl_dump_all, 0); + rtnl_register(PF_UNSPEC, RTM_GETNETCONF, NULL, rtnl_dump_all, 0); - rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, NULL); - rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, NULL); - rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, NULL); + rtnl_register(PF_BRIDGE, RTM_NEWNEIGH, rtnl_fdb_add, NULL, 0); + rtnl_register(PF_BRIDGE, RTM_DELNEIGH, rtnl_fdb_del, NULL, 0); + rtnl_register(PF_BRIDGE, RTM_GETNEIGH, NULL, rtnl_fdb_dump, 0); - rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, NULL); - rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, NULL); - rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, NULL); + rtnl_register(PF_BRIDGE, RTM_GETLINK, NULL, rtnl_bridge_getlink, 0); + rtnl_register(PF_BRIDGE, RTM_DELLINK, rtnl_bridge_dellink, NULL, 0); + rtnl_register(PF_BRIDGE, RTM_SETLINK, rtnl_bridge_setlink, NULL, 0); rtnl_register(PF_UNSPEC, RTM_GETSTATS, rtnl_stats_get, rtnl_stats_dump, - NULL); + 0); } diff --git a/net/dcb/dcbnl.c b/net/dcb/dcbnl.c index 733f523707ac..bae7d78aa068 100644 --- a/net/dcb/dcbnl.c +++ b/net/dcb/dcbnl.c @@ -1938,8 +1938,8 @@ static int __init dcbnl_init(void) { INIT_LIST_HEAD(&dcb_app_list); - rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_GETDCB, dcb_doit, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_SETDCB, dcb_doit, NULL, 0); return 0; } diff --git a/net/decnet/dn_dev.c b/net/decnet/dn_dev.c index fa0110b57ca1..4d339de56862 100644 --- a/net/decnet/dn_dev.c +++ b/net/decnet/dn_dev.c @@ -1419,9 +1419,9 @@ void __init dn_dev_init(void) dn_dev_devices_on(); - rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL, NULL); - rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL, NULL); - rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr, NULL); + rtnl_register(PF_DECnet, RTM_NEWADDR, dn_nl_newaddr, NULL, 0); + rtnl_register(PF_DECnet, RTM_DELADDR, dn_nl_deladdr, NULL, 0); + rtnl_register(PF_DECnet, RTM_GETADDR, NULL, dn_nl_dump_ifaddr, 0); proc_create("decnet_dev", S_IRUGO, init_net.proc_net, &dn_dev_seq_fops); diff --git a/net/decnet/dn_fib.c b/net/decnet/dn_fib.c index f9f6fb3f3c5b..3d37464c8b4a 100644 --- a/net/decnet/dn_fib.c +++ b/net/decnet/dn_fib.c @@ -791,8 +791,8 @@ void __init dn_fib_init(void) register_dnaddr_notifier(&dn_fib_dnaddr_notifier); - rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL, NULL); - rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL, NULL); + rtnl_register(PF_DECnet, RTM_NEWROUTE, dn_fib_rtm_newroute, NULL, 0); + rtnl_register(PF_DECnet, RTM_DELROUTE, dn_fib_rtm_delroute, NULL, 0); } diff --git a/net/decnet/dn_route.c b/net/decnet/dn_route.c index bcbe548f8854..0bd3afd01dd2 100644 --- a/net/decnet/dn_route.c +++ b/net/decnet/dn_route.c @@ -1922,10 +1922,10 @@ void __init dn_route_init(void) #ifdef CONFIG_DECNET_ROUTER rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, - dn_fib_dump, NULL); + dn_fib_dump, 0); #else rtnl_register(PF_DECnet, RTM_GETROUTE, dn_cache_getroute, - dn_cache_dump, NULL); + dn_cache_dump, 0); #endif } diff --git a/net/ipv4/devinet.c b/net/ipv4/devinet.c index 38d9af9b917c..d7adc0616599 100644 --- a/net/ipv4/devinet.c +++ b/net/ipv4/devinet.c @@ -2491,9 +2491,9 @@ void __init devinet_init(void) rtnl_af_register(&inet_af_ops); - rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, NULL); - rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, NULL); - rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, NULL); + rtnl_register(PF_INET, RTM_NEWADDR, inet_rtm_newaddr, NULL, 0); + rtnl_register(PF_INET, RTM_DELADDR, inet_rtm_deladdr, NULL, 0); + rtnl_register(PF_INET, RTM_GETADDR, NULL, inet_dump_ifaddr, 0); rtnl_register(PF_INET, RTM_GETNETCONF, inet_netconf_get_devconf, - inet_netconf_dump_devconf, NULL); + inet_netconf_dump_devconf, 0); } diff --git a/net/ipv4/fib_frontend.c b/net/ipv4/fib_frontend.c index 2cba559f14df..37819ab4cc74 100644 --- a/net/ipv4/fib_frontend.c +++ b/net/ipv4/fib_frontend.c @@ -1348,7 +1348,7 @@ void __init ip_fib_init(void) register_netdevice_notifier(&fib_netdev_notifier); register_inetaddr_notifier(&fib_inetaddr_notifier); - rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, NULL); - rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, NULL); - rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, NULL); + rtnl_register(PF_INET, RTM_NEWROUTE, inet_rtm_newroute, NULL, 0); + rtnl_register(PF_INET, RTM_DELROUTE, inet_rtm_delroute, NULL, 0); + rtnl_register(PF_INET, RTM_GETROUTE, NULL, inet_dump_fib, 0); } diff --git a/net/ipv4/ipmr.c b/net/ipv4/ipmr.c index 06863ea3fc5b..c9b3e6e069ae 100644 --- a/net/ipv4/ipmr.c +++ b/net/ipv4/ipmr.c @@ -3114,14 +3114,14 @@ int __init ip_mr_init(void) } #endif rtnl_register(RTNL_FAMILY_IPMR, RTM_GETROUTE, - ipmr_rtm_getroute, ipmr_rtm_dumproute, NULL); + ipmr_rtm_getroute, ipmr_rtm_dumproute, 0); rtnl_register(RTNL_FAMILY_IPMR, RTM_NEWROUTE, - ipmr_rtm_route, NULL, NULL); + ipmr_rtm_route, NULL, 0); rtnl_register(RTNL_FAMILY_IPMR, RTM_DELROUTE, - ipmr_rtm_route, NULL, NULL); + ipmr_rtm_route, NULL, 0); rtnl_register(RTNL_FAMILY_IPMR, RTM_GETLINK, - NULL, ipmr_rtm_dumplink, NULL); + NULL, ipmr_rtm_dumplink, 0); return 0; #ifdef CONFIG_IP_PIMSM_V2 diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 0383e66f59bc..2ef46294475f 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -3067,7 +3067,7 @@ int __init ip_rt_init(void) xfrm_init(); xfrm4_init(); #endif - rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, NULL); + rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, 0); #ifdef CONFIG_SYSCTL register_pernet_subsys(&sysctl_route_ops); diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 30ee23eef268..640792e1ecb7 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -6605,21 +6605,21 @@ int __init addrconf_init(void) rtnl_af_register(&inet6_ops); err = __rtnl_register(PF_INET6, RTM_GETLINK, NULL, inet6_dump_ifinfo, - NULL); + 0); if (err < 0) goto errout; /* Only the first call to __rtnl_register can fail */ - __rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL, NULL); - __rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL, NULL); + __rtnl_register(PF_INET6, RTM_NEWADDR, inet6_rtm_newaddr, NULL, 0); + __rtnl_register(PF_INET6, RTM_DELADDR, inet6_rtm_deladdr, NULL, 0); __rtnl_register(PF_INET6, RTM_GETADDR, inet6_rtm_getaddr, - inet6_dump_ifaddr, NULL); + inet6_dump_ifaddr, 0); __rtnl_register(PF_INET6, RTM_GETMULTICAST, NULL, - inet6_dump_ifmcaddr, NULL); + inet6_dump_ifmcaddr, 0); __rtnl_register(PF_INET6, RTM_GETANYCAST, NULL, - inet6_dump_ifacaddr, NULL); + inet6_dump_ifacaddr, 0); __rtnl_register(PF_INET6, RTM_GETNETCONF, inet6_netconf_get_devconf, - inet6_netconf_dump_devconf, NULL); + inet6_netconf_dump_devconf, 0); ipv6_addr_label_rtnl_register(); diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index 7a428f65c7ec..cea5eb488013 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -593,10 +593,10 @@ out: void __init ipv6_addr_label_rtnl_register(void) { __rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, - NULL, NULL); + NULL, 0); __rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, - NULL, NULL); + NULL, 0); __rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, - ip6addrlbl_dump, NULL); + ip6addrlbl_dump, 0); } diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 69ed0043d117..8c58c7558de0 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -2038,7 +2038,7 @@ int __init fib6_init(void) goto out_kmem_cache_create; ret = __rtnl_register(PF_INET6, RTM_GETROUTE, NULL, inet6_dump_fib, - NULL); + 0); if (ret) goto out_unregister_subsys; diff --git a/net/ipv6/ip6mr.c b/net/ipv6/ip6mr.c index 7454850f2098..f5500f5444e9 100644 --- a/net/ipv6/ip6mr.c +++ b/net/ipv6/ip6mr.c @@ -1427,7 +1427,7 @@ int __init ip6_mr_init(void) } #endif rtnl_register(RTNL_FAMILY_IP6MR, RTM_GETROUTE, NULL, - ip6mr_rtm_dumproute, NULL); + ip6mr_rtm_dumproute, 0); return 0; #ifdef CONFIG_IPV6_PIMSM_V2 add_proto_fail: diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c73e61750642..035762fed07d 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -4105,9 +4105,9 @@ int __init ip6_route_init(void) goto fib6_rules_init; ret = -ENOBUFS; - if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, NULL) || - __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, NULL) || - __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, NULL)) + if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, 0) || + __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, 0) || + __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, 0)) goto out_register_late_subsys; ret = register_netdevice_notifier(&ip6_route_dev_notifier); diff --git a/net/mpls/af_mpls.c b/net/mpls/af_mpls.c index ea4f481839dd..c5b9ce41d66f 100644 --- a/net/mpls/af_mpls.c +++ b/net/mpls/af_mpls.c @@ -2479,12 +2479,12 @@ static int __init mpls_init(void) rtnl_af_register(&mpls_af_ops); - rtnl_register(PF_MPLS, RTM_NEWROUTE, mpls_rtm_newroute, NULL, NULL); - rtnl_register(PF_MPLS, RTM_DELROUTE, mpls_rtm_delroute, NULL, NULL); + rtnl_register(PF_MPLS, RTM_NEWROUTE, mpls_rtm_newroute, NULL, 0); + rtnl_register(PF_MPLS, RTM_DELROUTE, mpls_rtm_delroute, NULL, 0); rtnl_register(PF_MPLS, RTM_GETROUTE, mpls_getroute, mpls_dump_routes, - NULL); + 0); rtnl_register(PF_MPLS, RTM_GETNETCONF, mpls_netconf_get_devconf, - mpls_netconf_dump_devconf, NULL); + mpls_netconf_dump_devconf, 0); err = 0; out: return err; diff --git a/net/phonet/pn_netlink.c b/net/phonet/pn_netlink.c index 45b3af3080d8..da754fc926e7 100644 --- a/net/phonet/pn_netlink.c +++ b/net/phonet/pn_netlink.c @@ -300,15 +300,15 @@ out: int __init phonet_netlink_register(void) { int err = __rtnl_register(PF_PHONET, RTM_NEWADDR, addr_doit, - NULL, NULL); + NULL, 0); if (err) return err; /* Further __rtnl_register() cannot fail */ - __rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL, NULL); - __rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit, NULL); - __rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL, NULL); - __rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL, NULL); - __rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit, NULL); + __rtnl_register(PF_PHONET, RTM_DELADDR, addr_doit, NULL, 0); + __rtnl_register(PF_PHONET, RTM_GETADDR, NULL, getaddr_dumpit, 0); + __rtnl_register(PF_PHONET, RTM_NEWROUTE, route_doit, NULL, 0); + __rtnl_register(PF_PHONET, RTM_DELROUTE, route_doit, NULL, 0); + __rtnl_register(PF_PHONET, RTM_GETROUTE, NULL, route_dumpit, 0); return 0; } diff --git a/net/qrtr/qrtr.c b/net/qrtr/qrtr.c index 5586609afa27..c2f5c13550c0 100644 --- a/net/qrtr/qrtr.c +++ b/net/qrtr/qrtr.c @@ -1081,7 +1081,7 @@ static int __init qrtr_proto_init(void) return rc; } - rtnl_register(PF_QIPCRTR, RTM_NEWADDR, qrtr_addr_doit, NULL, NULL); + rtnl_register(PF_QIPCRTR, RTM_NEWADDR, qrtr_addr_doit, NULL, 0); return 0; } diff --git a/net/sched/act_api.c b/net/sched/act_api.c index a2915d958279..02fcb0c78a28 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -1255,10 +1255,10 @@ out_module_put: static int __init tc_action_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWACTION, tc_ctl_action, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_DELACTION, tc_ctl_action, NULL, 0); rtnl_register(PF_UNSPEC, RTM_GETACTION, tc_ctl_action, tc_dump_action, - NULL); + 0); return 0; } diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index 8d1157aebaf7..ebeeb87e6d44 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -1010,10 +1010,10 @@ EXPORT_SYMBOL(tcf_exts_get_dev); static int __init tc_filter_init(void) { - rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWTFILTER, tc_ctl_tfilter, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_DELTFILTER, tc_ctl_tfilter, NULL, 0); rtnl_register(PF_UNSPEC, RTM_GETTFILTER, tc_ctl_tfilter, - tc_dump_tfilter, NULL); + tc_dump_tfilter, 0); return 0; } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index bd24a550e0f9..816c8092e601 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1952,14 +1952,14 @@ static int __init pktsched_init(void) register_qdisc(&mq_qdisc_ops); register_qdisc(&noqueue_qdisc_ops); - rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL, NULL); + rtnl_register(PF_UNSPEC, RTM_NEWQDISC, tc_modify_qdisc, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_DELQDISC, tc_get_qdisc, NULL, 0); rtnl_register(PF_UNSPEC, RTM_GETQDISC, tc_get_qdisc, tc_dump_qdisc, - NULL); - rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL, NULL); - rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL, NULL); + 0); + rtnl_register(PF_UNSPEC, RTM_NEWTCLASS, tc_ctl_tclass, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_DELTCLASS, tc_ctl_tclass, NULL, 0); rtnl_register(PF_UNSPEC, RTM_GETTCLASS, tc_ctl_tclass, tc_dump_tclass, - NULL); + 0); return 0; } -- cgit v1.2.3 From 019a316992ee0d9832b1c480c899d6bdf2a0a77e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 9 Aug 2017 20:41:49 +0200 Subject: rtnetlink: add reference counting to prevent module unload while dump is in progress I don't see what prevents rmmod (unregister_all is called) while a dump is active. Even if we'd add rtnl lock/unlock pair to unregister_all (as done here), thats not enough either as rtnl_lock is released right before the dump process starts. So this adds a refcount: * acquire rtnl mutex * bump refcount * release mutex * start the dump ... and make unregister_all remove the callbacks (no new dumps possible) and then wait until refcount is 0. Signed-off-by: Florian Westphal Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 67607c540c03..c45a7c5e3232 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -127,6 +127,7 @@ EXPORT_SYMBOL(lockdep_rtnl_is_held); #endif /* #ifdef CONFIG_PROVE_LOCKING */ static struct rtnl_link *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1]; +static refcount_t rtnl_msg_handlers_ref[RTNL_FAMILY_MAX + 1]; static inline int rtm_msgindex(int msgtype) { @@ -272,10 +273,18 @@ EXPORT_SYMBOL_GPL(rtnl_unregister); */ void rtnl_unregister_all(int protocol) { + struct rtnl_link *handlers; + BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); - kfree(rtnl_msg_handlers[protocol]); + rtnl_lock(); + handlers = rtnl_msg_handlers[protocol]; rtnl_msg_handlers[protocol] = NULL; + rtnl_unlock(); + + while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 0) + schedule(); + kfree(handlers); } EXPORT_SYMBOL_GPL(rtnl_unregister_all); @@ -4173,6 +4182,8 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (dumpit == NULL) return -EOPNOTSUPP; + refcount_inc(&rtnl_msg_handlers_ref[family]); + if (type == RTM_GETLINK) min_dump_alloc = rtnl_calcit(skb, nlh); @@ -4186,6 +4197,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, err = netlink_dump_start(rtnl, skb, nlh, &c); } rtnl_lock(); + refcount_dec(&rtnl_msg_handlers_ref[family]); return err; } -- cgit v1.2.3 From 0cc09020aefe832db23d63cd3a6c889ab7645eec Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 9 Aug 2017 20:41:50 +0200 Subject: rtnetlink: small rtnl lock pushdown instead of rtnl lock/unload at the top level, push it down to the called function. This is just an intermediate step, next commit switches protection of the rtnl_link ops table to rcu, in which case (for dumps) the rtnl lock is acquired only by the netlink dumper infrastructure (current lock/unlock/dump/lock/unlock rtnl sequence becomes rcu lock/rcu unlock/dump). Signed-off-by: Florian Westphal Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index c45a7c5e3232..be01d8e48661 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4178,9 +4178,11 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, rtnl_dumpit_func dumpit; u16 min_dump_alloc = 0; + rtnl_lock(); + dumpit = rtnl_get_dumpit(family, type); if (dumpit == NULL) - return -EOPNOTSUPP; + goto err_unlock; refcount_inc(&rtnl_msg_handlers_ref[family]); @@ -4196,23 +4198,28 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, }; err = netlink_dump_start(rtnl, skb, nlh, &c); } - rtnl_lock(); refcount_dec(&rtnl_msg_handlers_ref[family]); return err; } + rtnl_lock(); doit = rtnl_get_doit(family, type); if (doit == NULL) - return -EOPNOTSUPP; + goto err_unlock; - return doit(skb, nlh, extack); + err = doit(skb, nlh, extack); + rtnl_unlock(); + + return err; + +err_unlock: + rtnl_unlock(); + return -EOPNOTSUPP; } static void rtnetlink_rcv(struct sk_buff *skb) { - rtnl_lock(); netlink_rcv_skb(skb, &rtnetlink_rcv_msg); - rtnl_unlock(); } static int rtnetlink_bind(struct net *net, int group) -- cgit v1.2.3 From 6853dd488119a42d01ad994060dc10b95c687c76 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 9 Aug 2017 20:41:51 +0200 Subject: rtnetlink: protect handler table with rcu Note that netlink dumps still acquire rtnl mutex via the netlink dump infrastructure. Signed-off-by: Florian Westphal Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 121 +++++++++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 56 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index be01d8e48661..d45946177bc8 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -126,7 +126,7 @@ bool lockdep_rtnl_is_held(void) EXPORT_SYMBOL(lockdep_rtnl_is_held); #endif /* #ifdef CONFIG_PROVE_LOCKING */ -static struct rtnl_link *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1]; +static struct rtnl_link __rcu *rtnl_msg_handlers[RTNL_FAMILY_MAX + 1]; static refcount_t rtnl_msg_handlers_ref[RTNL_FAMILY_MAX + 1]; static inline int rtm_msgindex(int msgtype) @@ -143,36 +143,6 @@ static inline int rtm_msgindex(int msgtype) return msgindex; } -static rtnl_doit_func rtnl_get_doit(int protocol, int msgindex) -{ - struct rtnl_link *tab; - - if (protocol <= RTNL_FAMILY_MAX) - tab = rtnl_msg_handlers[protocol]; - else - tab = NULL; - - if (tab == NULL || tab[msgindex].doit == NULL) - tab = rtnl_msg_handlers[PF_UNSPEC]; - - return tab[msgindex].doit; -} - -static rtnl_dumpit_func rtnl_get_dumpit(int protocol, int msgindex) -{ - struct rtnl_link *tab; - - if (protocol <= RTNL_FAMILY_MAX) - tab = rtnl_msg_handlers[protocol]; - else - tab = NULL; - - if (tab == NULL || tab[msgindex].dumpit == NULL) - tab = rtnl_msg_handlers[PF_UNSPEC]; - - return tab[msgindex].dumpit; -} - /** * __rtnl_register - Register a rtnetlink message type * @protocol: Protocol family or PF_UNSPEC @@ -201,18 +171,17 @@ int __rtnl_register(int protocol, int msgtype, BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); msgindex = rtm_msgindex(msgtype); - tab = rtnl_msg_handlers[protocol]; + tab = rcu_dereference(rtnl_msg_handlers[protocol]); if (tab == NULL) { tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); if (tab == NULL) return -ENOBUFS; - rtnl_msg_handlers[protocol] = tab; + rcu_assign_pointer(rtnl_msg_handlers[protocol], tab); } if (doit) tab[msgindex].doit = doit; - if (dumpit) tab[msgindex].dumpit = dumpit; @@ -249,16 +218,22 @@ EXPORT_SYMBOL_GPL(rtnl_register); */ int rtnl_unregister(int protocol, int msgtype) { + struct rtnl_link *handlers; int msgindex; BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); msgindex = rtm_msgindex(msgtype); - if (rtnl_msg_handlers[protocol] == NULL) + rtnl_lock(); + handlers = rtnl_dereference(rtnl_msg_handlers[protocol]); + if (!handlers) { + rtnl_unlock(); return -ENOENT; + } - rtnl_msg_handlers[protocol][msgindex].doit = NULL; - rtnl_msg_handlers[protocol][msgindex].dumpit = NULL; + handlers[msgindex].doit = NULL; + handlers[msgindex].dumpit = NULL; + rtnl_unlock(); return 0; } @@ -278,10 +253,12 @@ void rtnl_unregister_all(int protocol) BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); rtnl_lock(); - handlers = rtnl_msg_handlers[protocol]; - rtnl_msg_handlers[protocol] = NULL; + handlers = rtnl_dereference(rtnl_msg_handlers[protocol]); + RCU_INIT_POINTER(rtnl_msg_handlers[protocol], NULL); rtnl_unlock(); + synchronize_net(); + while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 0) schedule(); kfree(handlers); @@ -2820,11 +2797,13 @@ static u16 rtnl_calcit(struct sk_buff *skb, struct nlmsghdr *nlh) * traverse the list of net devices and compute the minimum * buffer size based upon the filter mask. */ - list_for_each_entry(dev, &net->dev_base_head, dev_list) { + rcu_read_lock(); + for_each_netdev_rcu(net, dev) { min_ifinfo_dump_size = max_t(u16, min_ifinfo_dump_size, if_nlmsg_size(dev, ext_filter_mask)); } + rcu_read_unlock(); return nlmsg_total_size(min_ifinfo_dump_size); } @@ -2836,19 +2815,29 @@ static int rtnl_dump_all(struct sk_buff *skb, struct netlink_callback *cb) if (s_idx == 0) s_idx = 1; + for (idx = 1; idx <= RTNL_FAMILY_MAX; idx++) { int type = cb->nlh->nlmsg_type-RTM_BASE; + struct rtnl_link *handlers; + rtnl_dumpit_func dumpit; + if (idx < s_idx || idx == PF_PACKET) continue; - if (rtnl_msg_handlers[idx] == NULL || - rtnl_msg_handlers[idx][type].dumpit == NULL) + + handlers = rtnl_dereference(rtnl_msg_handlers[idx]); + if (!handlers) continue; + + dumpit = READ_ONCE(handlers[type].dumpit); + if (!dumpit) + continue; + if (idx > s_idx) { memset(&cb->args[0], 0, sizeof(cb->args)); cb->prev_seq = 0; cb->seq = 0; } - if (rtnl_msg_handlers[idx][type].dumpit(skb, cb)) + if (dumpit(skb, cb)) break; } cb->family = idx; @@ -4151,11 +4140,12 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { struct net *net = sock_net(skb->sk); + struct rtnl_link *handlers; + int err = -EOPNOTSUPP; rtnl_doit_func doit; int kind; int family; int type; - int err; type = nlh->nlmsg_type; if (type > RTM_MAX) @@ -4173,23 +4163,40 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN)) return -EPERM; + if (family > ARRAY_SIZE(rtnl_msg_handlers)) + family = PF_UNSPEC; + + rcu_read_lock(); + handlers = rcu_dereference(rtnl_msg_handlers[family]); + if (!handlers) { + family = PF_UNSPEC; + handlers = rcu_dereference(rtnl_msg_handlers[family]); + } + if (kind == 2 && nlh->nlmsg_flags&NLM_F_DUMP) { struct sock *rtnl; rtnl_dumpit_func dumpit; u16 min_dump_alloc = 0; - rtnl_lock(); + dumpit = READ_ONCE(handlers[type].dumpit); + if (!dumpit) { + family = PF_UNSPEC; + handlers = rcu_dereference(rtnl_msg_handlers[PF_UNSPEC]); + if (!handlers) + goto err_unlock; - dumpit = rtnl_get_dumpit(family, type); - if (dumpit == NULL) - goto err_unlock; + dumpit = READ_ONCE(handlers[type].dumpit); + if (!dumpit) + goto err_unlock; + } refcount_inc(&rtnl_msg_handlers_ref[family]); if (type == RTM_GETLINK) min_dump_alloc = rtnl_calcit(skb, nlh); - __rtnl_unlock(); + rcu_read_unlock(); + rtnl = net->rtnl; { struct netlink_dump_control c = { @@ -4202,18 +4209,20 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, return err; } - rtnl_lock(); - doit = rtnl_get_doit(family, type); - if (doit == NULL) - goto err_unlock; + rcu_read_unlock(); - err = doit(skb, nlh, extack); + rtnl_lock(); + handlers = rtnl_dereference(rtnl_msg_handlers[family]); + if (handlers) { + doit = READ_ONCE(handlers[type].doit); + if (doit) + err = doit(skb, nlh, extack); + } rtnl_unlock(); - return err; err_unlock: - rtnl_unlock(); + rcu_read_unlock(); return -EOPNOTSUPP; } -- cgit v1.2.3 From 62256f98f244fbb1c7a10465e1ee412f209d8978 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 9 Aug 2017 20:41:52 +0200 Subject: rtnetlink: add RTNL_FLAG_DOIT_UNLOCKED Allow callers to tell rtnetlink core that its doit callback should be invoked without holding rtnl mutex. Signed-off-by: Florian Westphal Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- include/net/rtnetlink.h | 4 ++++ net/core/rtnetlink.c | 15 +++++++++++++++ 2 files changed, 19 insertions(+) (limited to 'net') diff --git a/include/net/rtnetlink.h b/include/net/rtnetlink.h index ac32460a0adb..21837ca68ecc 100644 --- a/include/net/rtnetlink.h +++ b/include/net/rtnetlink.h @@ -8,6 +8,10 @@ typedef int (*rtnl_doit_func)(struct sk_buff *, struct nlmsghdr *, struct netlink_ext_ack *); typedef int (*rtnl_dumpit_func)(struct sk_buff *, struct netlink_callback *); +enum rtnl_link_flags { + RTNL_FLAG_DOIT_UNLOCKED = 1, +}; + int __rtnl_register(int protocol, int msgtype, rtnl_doit_func, rtnl_dumpit_func, unsigned int flags); void rtnl_register(int protocol, int msgtype, diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index d45946177bc8..dd4e50dfa248 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -62,6 +62,7 @@ struct rtnl_link { rtnl_doit_func doit; rtnl_dumpit_func dumpit; + unsigned int flags; }; static DEFINE_MUTEX(rtnl_mutex); @@ -184,6 +185,7 @@ int __rtnl_register(int protocol, int msgtype, tab[msgindex].doit = doit; if (dumpit) tab[msgindex].dumpit = dumpit; + tab[msgindex].flags |= flags; return 0; } @@ -233,6 +235,7 @@ int rtnl_unregister(int protocol, int msgtype) handlers[msgindex].doit = NULL; handlers[msgindex].dumpit = NULL; + handlers[msgindex].flags = 0; rtnl_unlock(); return 0; @@ -4143,6 +4146,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, struct rtnl_link *handlers; int err = -EOPNOTSUPP; rtnl_doit_func doit; + unsigned int flags; int kind; int family; int type; @@ -4209,6 +4213,17 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, return err; } + flags = READ_ONCE(handlers[type].flags); + if (flags & RTNL_FLAG_DOIT_UNLOCKED) { + refcount_inc(&rtnl_msg_handlers_ref[family]); + doit = READ_ONCE(handlers[type].doit); + rcu_read_unlock(); + if (doit) + err = doit(skb, nlh, extack); + refcount_dec(&rtnl_msg_handlers_ref[family]); + return err; + } + rcu_read_unlock(); rtnl_lock(); -- cgit v1.2.3 From 165b9117256c5432bf241fdf4637487e4af68cc8 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 9 Aug 2017 20:41:53 +0200 Subject: net: call newid/getid without rtnl mutex held Both functions take nsid_lock and don't rely on rtnl lock. Signed-off-by: Florian Westphal Reviewed-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/core/net_namespace.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/net_namespace.c b/net/core/net_namespace.c index a7f06d706aa0..6cfdc7c84c48 100644 --- a/net/core/net_namespace.c +++ b/net/core/net_namespace.c @@ -855,9 +855,10 @@ static int __init net_ns_init(void) register_pernet_subsys(&net_ns_ops); - rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL, 0); + rtnl_register(PF_UNSPEC, RTM_NEWNSID, rtnl_net_newid, NULL, + RTNL_FLAG_DOIT_UNLOCKED); rtnl_register(PF_UNSPEC, RTM_GETNSID, rtnl_net_getid, rtnl_net_dumpid, - 0); + RTNL_FLAG_DOIT_UNLOCKED); return 0; } -- cgit v1.2.3 From 6eb7939371104e4ff78261c165c6f33ff6d90031 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Tue, 8 Aug 2017 15:51:02 -0600 Subject: net: ipv6: lower ndisc notifier priority below addrconf ndisc_notify is used to send unsolicited neighbor advertisements (e.g., on a link up). Currently, the ndisc notifier is run before the addrconf notifer which means NA's are not sent for link-local addresses which are added by the addrconf notifier. Fix by lowering the priority of the ndisc notifier. Setting the priority to ADDRCONF_NOTIFY_PRIORITY - 5 means it runs after addrconf and before the route notifier which is ADDRCONF_NOTIFY_PRIORITY - 10. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/ndisc.c | 1 + 1 file changed, 1 insertion(+) (limited to 'net') diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 0327c1f2e6fc..5e338eb89509 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -1779,6 +1779,7 @@ static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, static struct notifier_block ndisc_netdev_notifier = { .notifier_call = ndisc_netdev_event, + .priority = ADDRCONF_NOTIFY_PRIORITY - 5, }; #ifdef CONFIG_SYSCTL -- cgit v1.2.3 From 800bb47e71cac00e98c822919174bb2e1d2e8071 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Wed, 9 Aug 2017 15:02:08 +0530 Subject: net: atm: make atmdev_ops const Make these const as they are only stored in the ops field of a atm_dev structure, which is const. Done using Coccinelle. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- net/atm/clip.c | 2 +- net/atm/lec.c | 2 +- net/atm/mpc.c | 2 +- net/atm/signaling.c | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/atm/clip.c b/net/atm/clip.c index f271a7bcf5b2..65f706e4344c 100644 --- a/net/atm/clip.c +++ b/net/atm/clip.c @@ -617,7 +617,7 @@ static void atmarpd_close(struct atm_vcc *vcc) module_put(THIS_MODULE); } -static struct atmdev_ops atmarpd_dev_ops = { +static const struct atmdev_ops atmarpd_dev_ops = { .close = atmarpd_close }; diff --git a/net/atm/lec.c b/net/atm/lec.c index 093fe8707731..a3d93a1bb133 100644 --- a/net/atm/lec.c +++ b/net/atm/lec.c @@ -486,7 +486,7 @@ static void lec_atm_close(struct atm_vcc *vcc) module_put(THIS_MODULE); } -static struct atmdev_ops lecdev_ops = { +static const struct atmdev_ops lecdev_ops = { .close = lec_atm_close, .send = lec_atm_send }; diff --git a/net/atm/mpc.c b/net/atm/mpc.c index 680a4b9095a1..5677147209e8 100644 --- a/net/atm/mpc.c +++ b/net/atm/mpc.c @@ -779,7 +779,7 @@ static void mpc_push(struct atm_vcc *vcc, struct sk_buff *skb) netif_rx(new_skb); } -static struct atmdev_ops mpc_ops = { /* only send is required */ +static const struct atmdev_ops mpc_ops = { /* only send is required */ .close = mpoad_close, .send = msg_from_mpoad }; diff --git a/net/atm/signaling.c b/net/atm/signaling.c index 983c3a21a133..0a20f6e953ac 100644 --- a/net/atm/signaling.c +++ b/net/atm/signaling.c @@ -217,7 +217,7 @@ static void sigd_close(struct atm_vcc *vcc) read_unlock(&vcc_sklist_lock); } -static struct atmdev_ops sigd_dev_ops = { +static const struct atmdev_ops sigd_dev_ops = { .close = sigd_close, .send = sigd_send }; -- cgit v1.2.3 From 68277a2c9d32fd9090247a5c08aaf1353049c0b1 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 9 Aug 2017 14:41:16 +0200 Subject: net-next: dsa: move struct dsa_device_ops to the global header file We need to access this struct from within the flow_dissector to fix dissection for packets coming in on DSA devices. Signed-off-by: Muciri Gatimu Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin Signed-off-by: David S. Miller --- include/net/dsa.h | 7 +++++++ net/dsa/dsa_priv.h | 7 ------- 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/dsa.h b/include/net/dsa.h index a4f66dbb4b7c..65d7804c6f69 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -101,6 +101,13 @@ struct dsa_platform_data { struct packet_type; +struct dsa_device_ops { + struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); + struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, + struct packet_type *pt, + struct net_device *orig_dev); +}; + struct dsa_switch_tree { struct list_head list; diff --git a/net/dsa/dsa_priv.h b/net/dsa/dsa_priv.h index 1debf9c42fc4..9c3eeb72462d 100644 --- a/net/dsa/dsa_priv.h +++ b/net/dsa/dsa_priv.h @@ -65,13 +65,6 @@ struct dsa_notifier_vlan_info { int port; }; -struct dsa_device_ops { - struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); - struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev); -}; - struct dsa_slave_priv { /* Copy of dp->ds->dst->tag_ops->xmit for faster access in hot path */ struct sk_buff * (*xmit)(struct sk_buff *skb, -- cgit v1.2.3 From 2dd592b274861021018446102fb443e24f72fe72 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 9 Aug 2017 14:41:18 +0200 Subject: net-next: tag_mtk: add flow_dissect callback to the ops struct The MT7530 inserts the 4 magic header in between the 802.3 address and protocol field. The patch implements the callback that can be called by the flow dissector to figure out the real protocol and offset of the network header. With this patch applied we can properly parse the packet and thus make hashing function properly. Signed-off-by: Muciri Gatimu Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- net/dsa/tag_mtk.c | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index 2f32b7ea3365..02163c045a96 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -87,7 +87,17 @@ static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, return skb; } +static int mtk_tag_flow_dissect(const struct sk_buff *skb, __be16 *proto, + int *offset) +{ + *offset = 4; + *proto = ((__be16 *)skb->data)[1]; + + return 0; +} + const struct dsa_device_ops mtk_netdev_ops = { - .xmit = mtk_tag_xmit, - .rcv = mtk_tag_rcv, + .xmit = mtk_tag_xmit, + .rcv = mtk_tag_rcv, + .flow_dissect = mtk_tag_flow_dissect, }; -- cgit v1.2.3 From 43e665287f931a167cd2eea3387efda901bff0ce Mon Sep 17 00:00:00 2001 From: John Crispin Date: Wed, 9 Aug 2017 14:41:19 +0200 Subject: net-next: dsa: fix flow dissection RPS and probably other kernel features are currently broken on some if not all DSA devices. The root cause of this is that skb_hash will call the flow_dissector. At this point the skb still contains the magic switch header and the skb->protocol field is not set up to the correct 802.3 value yet. By the time the tag specific code is called, removing the header and properly setting the protocol an invalid hash is already set. In the case of the mt7530 this will result in all flows always having the same hash. Signed-off-by: Muciri Gatimu Signed-off-by: Shashidhar Lakkavalli Signed-off-by: John Crispin Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'net') diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 0cc672aba1f0..5b5be9577257 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -440,6 +441,17 @@ bool __skb_flow_dissect(const struct sk_buff *skb, skb->vlan_proto : skb->protocol; nhoff = skb_network_offset(skb); hlen = skb_headlen(skb); + if (unlikely(netdev_uses_dsa(skb->dev))) { + const struct dsa_device_ops *ops; + int offset; + + ops = skb->dev->dsa_ptr->tag_ops; + if (ops->flow_dissect && + !ops->flow_dissect(skb, &proto, &offset)) { + hlen -= offset; + nhoff += offset; + } + } } /* It is ensured by skb_flow_dissector_init() that control key will -- cgit v1.2.3 From 2d5716456404a1ba097d46770f82f23a2457a873 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 10 Aug 2017 10:09:03 +0200 Subject: net: core: fix compile error inside flow_dissector due to new dsa callback The following error was introduced by commit 43e665287f93 ("net-next: dsa: fix flow dissection") due to a missing #if guard net/core/flow_dissector.c: In function '__skb_flow_dissect': net/core/flow_dissector.c:448:18: error: 'struct net_device' has no member named 'dsa_ptr' ops = skb->dev->dsa_ptr->tag_ops; ^ make[3]: *** [net/core/flow_dissector.o] Error 1 Signed-off-by: John Crispin Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 5b5be9577257..79b9c06c83ad 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -441,6 +441,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb, skb->vlan_proto : skb->protocol; nhoff = skb_network_offset(skb); hlen = skb_headlen(skb); +#if IS_ENABLED(CONFIG_NET_DSA) if (unlikely(netdev_uses_dsa(skb->dev))) { const struct dsa_device_ops *ops; int offset; @@ -452,6 +453,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb, nhoff += offset; } } +#endif } /* It is ensured by skb_flow_dissector_init() that control key will -- cgit v1.2.3 From 377cb248840907adc407324b4d23f97b3ee70c98 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 10 Aug 2017 16:52:57 +0200 Subject: rtnetlink: use rcu_dereference_raw to silence rcu splat Ido reports a rcu splat in __rtnl_register. The splat is correct; as rtnl_register doesn't grab any logs and doesn't use rcu locks either. It has always been like this. handler families are not registered in parallel so there are no races wrt. the kmalloc ordering. The only reason to use rcu_dereference in the first place was to avoid sparse from complaining about this. Thus this switches to _raw() to not have rcu checks here. The alternative is to add rtnl locking to register/unregister, however, I don't see a compelling reason to do so as this has been lockless for the past twenty years or so. Fixes: 6853dd4881 ("rtnetlink: protect handler table with rcu") Reported-by: Ido Schimmel Signed-off-by: Florian Westphal Tested-by: Ido Schimmel Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index dd4e50dfa248..a5bc5bd0dc12 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -172,7 +172,7 @@ int __rtnl_register(int protocol, int msgtype, BUG_ON(protocol < 0 || protocol > RTNL_FAMILY_MAX); msgindex = rtm_msgindex(msgtype); - tab = rcu_dereference(rtnl_msg_handlers[protocol]); + tab = rcu_dereference_raw(rtnl_msg_handlers[protocol]); if (tab == NULL) { tab = kcalloc(RTM_NR_MSGTYPES, sizeof(*tab), GFP_KERNEL); if (tab == NULL) -- cgit v1.2.3 From 5c2bb9b6e27d9207c2e12b9cdb6bfc171afa663e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 10 Aug 2017 16:52:58 +0200 Subject: rtnetlink: do not use RTM_GETLINK directly Userspace sends RTM_GETLINK type, but the kernel substracts RTM_BASE from this, i.e. 'type' doesn't contain RTM_GETLINK anymore but instead RTM_GETLINK - RTM_BASE. This caused the calcit callback to not be invoked when it should have been (and vice versa). While at it, also fix a off-by one when checking family index. vs handler array size. Fixes: e1fa6d216dd ("rtnetlink: call rtnl_calcit directly") Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index a5bc5bd0dc12..a9b5ebc1af21 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4167,7 +4167,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, if (kind != 2 && !netlink_net_capable(skb, CAP_NET_ADMIN)) return -EPERM; - if (family > ARRAY_SIZE(rtnl_msg_handlers)) + if (family >= ARRAY_SIZE(rtnl_msg_handlers)) family = PF_UNSPEC; rcu_read_lock(); @@ -4196,7 +4196,7 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, refcount_inc(&rtnl_msg_handlers_ref[family]); - if (type == RTM_GETLINK) + if (type == RTM_GETLINK - RTM_BASE) min_dump_alloc = rtnl_calcit(skb, nlh); rcu_read_unlock(); -- cgit v1.2.3 From 8515ae38435895ba2862840d3e82140fc0a77554 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 10 Aug 2017 16:52:59 +0200 Subject: rtnetlink: switch rtnl_link_get_slave_info_data_size to rcu David Ahern reports following splat: RTNL: assertion failed at net/core/dev.c (5717) netdev_master_upper_dev_get+0x5f/0x70 if_nlmsg_size+0x158/0x240 rtnl_calcit.isra.26+0xa3/0xf0 rtnl_link_get_slave_info_data_size currently assumes RTNL protection, but there appears to be no hard requirement for this, so use rcu instead. At the time of this writing, there are three 'get_slave_size' callbacks (now invoked under rcu): bond_get_slave_size, vrf_get_slave_size and br_port_get_slave_size, all return constant only (i.e. they don't sleep). Fixes: 6853dd488119 ("rtnetlink: protect handler table with rcu") Reported-by: David Ahern Signed-off-by: Florian Westphal Acked-by: David Ahern Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index a9b5ebc1af21..087f2434813a 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -402,16 +402,24 @@ static size_t rtnl_link_get_slave_info_data_size(const struct net_device *dev) { struct net_device *master_dev; const struct rtnl_link_ops *ops; + size_t size = 0; - master_dev = netdev_master_upper_dev_get((struct net_device *) dev); + rcu_read_lock(); + + master_dev = netdev_master_upper_dev_get_rcu((struct net_device *)dev); if (!master_dev) - return 0; + goto out; + ops = master_dev->rtnl_link_ops; if (!ops || !ops->get_slave_size) - return 0; + goto out; /* IFLA_INFO_SLAVE_DATA + nested data */ - return nla_total_size(sizeof(struct nlattr)) + + size = nla_total_size(sizeof(struct nlattr)) + ops->get_slave_size(master_dev, dev); + +out: + rcu_read_unlock(); + return size; } static size_t rtnl_link_get_size(const struct net_device *dev) -- cgit v1.2.3 From d38a65125f93b2fbd17cd37ccc5ec0f0b799cc55 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 10 Aug 2017 16:53:00 +0200 Subject: rtnetlink: init handler refcounts to 1 If using CONFIG_REFCOUNT_FULL=y we get following splat: refcount_t: increment on 0; use-after-free. WARNING: CPU: 0 PID: 304 at lib/refcount.c:152 refcount_inc+0x47/0x50 Call Trace: rtnetlink_rcv_msg+0x191/0x260 ... This warning is harmless (0 is "no callback running", not "memory was freed"). Use '1' as the new 'no handler is running' base instead of 0 to avoid this. Fixes: 019a316992ee ("rtnetlink: add reference counting to prevent module unload while dump is in progress") Reported-by: Sabrina Dubroca Reported-by: kernel test robot Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 087f2434813a..59eda6952bc9 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -262,7 +262,7 @@ void rtnl_unregister_all(int protocol) synchronize_net(); - while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 0) + while (refcount_read(&rtnl_msg_handlers_ref[protocol]) > 1) schedule(); kfree(handlers); } @@ -4324,6 +4324,11 @@ static struct pernet_operations rtnetlink_net_ops = { void __init rtnetlink_init(void) { + int i; + + for (i = 0; i < ARRAY_SIZE(rtnl_msg_handlers_ref); i++) + refcount_set(&rtnl_msg_handlers_ref[i], 1); + if (register_pernet_subsys(&rtnetlink_net_ops)) panic("rtnetlink_init: cannot initialize rtnetlink\n"); -- cgit v1.2.3 From 8caa38b56c56ef48af48f8c70fcd7cb4580e0c24 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 10 Aug 2017 16:53:01 +0200 Subject: rtnetlink: fallback to UNSPEC if current family has no doit callback We need to use PF_UNSPEC in case the requested family has no doit callback, otherwise this now fails with EOPNOTSUPP instead of running the unspec doit callback, as before. Fixes: 6853dd488119 ("rtnetlink: protect handler table with rcu") Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 59eda6952bc9..9e9f1419be60 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -4221,6 +4221,12 @@ static int rtnetlink_rcv_msg(struct sk_buff *skb, struct nlmsghdr *nlh, return err; } + doit = READ_ONCE(handlers[type].doit); + if (!doit) { + family = PF_UNSPEC; + handlers = rcu_dereference(rtnl_msg_handlers[family]); + } + flags = READ_ONCE(handlers[type].flags); if (flags & RTNL_FLAG_DOIT_UNLOCKED) { refcount_inc(&rtnl_msg_handlers_ref[family]); -- cgit v1.2.3 From 077fbac405bfc6d41419ad6c1725804ad4e9887c Mon Sep 17 00:00:00 2001 From: Lorenzo Colitti Date: Fri, 11 Aug 2017 02:11:33 +0900 Subject: net: xfrm: support setting an output mark. On systems that use mark-based routing it may be necessary for routing lookups to use marks in order for packets to be routed correctly. An example of such a system is Android, which uses socket marks to route packets via different networks. Currently, routing lookups in tunnel mode always use a mark of zero, making routing incorrect on such systems. This patch adds a new output_mark element to the xfrm state and a corresponding XFRMA_OUTPUT_MARK netlink attribute. The output mark differs from the existing xfrm mark in two ways: 1. The xfrm mark is used to match xfrm policies and states, while the xfrm output mark is used to set the mark (and influence the routing) of the packets emitted by those states. 2. The existing mark is constrained to be a subset of the bits of the originating socket or transformed packet, but the output mark is arbitrary and depends only on the state. The use of a separate mark provides additional flexibility. For example: - A packet subject to two transforms (e.g., transport mode inside tunnel mode) can have two different output marks applied to it, one for the transport mode SA and one for the tunnel mode SA. - On a system where socket marks determine routing, the packets emitted by an IPsec tunnel can be routed based on a mark that is determined by the tunnel, not by the marks of the unencrypted packets. - Support for setting the output marks can be introduced without breaking any existing setups that employ both mark-based routing and xfrm tunnel mode. Simply changing the code to use the xfrm mark for routing output packets could xfrm mark could change behaviour in a way that breaks these setups. If the output mark is unspecified or set to zero, the mark is not set or changed. Tested: make allyesconfig; make -j64 Tested: https://android-review.googlesource.com/452776 Signed-off-by: Lorenzo Colitti Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 9 ++++++--- include/uapi/linux/xfrm.h | 1 + net/ipv4/xfrm4_policy.c | 14 +++++++++----- net/ipv6/xfrm6_policy.c | 9 ++++++--- net/xfrm/xfrm_device.c | 3 ++- net/xfrm/xfrm_output.c | 3 +++ net/xfrm/xfrm_policy.c | 17 +++++++++-------- net/xfrm/xfrm_user.c | 11 +++++++++++ 8 files changed, 47 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 18d7de34a5c3..9c7b70cce6d6 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -165,6 +165,7 @@ struct xfrm_state { int header_len; int trailer_len; u32 extra_flags; + u32 output_mark; } props; struct xfrm_lifetime_cfg lft; @@ -298,10 +299,12 @@ struct xfrm_policy_afinfo { struct dst_entry *(*dst_lookup)(struct net *net, int tos, int oif, const xfrm_address_t *saddr, - const xfrm_address_t *daddr); + const xfrm_address_t *daddr, + u32 mark); int (*get_saddr)(struct net *net, int oif, xfrm_address_t *saddr, - xfrm_address_t *daddr); + xfrm_address_t *daddr, + u32 mark); void (*decode_session)(struct sk_buff *skb, struct flowi *fl, int reverse); @@ -1640,7 +1643,7 @@ static inline int xfrm4_udp_encap_rcv(struct sock *sk, struct sk_buff *skb) struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif, const xfrm_address_t *saddr, const xfrm_address_t *daddr, - int family); + int family, u32 mark); struct xfrm_policy *xfrm_policy_alloc(struct net *net, gfp_t gfp); diff --git a/include/uapi/linux/xfrm.h b/include/uapi/linux/xfrm.h index 2b384ff09fa0..5fe7370a2bef 100644 --- a/include/uapi/linux/xfrm.h +++ b/include/uapi/linux/xfrm.h @@ -304,6 +304,7 @@ enum xfrm_attr_type_t { XFRMA_ADDRESS_FILTER, /* struct xfrm_address_filter */ XFRMA_PAD, XFRMA_OFFLOAD_DEV, /* struct xfrm_state_offload */ + XFRMA_OUTPUT_MARK, /* __u32 */ __XFRMA_MAX #define XFRMA_MAX (__XFRMA_MAX - 1) diff --git a/net/ipv4/xfrm4_policy.c b/net/ipv4/xfrm4_policy.c index 4aefb149fe0a..d7bf0b041885 100644 --- a/net/ipv4/xfrm4_policy.c +++ b/net/ipv4/xfrm4_policy.c @@ -20,7 +20,8 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, int tos, int oif, const xfrm_address_t *saddr, - const xfrm_address_t *daddr) + const xfrm_address_t *daddr, + u32 mark) { struct rtable *rt; @@ -28,6 +29,7 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, fl4->daddr = daddr->a4; fl4->flowi4_tos = tos; fl4->flowi4_oif = l3mdev_master_ifindex_by_index(net, oif); + fl4->flowi4_mark = mark; if (saddr) fl4->saddr = saddr->a4; @@ -42,20 +44,22 @@ static struct dst_entry *__xfrm4_dst_lookup(struct net *net, struct flowi4 *fl4, static struct dst_entry *xfrm4_dst_lookup(struct net *net, int tos, int oif, const xfrm_address_t *saddr, - const xfrm_address_t *daddr) + const xfrm_address_t *daddr, + u32 mark) { struct flowi4 fl4; - return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr); + return __xfrm4_dst_lookup(net, &fl4, tos, oif, saddr, daddr, mark); } static int xfrm4_get_saddr(struct net *net, int oif, - xfrm_address_t *saddr, xfrm_address_t *daddr) + xfrm_address_t *saddr, xfrm_address_t *daddr, + u32 mark) { struct dst_entry *dst; struct flowi4 fl4; - dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr); + dst = __xfrm4_dst_lookup(net, &fl4, 0, oif, NULL, daddr, mark); if (IS_ERR(dst)) return -EHOSTUNREACH; diff --git a/net/ipv6/xfrm6_policy.c b/net/ipv6/xfrm6_policy.c index f44b25a48478..11d1314ab6c5 100644 --- a/net/ipv6/xfrm6_policy.c +++ b/net/ipv6/xfrm6_policy.c @@ -27,7 +27,8 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, const xfrm_address_t *saddr, - const xfrm_address_t *daddr) + const xfrm_address_t *daddr, + u32 mark) { struct flowi6 fl6; struct dst_entry *dst; @@ -36,6 +37,7 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, memset(&fl6, 0, sizeof(fl6)); fl6.flowi6_oif = l3mdev_master_ifindex_by_index(net, oif); fl6.flowi6_flags = FLOWI_FLAG_SKIP_NH_OIF; + fl6.flowi6_mark = mark; memcpy(&fl6.daddr, daddr, sizeof(fl6.daddr)); if (saddr) memcpy(&fl6.saddr, saddr, sizeof(fl6.saddr)); @@ -52,12 +54,13 @@ static struct dst_entry *xfrm6_dst_lookup(struct net *net, int tos, int oif, } static int xfrm6_get_saddr(struct net *net, int oif, - xfrm_address_t *saddr, xfrm_address_t *daddr) + xfrm_address_t *saddr, xfrm_address_t *daddr, + u32 mark) { struct dst_entry *dst; struct net_device *dev; - dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr); + dst = xfrm6_dst_lookup(net, 0, oif, NULL, daddr, mark); if (IS_ERR(dst)) return -EHOSTUNREACH; diff --git a/net/xfrm/xfrm_device.c b/net/xfrm/xfrm_device.c index 1904127f5fb8..acf00104ef31 100644 --- a/net/xfrm/xfrm_device.c +++ b/net/xfrm/xfrm_device.c @@ -79,7 +79,8 @@ int xfrm_dev_state_add(struct net *net, struct xfrm_state *x, daddr = &x->props.saddr; } - dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr, x->props.family); + dst = __xfrm_dst_lookup(net, 0, 0, saddr, daddr, + x->props.family, x->props.output_mark); if (IS_ERR(dst)) return 0; diff --git a/net/xfrm/xfrm_output.c b/net/xfrm/xfrm_output.c index 8c0b6722aaa8..31a2e6d34dba 100644 --- a/net/xfrm/xfrm_output.c +++ b/net/xfrm/xfrm_output.c @@ -66,6 +66,9 @@ static int xfrm_output_one(struct sk_buff *skb, int err) goto error_nolock; } + if (x->props.output_mark) + skb->mark = x->props.output_mark; + err = x->outer_mode->output(x, skb); if (err) { XFRM_INC_STATS(net, LINUX_MIB_XFRMOUTSTATEMODEERROR); diff --git a/net/xfrm/xfrm_policy.c b/net/xfrm/xfrm_policy.c index 06c3bf7ab86b..1de52f36caf5 100644 --- a/net/xfrm/xfrm_policy.c +++ b/net/xfrm/xfrm_policy.c @@ -122,7 +122,7 @@ static const struct xfrm_policy_afinfo *xfrm_policy_get_afinfo(unsigned short fa struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif, const xfrm_address_t *saddr, const xfrm_address_t *daddr, - int family) + int family, u32 mark) { const struct xfrm_policy_afinfo *afinfo; struct dst_entry *dst; @@ -131,7 +131,7 @@ struct dst_entry *__xfrm_dst_lookup(struct net *net, int tos, int oif, if (unlikely(afinfo == NULL)) return ERR_PTR(-EAFNOSUPPORT); - dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr); + dst = afinfo->dst_lookup(net, tos, oif, saddr, daddr, mark); rcu_read_unlock(); @@ -143,7 +143,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, int tos, int oif, xfrm_address_t *prev_saddr, xfrm_address_t *prev_daddr, - int family) + int family, u32 mark) { struct net *net = xs_net(x); xfrm_address_t *saddr = &x->props.saddr; @@ -159,7 +159,7 @@ static inline struct dst_entry *xfrm_dst_lookup(struct xfrm_state *x, daddr = x->coaddr; } - dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family); + dst = __xfrm_dst_lookup(net, tos, oif, saddr, daddr, family, mark); if (!IS_ERR(dst)) { if (prev_saddr != saddr) @@ -1340,14 +1340,14 @@ int __xfrm_sk_clone_policy(struct sock *sk, const struct sock *osk) static int xfrm_get_saddr(struct net *net, int oif, xfrm_address_t *local, - xfrm_address_t *remote, unsigned short family) + xfrm_address_t *remote, unsigned short family, u32 mark) { int err; const struct xfrm_policy_afinfo *afinfo = xfrm_policy_get_afinfo(family); if (unlikely(afinfo == NULL)) return -EINVAL; - err = afinfo->get_saddr(net, oif, local, remote); + err = afinfo->get_saddr(net, oif, local, remote, mark); rcu_read_unlock(); return err; } @@ -1378,7 +1378,7 @@ xfrm_tmpl_resolve_one(struct xfrm_policy *policy, const struct flowi *fl, if (xfrm_addr_any(local, tmpl->encap_family)) { error = xfrm_get_saddr(net, fl->flowi_oif, &tmp, remote, - tmpl->encap_family); + tmpl->encap_family, 0); if (error) goto fail; local = &tmp; @@ -1598,7 +1598,8 @@ static struct dst_entry *xfrm_bundle_create(struct xfrm_policy *policy, if (xfrm[i]->props.mode != XFRM_MODE_TRANSPORT) { family = xfrm[i]->props.family; dst = xfrm_dst_lookup(xfrm[i], tos, fl->flowi_oif, - &saddr, &daddr, family); + &saddr, &daddr, family, + xfrm[i]->props.output_mark); err = PTR_ERR(dst); if (IS_ERR(dst)) goto put_states; diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index ffe8d5ef09eb..cc3268d814b4 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -584,6 +584,9 @@ static struct xfrm_state *xfrm_state_construct(struct net *net, xfrm_mark_get(attrs, &x->mark); + if (attrs[XFRMA_OUTPUT_MARK]) + x->props.output_mark = nla_get_u32(attrs[XFRMA_OUTPUT_MARK]); + err = __xfrm_init_state(x, false, attrs[XFRMA_OFFLOAD_DEV]); if (err) goto error; @@ -899,6 +902,11 @@ static int copy_to_user_state_extra(struct xfrm_state *x, goto out; if (x->security) ret = copy_sec_ctx(x->security, skb); + if (x->props.output_mark) { + ret = nla_put_u32(skb, XFRMA_OUTPUT_MARK, x->props.output_mark); + if (ret) + goto out; + } out: return ret; } @@ -2454,6 +2462,7 @@ static const struct nla_policy xfrma_policy[XFRMA_MAX+1] = { [XFRMA_PROTO] = { .type = NLA_U8 }, [XFRMA_ADDRESS_FILTER] = { .len = sizeof(struct xfrm_address_filter) }, [XFRMA_OFFLOAD_DEV] = { .len = sizeof(struct xfrm_user_offload) }, + [XFRMA_OUTPUT_MARK] = { .len = NLA_U32 }, }; static const struct nla_policy xfrma_spd_policy[XFRMA_SPD_MAX+1] = { @@ -2673,6 +2682,8 @@ static inline size_t xfrm_sa_len(struct xfrm_state *x) l += nla_total_size(sizeof(x->props.extra_flags)); if (x->xso.dev) l += nla_total_size(sizeof(x->xso)); + if (x->props.output_mark) + l += nla_total_size(sizeof(x->props.output_mark)); /* Must count x->lastused as it may become non-zero behind our back. */ l += nla_total_size_64bit(sizeof(u64)); -- cgit v1.2.3 From edf903f83ebca988e04a39f515ab6eacb92055df Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:46 +0800 Subject: sctp: remove the typedef sctp_sender_hb_info_t This patch is to remove the typedef sctp_sender_hb_info_t, and replace with struct sctp_sender_hb_info in the places where it's using this typedef. It is also to use sizeof(variable) instead of sizeof(type). Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 4 ++-- net/sctp/sm_make_chunk.c | 6 +++--- net/sctp/sm_sideeffect.c | 4 ++-- net/sctp/sm_statefuns.c | 12 +++++------- 4 files changed, 12 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 73e9509c057e..60033a263193 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -371,12 +371,12 @@ union sctp_params { * chunk is sent and the destination transport address to which this * HEARTBEAT is sent (see Section 8.3). */ -typedef struct sctp_sender_hb_info { +struct sctp_sender_hb_info { struct sctp_paramhdr param_hdr; union sctp_addr daddr; unsigned long sent_at; __u64 hb_nonce; -} sctp_sender_hb_info_t; +}; int sctp_stream_init(struct sctp_stream *stream, __u16 outcnt, __u16 incnt, gfp_t gfp); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 3a8fb1dffbc1..51de638a88b2 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -1142,10 +1142,10 @@ nodata: /* Make a HEARTBEAT chunk. */ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, - const struct sctp_transport *transport) + const struct sctp_transport *transport) { + struct sctp_sender_hb_info hbinfo; struct sctp_chunk *retval; - sctp_sender_hb_info_t hbinfo; retval = sctp_make_control(asoc, SCTP_CID_HEARTBEAT, 0, sizeof(hbinfo), GFP_ATOMIC); @@ -1154,7 +1154,7 @@ struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, goto nodata; hbinfo.param_hdr.type = SCTP_PARAM_HEARTBEAT_INFO; - hbinfo.param_hdr.length = htons(sizeof(sctp_sender_hb_info_t)); + hbinfo.param_hdr.length = htons(sizeof(hbinfo)); hbinfo.daddr = transport->ipaddr; hbinfo.sent_at = jiffies; hbinfo.hb_nonce = transport->hb_nonce; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 4a12d29d9aa1..5e8e41879b03 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -714,7 +714,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, struct sctp_transport *t, struct sctp_chunk *chunk) { - sctp_sender_hb_info_t *hbinfo; + struct sctp_sender_hb_info *hbinfo; int was_unconfirmed = 0; /* 8.3 Upon the receipt of the HEARTBEAT ACK, the sender of the @@ -768,7 +768,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, if (t->rto_pending == 0) t->rto_pending = 1; - hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; + hbinfo = (struct sctp_sender_hb_info *)chunk->skb->data; sctp_transport_update_rto(t, (jiffies - hbinfo->sent_at)); /* Update the heartbeat timer. */ diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index ac6aaa046529..af93419209df 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -1155,27 +1155,25 @@ sctp_disposition_t sctp_sf_backbeat_8_3(struct net *net, void *arg, sctp_cmd_seq_t *commands) { + struct sctp_sender_hb_info *hbinfo; struct sctp_chunk *chunk = arg; - union sctp_addr from_addr; struct sctp_transport *link; - sctp_sender_hb_info_t *hbinfo; unsigned long max_interval; + union sctp_addr from_addr; if (!sctp_vtag_verify(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); /* Make sure that the HEARTBEAT-ACK chunk has a valid length. */ if (!sctp_chunk_length_valid(chunk, sizeof(struct sctp_chunkhdr) + - sizeof(sctp_sender_hb_info_t))) + sizeof(*hbinfo))) return sctp_sf_violation_chunklen(net, ep, asoc, type, arg, commands); - hbinfo = (sctp_sender_hb_info_t *) chunk->skb->data; + hbinfo = (struct sctp_sender_hb_info *)chunk->skb->data; /* Make sure that the length of the parameter is what we expect */ - if (ntohs(hbinfo->param_hdr.length) != - sizeof(sctp_sender_hb_info_t)) { + if (ntohs(hbinfo->param_hdr.length) != sizeof(*hbinfo)) return SCTP_DISPOSITION_DISCARD; - } from_addr = hbinfo->daddr; link = sctp_assoc_lookup_paddr(asoc, &from_addr); -- cgit v1.2.3 From a05437ac5deb100f94e290ad4c5eef3e78f4b6bb Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:48 +0800 Subject: sctp: remove the typedef sctp_cmsgs_t This patch is to remove the typedef sctp_cmsgs_t, and replace with struct sctp_cmsgs in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 4 ++-- net/sctp/socket.c | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index 937187f3bffc..e171d3a3d2b4 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1985,11 +1985,11 @@ int sctp_cmp_addr_exact(const union sctp_addr *ss1, struct sctp_chunk *sctp_get_ecne_prepend(struct sctp_association *asoc); /* A convenience structure to parse out SCTP specific CMSGs. */ -typedef struct sctp_cmsgs { +struct sctp_cmsgs { struct sctp_initmsg *init; struct sctp_sndrcvinfo *srinfo; struct sctp_sndinfo *sinfo; -} sctp_cmsgs_t; +}; /* Structure for tracking memory objects */ typedef struct { diff --git a/net/sctp/socket.c b/net/sctp/socket.c index a1e2113806dd..018190655d63 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -1593,7 +1593,8 @@ static int sctp_error(struct sock *sk, int flags, int err) */ /* BUG: We do not implement the equivalent of sk_stream_wait_memory(). */ -static int sctp_msghdr_parse(const struct msghdr *, sctp_cmsgs_t *); +static int sctp_msghdr_parse(const struct msghdr *msg, + struct sctp_cmsgs *cmsgs); static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) { @@ -1609,7 +1610,7 @@ static int sctp_sendmsg(struct sock *sk, struct msghdr *msg, size_t msg_len) struct sctp_sndrcvinfo *sinfo; struct sctp_initmsg *sinit; sctp_assoc_t associd = 0; - sctp_cmsgs_t cmsgs = { NULL }; + struct sctp_cmsgs cmsgs = { NULL }; enum sctp_scope scope; bool fill_sinfo_ttl = false, wait_connect = false; struct sctp_datamsg *datamsg; @@ -7445,10 +7446,10 @@ static int sctp_autobind(struct sock *sk) * msg_control * points here */ -static int sctp_msghdr_parse(const struct msghdr *msg, sctp_cmsgs_t *cmsgs) +static int sctp_msghdr_parse(const struct msghdr *msg, struct sctp_cmsgs *cmsgs) { - struct cmsghdr *cmsg; struct msghdr *my_msg = (struct msghdr *)msg; + struct cmsghdr *cmsg; for_each_cmsghdr(cmsg, my_msg) { if (!CMSG_OK(my_msg, cmsg)) -- cgit v1.2.3 From d38ef5ae35b0960f3219f1cf0203e19819e757c7 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:49 +0800 Subject: sctp: remove the typedef sctp_dbg_objcnt_entry_t This patch is to remove the typedef sctp_dbg_objcnt_entry_t, and replace with struct sctp_dbg_objcnt_entry in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/structs.h | 4 ++-- net/sctp/objcnt.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index e171d3a3d2b4..b6d75b37f84d 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -1992,9 +1992,9 @@ struct sctp_cmsgs { }; /* Structure for tracking memory objects */ -typedef struct { +struct sctp_dbg_objcnt_entry { char *label; atomic_t *counter; -} sctp_dbg_objcnt_entry_t; +}; #endif /* __sctp_structs_h__ */ diff --git a/net/sctp/objcnt.c b/net/sctp/objcnt.c index 105ac3327b28..aeea6da81441 100644 --- a/net/sctp/objcnt.c +++ b/net/sctp/objcnt.c @@ -57,7 +57,7 @@ SCTP_DBG_OBJCNT(keys); /* An array to make it easy to pretty print the debug information * to the proc fs. */ -static sctp_dbg_objcnt_entry_t sctp_dbg_objcnt[] = { +static struct sctp_dbg_objcnt_entry sctp_dbg_objcnt[] = { SCTP_DBG_OBJCNT_ENTRY(sock), SCTP_DBG_OBJCNT_ENTRY(ep), SCTP_DBG_OBJCNT_ENTRY(assoc), -- cgit v1.2.3 From b7ef2618a0bf75c1e480b05739b0c5f2a42081cd Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:50 +0800 Subject: sctp: remove the typedef sctp_socket_type_t This patch is to remove the typedef sctp_socket_type_t, and replace with enum sctp_socket_type in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/sctp.h | 3 ++- include/net/sctp/structs.h | 6 +++--- net/sctp/socket.c | 7 ++++--- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/sctp/sctp.h b/include/net/sctp/sctp.h index 24ff7931d38c..06b4f515e157 100644 --- a/include/net/sctp/sctp.h +++ b/include/net/sctp/sctp.h @@ -550,7 +550,8 @@ static inline int sctp_ep_hashfn(struct net *net, __u16 lport) /* Is a socket of this style? */ #define sctp_style(sk, style) __sctp_style((sk), (SCTP_SOCKET_##style)) -static inline int __sctp_style(const struct sock *sk, sctp_socket_type_t style) +static inline int __sctp_style(const struct sock *sk, + enum sctp_socket_type style) { return sctp_sk(sk)->type == style; } diff --git a/include/net/sctp/structs.h b/include/net/sctp/structs.h index b6d75b37f84d..0477945de1a3 100644 --- a/include/net/sctp/structs.h +++ b/include/net/sctp/structs.h @@ -150,18 +150,18 @@ extern struct sctp_globals { #define sctp_checksum_disable (sctp_globals.checksum_disable) /* SCTP Socket type: UDP or TCP style. */ -typedef enum { +enum sctp_socket_type { SCTP_SOCKET_UDP = 0, SCTP_SOCKET_UDP_HIGH_BANDWIDTH, SCTP_SOCKET_TCP -} sctp_socket_type_t; +}; /* Per socket SCTP information. */ struct sctp_sock { /* inet_sock has to be the first member of sctp_sock */ struct inet_sock inet; /* What kind of a socket is this? */ - sctp_socket_type_t type; + enum sctp_socket_type type; /* PF_ family specific functions. */ struct sctp_pf *pf; diff --git a/net/sctp/socket.c b/net/sctp/socket.c index 018190655d63..c01af72cc603 100644 --- a/net/sctp/socket.c +++ b/net/sctp/socket.c @@ -100,8 +100,9 @@ static int sctp_send_asconf(struct sctp_association *asoc, struct sctp_chunk *chunk); static int sctp_do_bind(struct sock *, union sctp_addr *, int); static int sctp_autobind(struct sock *sk); -static void sctp_sock_migrate(struct sock *, struct sock *, - struct sctp_association *, sctp_socket_type_t); +static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, + struct sctp_association *assoc, + enum sctp_socket_type type); static unsigned long sctp_memory_pressure; static atomic_long_t sctp_memory_allocated; @@ -8086,7 +8087,7 @@ static inline void sctp_copy_descendant(struct sock *sk_to, */ static void sctp_sock_migrate(struct sock *oldsk, struct sock *newsk, struct sctp_association *assoc, - sctp_socket_type_t type) + enum sctp_socket_type type) { struct sctp_sock *oldsp = sctp_sk(oldsk); struct sctp_sock *newsp = sctp_sk(newsk); -- cgit v1.2.3 From e2c3108ab25b4dbab3821e8b6084bfb73afb655c Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:51 +0800 Subject: sctp: remove the typedef sctp_cmd_t This patch is to remove the typedef sctp_cmd_t, and replace with enum sctp_cmd in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/command.h | 14 +++++++------- net/sctp/sm_sideeffect.c | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 376cb78b6247..4e9e589b8f18 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -196,15 +196,15 @@ static inline sctp_arg_t SCTP_NULL(void) return retval; } -typedef struct { +struct sctp_cmd { sctp_arg_t obj; sctp_verb_t verb; -} sctp_cmd_t; +}; typedef struct { - sctp_cmd_t cmds[SCTP_MAX_NUM_COMMANDS]; - sctp_cmd_t *last_used_slot; - sctp_cmd_t *next_cmd; + struct sctp_cmd cmds[SCTP_MAX_NUM_COMMANDS]; + struct sctp_cmd *last_used_slot; + struct sctp_cmd *next_cmd; } sctp_cmd_seq_t; @@ -228,7 +228,7 @@ static inline int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, sctp_arg_t obj) { - sctp_cmd_t *cmd = seq->last_used_slot - 1; + struct sctp_cmd *cmd = seq->last_used_slot - 1; BUG_ON(cmd < seq->cmds); @@ -240,7 +240,7 @@ static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, /* Return the next command structure in an sctp_cmd_seq. * Return NULL at the end of the sequence. */ -static inline sctp_cmd_t *sctp_next_cmd(sctp_cmd_seq_t *seq) +static inline struct sctp_cmd *sctp_next_cmd(sctp_cmd_seq_t *seq) { if (seq->next_cmd <= seq->last_used_slot) return NULL; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 5e8e41879b03..0cb3d5a723af 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1277,7 +1277,7 @@ static int sctp_cmd_interpreter(enum sctp_event event_type, struct sctp_sock *sp = sctp_sk(sk); int error = 0; int force; - sctp_cmd_t *cmd; + struct sctp_cmd *cmd; struct sctp_chunk *new_obj; struct sctp_chunk *chunk = NULL; struct sctp_packet *packet; -- cgit v1.2.3 From a85bbeb221d860097859f110ba1321f2b0653f07 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:52 +0800 Subject: sctp: remove the typedef sctp_cmd_seq_t This patch is to remove the typedef sctp_cmd_seq_t, and replace with struct sctp_cmd_seq in the places where it's using this typedef. Note that it doesn't fix many indents although it should, as sctp_disposition_t's removal would mess them up again. So better to fix them when removing sctp_disposition_t in the later patch. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/command.h | 14 +-- include/net/sctp/sm.h | 2 +- net/sctp/probe.c | 2 +- net/sctp/sm_sideeffect.c | 54 ++++++------ net/sctp/sm_statefuns.c | 216 +++++++++++++++++++++++---------------------- 5 files changed, 145 insertions(+), 143 deletions(-) (limited to 'net') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index 4e9e589b8f18..cbf6798866ef 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -110,7 +110,7 @@ typedef enum { SCTP_CMD_LAST } sctp_verb_t; -/* How many commands can you put in an sctp_cmd_seq_t? +/* How many commands can you put in an struct sctp_cmd_seq? * This is a rather arbitrary number, ideally derived from a careful * analysis of the state functions, but in reality just taken from * thin air in the hopes othat we don't trigger a kernel panic. @@ -201,17 +201,17 @@ struct sctp_cmd { sctp_verb_t verb; }; -typedef struct { +struct sctp_cmd_seq { struct sctp_cmd cmds[SCTP_MAX_NUM_COMMANDS]; struct sctp_cmd *last_used_slot; struct sctp_cmd *next_cmd; -} sctp_cmd_seq_t; +}; /* Initialize a block of memory as a command sequence. * Return 0 if the initialization fails. */ -static inline int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) +static inline int sctp_init_cmd_seq(struct sctp_cmd_seq *seq) { /* cmds[] is filled backwards to simplify the overflow BUG() check */ seq->last_used_slot = seq->cmds + SCTP_MAX_NUM_COMMANDS; @@ -220,12 +220,12 @@ static inline int sctp_init_cmd_seq(sctp_cmd_seq_t *seq) } -/* Add a command to an sctp_cmd_seq_t. +/* Add a command to an struct sctp_cmd_seq. * * Use the SCTP_* constructors defined by SCTP_ARG_CONSTRUCTOR() above * to wrap data which goes in the obj argument. */ -static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, +static inline void sctp_add_cmd_sf(struct sctp_cmd_seq *seq, sctp_verb_t verb, sctp_arg_t obj) { struct sctp_cmd *cmd = seq->last_used_slot - 1; @@ -240,7 +240,7 @@ static inline void sctp_add_cmd_sf(sctp_cmd_seq_t *seq, sctp_verb_t verb, /* Return the next command structure in an sctp_cmd_seq. * Return NULL at the end of the sequence. */ -static inline struct sctp_cmd *sctp_next_cmd(sctp_cmd_seq_t *seq) +static inline struct sctp_cmd *sctp_next_cmd(struct sctp_cmd_seq *seq) { if (seq->next_cmd <= seq->last_used_slot) return NULL; diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 1e7651c3b158..9af64b98d96b 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -75,7 +75,7 @@ typedef sctp_disposition_t (sctp_state_fn_t) (struct net *, const struct sctp_association *, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *); + struct sctp_cmd_seq *); typedef void (sctp_timer_event_t) (unsigned long); typedef struct { sctp_state_fn_t *fn; diff --git a/net/sctp/probe.c b/net/sctp/probe.c index 43837dfc86a7..34097a167431 100644 --- a/net/sctp/probe.c +++ b/net/sctp/probe.c @@ -132,7 +132,7 @@ static sctp_disposition_t jsctp_sf_eat_sack(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sk_buff *skb = chunk->skb; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 0cb3d5a723af..6dd5934cbda6 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -58,7 +58,7 @@ static int sctp_cmd_interpreter(enum sctp_event event_type, struct sctp_association *asoc, void *event_arg, sctp_disposition_t status, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, gfp_t gfp); static int sctp_side_effects(enum sctp_event event_type, union sctp_subtype subtype, @@ -67,7 +67,7 @@ static int sctp_side_effects(enum sctp_event event_type, struct sctp_association **asoc, void *event_arg, sctp_disposition_t status, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, gfp_t gfp); /******************************************************************** @@ -150,7 +150,7 @@ static void sctp_do_ecn_cwr_work(struct sctp_association *asoc, /* Generate SACK if necessary. We call this at the end of a packet. */ static int sctp_gen_sack(struct sctp_association *asoc, int force, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { __u32 ctsn, max_tsn_seen; struct sctp_chunk *sack; @@ -506,7 +506,7 @@ sctp_timer_event_t *sctp_timer_events[SCTP_NUM_TIMEOUT_TYPES] = { * notification SHOULD be sent to the upper layer. * */ -static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands, +static void sctp_do_8_2_transport_strike(struct sctp_cmd_seq *commands, struct sctp_association *asoc, struct sctp_transport *transport, int is_hb) @@ -578,7 +578,7 @@ static void sctp_do_8_2_transport_strike(sctp_cmd_seq_t *commands, } /* Worker routine to handle INIT command failure. */ -static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, +static void sctp_cmd_init_failed(struct sctp_cmd_seq *commands, struct sctp_association *asoc, unsigned int error) { @@ -601,7 +601,7 @@ static void sctp_cmd_init_failed(sctp_cmd_seq_t *commands, } /* Worker routine to handle SCTP_CMD_ASSOC_FAILED. */ -static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, +static void sctp_cmd_assoc_failed(struct sctp_cmd_seq *commands, struct sctp_association *asoc, enum sctp_event event_type, union sctp_subtype subtype, @@ -645,7 +645,7 @@ static void sctp_cmd_assoc_failed(sctp_cmd_seq_t *commands, * since all other cases use "temporary" associations and can do all * their work in statefuns directly. */ -static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, +static int sctp_cmd_process_init(struct sctp_cmd_seq *commands, struct sctp_association *asoc, struct sctp_chunk *chunk, struct sctp_init_chunk *peer_init, @@ -667,7 +667,7 @@ static int sctp_cmd_process_init(sctp_cmd_seq_t *commands, } /* Helper function to break out starting up of heartbeat timers. */ -static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds, +static void sctp_cmd_hb_timers_start(struct sctp_cmd_seq *cmds, struct sctp_association *asoc) { struct sctp_transport *t; @@ -680,7 +680,7 @@ static void sctp_cmd_hb_timers_start(sctp_cmd_seq_t *cmds, sctp_transport_reset_hb_timer(t); } -static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds, +static void sctp_cmd_hb_timers_stop(struct sctp_cmd_seq *cmds, struct sctp_association *asoc) { struct sctp_transport *t; @@ -695,7 +695,7 @@ static void sctp_cmd_hb_timers_stop(sctp_cmd_seq_t *cmds, } /* Helper function to stop any pending T3-RTX timers */ -static void sctp_cmd_t3_rtx_timers_stop(sctp_cmd_seq_t *cmds, +static void sctp_cmd_t3_rtx_timers_stop(struct sctp_cmd_seq *cmds, struct sctp_association *asoc) { struct sctp_transport *t; @@ -709,7 +709,7 @@ static void sctp_cmd_t3_rtx_timers_stop(sctp_cmd_seq_t *cmds, /* Helper function to handle the reception of an HEARTBEAT ACK. */ -static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, +static void sctp_cmd_transport_on(struct sctp_cmd_seq *cmds, struct sctp_association *asoc, struct sctp_transport *t, struct sctp_chunk *chunk) @@ -780,7 +780,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, /* Helper function to process the process SACK command. */ -static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, +static int sctp_cmd_process_sack(struct sctp_cmd_seq *cmds, struct sctp_association *asoc, struct sctp_chunk *chunk) { @@ -802,7 +802,7 @@ static int sctp_cmd_process_sack(sctp_cmd_seq_t *cmds, /* Helper function to set the timeout value for T2-SHUTDOWN timer and to set * the transport for a shutdown chunk. */ -static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, +static void sctp_cmd_setup_t2(struct sctp_cmd_seq *cmds, struct sctp_association *asoc, struct sctp_chunk *chunk) { @@ -819,7 +819,7 @@ static void sctp_cmd_setup_t2(sctp_cmd_seq_t *cmds, asoc->timeouts[SCTP_EVENT_TIMEOUT_T2_SHUTDOWN] = t->rto; } -static void sctp_cmd_assoc_update(sctp_cmd_seq_t *cmds, +static void sctp_cmd_assoc_update(struct sctp_cmd_seq *cmds, struct sctp_association *asoc, struct sctp_association *new) { @@ -842,7 +842,7 @@ static void sctp_cmd_assoc_update(sctp_cmd_seq_t *cmds, } /* Helper function to change the state of an association. */ -static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, +static void sctp_cmd_new_state(struct sctp_cmd_seq *cmds, struct sctp_association *asoc, enum sctp_state state) { @@ -902,7 +902,7 @@ static void sctp_cmd_new_state(sctp_cmd_seq_t *cmds, } /* Helper function to delete an association. */ -static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds, +static void sctp_cmd_delete_tcb(struct sctp_cmd_seq *cmds, struct sctp_association *asoc) { struct sock *sk = asoc->base.sk; @@ -924,9 +924,9 @@ static void sctp_cmd_delete_tcb(sctp_cmd_seq_t *cmds, * destination address (we use active path instead of primary path just * because primary path may be inactive. */ -static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds, - struct sctp_association *asoc, - struct sctp_chunk *chunk) +static void sctp_cmd_setup_t4(struct sctp_cmd_seq *cmds, + struct sctp_association *asoc, + struct sctp_chunk *chunk) { struct sctp_transport *t; @@ -936,7 +936,7 @@ static void sctp_cmd_setup_t4(sctp_cmd_seq_t *cmds, } /* Process an incoming Operation Error Chunk. */ -static void sctp_cmd_process_operr(sctp_cmd_seq_t *cmds, +static void sctp_cmd_process_operr(struct sctp_cmd_seq *cmds, struct sctp_association *asoc, struct sctp_chunk *chunk) { @@ -1025,9 +1025,9 @@ static void sctp_cmd_set_sk_err(struct sctp_association *asoc, int error) } /* Helper function to generate an association change event */ -static void sctp_cmd_assoc_change(sctp_cmd_seq_t *commands, - struct sctp_association *asoc, - u8 state) +static void sctp_cmd_assoc_change(struct sctp_cmd_seq *commands, + struct sctp_association *asoc, + u8 state) { struct sctp_ulpevent *ev; @@ -1040,7 +1040,7 @@ static void sctp_cmd_assoc_change(sctp_cmd_seq_t *commands, } /* Helper function to generate an adaptation indication event */ -static void sctp_cmd_adaptation_ind(sctp_cmd_seq_t *commands, +static void sctp_cmd_adaptation_ind(struct sctp_cmd_seq *commands, struct sctp_association *asoc) { struct sctp_ulpevent *ev; @@ -1145,7 +1145,7 @@ int sctp_do_sm(struct net *net, enum sctp_event event_type, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, gfp_t gfp) { - sctp_cmd_seq_t commands; + struct sctp_cmd_seq commands; const sctp_sm_table_entry_t *state_fn; sctp_disposition_t status; int error = 0; @@ -1184,7 +1184,7 @@ static int sctp_side_effects(enum sctp_event event_type, struct sctp_association **asoc, void *event_arg, sctp_disposition_t status, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, gfp_t gfp) { int error; @@ -1270,7 +1270,7 @@ static int sctp_cmd_interpreter(enum sctp_event event_type, struct sctp_association *asoc, void *event_arg, sctp_disposition_t status, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, gfp_t gfp) { struct sock *sk = ep->base.sk; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index af93419209df..93b6f42a9252 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -67,7 +67,7 @@ static struct sctp_packet *sctp_abort_pkt_new(struct net *net, size_t paylen); static int sctp_eat_data(const struct sctp_association *asoc, struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands); + struct sctp_cmd_seq *commands); static struct sctp_packet *sctp_ootb_pkt_new(struct net *net, const struct sctp_association *asoc, const struct sctp_chunk *chunk); @@ -75,30 +75,30 @@ static void sctp_send_stale_cookie_err(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, struct sctp_chunk *err_chunk); static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands); + struct sctp_cmd_seq *commands); static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands); + struct sctp_cmd_seq *commands); static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands); + struct sctp_cmd_seq *commands); static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk); static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, __be16 error, int sk_err, const struct sctp_association *asoc, struct sctp_transport *transport); @@ -108,7 +108,7 @@ static sctp_disposition_t sctp_sf_abort_violation( const struct sctp_endpoint *ep, const struct sctp_association *asoc, void *arg, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, const __u8 *payload, const size_t paylen); @@ -118,7 +118,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands); + struct sctp_cmd_seq *commands); static sctp_disposition_t sctp_sf_violation_paramlen( struct net *net, @@ -126,7 +126,7 @@ static sctp_disposition_t sctp_sf_violation_paramlen( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, void *ext, - sctp_cmd_seq_t *commands); + struct sctp_cmd_seq *commands); static sctp_disposition_t sctp_sf_violation_ctsn( struct net *net, @@ -134,7 +134,7 @@ static sctp_disposition_t sctp_sf_violation_ctsn( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands); + struct sctp_cmd_seq *commands); static sctp_disposition_t sctp_sf_violation_chunk( struct net *net, @@ -142,7 +142,7 @@ static sctp_disposition_t sctp_sf_violation_chunk( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands); + struct sctp_cmd_seq *commands); static enum sctp_ierror sctp_sf_authenticate( struct net *net, @@ -156,7 +156,7 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands); + struct sctp_cmd_seq *commands); /* Small helper function that checks if the chunk length * is of the appropriate length. The 'required_length' argument @@ -219,7 +219,7 @@ sctp_disposition_t sctp_sf_do_4_C(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; @@ -305,7 +305,7 @@ sctp_disposition_t sctp_sf_do_5_1B_init(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg, *repl, *err_chunk; struct sctp_unrecognized_param *unk_param; @@ -499,7 +499,7 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_init_chunk *initchunk; @@ -648,7 +648,7 @@ sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_association *new_asoc; @@ -875,7 +875,7 @@ sctp_disposition_t sctp_sf_do_5_1E_ca(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; @@ -953,7 +953,7 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_transport *transport = (struct sctp_transport *) arg; struct sctp_chunk *reply; @@ -979,7 +979,7 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_transport *transport = (struct sctp_transport *) arg; @@ -1026,7 +1026,7 @@ sctp_disposition_t sctp_sf_send_reconf(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_transport *transport = arg; @@ -1078,7 +1078,7 @@ sctp_disposition_t sctp_sf_beat_8_3(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_paramhdr *param_hdr; struct sctp_chunk *chunk = arg; @@ -1153,7 +1153,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_sender_hb_info *hbinfo; struct sctp_chunk *chunk = arg; @@ -1225,7 +1225,7 @@ sctp_disposition_t sctp_sf_backbeat_8_3(struct net *net, */ static int sctp_sf_send_restart_abort(struct net *net, union sctp_addr *ssa, struct sctp_chunk *init, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { int len; struct sctp_packet *pkt; @@ -1290,7 +1290,7 @@ static bool list_has_sctp_addr(const struct list_head *list, static int sctp_sf_check_restart_addrs(const struct sctp_association *new_asoc, const struct sctp_association *asoc, struct sctp_chunk *init, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct net *net = sock_net(new_asoc->base.sk); struct sctp_transport *new_addr; @@ -1415,7 +1415,7 @@ static sctp_disposition_t sctp_sf_do_unexpected_init( const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, - void *arg, sctp_cmd_seq_t *commands) + void *arg, struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg, *repl, *err_chunk; struct sctp_unrecognized_param *unk_param; @@ -1627,7 +1627,7 @@ sctp_disposition_t sctp_sf_do_5_2_1_siminit(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { /* Call helper to do the real work for both simulataneous and * duplicate INIT chunk handling. @@ -1681,7 +1681,7 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { /* Call helper to do the real work for both simulataneous and * duplicate INIT chunk handling. @@ -1703,7 +1703,8 @@ sctp_disposition_t sctp_sf_do_5_2_3_initack(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, - void *arg, sctp_cmd_seq_t *commands) + void *arg, + struct sctp_cmd_seq *commands) { /* Per the above section, we'll discard the chunk if we have an * endpoint. If this is an OOTB INIT-ACK, treat it as such. @@ -1723,7 +1724,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, struct sctp_association *new_asoc) { struct sctp_init_chunk *peer_init; @@ -1838,7 +1839,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_b(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, struct sctp_association *new_asoc) { struct sctp_init_chunk *peer_init; @@ -1909,7 +1910,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_c(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, struct sctp_association *new_asoc) { /* The cookie should be silently discarded. @@ -1931,7 +1932,7 @@ static sctp_disposition_t sctp_sf_do_dupcook_d(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, struct sctp_association *new_asoc) { struct sctp_ulpevent *ev = NULL, *ai_ev = NULL; @@ -2027,7 +2028,7 @@ sctp_disposition_t sctp_sf_do_5_2_4_dupcook(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { sctp_disposition_t retval; struct sctp_chunk *chunk = arg; @@ -2146,7 +2147,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -2188,7 +2189,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -2239,7 +2240,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { /* The same T2 timer, so we should be able to use * common function with the SHUTDOWN-SENT state. @@ -2266,7 +2267,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_errhdr *err; @@ -2330,7 +2331,7 @@ static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { int attempts = asoc->init_err_counter + 1; struct sctp_chunk *chunk = arg, *reply; @@ -2452,7 +2453,7 @@ sctp_disposition_t sctp_sf_do_9_1_abort(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -2489,7 +2490,7 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; unsigned int len; @@ -2527,7 +2528,7 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; unsigned int len; @@ -2566,7 +2567,7 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { return sctp_stop_t1_and_abort(net, commands, SCTP_ERROR_NO_ERROR, ENOPROTOOPT, asoc, @@ -2581,7 +2582,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { /* There is a single T1 timer, so we should be able to use * common function with the COOKIE-WAIT state. @@ -2595,7 +2596,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(struct net *net, * This is common code called by several sctp_sf_*_abort() functions above. */ static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, __be16 error, int sk_err, const struct sctp_association *asoc, struct sctp_transport *transport) @@ -2653,7 +2654,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; sctp_disposition_t disposition; @@ -2742,7 +2743,7 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_shutdownhdr *sdh; @@ -2795,7 +2796,7 @@ sctp_disposition_t sctp_sf_do_9_2_reshutack(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = (struct sctp_chunk *) arg; struct sctp_chunk *reply; @@ -2859,7 +2860,7 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_cwrhdr *cwr; @@ -2915,7 +2916,7 @@ sctp_disposition_t sctp_sf_do_ecne(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_ecnehdr *ecne; @@ -2972,7 +2973,7 @@ sctp_disposition_t sctp_sf_eat_data_6_2(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; sctp_arg_t force = SCTP_NOFORCE(); @@ -3092,7 +3093,7 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; int error; @@ -3183,7 +3184,7 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_sackhdr *sackh; @@ -3257,7 +3258,7 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; @@ -3307,7 +3308,7 @@ sctp_disposition_t sctp_sf_operr_notify(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_errhdr *err; @@ -3345,7 +3346,7 @@ sctp_disposition_t sctp_sf_do_9_2_final(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_chunk *reply; @@ -3428,7 +3429,7 @@ sctp_disposition_t sctp_sf_ootb(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sk_buff *skb = chunk->skb; @@ -3521,7 +3522,7 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; @@ -3583,7 +3584,7 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -3607,7 +3608,7 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_chunk *asconf_ack = NULL; @@ -3725,7 +3726,7 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *asconf_ack = arg; struct sctp_chunk *last_asconf = asoc->addip_last_asconf; @@ -3843,7 +3844,7 @@ sctp_disposition_t sctp_sf_do_reconf(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_paramhdr *err_param = NULL; struct sctp_chunk *chunk = arg; @@ -3920,7 +3921,7 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_fwdtsn_hdr *fwdtsn_hdr; @@ -3991,7 +3992,7 @@ sctp_disposition_t sctp_sf_eat_fwd_tsn_fast( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_fwdtsn_hdr *fwdtsn_hdr; @@ -4158,7 +4159,7 @@ sctp_disposition_t sctp_sf_eat_auth(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_authhdr *auth_hdr; @@ -4255,7 +4256,7 @@ sctp_disposition_t sctp_sf_unk_chunk(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *unk_chunk = arg; struct sctp_chunk *err_chunk; @@ -4335,7 +4336,7 @@ sctp_disposition_t sctp_sf_discard_chunk(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -4375,7 +4376,7 @@ sctp_disposition_t sctp_sf_pdiscard(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { SCTP_INC_STATS(net, SCTP_MIB_IN_PKT_DISCARDS); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); @@ -4403,7 +4404,7 @@ sctp_disposition_t sctp_sf_violation(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -4423,7 +4424,7 @@ static sctp_disposition_t sctp_sf_abort_violation( const struct sctp_endpoint *ep, const struct sctp_association *asoc, void *arg, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, const __u8 *payload, const size_t paylen) { @@ -4541,7 +4542,7 @@ static sctp_disposition_t sctp_sf_violation_chunklen( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { static const char err_str[] = "The following chunk had invalid length:"; @@ -4561,7 +4562,7 @@ static sctp_disposition_t sctp_sf_violation_paramlen( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, void *ext, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_paramhdr *param = ext; @@ -4604,7 +4605,7 @@ static sctp_disposition_t sctp_sf_violation_ctsn( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { static const char err_str[] = "The cumulative tsn ack beyond the max tsn currently sent:"; @@ -4624,7 +4625,7 @@ static sctp_disposition_t sctp_sf_violation_chunk( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { static const char err_str[] = "The following chunk violates protocol:"; @@ -4699,7 +4700,7 @@ sctp_disposition_t sctp_sf_do_prm_asoc(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *repl; struct sctp_association *my_asoc; @@ -4811,7 +4812,7 @@ sctp_disposition_t sctp_sf_do_prm_send(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_datamsg *msg = arg; @@ -4851,7 +4852,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { int disposition; @@ -4907,7 +4908,7 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { /* From 9.1 Abort of an Association * Upon receipt of the ABORT primitive from its upper @@ -4944,7 +4945,7 @@ sctp_disposition_t sctp_sf_error_closed(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR, SCTP_ERROR(-EINVAL)); return SCTP_DISPOSITION_CONSUME; @@ -4958,7 +4959,7 @@ sctp_disposition_t sctp_sf_error_shutdown(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR, SCTP_ERROR(-ESHUTDOWN)); @@ -4985,7 +4986,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); @@ -5019,7 +5020,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown( const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, - void *arg, sctp_cmd_seq_t *commands) + void *arg, struct sctp_cmd_seq *commands) { /* There is a single T1 timer, so we should be able to use * common function with the COOKIE-WAIT state. @@ -5047,7 +5048,7 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *abort = arg; @@ -5096,7 +5097,7 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_abort( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { /* There is a single T1 timer, so we should be able to use * common function with the COOKIE-WAIT state. @@ -5122,7 +5123,7 @@ sctp_disposition_t sctp_sf_shutdown_pending_prm_abort( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { /* Stop the T5-shutdown guard timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -5149,7 +5150,7 @@ sctp_disposition_t sctp_sf_shutdown_sent_prm_abort( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { /* Stop the T2-shutdown timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -5180,7 +5181,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { /* The same T2 timer, so we should be able to use * common function with the SHUTDOWN-SENT state. @@ -5216,7 +5217,7 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { if (SCTP_DISPOSITION_NOMEM == sctp_sf_heartbeat(ep, asoc, type, (struct sctp_transport *)arg, commands)) @@ -5248,7 +5249,7 @@ sctp_disposition_t sctp_sf_do_prm_asconf(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -5264,7 +5265,8 @@ sctp_disposition_t sctp_sf_do_prm_reconf(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, - void *arg, sctp_cmd_seq_t *commands) + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -5283,7 +5285,7 @@ sctp_disposition_t sctp_sf_ignore_primitive( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { pr_debug("%s: primitive type:%d is ignored\n", __func__, type.primitive); @@ -5307,7 +5309,7 @@ sctp_disposition_t sctp_sf_do_no_pending_tsn( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_ulpevent *event; @@ -5339,7 +5341,7 @@ sctp_disposition_t sctp_sf_do_9_2_start_shutdown( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *reply; @@ -5409,7 +5411,7 @@ sctp_disposition_t sctp_sf_do_9_2_shutdown_ack( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = (struct sctp_chunk *) arg; struct sctp_chunk *reply; @@ -5482,7 +5484,7 @@ sctp_disposition_t sctp_sf_ignore_other(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { pr_debug("%s: the event other type:%d is ignored\n", __func__, type.other); @@ -5510,7 +5512,7 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_transport *transport = arg; @@ -5598,7 +5600,7 @@ sctp_disposition_t sctp_sf_do_6_2_sack(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { SCTP_INC_STATS(net, SCTP_MIB_DELAY_SACK_EXPIREDS); sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); @@ -5629,7 +5631,7 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *repl = NULL; struct sctp_bind_addr *bp; @@ -5693,7 +5695,7 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *repl = NULL; int attempts = asoc->init_err_counter + 1; @@ -5743,7 +5745,7 @@ sctp_disposition_t sctp_sf_t2_timer_expire(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *reply = NULL; @@ -5814,7 +5816,7 @@ sctp_disposition_t sctp_sf_t4_timer_expire( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = asoc->addip_last_asconf; struct sctp_transport *transport = chunk->transport; @@ -5885,7 +5887,7 @@ sctp_disposition_t sctp_sf_t5_timer_expire(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_chunk *reply = NULL; @@ -5922,7 +5924,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire( const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { int disposition; @@ -5964,7 +5966,7 @@ sctp_disposition_t sctp_sf_not_impl(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { return SCTP_DISPOSITION_NOT_IMPL; } @@ -5982,7 +5984,7 @@ sctp_disposition_t sctp_sf_bug(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { return SCTP_DISPOSITION_BUG; } @@ -6003,7 +6005,7 @@ sctp_disposition_t sctp_sf_timer_ignore(struct net *net, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { pr_debug("%s: timer %d ignored\n", __func__, type.chunk); @@ -6169,7 +6171,7 @@ static void sctp_send_stale_cookie_err(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands, + struct sctp_cmd_seq *commands, struct sctp_chunk *err_chunk) { struct sctp_packet *packet; @@ -6198,7 +6200,7 @@ static void sctp_send_stale_cookie_err(struct net *net, /* Process a data chunk */ static int sctp_eat_data(const struct sctp_association *asoc, struct sctp_chunk *chunk, - sctp_cmd_seq_t *commands) + struct sctp_cmd_seq *commands) { struct sctp_datahdr *data_hdr; struct sctp_chunk *err; -- cgit v1.2.3 From c488b7704ed0eed18e11f9b685931558735f2a68 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:53 +0800 Subject: sctp: remove the typedef sctp_arg_t This patch is to remove the typedef sctp_arg_t, and replace with union sctp_arg in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/command.h | 26 +++++++++++++------------- net/sctp/sm_statefuns.c | 2 +- 2 files changed, 14 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index cbf6798866ef..f5fc425b5a4f 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -117,7 +117,7 @@ typedef enum { */ #define SCTP_MAX_NUM_COMMANDS 20 -typedef union { +union sctp_arg { void *zero_all; /* Set to NULL to clear the entire union */ __s32 i32; __u32 u32; @@ -137,24 +137,24 @@ typedef union { struct sctp_packet *packet; struct sctp_sackhdr *sackh; struct sctp_datamsg *msg; -} sctp_arg_t; +}; /* We are simulating ML type constructors here. * * SCTP_ARG_CONSTRUCTOR(NAME, TYPE, ELT) builds a function called * SCTP_NAME() which takes an argument of type TYPE and returns an - * sctp_arg_t. It does this by inserting the sole argument into the - * ELT union element of a local sctp_arg_t. + * union sctp_arg. It does this by inserting the sole argument into + * the ELT union element of a local union sctp_arg. * * E.g., SCTP_ARG_CONSTRUCTOR(I32, __s32, i32) builds SCTP_I32(arg), - * which takes an __s32 and returns a sctp_arg_t containing the + * which takes an __s32 and returns a union sctp_arg containing the * __s32. So, after foo = SCTP_I32(arg), foo.i32 == arg. */ #define SCTP_ARG_CONSTRUCTOR(name, type, elt) \ -static inline sctp_arg_t \ +static inline union sctp_arg \ SCTP_## name (type arg) \ -{ sctp_arg_t retval;\ +{ union sctp_arg retval;\ retval.zero_all = NULL;\ retval.elt = arg;\ return retval;\ @@ -179,25 +179,25 @@ SCTP_ARG_CONSTRUCTOR(PACKET, struct sctp_packet *, packet) SCTP_ARG_CONSTRUCTOR(SACKH, struct sctp_sackhdr *, sackh) SCTP_ARG_CONSTRUCTOR(DATAMSG, struct sctp_datamsg *, msg) -static inline sctp_arg_t SCTP_FORCE(void) +static inline union sctp_arg SCTP_FORCE(void) { return SCTP_I32(1); } -static inline sctp_arg_t SCTP_NOFORCE(void) +static inline union sctp_arg SCTP_NOFORCE(void) { return SCTP_I32(0); } -static inline sctp_arg_t SCTP_NULL(void) +static inline union sctp_arg SCTP_NULL(void) { - sctp_arg_t retval; + union sctp_arg retval; retval.zero_all = NULL; return retval; } struct sctp_cmd { - sctp_arg_t obj; + union sctp_arg obj; sctp_verb_t verb; }; @@ -226,7 +226,7 @@ static inline int sctp_init_cmd_seq(struct sctp_cmd_seq *seq) * to wrap data which goes in the obj argument. */ static inline void sctp_add_cmd_sf(struct sctp_cmd_seq *seq, sctp_verb_t verb, - sctp_arg_t obj) + union sctp_arg obj) { struct sctp_cmd *cmd = seq->last_used_slot - 1; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 93b6f42a9252..3394c4d34ea4 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -2975,8 +2975,8 @@ sctp_disposition_t sctp_sf_eat_data_6_2(struct net *net, void *arg, struct sctp_cmd_seq *commands) { + union sctp_arg force = SCTP_NOFORCE(); struct sctp_chunk *chunk = arg; - sctp_arg_t force = SCTP_NOFORCE(); int error; if (!sctp_vtag_verify(chunk, asoc)) { -- cgit v1.2.3 From e08af95df1130883762b388a19bb150ae5d16c09 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:54 +0800 Subject: sctp: remove the typedef sctp_verb_t This patch is to remove the typedef sctp_verb_t, and replace with enum sctp_verb in the places where it's using this typedef. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/command.h | 10 +++++----- net/sctp/sm_statefuns.c | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/net/sctp/command.h b/include/net/sctp/command.h index f5fc425b5a4f..b55c6a48a206 100644 --- a/include/net/sctp/command.h +++ b/include/net/sctp/command.h @@ -40,7 +40,7 @@ #include -typedef enum { +enum sctp_verb { SCTP_CMD_NOP = 0, /* Do nothing. */ SCTP_CMD_NEW_ASOC, /* Register a new association. */ SCTP_CMD_DELETE_TCB, /* Delete the current association. */ @@ -108,7 +108,7 @@ typedef enum { SCTP_CMD_PURGE_ASCONF_QUEUE, /* Purge all asconf queues.*/ SCTP_CMD_SET_ASOC, /* Restore association context */ SCTP_CMD_LAST -} sctp_verb_t; +}; /* How many commands can you put in an struct sctp_cmd_seq? * This is a rather arbitrary number, ideally derived from a careful @@ -198,7 +198,7 @@ static inline union sctp_arg SCTP_NULL(void) struct sctp_cmd { union sctp_arg obj; - sctp_verb_t verb; + enum sctp_verb verb; }; struct sctp_cmd_seq { @@ -225,8 +225,8 @@ static inline int sctp_init_cmd_seq(struct sctp_cmd_seq *seq) * Use the SCTP_* constructors defined by SCTP_ARG_CONSTRUCTOR() above * to wrap data which goes in the obj argument. */ -static inline void sctp_add_cmd_sf(struct sctp_cmd_seq *seq, sctp_verb_t verb, - union sctp_arg obj) +static inline void sctp_add_cmd_sf(struct sctp_cmd_seq *seq, + enum sctp_verb verb, union sctp_arg obj) { struct sctp_cmd *cmd = seq->last_used_slot - 1; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index 3394c4d34ea4..adc1dde34bfe 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -6205,7 +6205,7 @@ static int sctp_eat_data(const struct sctp_association *asoc, struct sctp_datahdr *data_hdr; struct sctp_chunk *err; size_t datalen; - sctp_verb_t deliver; + enum sctp_verb deliver; int tmp; __u32 tsn; struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; -- cgit v1.2.3 From 8ee821aea39c6bf4142c9319adecea6d3e1af4a2 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:56 +0800 Subject: sctp: remove the typedef sctp_sm_table_entry_t This patch is to remove the typedef sctp_sm_table_entry_t, and replace with struct sctp_sm_table_entry in the places where it's using this typedef. It is also to fix some indents. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/sm.h | 6 +++--- net/sctp/sm_sideeffect.c | 2 +- net/sctp/sm_statetable.c | 42 +++++++++++++++++++++++++----------------- 3 files changed, 29 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 3ca75a6c4b5e..7ad240228a0f 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -72,10 +72,10 @@ typedef sctp_disposition_t (sctp_state_fn_t) (struct net *, void *arg, struct sctp_cmd_seq *); typedef void (sctp_timer_event_t) (unsigned long); -typedef struct { +struct sctp_sm_table_entry { sctp_state_fn_t *fn; const char *name; -} sctp_sm_table_entry_t; +}; /* A naming convention of "sctp_sf_xxx" applies to all the state functions * currently in use. @@ -170,7 +170,7 @@ sctp_state_fn_t sctp_sf_autoclose_timer_expire; /* Prototypes for utility support functions. */ __u8 sctp_get_chunk_type(struct sctp_chunk *chunk); -const sctp_sm_table_entry_t *sctp_sm_lookup_event( +const struct sctp_sm_table_entry *sctp_sm_lookup_event( struct net *net, enum sctp_event event_type, enum sctp_state state, diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 6dd5934cbda6..2bc1204becbd 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -1146,7 +1146,7 @@ int sctp_do_sm(struct net *net, enum sctp_event event_type, void *event_arg, gfp_t gfp) { struct sctp_cmd_seq commands; - const sctp_sm_table_entry_t *state_fn; + const struct sctp_sm_table_entry *state_fn; sctp_disposition_t status; int error = 0; typedef const char *(printfn_t)(union sctp_subtype); diff --git a/net/sctp/sm_statetable.c b/net/sctp/sm_statetable.c index d437f3801399..79b6bee5b768 100644 --- a/net/sctp/sm_statetable.c +++ b/net/sctp/sm_statetable.c @@ -45,27 +45,27 @@ #include #include -static const sctp_sm_table_entry_t +static const struct sctp_sm_table_entry primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES]; -static const sctp_sm_table_entry_t +static const struct sctp_sm_table_entry other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES]; -static const sctp_sm_table_entry_t +static const struct sctp_sm_table_entry timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES]; -static const sctp_sm_table_entry_t *sctp_chunk_event_lookup( +static const struct sctp_sm_table_entry *sctp_chunk_event_lookup( struct net *net, enum sctp_cid cid, enum sctp_state state); -static const sctp_sm_table_entry_t bug = { +static const struct sctp_sm_table_entry bug = { .fn = sctp_sf_bug, .name = "sctp_sf_bug" }; #define DO_LOOKUP(_max, _type, _table) \ ({ \ - const sctp_sm_table_entry_t *rtn; \ + const struct sctp_sm_table_entry *rtn; \ \ if ((event_subtype._type > (_max))) { \ pr_warn("table %p possible attack: event %d exceeds max %d\n", \ @@ -77,7 +77,7 @@ static const sctp_sm_table_entry_t bug = { rtn; \ }) -const sctp_sm_table_entry_t *sctp_sm_lookup_event( +const struct sctp_sm_table_entry *sctp_sm_lookup_event( struct net *net, enum sctp_event event_type, enum sctp_state state, @@ -394,7 +394,8 @@ const sctp_sm_table_entry_t *sctp_sm_lookup_event( * * For base protocol (RFC 2960). */ -static const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { +static const struct sctp_sm_table_entry +chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_DATA, TYPE_SCTP_INIT, TYPE_SCTP_INIT_ACK, @@ -453,7 +454,8 @@ static const sctp_sm_table_entry_t chunk_event_table[SCTP_NUM_BASE_CHUNK_TYPES][ /* The primary index for this table is the chunk type. * The secondary index for this table is the state. */ -static const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { +static const struct sctp_sm_table_entry +addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_ASCONF, TYPE_SCTP_ASCONF_ACK, }; /*state_fn_t addip_chunk_event_table[][] */ @@ -480,7 +482,8 @@ static const sctp_sm_table_entry_t addip_chunk_event_table[SCTP_NUM_ADDIP_CHUNK_ /* The primary index for this table is the chunk type. * The secondary index for this table is the state. */ -static const sctp_sm_table_entry_t prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { +static const struct sctp_sm_table_entry +prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_FWD_TSN, }; /*state_fn_t prsctp_chunk_event_table[][] */ @@ -506,7 +509,8 @@ static const sctp_sm_table_entry_t prsctp_chunk_event_table[SCTP_NUM_PRSCTP_CHUN /* The primary index for this table is the chunk type. * The secondary index for this table is the state. */ -static const sctp_sm_table_entry_t reconf_chunk_event_table[SCTP_NUM_RECONF_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { +static const struct sctp_sm_table_entry +reconf_chunk_event_table[SCTP_NUM_RECONF_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_RECONF, }; /*state_fn_t reconf_chunk_event_table[][] */ @@ -532,11 +536,12 @@ static const sctp_sm_table_entry_t reconf_chunk_event_table[SCTP_NUM_RECONF_CHUN /* The primary index for this table is the chunk type. * The secondary index for this table is the state. */ -static const sctp_sm_table_entry_t auth_chunk_event_table[SCTP_NUM_AUTH_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { +static const struct sctp_sm_table_entry +auth_chunk_event_table[SCTP_NUM_AUTH_CHUNK_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_AUTH, }; /*state_fn_t auth_chunk_event_table[][] */ -static const sctp_sm_table_entry_t +static const struct sctp_sm_table_entry chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { /* SCTP_STATE_CLOSED */ TYPE_SCTP_FUNC(sctp_sf_ootb), @@ -693,7 +698,8 @@ chunk_event_table_unknown[SCTP_STATE_NUM_STATES] = { /* The primary index for this table is the primitive type. * The secondary index for this table is the state. */ -static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = { +static const struct sctp_sm_table_entry +primitive_event_table[SCTP_NUM_PRIMITIVE_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_PRIMITIVE_ASSOCIATE, TYPE_SCTP_PRIMITIVE_SHUTDOWN, TYPE_SCTP_PRIMITIVE_ABORT, @@ -741,7 +747,8 @@ static const sctp_sm_table_entry_t primitive_event_table[SCTP_NUM_PRIMITIVE_TYPE TYPE_SCTP_FUNC(sctp_sf_ignore_other), \ } -static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = { +static const struct sctp_sm_table_entry +other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_OTHER_NO_PENDING_TSN, TYPE_SCTP_OTHER_ICMP_PROTO_UNREACH, }; @@ -955,7 +962,8 @@ static const sctp_sm_table_entry_t other_event_table[SCTP_NUM_OTHER_TYPES][SCTP_ TYPE_SCTP_FUNC(sctp_sf_timer_ignore), \ } -static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = { +static const struct sctp_sm_table_entry +timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][SCTP_STATE_NUM_STATES] = { TYPE_SCTP_EVENT_TIMEOUT_NONE, TYPE_SCTP_EVENT_TIMEOUT_T1_COOKIE, TYPE_SCTP_EVENT_TIMEOUT_T1_INIT, @@ -969,7 +977,7 @@ static const sctp_sm_table_entry_t timeout_event_table[SCTP_NUM_TIMEOUT_TYPES][S TYPE_SCTP_EVENT_TIMEOUT_AUTOCLOSE, }; -static const sctp_sm_table_entry_t *sctp_chunk_event_lookup( +static const struct sctp_sm_table_entry *sctp_chunk_event_lookup( struct net *net, enum sctp_cid cid, enum sctp_state state) -- cgit v1.2.3 From 172a1599ba88df7147f6503a75686fb89c8a1f3f Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:57 +0800 Subject: sctp: remove the typedef sctp_disposition_t This patch is to remove the typedef sctp_disposition_t, and replace with enum sctp_disposition in the places where it's using this typedef. It's also to fix the indent for many functions' defination. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/sm.h | 19 +- net/sctp/probe.c | 13 +- net/sctp/sm_sideeffect.c | 50 +- net/sctp/sm_statefuns.c | 1316 ++++++++++++++++++++++++---------------------- 4 files changed, 717 insertions(+), 681 deletions(-) (limited to 'net') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 7ad240228a0f..33077f317995 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -53,7 +53,7 @@ /* * Possible values for the disposition are: */ -typedef enum { +enum sctp_disposition { SCTP_DISPOSITION_DISCARD, /* No further processing. */ SCTP_DISPOSITION_CONSUME, /* Process return values normally. */ SCTP_DISPOSITION_NOMEM, /* We ran out of memory--recover. */ @@ -63,14 +63,15 @@ typedef enum { SCTP_DISPOSITION_NOT_IMPL, /* This entry is not implemented. */ SCTP_DISPOSITION_ERROR, /* This is plain old user error. */ SCTP_DISPOSITION_BUG, /* This is a bug. */ -} sctp_disposition_t; - -typedef sctp_disposition_t (sctp_state_fn_t) (struct net *, - const struct sctp_endpoint *, - const struct sctp_association *, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *); +}; + +typedef enum sctp_disposition (sctp_state_fn_t) ( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands); typedef void (sctp_timer_event_t) (unsigned long); struct sctp_sm_table_entry { sctp_state_fn_t *fn; diff --git a/net/sctp/probe.c b/net/sctp/probe.c index 34097a167431..1280f85a598d 100644 --- a/net/sctp/probe.c +++ b/net/sctp/probe.c @@ -127,12 +127,13 @@ static const struct file_operations sctpprobe_fops = { .llseek = noop_llseek, }; -static sctp_disposition_t jsctp_sf_eat_sack(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +static enum sctp_disposition jsctp_sf_eat_sack( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sk_buff *skb = chunk->skb; diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index 2bc1204becbd..e6a2974e020e 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -57,7 +57,7 @@ static int sctp_cmd_interpreter(enum sctp_event event_type, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, - sctp_disposition_t status, + enum sctp_disposition status, struct sctp_cmd_seq *commands, gfp_t gfp); static int sctp_side_effects(enum sctp_event event_type, @@ -66,7 +66,7 @@ static int sctp_side_effects(enum sctp_event event_type, struct sctp_endpoint *ep, struct sctp_association **asoc, void *event_arg, - sctp_disposition_t status, + enum sctp_disposition status, struct sctp_cmd_seq *commands, gfp_t gfp); @@ -97,8 +97,8 @@ static void sctp_do_ecn_ce_work(struct sctp_association *asoc, * that was originally marked with the CE bit. */ static struct sctp_chunk *sctp_do_ecn_ecne_work(struct sctp_association *asoc, - __u32 lowest_tsn, - struct sctp_chunk *chunk) + __u32 lowest_tsn, + struct sctp_chunk *chunk) { struct sctp_chunk *repl; @@ -152,9 +152,9 @@ static void sctp_do_ecn_cwr_work(struct sctp_association *asoc, static int sctp_gen_sack(struct sctp_association *asoc, int force, struct sctp_cmd_seq *commands) { + struct sctp_transport *trans = asoc->peer.last_data_from; __u32 ctsn, max_tsn_seen; struct sctp_chunk *sack; - struct sctp_transport *trans = asoc->peer.last_data_from; int error = 0; if (force || @@ -244,11 +244,11 @@ nomem: */ void sctp_generate_t3_rtx_event(unsigned long peer) { - int error; struct sctp_transport *transport = (struct sctp_transport *) peer; struct sctp_association *asoc = transport->asoc; struct sock *sk = asoc->base.sk; struct net *net = sock_net(sk); + int error; /* Check whether a task is in the sock. */ @@ -361,12 +361,12 @@ static void sctp_generate_autoclose_event(unsigned long data) */ void sctp_generate_heartbeat_event(unsigned long data) { - int error = 0; struct sctp_transport *transport = (struct sctp_transport *) data; struct sctp_association *asoc = transport->asoc; struct sock *sk = asoc->base.sk; struct net *net = sock_net(sk); u32 elapsed, timeout; + int error = 0; bh_lock_sock(sk); if (sock_owned_by_user(sk)) { @@ -406,7 +406,7 @@ out_unlock: */ void sctp_generate_proto_unreach_event(unsigned long data) { - struct sctp_transport *transport = (struct sctp_transport *) data; + struct sctp_transport *transport = (struct sctp_transport *)data; struct sctp_association *asoc = transport->asoc; struct sock *sk = asoc->base.sk; struct net *net = sock_net(sk); @@ -472,7 +472,7 @@ out_unlock: /* Inject a SACK Timeout event into the state machine. */ static void sctp_generate_sack_event(unsigned long data) { - struct sctp_association *asoc = (struct sctp_association *) data; + struct sctp_association *asoc = (struct sctp_association *)data; sctp_generate_timeout_event(asoc, SCTP_EVENT_TIMEOUT_SACK); } @@ -610,6 +610,7 @@ static void sctp_cmd_assoc_failed(struct sctp_cmd_seq *commands, { struct sctp_ulpevent *event; struct sctp_chunk *abort; + /* Cancel any partial delivery in progress. */ sctp_ulpq_abort_pd(&asoc->ulpq, GFP_ATOMIC); @@ -991,6 +992,7 @@ static void sctp_cmd_process_fwdtsn(struct sctp_ulpq *ulpq, struct sctp_chunk *chunk) { struct sctp_fwdtsn_skip *skip; + /* Walk through all the skipped SSNs */ sctp_walk_fwdtsn(skip, chunk) { sctp_ulpq_skip(ulpq, ntohs(skip->stream), ntohs(skip->ssn)); @@ -1003,8 +1005,8 @@ static void sctp_cmd_process_fwdtsn(struct sctp_ulpq *ulpq, static void sctp_cmd_del_non_primary(struct sctp_association *asoc) { struct sctp_transport *t; - struct list_head *pos; struct list_head *temp; + struct list_head *pos; list_for_each_safe(pos, temp, &asoc->peer.transport_addr_list) { t = list_entry(pos, struct sctp_transport, transports); @@ -1145,15 +1147,15 @@ int sctp_do_sm(struct net *net, enum sctp_event event_type, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, gfp_t gfp) { - struct sctp_cmd_seq commands; - const struct sctp_sm_table_entry *state_fn; - sctp_disposition_t status; - int error = 0; typedef const char *(printfn_t)(union sctp_subtype); static printfn_t *table[] = { NULL, sctp_cname, sctp_tname, sctp_oname, sctp_pname, }; printfn_t *debug_fn __attribute__ ((unused)) = table[event_type]; + const struct sctp_sm_table_entry *state_fn; + struct sctp_cmd_seq commands; + enum sctp_disposition status; + int error = 0; /* Look up the state function, run it, and then process the * side effects. These three steps are the heart of lksctp. @@ -1183,7 +1185,7 @@ static int sctp_side_effects(enum sctp_event event_type, struct sctp_endpoint *ep, struct sctp_association **asoc, void *event_arg, - sctp_disposition_t status, + enum sctp_disposition status, struct sctp_cmd_seq *commands, gfp_t gfp) { @@ -1269,23 +1271,21 @@ static int sctp_cmd_interpreter(enum sctp_event event_type, struct sctp_endpoint *ep, struct sctp_association *asoc, void *event_arg, - sctp_disposition_t status, + enum sctp_disposition status, struct sctp_cmd_seq *commands, gfp_t gfp) { - struct sock *sk = ep->base.sk; - struct sctp_sock *sp = sctp_sk(sk); - int error = 0; - int force; - struct sctp_cmd *cmd; - struct sctp_chunk *new_obj; - struct sctp_chunk *chunk = NULL; + struct sctp_sock *sp = sctp_sk(ep->base.sk); + struct sctp_chunk *chunk = NULL, *new_obj; struct sctp_packet *packet; + struct sctp_sackhdr sackh; struct timer_list *timer; - unsigned long timeout; struct sctp_transport *t; - struct sctp_sackhdr sackh; + unsigned long timeout; + struct sctp_cmd *cmd; int local_cork = 0; + int error = 0; + int force; if (SCTP_EVENT_T_TIMEOUT != event_type) chunk = event_arg; diff --git a/net/sctp/sm_statefuns.c b/net/sctp/sm_statefuns.c index adc1dde34bfe..8f8ccded13e4 100644 --- a/net/sctp/sm_statefuns.c +++ b/net/sctp/sm_statefuns.c @@ -59,37 +59,41 @@ #include #include -static struct sctp_packet *sctp_abort_pkt_new(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - struct sctp_chunk *chunk, - const void *payload, - size_t paylen); +static struct sctp_packet *sctp_abort_pkt_new( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + struct sctp_chunk *chunk, + const void *payload, size_t paylen); static int sctp_eat_data(const struct sctp_association *asoc, struct sctp_chunk *chunk, struct sctp_cmd_seq *commands); -static struct sctp_packet *sctp_ootb_pkt_new(struct net *net, - const struct sctp_association *asoc, - const struct sctp_chunk *chunk); +static struct sctp_packet *sctp_ootb_pkt_new( + struct net *net, + const struct sctp_association *asoc, + const struct sctp_chunk *chunk); static void sctp_send_stale_cookie_err(struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *chunk, struct sctp_cmd_seq *commands, struct sctp_chunk *err_chunk); -static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands); -static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands); -static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, +static enum sctp_disposition sctp_sf_do_5_2_6_stale( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands); +static enum sctp_disposition sctp_sf_shut_8_4_5( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands); +static enum sctp_disposition sctp_sf_tabort_8_4_8( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, @@ -97,61 +101,63 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, struct sctp_cmd_seq *commands); static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk); -static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, - struct sctp_cmd_seq *commands, - __be16 error, int sk_err, - const struct sctp_association *asoc, - struct sctp_transport *transport); +static enum sctp_disposition sctp_stop_t1_and_abort( + struct net *net, + struct sctp_cmd_seq *commands, + __be16 error, int sk_err, + const struct sctp_association *asoc, + struct sctp_transport *transport); -static sctp_disposition_t sctp_sf_abort_violation( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - void *arg, - struct sctp_cmd_seq *commands, - const __u8 *payload, - const size_t paylen); +static enum sctp_disposition sctp_sf_abort_violation( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + void *arg, + struct sctp_cmd_seq *commands, + const __u8 *payload, + const size_t paylen); -static sctp_disposition_t sctp_sf_violation_chunklen( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands); +static enum sctp_disposition sctp_sf_violation_chunklen( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands); -static sctp_disposition_t sctp_sf_violation_paramlen( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, void *ext, - struct sctp_cmd_seq *commands); +static enum sctp_disposition sctp_sf_violation_paramlen( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, void *ext, + struct sctp_cmd_seq *commands); -static sctp_disposition_t sctp_sf_violation_ctsn( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands); +static enum sctp_disposition sctp_sf_violation_ctsn( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands); -static sctp_disposition_t sctp_sf_violation_chunk( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands); +static enum sctp_disposition sctp_sf_violation_chunk( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands); static enum sctp_ierror sctp_sf_authenticate( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - struct sctp_chunk *chunk); + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + struct sctp_chunk *chunk); -static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, +static enum sctp_disposition __sctp_sf_do_9_1_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, @@ -165,8 +171,8 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, * false = Invalid length * */ -static inline bool -sctp_chunk_length_valid(struct sctp_chunk *chunk, __u16 required_length) +static inline bool sctp_chunk_length_valid(struct sctp_chunk *chunk, + __u16 required_length) { __u16 chunk_length = ntohs(chunk->chunk_hdr->length); @@ -214,12 +220,11 @@ sctp_chunk_length_valid(struct sctp_chunk *chunk, __u16 required_length) * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_4_C(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_4_C(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; @@ -300,12 +305,12 @@ sctp_disposition_t sctp_sf_do_4_C(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_1B_init(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_5_1B_init(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg, *repl, *err_chunk; struct sctp_unrecognized_param *unk_param; @@ -494,15 +499,15 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_5_1C_ack(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *chunk = arg; struct sctp_init_chunk *initchunk; + struct sctp_chunk *chunk = arg; struct sctp_chunk *err_chunk; struct sctp_packet *packet; @@ -644,20 +649,21 @@ sctp_disposition_t sctp_sf_do_5_1C_ack(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_1D_ce(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_5_1D_ce(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *chunk = arg; + struct sctp_ulpevent *ev, *ai_ev = NULL; struct sctp_association *new_asoc; struct sctp_init_chunk *peer_init; - struct sctp_chunk *repl; - struct sctp_ulpevent *ev, *ai_ev = NULL; - int error = 0; + struct sctp_chunk *chunk = arg; struct sctp_chunk *err_chk_p; + struct sctp_chunk *repl; struct sock *sk; + int error = 0; /* If the packet is an OOTB packet which is temporarily on the * control endpoint, respond with an ABORT. @@ -871,11 +877,12 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_1E_ca(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_5_1E_ca(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_ulpevent *ev; @@ -949,11 +956,12 @@ nomem: } /* Generate and sendout a heartbeat packet. */ -static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +static enum sctp_disposition sctp_sf_heartbeat( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_transport *transport = (struct sctp_transport *) arg; struct sctp_chunk *reply; @@ -974,12 +982,12 @@ static sctp_disposition_t sctp_sf_heartbeat(const struct sctp_endpoint *ep, } /* Generate a HEARTBEAT packet on the given transport. */ -sctp_disposition_t sctp_sf_sendbeat_8_3(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_sendbeat_8_3(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_transport *transport = (struct sctp_transport *) arg; @@ -1022,11 +1030,12 @@ sctp_disposition_t sctp_sf_sendbeat_8_3(struct net *net, } /* resend asoc strreset_chunk. */ -sctp_disposition_t sctp_sf_send_reconf(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_send_reconf(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_transport *transport = arg; @@ -1073,12 +1082,11 @@ sctp_disposition_t sctp_sf_send_reconf(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_beat_8_3(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_beat_8_3(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, struct sctp_cmd_seq *commands) { struct sctp_paramhdr *param_hdr; struct sctp_chunk *chunk = arg; @@ -1148,12 +1156,12 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_backbeat_8_3(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_backbeat_8_3(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_sender_hb_info *hbinfo; struct sctp_chunk *chunk = arg; @@ -1227,13 +1235,13 @@ static int sctp_sf_send_restart_abort(struct net *net, union sctp_addr *ssa, struct sctp_chunk *init, struct sctp_cmd_seq *commands) { - int len; - struct sctp_packet *pkt; + struct sctp_af *af = sctp_get_af_specific(ssa->v4.sin_family); union sctp_addr_param *addrparm; struct sctp_errhdr *errhdr; - struct sctp_endpoint *ep; char buffer[sizeof(*errhdr) + sizeof(*addrparm)]; - struct sctp_af *af = sctp_get_af_specific(ssa->v4.sin_family); + struct sctp_endpoint *ep; + struct sctp_packet *pkt; + int len; /* Build the error on the stack. We are way to malloc crazy * throughout the code today. @@ -1410,18 +1418,19 @@ static char sctp_tietags_compare(struct sctp_association *new_asoc, /* Common helper routine for both duplicate and simulataneous INIT * chunk handling. */ -static sctp_disposition_t sctp_sf_do_unexpected_init( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, struct sctp_cmd_seq *commands) +static enum sctp_disposition sctp_sf_do_unexpected_init( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg, *repl, *err_chunk; struct sctp_unrecognized_param *unk_param; struct sctp_association *new_asoc; + enum sctp_disposition retval; struct sctp_packet *packet; - sctp_disposition_t retval; int len; /* 6.10 Bundling @@ -1622,12 +1631,13 @@ cleanup: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_2_1_siminit(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_5_2_1_siminit( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* Call helper to do the real work for both simulataneous and * duplicate INIT chunk handling. @@ -1676,7 +1686,8 @@ sctp_disposition_t sctp_sf_do_5_2_1_siminit(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_2_2_dupinit(struct net *net, +enum sctp_disposition sctp_sf_do_5_2_2_dupinit( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, @@ -1699,12 +1710,13 @@ sctp_disposition_t sctp_sf_do_5_2_2_dupinit(struct net *net, * An unexpected INIT ACK usually indicates the processing of an old or * duplicated INIT chunk. */ -sctp_disposition_t sctp_sf_do_5_2_3_initack(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_5_2_3_initack( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* Per the above section, we'll discard the chunk if we have an * endpoint. If this is an OOTB INIT-ACK, treat it as such. @@ -1720,7 +1732,8 @@ sctp_disposition_t sctp_sf_do_5_2_3_initack(struct net *net, * Section 5.2.4 * A) In this case, the peer may have restarted. */ -static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net, +static enum sctp_disposition sctp_sf_do_dupcook_a( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, @@ -1728,10 +1741,10 @@ static sctp_disposition_t sctp_sf_do_dupcook_a(struct net *net, struct sctp_association *new_asoc) { struct sctp_init_chunk *peer_init; + enum sctp_disposition disposition; struct sctp_ulpevent *ev; struct sctp_chunk *repl; struct sctp_chunk *err; - sctp_disposition_t disposition; /* new_asoc is a brand-new association, so these are not yet * side effects--it is safe to run them here. @@ -1835,7 +1848,8 @@ nomem: * after responding to the local endpoint's INIT */ /* This case represents an initialization collision. */ -static sctp_disposition_t sctp_sf_do_dupcook_b(struct net *net, +static enum sctp_disposition sctp_sf_do_dupcook_b( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, @@ -1906,7 +1920,8 @@ nomem: * but a new tag of its own. */ /* This case represents an initialization collision. */ -static sctp_disposition_t sctp_sf_do_dupcook_c(struct net *net, +static enum sctp_disposition sctp_sf_do_dupcook_c( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, @@ -1928,7 +1943,8 @@ static sctp_disposition_t sctp_sf_do_dupcook_c(struct net *net, * enter the ESTABLISHED state, if it has not already done so. */ /* This case represents an initialization collision. */ -static sctp_disposition_t sctp_sf_do_dupcook_d(struct net *net, +static enum sctp_disposition sctp_sf_do_dupcook_d( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, struct sctp_chunk *chunk, @@ -2023,19 +2039,20 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_5_2_4_dupcook(struct net *net, +enum sctp_disposition sctp_sf_do_5_2_4_dupcook( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, struct sctp_cmd_seq *commands) { - sctp_disposition_t retval; - struct sctp_chunk *chunk = arg; struct sctp_association *new_asoc; + struct sctp_chunk *chunk = arg; + enum sctp_disposition retval; + struct sctp_chunk *err_chk_p; int error = 0; char action; - struct sctp_chunk *err_chk_p; /* Make sure that the chunk has a valid length from the protocol * perspective. In this case check to make sure we have at least @@ -2141,13 +2158,13 @@ nomem: * * See sctp_sf_do_9_1_abort(). */ -sctp_disposition_t sctp_sf_shutdown_pending_abort( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_shutdown_pending_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -2184,7 +2201,8 @@ sctp_disposition_t sctp_sf_shutdown_pending_abort( * * See sctp_sf_do_9_1_abort(). */ -sctp_disposition_t sctp_sf_shutdown_sent_abort(struct net *net, +enum sctp_disposition sctp_sf_shutdown_sent_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, @@ -2234,13 +2252,13 @@ sctp_disposition_t sctp_sf_shutdown_sent_abort(struct net *net, * * See sctp_sf_do_9_1_abort(). */ -sctp_disposition_t sctp_sf_shutdown_ack_sent_abort( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_shutdown_ack_sent_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* The same T2 timer, so we should be able to use * common function with the SHUTDOWN-SENT state. @@ -2262,7 +2280,8 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_abort( * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_cookie_echoed_err(struct net *net, +enum sctp_disposition sctp_sf_cookie_echoed_err( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, @@ -2326,12 +2345,13 @@ sctp_disposition_t sctp_sf_cookie_echoed_err(struct net *net, * * The return value is the disposition of the chunk. */ -static sctp_disposition_t sctp_sf_do_5_2_6_stale(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +static enum sctp_disposition sctp_sf_do_5_2_6_stale( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { int attempts = asoc->init_err_counter + 1; struct sctp_chunk *chunk = arg, *reply; @@ -2448,7 +2468,8 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_9_1_abort(struct net *net, +enum sctp_disposition sctp_sf_do_9_1_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, @@ -2485,16 +2506,17 @@ sctp_disposition_t sctp_sf_do_9_1_abort(struct net *net, return __sctp_sf_do_9_1_abort(net, ep, asoc, type, arg, commands); } -static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, +static enum sctp_disposition __sctp_sf_do_9_1_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, void *arg, struct sctp_cmd_seq *commands) { + __be16 error = SCTP_ERROR_NO_ERROR; struct sctp_chunk *chunk = arg; unsigned int len; - __be16 error = SCTP_ERROR_NO_ERROR; /* See if we have an error cause code in the chunk. */ len = ntohs(chunk->chunk_hdr->length); @@ -2523,16 +2545,17 @@ static sctp_disposition_t __sctp_sf_do_9_1_abort(struct net *net, * * See sctp_sf_do_9_1_abort() above. */ -sctp_disposition_t sctp_sf_cookie_wait_abort(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_cookie_wait_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { + __be16 error = SCTP_ERROR_NO_ERROR; struct sctp_chunk *chunk = arg; unsigned int len; - __be16 error = SCTP_ERROR_NO_ERROR; if (!sctp_vtag_verify_either(chunk, asoc)) return sctp_sf_pdiscard(net, ep, asoc, type, arg, commands); @@ -2562,7 +2585,8 @@ sctp_disposition_t sctp_sf_cookie_wait_abort(struct net *net, /* * Process an incoming ICMP as an ABORT. (COOKIE-WAIT state) */ -sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(struct net *net, +enum sctp_disposition sctp_sf_cookie_wait_icmp_abort( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, @@ -2577,12 +2601,13 @@ sctp_disposition_t sctp_sf_cookie_wait_icmp_abort(struct net *net, /* * Process an ABORT. (COOKIE-ECHOED state) */ -sctp_disposition_t sctp_sf_cookie_echoed_abort(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_cookie_echoed_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* There is a single T1 timer, so we should be able to use * common function with the COOKIE-WAIT state. @@ -2595,11 +2620,12 @@ sctp_disposition_t sctp_sf_cookie_echoed_abort(struct net *net, * * This is common code called by several sctp_sf_*_abort() functions above. */ -static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, - struct sctp_cmd_seq *commands, - __be16 error, int sk_err, - const struct sctp_association *asoc, - struct sctp_transport *transport) +static enum sctp_disposition sctp_stop_t1_and_abort( + struct net *net, + struct sctp_cmd_seq *commands, + __be16 error, int sk_err, + const struct sctp_association *asoc, + struct sctp_transport *transport) { pr_debug("%s: ABORT received (INIT)\n", __func__); @@ -2649,15 +2675,16 @@ static sctp_disposition_t sctp_stop_t1_and_abort(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_9_2_shutdown(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) -{ +enum sctp_disposition sctp_sf_do_9_2_shutdown( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) +{ + enum sctp_disposition disposition; struct sctp_chunk *chunk = arg; - sctp_disposition_t disposition; struct sctp_shutdownhdr *sdh; struct sctp_ulpevent *ev; __u32 ctsn; @@ -2738,12 +2765,13 @@ out: * The Cumulative TSN Ack of the received SHUTDOWN chunk * MUST be processed. */ -sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_9_2_shut_ctsn( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_shutdownhdr *sdh; @@ -2791,14 +2819,15 @@ sctp_disposition_t sctp_sf_do_9_2_shut_ctsn(struct net *net, * that belong to this association, it should discard the INIT chunk and * retransmit the SHUTDOWN ACK chunk. */ -sctp_disposition_t sctp_sf_do_9_2_reshutack(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_9_2_reshutack( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *chunk = (struct sctp_chunk *) arg; + struct sctp_chunk *chunk = arg; struct sctp_chunk *reply; /* Make sure that the chunk has a valid length */ @@ -2855,12 +2884,12 @@ nomem: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_ecn_cwr(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_cwrhdr *cwr; @@ -2911,12 +2940,11 @@ sctp_disposition_t sctp_sf_do_ecn_cwr(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_ecne(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_ecne(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_ecnehdr *ecne; @@ -2968,12 +2996,12 @@ sctp_disposition_t sctp_sf_do_ecne(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_eat_data_6_2(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_eat_data_6_2(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { union sctp_arg force = SCTP_NOFORCE(); struct sctp_chunk *chunk = arg; @@ -3088,12 +3116,13 @@ discard_noforce: * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_eat_data_fast_4_4( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; int error; @@ -3179,12 +3208,12 @@ sctp_disposition_t sctp_sf_eat_data_fast_4_4(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_eat_sack_6_2(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_sackhdr *sackh; @@ -3253,7 +3282,8 @@ sctp_disposition_t sctp_sf_eat_sack_6_2(struct net *net, * * The return value is the disposition of the chunk. */ -static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, +static enum sctp_disposition sctp_sf_tabort_8_4_8( + struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, const union sctp_subtype type, @@ -3303,12 +3333,12 @@ static sctp_disposition_t sctp_sf_tabort_8_4_8(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_operr_notify(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_operr_notify(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_errhdr *err; @@ -3341,12 +3371,12 @@ sctp_disposition_t sctp_sf_operr_notify(struct net *net, * * The return value is the disposition. */ -sctp_disposition_t sctp_sf_do_9_2_final(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_9_2_final(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_chunk *reply; @@ -3424,20 +3454,19 @@ nomem: * receiver of the OOTB packet shall discard the OOTB packet and take * no further action. */ -sctp_disposition_t sctp_sf_ootb(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_ootb(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sk_buff *skb = chunk->skb; struct sctp_chunkhdr *ch; struct sctp_errhdr *err; - __u8 *ch_end; - int ootb_shut_ack = 0; int ootb_cookie_ack = 0; + int ootb_shut_ack = 0; + __u8 *ch_end; SCTP_INC_STATS(net, SCTP_MIB_OUTOFBLUES); @@ -3513,16 +3542,17 @@ sctp_disposition_t sctp_sf_ootb(struct net *net, * (endpoint, asoc, type, arg, commands) * * Outputs - * (sctp_disposition_t) + * (enum sctp_disposition) * * The return value is the disposition of the chunk. */ -static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +static enum sctp_disposition sctp_sf_shut_8_4_5( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; @@ -3579,12 +3609,12 @@ static sctp_disposition_t sctp_sf_shut_8_4_5(struct net *net, * chunks. --piggy ] * */ -sctp_disposition_t sctp_sf_do_8_5_1_E_sa(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_8_5_1_E_sa(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -3604,17 +3634,18 @@ sctp_disposition_t sctp_sf_do_8_5_1_E_sa(struct net *net, } /* ADDIP Section 4.2 Upon reception of an ASCONF Chunk. */ -sctp_disposition_t sctp_sf_do_asconf(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_asconf(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *chunk = arg; - struct sctp_chunk *asconf_ack = NULL; - struct sctp_paramhdr *err_param = NULL; - struct sctp_addiphdr *hdr; - __u32 serial; + struct sctp_paramhdr *err_param = NULL; + struct sctp_chunk *asconf_ack = NULL; + struct sctp_chunk *chunk = arg; + struct sctp_addiphdr *hdr; + __u32 serial; if (!sctp_vtag_verify(chunk, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, @@ -3721,19 +3752,19 @@ sctp_disposition_t sctp_sf_do_asconf(struct net *net, * When building TLV parameters for the ASCONF Chunk that will add or * delete IP addresses the D0 to D13 rules should be applied: */ -sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_asconf_ack(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *asconf_ack = arg; - struct sctp_chunk *last_asconf = asoc->addip_last_asconf; - struct sctp_chunk *abort; - struct sctp_paramhdr *err_param = NULL; - struct sctp_addiphdr *addip_hdr; - __u32 sent_serial, rcvd_serial; + struct sctp_chunk *last_asconf = asoc->addip_last_asconf; + struct sctp_paramhdr *err_param = NULL; + struct sctp_chunk *asconf_ack = arg; + struct sctp_addiphdr *addip_hdr; + __u32 sent_serial, rcvd_serial; + struct sctp_chunk *abort; if (!sctp_vtag_verify(asconf_ack, asoc)) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_BAD_TAG, @@ -3840,11 +3871,12 @@ sctp_disposition_t sctp_sf_do_asconf_ack(struct net *net, } /* RE-CONFIG Section 5.2 Upon reception of an RECONF Chunk. */ -sctp_disposition_t sctp_sf_do_reconf(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_reconf(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_paramhdr *err_param = NULL; struct sctp_chunk *chunk = arg; @@ -3916,15 +3948,15 @@ sctp_disposition_t sctp_sf_do_reconf(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_eat_fwd_tsn(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_eat_fwd_tsn(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *chunk = arg; struct sctp_fwdtsn_hdr *fwdtsn_hdr; + struct sctp_chunk *chunk = arg; struct sctp_fwdtsn_skip *skip; __u16 len; __u32 tsn; @@ -3986,16 +4018,16 @@ discard_noforce: return SCTP_DISPOSITION_DISCARD; } -sctp_disposition_t sctp_sf_eat_fwd_tsn_fast( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_eat_fwd_tsn_fast( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *chunk = arg; struct sctp_fwdtsn_hdr *fwdtsn_hdr; + struct sctp_chunk *chunk = arg; struct sctp_fwdtsn_skip *skip; __u16 len; __u32 tsn; @@ -4079,18 +4111,17 @@ gen_shutdown: * The return value is the disposition of the chunk. */ static enum sctp_ierror sctp_sf_authenticate( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - struct sctp_chunk *chunk) + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + struct sctp_chunk *chunk) { struct sctp_authhdr *auth_hdr; + __u8 *save_digest, *digest; struct sctp_hmac *hmac; unsigned int sig_len; __u16 key_id; - __u8 *save_digest; - __u8 *digest; /* Pull in the auth header, so we can do some more verification */ auth_hdr = (struct sctp_authhdr *)chunk->skb->data; @@ -4154,12 +4185,11 @@ nomem: return SCTP_IERROR_NOMEM; } -sctp_disposition_t sctp_sf_eat_auth(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_eat_auth(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; struct sctp_authhdr *auth_hdr; @@ -4251,12 +4281,12 @@ sctp_disposition_t sctp_sf_eat_auth(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_unk_chunk(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_unk_chunk(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *unk_chunk = arg; struct sctp_chunk *err_chunk; @@ -4331,12 +4361,12 @@ sctp_disposition_t sctp_sf_unk_chunk(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_discard_chunk(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_discard_chunk(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -4371,12 +4401,11 @@ sctp_disposition_t sctp_sf_discard_chunk(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_pdiscard(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_pdiscard(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, struct sctp_cmd_seq *commands) { SCTP_INC_STATS(net, SCTP_MIB_IN_PKT_DISCARDS); sctp_add_cmd_sf(commands, SCTP_CMD_DISCARD_PACKET, SCTP_NULL()); @@ -4399,12 +4428,12 @@ sctp_disposition_t sctp_sf_pdiscard(struct net *net, * We simply tag the chunk as a violation. The state machine will log * the violation and continue. */ -sctp_disposition_t sctp_sf_violation(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_violation(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -4419,14 +4448,14 @@ sctp_disposition_t sctp_sf_violation(struct net *net, /* * Common function to handle a protocol violation. */ -static sctp_disposition_t sctp_sf_abort_violation( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - void *arg, - struct sctp_cmd_seq *commands, - const __u8 *payload, - const size_t paylen) +static enum sctp_disposition sctp_sf_abort_violation( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + void *arg, + struct sctp_cmd_seq *commands, + const __u8 *payload, + const size_t paylen) { struct sctp_packet *packet = NULL; struct sctp_chunk *chunk = arg; @@ -4536,18 +4565,18 @@ nomem: * * Generate an ABORT chunk and terminate the association. */ -static sctp_disposition_t sctp_sf_violation_chunklen( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +static enum sctp_disposition sctp_sf_violation_chunklen( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { static const char err_str[] = "The following chunk had invalid length:"; return sctp_sf_abort_violation(net, ep, asoc, arg, commands, err_str, - sizeof(err_str)); + sizeof(err_str)); } /* @@ -4556,17 +4585,17 @@ static sctp_disposition_t sctp_sf_violation_chunklen( * or accumulated length in multi parameters exceeds the end of the chunk, * the length is considered as invalid. */ -static sctp_disposition_t sctp_sf_violation_paramlen( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, void *ext, - struct sctp_cmd_seq *commands) +static enum sctp_disposition sctp_sf_violation_paramlen( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, void *ext, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *chunk = arg; struct sctp_paramhdr *param = ext; struct sctp_chunk *abort = NULL; + struct sctp_chunk *chunk = arg; if (sctp_auth_recv_cid(SCTP_CID_ABORT, asoc)) goto discard; @@ -4599,18 +4628,18 @@ nomem: * We inform the other end by sending an ABORT with a Protocol Violation * error code. */ -static sctp_disposition_t sctp_sf_violation_ctsn( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +static enum sctp_disposition sctp_sf_violation_ctsn( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { static const char err_str[] = "The cumulative tsn ack beyond the max tsn currently sent:"; return sctp_sf_abort_violation(net, ep, asoc, arg, commands, err_str, - sizeof(err_str)); + sizeof(err_str)); } /* Handle protocol violation of an invalid chunk bundling. For example, @@ -4619,13 +4648,13 @@ static sctp_disposition_t sctp_sf_violation_ctsn( * statement from the specs. Additionally, there might be an attacker * on the path and we may not want to continue this communication. */ -static sctp_disposition_t sctp_sf_violation_chunk( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +static enum sctp_disposition sctp_sf_violation_chunk( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { static const char err_str[] = "The following chunk violates protocol:"; @@ -4633,7 +4662,7 @@ static sctp_disposition_t sctp_sf_violation_chunk( return sctp_sf_violation(net, ep, asoc, type, arg, commands); return sctp_sf_abort_violation(net, ep, asoc, arg, commands, err_str, - sizeof(err_str)); + sizeof(err_str)); } /*************************************************************************** * These are the state functions for handling primitive (Section 10) events. @@ -4695,15 +4724,15 @@ static sctp_disposition_t sctp_sf_violation_chunk( * * The return value is a disposition. */ -sctp_disposition_t sctp_sf_do_prm_asoc(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_prm_asoc(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *repl; struct sctp_association *my_asoc; + struct sctp_chunk *repl; /* The comment below says that we enter COOKIE-WAIT AFTER * sending the INIT, but that doesn't actually work in our @@ -4807,12 +4836,12 @@ nomem: * * The return value is the disposition. */ -sctp_disposition_t sctp_sf_do_prm_send(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_prm_send(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_datamsg *msg = arg; @@ -4846,15 +4875,15 @@ sctp_disposition_t sctp_sf_do_prm_send(struct net *net, * * The return value is the disposition. */ -sctp_disposition_t sctp_sf_do_9_2_prm_shutdown( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_9_2_prm_shutdown( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - int disposition; + enum sctp_disposition disposition; /* From 9.2 Shutdown of an Association * Upon receipt of the SHUTDOWN primitive from its upper @@ -4872,6 +4901,7 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown( disposition = sctp_sf_do_9_2_start_shutdown(net, ep, asoc, type, arg, commands); } + return disposition; } @@ -4902,13 +4932,13 @@ sctp_disposition_t sctp_sf_do_9_2_prm_shutdown( * * The return value is the disposition. */ -sctp_disposition_t sctp_sf_do_9_1_prm_abort( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_9_1_prm_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* From 9.1 Abort of an Association * Upon receipt of the ABORT primitive from its upper @@ -4940,12 +4970,12 @@ sctp_disposition_t sctp_sf_do_9_1_prm_abort( } /* We tried an illegal operation on an association which is closed. */ -sctp_disposition_t sctp_sf_error_closed(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_error_closed(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR, SCTP_ERROR(-EINVAL)); return SCTP_DISPOSITION_CONSUME; @@ -4954,12 +4984,13 @@ sctp_disposition_t sctp_sf_error_closed(struct net *net, /* We tried an illegal operation on an association which is shutting * down. */ -sctp_disposition_t sctp_sf_error_shutdown(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_error_shutdown( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { sctp_add_cmd_sf(commands, SCTP_CMD_REPORT_ERROR, SCTP_ERROR(-ESHUTDOWN)); @@ -4980,13 +5011,13 @@ sctp_disposition_t sctp_sf_error_shutdown(struct net *net, * Outputs * (timers) */ -sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_cookie_wait_prm_shutdown( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, SCTP_TO(SCTP_EVENT_TIMEOUT_T1_INIT)); @@ -5015,12 +5046,13 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_shutdown( * Outputs * (timers) */ -sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_cookie_echoed_prm_shutdown( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* There is a single T1 timer, so we should be able to use * common function with the COOKIE-WAIT state. @@ -5042,13 +5074,13 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_shutdown( * Outputs * (timers) */ -sctp_disposition_t sctp_sf_cookie_wait_prm_abort( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_cookie_wait_prm_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *abort = arg; @@ -5091,13 +5123,13 @@ sctp_disposition_t sctp_sf_cookie_wait_prm_abort( * Outputs * (timers) */ -sctp_disposition_t sctp_sf_cookie_echoed_prm_abort( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_cookie_echoed_prm_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* There is a single T1 timer, so we should be able to use * common function with the COOKIE-WAIT state. @@ -5117,13 +5149,13 @@ sctp_disposition_t sctp_sf_cookie_echoed_prm_abort( * Outputs * (timers) */ -sctp_disposition_t sctp_sf_shutdown_pending_prm_abort( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_shutdown_pending_prm_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* Stop the T5-shutdown guard timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -5144,13 +5176,13 @@ sctp_disposition_t sctp_sf_shutdown_pending_prm_abort( * Outputs * (timers) */ -sctp_disposition_t sctp_sf_shutdown_sent_prm_abort( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_shutdown_sent_prm_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* Stop the T2-shutdown timer. */ sctp_add_cmd_sf(commands, SCTP_CMD_TIMER_STOP, @@ -5175,13 +5207,13 @@ sctp_disposition_t sctp_sf_shutdown_sent_prm_abort( * Outputs * (timers) */ -sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_shutdown_ack_sent_prm_abort( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { /* The same T2 timer, so we should be able to use * common function with the SHUTDOWN-SENT state. @@ -5211,7 +5243,7 @@ sctp_disposition_t sctp_sf_shutdown_ack_sent_prm_abort( * o destination transport address - the transport address of the * association on which a heartbeat should be issued. */ -sctp_disposition_t sctp_sf_do_prm_requestheartbeat( +enum sctp_disposition sctp_sf_do_prm_requestheartbeat( struct net *net, const struct sctp_endpoint *ep, const struct sctp_association *asoc, @@ -5244,12 +5276,12 @@ sctp_disposition_t sctp_sf_do_prm_requestheartbeat( * When an endpoint has an ASCONF signaled change to be sent to the * remote endpoint it should do A1 to A9 */ -sctp_disposition_t sctp_sf_do_prm_asconf(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_prm_asconf(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -5261,12 +5293,12 @@ sctp_disposition_t sctp_sf_do_prm_asconf(struct net *net, } /* RE-CONFIG Section 5.1 RECONF Chunk Procedures */ -sctp_disposition_t sctp_sf_do_prm_reconf(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_prm_reconf(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = arg; @@ -5279,13 +5311,13 @@ sctp_disposition_t sctp_sf_do_prm_reconf(struct net *net, * * The return value is the disposition of the primitive. */ -sctp_disposition_t sctp_sf_ignore_primitive( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_ignore_primitive( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { pr_debug("%s: primitive type:%d is ignored\n", __func__, type.primitive); @@ -5303,13 +5335,13 @@ sctp_disposition_t sctp_sf_ignore_primitive( * subscribes to this event, if there is no data to be sent or * retransmit, the stack will immediately send up this notification. */ -sctp_disposition_t sctp_sf_do_no_pending_tsn( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_no_pending_tsn( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_ulpevent *event; @@ -5335,13 +5367,13 @@ sctp_disposition_t sctp_sf_do_no_pending_tsn( * * The return value is the disposition. */ -sctp_disposition_t sctp_sf_do_9_2_start_shutdown( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_9_2_start_shutdown( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *reply; @@ -5405,15 +5437,15 @@ nomem: * * The return value is the disposition. */ -sctp_disposition_t sctp_sf_do_9_2_shutdown_ack( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_9_2_shutdown_ack( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *chunk = (struct sctp_chunk *) arg; + struct sctp_chunk *chunk = arg; struct sctp_chunk *reply; /* There are 2 ways of getting here: @@ -5479,12 +5511,12 @@ nomem: * * The return value is the disposition of the event. */ -sctp_disposition_t sctp_sf_ignore_other(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_ignore_other(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { pr_debug("%s: the event other type:%d is ignored\n", __func__, type.other); @@ -5507,12 +5539,12 @@ sctp_disposition_t sctp_sf_ignore_other(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_do_6_3_3_rtx(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_6_3_3_rtx(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_transport *transport = arg; @@ -5595,12 +5627,12 @@ sctp_disposition_t sctp_sf_do_6_3_3_rtx(struct net *net, * allow. However, an SCTP transmitter MUST NOT be more aggressive than * the following algorithms allow. */ -sctp_disposition_t sctp_sf_do_6_2_sack(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_do_6_2_sack(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { SCTP_INC_STATS(net, SCTP_MIB_DELAY_SACK_EXPIREDS); sctp_add_cmd_sf(commands, SCTP_CMD_GEN_SACK, SCTP_FORCE()); @@ -5626,16 +5658,17 @@ sctp_disposition_t sctp_sf_do_6_2_sack(struct net *net, * (timers, events) * */ -sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_t1_init_timer_expire( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { + int attempts = asoc->init_err_counter + 1; struct sctp_chunk *repl = NULL; struct sctp_bind_addr *bp; - int attempts = asoc->init_err_counter + 1; pr_debug("%s: timer T1 expired (INIT)\n", __func__); @@ -5690,15 +5723,16 @@ sctp_disposition_t sctp_sf_t1_init_timer_expire(struct net *net, * (timers, events) * */ -sctp_disposition_t sctp_sf_t1_cookie_timer_expire(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_t1_cookie_timer_expire( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - struct sctp_chunk *repl = NULL; int attempts = asoc->init_err_counter + 1; + struct sctp_chunk *repl = NULL; pr_debug("%s: timer T1 expired (COOKIE-ECHO)\n", __func__); @@ -5740,12 +5774,13 @@ sctp_disposition_t sctp_sf_t1_cookie_timer_expire(struct net *net, * the T2-Shutdown timer, giving its peer ample opportunity to transmit * all of its queued DATA chunks that have not yet been sent. */ -sctp_disposition_t sctp_sf_t2_timer_expire(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_t2_timer_expire( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *reply = NULL; @@ -5810,13 +5845,13 @@ nomem: * ADDIP Section 4.1 ASCONF CHunk Procedures * If the T4 RTO timer expires the endpoint should do B1 to B5 */ -sctp_disposition_t sctp_sf_t4_timer_expire( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_t4_timer_expire( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *chunk = asoc->addip_last_asconf; struct sctp_transport *transport = chunk->transport; @@ -5882,12 +5917,13 @@ sctp_disposition_t sctp_sf_t4_timer_expire( * At the expiration of this timer the sender SHOULD abort the association * by sending an ABORT chunk. */ -sctp_disposition_t sctp_sf_t5_timer_expire(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_t5_timer_expire( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { struct sctp_chunk *reply = NULL; @@ -5918,15 +5954,15 @@ nomem: * The work that needs to be done is same as when SHUTDOWN is initiated by * the user. So this routine looks same as sctp_sf_do_9_2_prm_shutdown(). */ -sctp_disposition_t sctp_sf_autoclose_timer_expire( - struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_autoclose_timer_expire( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { - int disposition; + enum sctp_disposition disposition; SCTP_INC_STATS(net, SCTP_MIB_AUTOCLOSE_EXPIREDS); @@ -5946,6 +5982,7 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire( disposition = sctp_sf_do_9_2_start_shutdown(net, ep, asoc, type, arg, commands); } + return disposition; } @@ -5961,12 +5998,11 @@ sctp_disposition_t sctp_sf_autoclose_timer_expire( * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_not_impl(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_not_impl(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, struct sctp_cmd_seq *commands) { return SCTP_DISPOSITION_NOT_IMPL; } @@ -5979,12 +6015,11 @@ sctp_disposition_t sctp_sf_not_impl(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_bug(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_bug(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, struct sctp_cmd_seq *commands) { return SCTP_DISPOSITION_BUG; } @@ -6000,12 +6035,12 @@ sctp_disposition_t sctp_sf_bug(struct net *net, * * The return value is the disposition of the chunk. */ -sctp_disposition_t sctp_sf_timer_ignore(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - const union sctp_subtype type, - void *arg, - struct sctp_cmd_seq *commands) +enum sctp_disposition sctp_sf_timer_ignore(struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + const union sctp_subtype type, + void *arg, + struct sctp_cmd_seq *commands) { pr_debug("%s: timer %d ignored\n", __func__, type.chunk); @@ -6020,9 +6055,9 @@ sctp_disposition_t sctp_sf_timer_ignore(struct net *net, static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk) { struct sctp_sackhdr *sack; + __u16 num_dup_tsns; unsigned int len; __u16 num_blocks; - __u16 num_dup_tsns; /* Protect ourselves from reading too far into * the skb from a bogus sender. @@ -6044,12 +6079,12 @@ static struct sctp_sackhdr *sctp_sm_pull_sack(struct sctp_chunk *chunk) /* Create an ABORT packet to be sent as a response, with the specified * error causes. */ -static struct sctp_packet *sctp_abort_pkt_new(struct net *net, - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - struct sctp_chunk *chunk, - const void *payload, - size_t paylen) +static struct sctp_packet *sctp_abort_pkt_new( + struct net *net, + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + struct sctp_chunk *chunk, + const void *payload, size_t paylen) { struct sctp_packet *packet; struct sctp_chunk *abort; @@ -6086,14 +6121,14 @@ static struct sctp_packet *sctp_abort_pkt_new(struct net *net, } /* Allocate a packet for responding in the OOTB conditions. */ -static struct sctp_packet *sctp_ootb_pkt_new(struct net *net, - const struct sctp_association *asoc, - const struct sctp_chunk *chunk) +static struct sctp_packet *sctp_ootb_pkt_new( + struct net *net, + const struct sctp_association *asoc, + const struct sctp_chunk *chunk) { - struct sctp_packet *packet; struct sctp_transport *transport; - __u16 sport; - __u16 dport; + struct sctp_packet *packet; + __u16 sport, dport; __u32 vtag; /* Get the source and destination port from the inbound packet. */ @@ -6202,18 +6237,17 @@ static int sctp_eat_data(const struct sctp_association *asoc, struct sctp_chunk *chunk, struct sctp_cmd_seq *commands) { - struct sctp_datahdr *data_hdr; - struct sctp_chunk *err; - size_t datalen; - enum sctp_verb deliver; - int tmp; - __u32 tsn; struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; struct sock *sk = asoc->base.sk; struct net *net = sock_net(sk); - u16 ssn; - u16 sid; + struct sctp_datahdr *data_hdr; + struct sctp_chunk *err; + enum sctp_verb deliver; + size_t datalen; u8 ordered = 0; + u16 ssn, sid; + __u32 tsn; + int tmp; data_hdr = (struct sctp_datahdr *)chunk->skb->data; chunk->subh.data_hdr = data_hdr; -- cgit v1.2.3 From 327c0dab8d1301cd866816de8c7f32eb872cabac Mon Sep 17 00:00:00 2001 From: Xin Long Date: Fri, 11 Aug 2017 10:23:58 +0800 Subject: sctp: fix some indents in sm_make_chunk.c There are some bad indents of functions' defination in sm_make_chunk.c. They have been there since beginning, it was probably caused by that the typedef sctp_chunk_t was replaced with struct sctp_chunk. So it's the best time to fix them in this patchset, it's also to fix some bad indents in other functions' defination in sm_make_chunk.c. Signed-off-by: Xin Long Signed-off-by: David S. Miller --- include/net/sctp/sm.h | 158 ++++++++++----------- net/sctp/sm_make_chunk.c | 348 +++++++++++++++++++++++------------------------ 2 files changed, 249 insertions(+), 257 deletions(-) (limited to 'net') diff --git a/include/net/sctp/sm.h b/include/net/sctp/sm.h index 33077f317995..2db3d3a9ce1d 100644 --- a/include/net/sctp/sm.h +++ b/include/net/sctp/sm.h @@ -184,68 +184,69 @@ __u32 sctp_generate_verification_tag(void); void sctp_populate_tie_tags(__u8 *cookie, __u32 curTag, __u32 hisTag); /* Prototypes for chunk-building functions. */ -struct sctp_chunk *sctp_make_init(const struct sctp_association *, - const struct sctp_bind_addr *, - gfp_t gfp, int vparam_len); -struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *, - const struct sctp_chunk *, - const gfp_t gfp, - const int unkparam_len); -struct sctp_chunk *sctp_make_cookie_echo(const struct sctp_association *, - const struct sctp_chunk *); -struct sctp_chunk *sctp_make_cookie_ack(const struct sctp_association *, - const struct sctp_chunk *); -struct sctp_chunk *sctp_make_cwr(const struct sctp_association *, +struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, + const struct sctp_bind_addr *bp, + gfp_t gfp, int vparam_len); +struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + const gfp_t gfp, const int unkparam_len); +struct sctp_chunk *sctp_make_cookie_echo(const struct sctp_association *asoc, + const struct sctp_chunk *chunk); +struct sctp_chunk *sctp_make_cookie_ack(const struct sctp_association *asoc, + const struct sctp_chunk *chunk); +struct sctp_chunk *sctp_make_cwr(const struct sctp_association *asoc, const __u32 lowest_tsn, - const struct sctp_chunk *); -struct sctp_chunk * sctp_make_datafrag_empty(struct sctp_association *, - const struct sctp_sndrcvinfo *sinfo, - int len, const __u8 flags, - __u16 ssn, gfp_t gfp); -struct sctp_chunk *sctp_make_ecne(const struct sctp_association *, - const __u32); -struct sctp_chunk *sctp_make_sack(const struct sctp_association *); + const struct sctp_chunk *chunk); +struct sctp_chunk *sctp_make_datafrag_empty(struct sctp_association *asoc, + const struct sctp_sndrcvinfo *sinfo, + int len, const __u8 flags, + __u16 ssn, gfp_t gfp); +struct sctp_chunk *sctp_make_ecne(const struct sctp_association *asoc, + const __u32 lowest_tsn); +struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc); struct sctp_chunk *sctp_make_shutdown(const struct sctp_association *asoc, const struct sctp_chunk *chunk); struct sctp_chunk *sctp_make_shutdown_ack(const struct sctp_association *asoc, - const struct sctp_chunk *); -struct sctp_chunk *sctp_make_shutdown_complete(const struct sctp_association *, - const struct sctp_chunk *); -void sctp_init_cause(struct sctp_chunk *, __be16 cause, size_t); -struct sctp_chunk *sctp_make_abort(const struct sctp_association *, - const struct sctp_chunk *, - const size_t hint); -struct sctp_chunk *sctp_make_abort_no_data(const struct sctp_association *, - const struct sctp_chunk *, - __u32 tsn); -struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *, - struct msghdr *, size_t msg_len); -struct sctp_chunk *sctp_make_abort_violation(const struct sctp_association *, - const struct sctp_chunk *, - const __u8 *, - const size_t ); -struct sctp_chunk *sctp_make_violation_paramlen(const struct sctp_association *, - const struct sctp_chunk *, - struct sctp_paramhdr *); -struct sctp_chunk *sctp_make_violation_max_retrans(const struct sctp_association *, - const struct sctp_chunk *); -struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *, - const struct sctp_transport *); -struct sctp_chunk *sctp_make_heartbeat_ack(const struct sctp_association *, - const struct sctp_chunk *, - const void *payload, - const size_t paylen); -struct sctp_chunk *sctp_make_op_error(const struct sctp_association *, - const struct sctp_chunk *chunk, - __be16 cause_code, - const void *payload, - size_t paylen, - size_t reserve_tail); - -struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *, - union sctp_addr *, - struct sockaddr *, - int, __be16); + const struct sctp_chunk *chunk); +struct sctp_chunk *sctp_make_shutdown_complete( + const struct sctp_association *asoc, + const struct sctp_chunk *chunk); +void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause, size_t paylen); +struct sctp_chunk *sctp_make_abort(const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + const size_t hint); +struct sctp_chunk *sctp_make_abort_no_data(const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + __u32 tsn); +struct sctp_chunk *sctp_make_abort_user(const struct sctp_association *asoc, + struct msghdr *msg, size_t msg_len); +struct sctp_chunk *sctp_make_abort_violation( + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + const __u8 *payload, + const size_t paylen); +struct sctp_chunk *sctp_make_violation_paramlen( + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + struct sctp_paramhdr *param); +struct sctp_chunk *sctp_make_violation_max_retrans( + const struct sctp_association *asoc, + const struct sctp_chunk *chunk); +struct sctp_chunk *sctp_make_heartbeat(const struct sctp_association *asoc, + const struct sctp_transport *transport); +struct sctp_chunk *sctp_make_heartbeat_ack(const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + const void *payload, + const size_t paylen); +struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + __be16 cause_code, const void *payload, + size_t paylen, size_t reserve_tail); + +struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, + union sctp_addr *laddr, + struct sockaddr *addrs, + int addrcnt, __be16 flags); struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, union sctp_addr *addr); bool sctp_verify_asconf(const struct sctp_association *asoc, @@ -259,27 +260,25 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc, __u32 new_cum_tsn, size_t nstreams, struct sctp_fwdtsn_skip *skiplist); struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc); -struct sctp_chunk *sctp_make_strreset_req( - const struct sctp_association *asoc, - __u16 stream_num, __u16 *stream_list, - bool out, bool in); +struct sctp_chunk *sctp_make_strreset_req(const struct sctp_association *asoc, + __u16 stream_num, __u16 *stream_list, + bool out, bool in); struct sctp_chunk *sctp_make_strreset_tsnreq( - const struct sctp_association *asoc); + const struct sctp_association *asoc); struct sctp_chunk *sctp_make_strreset_addstrm( - const struct sctp_association *asoc, - __u16 out, __u16 in); -struct sctp_chunk *sctp_make_strreset_resp( - const struct sctp_association *asoc, - __u32 result, __u32 sn); -struct sctp_chunk *sctp_make_strreset_tsnresp( - struct sctp_association *asoc, - __u32 result, __u32 sn, - __u32 sender_tsn, __u32 receiver_tsn); + const struct sctp_association *asoc, + __u16 out, __u16 in); +struct sctp_chunk *sctp_make_strreset_resp(const struct sctp_association *asoc, + __u32 result, __u32 sn); +struct sctp_chunk *sctp_make_strreset_tsnresp(struct sctp_association *asoc, + __u32 result, __u32 sn, + __u32 sender_tsn, + __u32 receiver_tsn); bool sctp_verify_reconf(const struct sctp_association *asoc, struct sctp_chunk *chunk, struct sctp_paramhdr **errp); -void sctp_chunk_assign_tsn(struct sctp_chunk *); -void sctp_chunk_assign_ssn(struct sctp_chunk *); +void sctp_chunk_assign_tsn(struct sctp_chunk *chunk); +void sctp_chunk_assign_ssn(struct sctp_chunk *chunk); /* Prototypes for stream-processing functions. */ struct sctp_chunk *sctp_process_strreset_outreq( @@ -322,11 +321,12 @@ void sctp_generate_proto_unreach_event(unsigned long peer); void sctp_ootb_pkt_free(struct sctp_packet *packet); -struct sctp_association *sctp_unpack_cookie(const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - struct sctp_chunk *chunk, - gfp_t gfp, int *err, - struct sctp_chunk **err_chk_p); +struct sctp_association *sctp_unpack_cookie( + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + struct sctp_chunk *chunk, + gfp_t gfp, int *err, + struct sctp_chunk **err_chk_p); /* 3rd level prototypes */ __u32 sctp_generate_tag(const struct sctp_endpoint *ep); diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 51de638a88b2..ca8f196b6c6c 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -132,8 +132,8 @@ static const struct sctp_paramhdr prsctp_param = { * provided chunk, as most cause codes will be embedded inside an * abort chunk. */ -void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code, - size_t paylen) +void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code, + size_t paylen) { struct sctp_errhdr err; __u16 len; @@ -151,7 +151,7 @@ void sctp_init_cause(struct sctp_chunk *chunk, __be16 cause_code, * if there isn't enough space in the op error chunk */ static int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code, - size_t paylen) + size_t paylen) { struct sctp_errhdr err; __u16 len; @@ -213,32 +213,31 @@ static int sctp_init_cause_fixed(struct sctp_chunk *chunk, __be16 cause_code, * Supported Address Types (Note 4) Optional 12 */ struct sctp_chunk *sctp_make_init(const struct sctp_association *asoc, - const struct sctp_bind_addr *bp, - gfp_t gfp, int vparam_len) + const struct sctp_bind_addr *bp, + gfp_t gfp, int vparam_len) { struct net *net = sock_net(asoc->base.sk); + struct sctp_supported_ext_param ext_param; + struct sctp_adaptation_ind_param aiparam; + struct sctp_paramhdr *auth_chunks = NULL; + struct sctp_paramhdr *auth_hmacs = NULL; + struct sctp_supported_addrs_param sat; struct sctp_endpoint *ep = asoc->ep; - struct sctp_inithdr init; - union sctp_params addrs; - size_t chunksize; struct sctp_chunk *retval = NULL; int num_types, addrs_len = 0; + struct sctp_inithdr init; + union sctp_params addrs; struct sctp_sock *sp; - struct sctp_supported_addrs_param sat; + __u8 extensions[4]; + size_t chunksize; __be16 types[2]; - struct sctp_adaptation_ind_param aiparam; - struct sctp_supported_ext_param ext_param; int num_ext = 0; - __u8 extensions[4]; - struct sctp_paramhdr *auth_chunks = NULL, - *auth_hmacs = NULL; /* RFC 2960 3.3.2 Initiation (INIT) (1) * * Note 1: The INIT chunks can contain multiple addresses that * can be IPv4 and/or IPv6 in any combination. */ - retval = NULL; /* Convert the provided bind address list to raw format. */ addrs = sctp_bind_addrs_to_raw(bp, &addrs_len, gfp); @@ -380,26 +379,24 @@ nodata: } struct sctp_chunk *sctp_make_init_ack(const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - gfp_t gfp, int unkparam_len) + const struct sctp_chunk *chunk, + gfp_t gfp, int unkparam_len) { + struct sctp_supported_ext_param ext_param; + struct sctp_adaptation_ind_param aiparam; + struct sctp_paramhdr *auth_chunks = NULL; + struct sctp_paramhdr *auth_random = NULL; + struct sctp_paramhdr *auth_hmacs = NULL; + struct sctp_chunk *retval = NULL; + struct sctp_cookie_param *cookie; struct sctp_inithdr initack; - struct sctp_chunk *retval; union sctp_params addrs; struct sctp_sock *sp; - int addrs_len; - struct sctp_cookie_param *cookie; - int cookie_len; + __u8 extensions[4]; size_t chunksize; - struct sctp_adaptation_ind_param aiparam; - struct sctp_supported_ext_param ext_param; int num_ext = 0; - __u8 extensions[4]; - struct sctp_paramhdr *auth_chunks = NULL, - *auth_hmacs = NULL, - *auth_random = NULL; - - retval = NULL; + int cookie_len; + int addrs_len; /* Note: there may be no addresses to embed. */ addrs = sctp_bind_addrs_to_raw(&asoc->base.bind_addr, &addrs_len, gfp); @@ -562,11 +559,11 @@ nomem_cookie: * to insure interoperability. */ struct sctp_chunk *sctp_make_cookie_echo(const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; - void *cookie; int cookie_len; + void *cookie; cookie = asoc->peer.cookie; cookie_len = asoc->peer.cookie_len; @@ -614,7 +611,7 @@ nodata: * Set to zero on transmit and ignored on receipt. */ struct sctp_chunk *sctp_make_cookie_ack(const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; @@ -659,8 +656,8 @@ struct sctp_chunk *sctp_make_cookie_ack(const struct sctp_association *asoc, * Note: The CWR is considered a Control chunk. */ struct sctp_chunk *sctp_make_cwr(const struct sctp_association *asoc, - const __u32 lowest_tsn, - const struct sctp_chunk *chunk) + const __u32 lowest_tsn, + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; struct sctp_cwrhdr cwr; @@ -694,7 +691,7 @@ nodata: /* Make an ECNE chunk. This is a congestion experienced report. */ struct sctp_chunk *sctp_make_ecne(const struct sctp_association *asoc, - const __u32 lowest_tsn) + const __u32 lowest_tsn) { struct sctp_chunk *retval; struct sctp_ecnehdr ecne; @@ -715,9 +712,9 @@ nodata: * parameters. However, do not populate the data payload. */ struct sctp_chunk *sctp_make_datafrag_empty(struct sctp_association *asoc, - const struct sctp_sndrcvinfo *sinfo, - int data_len, __u8 flags, __u16 ssn, - gfp_t gfp) + const struct sctp_sndrcvinfo *sinfo, + int data_len, __u8 flags, __u16 ssn, + gfp_t gfp) { struct sctp_chunk *retval; struct sctp_datahdr dp; @@ -755,15 +752,15 @@ nodata: */ struct sctp_chunk *sctp_make_sack(const struct sctp_association *asoc) { - struct sctp_chunk *retval; - struct sctp_sackhdr sack; - int len; - __u32 ctsn; - __u16 num_gabs, num_dup_tsns; - struct sctp_association *aptr = (struct sctp_association *)asoc; struct sctp_tsnmap *map = (struct sctp_tsnmap *)&asoc->peer.tsn_map; + struct sctp_association *aptr = (struct sctp_association *)asoc; struct sctp_gap_ack_block gabs[SCTP_MAX_GABS]; + __u16 num_gabs, num_dup_tsns; struct sctp_transport *trans; + struct sctp_chunk *retval; + struct sctp_sackhdr sack; + __u32 ctsn; + int len; memset(gabs, 0, sizeof(gabs)); ctsn = sctp_tsnmap_get_ctsn(map); @@ -879,7 +876,7 @@ nodata: } struct sctp_chunk *sctp_make_shutdown_ack(const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; @@ -902,8 +899,8 @@ struct sctp_chunk *sctp_make_shutdown_ack(const struct sctp_association *asoc, } struct sctp_chunk *sctp_make_shutdown_complete( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk) { struct sctp_chunk *retval; __u8 flags = 0; @@ -936,8 +933,8 @@ struct sctp_chunk *sctp_make_shutdown_complete( * association, except when responding to an INIT (sctpimpguide 2.41). */ struct sctp_chunk *sctp_make_abort(const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - const size_t hint) + const struct sctp_chunk *chunk, + const size_t hint) { struct sctp_chunk *retval; __u8 flags = 0; @@ -973,8 +970,9 @@ struct sctp_chunk *sctp_make_abort(const struct sctp_association *asoc, /* Helper to create ABORT with a NO_USER_DATA error. */ struct sctp_chunk *sctp_make_abort_no_data( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk, __u32 tsn) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + __u32 tsn) { struct sctp_chunk *retval; __be32 payload; @@ -1054,8 +1052,8 @@ err_chunk: static void *sctp_addto_param(struct sctp_chunk *chunk, int len, const void *data) { - void *target; int chunklen = ntohs(chunk->chunk_hdr->length); + void *target; target = skb_put(chunk->skb, len); @@ -1073,10 +1071,10 @@ static void *sctp_addto_param(struct sctp_chunk *chunk, int len, /* Make an ABORT chunk with a PROTOCOL VIOLATION cause code. */ struct sctp_chunk *sctp_make_abort_violation( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - const __u8 *payload, - const size_t paylen) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + const __u8 *payload, + const size_t paylen) { struct sctp_chunk *retval; struct sctp_paramhdr phdr; @@ -1099,14 +1097,14 @@ end: } struct sctp_chunk *sctp_make_violation_paramlen( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - struct sctp_paramhdr *param) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + struct sctp_paramhdr *param) { - struct sctp_chunk *retval; static const char error[] = "The following parameter had invalid length:"; size_t payload_len = sizeof(error) + sizeof(struct sctp_errhdr) + sizeof(*param); + struct sctp_chunk *retval; retval = sctp_make_abort(asoc, chunk, payload_len); if (!retval) @@ -1122,12 +1120,12 @@ nodata: } struct sctp_chunk *sctp_make_violation_max_retrans( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk) { - struct sctp_chunk *retval; static const char error[] = "Association exceeded its max_retans count"; size_t payload_len = sizeof(error) + sizeof(struct sctp_errhdr); + struct sctp_chunk *retval; retval = sctp_make_abort(asoc, chunk, payload_len); if (!retval) @@ -1171,8 +1169,9 @@ nodata: } struct sctp_chunk *sctp_make_heartbeat_ack(const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - const void *payload, const size_t paylen) + const struct sctp_chunk *chunk, + const void *payload, + const size_t paylen) { struct sctp_chunk *retval; @@ -1203,9 +1202,9 @@ nodata: * This routine can be used for containing multiple causes in the chunk. */ static struct sctp_chunk *sctp_make_op_error_space( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - size_t size) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk, + size_t size) { struct sctp_chunk *retval; @@ -1237,8 +1236,8 @@ nodata: * to report all the errors, if the incoming chunk is large */ static inline struct sctp_chunk *sctp_make_op_error_fixed( - const struct sctp_association *asoc, - const struct sctp_chunk *chunk) + const struct sctp_association *asoc, + const struct sctp_chunk *chunk) { size_t size = asoc ? asoc->pathmtu : 0; @@ -1250,9 +1249,9 @@ static inline struct sctp_chunk *sctp_make_op_error_fixed( /* Create an Operation Error chunk. */ struct sctp_chunk *sctp_make_op_error(const struct sctp_association *asoc, - const struct sctp_chunk *chunk, - __be16 cause_code, const void *payload, - size_t paylen, size_t reserve_tail) + const struct sctp_chunk *chunk, + __be16 cause_code, const void *payload, + size_t paylen, size_t reserve_tail) { struct sctp_chunk *retval; @@ -1271,9 +1270,9 @@ nodata: struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc) { - struct sctp_chunk *retval; - struct sctp_hmac *hmac_desc; struct sctp_authhdr auth_hdr; + struct sctp_hmac *hmac_desc; + struct sctp_chunk *retval; __u8 *hmac; /* Get the first hmac that the peer told us to use */ @@ -1319,8 +1318,8 @@ struct sctp_chunk *sctp_make_auth(const struct sctp_association *asoc) * */ struct sctp_chunk *sctp_chunkify(struct sk_buff *skb, - const struct sctp_association *asoc, - struct sock *sk, gfp_t gfp) + const struct sctp_association *asoc, + struct sock *sk, gfp_t gfp) { struct sctp_chunk *retval; @@ -1372,11 +1371,11 @@ const union sctp_addr *sctp_source(const struct sctp_chunk *chunk) * arguments, reserving enough space for a 'paylen' byte payload. */ static struct sctp_chunk *_sctp_make_chunk(const struct sctp_association *asoc, - __u8 type, __u8 flags, int paylen, - gfp_t gfp) + __u8 type, __u8 flags, int paylen, + gfp_t gfp) { - struct sctp_chunk *retval; struct sctp_chunkhdr *chunk_hdr; + struct sctp_chunk *retval; struct sk_buff *skb; struct sock *sk; @@ -1470,9 +1469,9 @@ void sctp_chunk_put(struct sctp_chunk *ch) */ void *sctp_addto_chunk(struct sctp_chunk *chunk, int len, const void *data) { - void *target; int chunklen = ntohs(chunk->chunk_hdr->length); int padlen = SCTP_PAD4(chunklen) - chunklen; + void *target; skb_put_zero(chunk->skb, padlen); target = skb_put_data(chunk->skb, data, len); @@ -1525,11 +1524,10 @@ int sctp_user_addto_chunk(struct sctp_chunk *chunk, int len, */ void sctp_chunk_assign_ssn(struct sctp_chunk *chunk) { - struct sctp_datamsg *msg; - struct sctp_chunk *lchunk; struct sctp_stream *stream; - __u16 ssn; - __u16 sid; + struct sctp_chunk *lchunk; + struct sctp_datamsg *msg; + __u16 ssn, sid; if (chunk->has_ssn) return; @@ -1574,8 +1572,8 @@ void sctp_chunk_assign_tsn(struct sctp_chunk *chunk) /* Create a CLOSED association to use with an incoming packet. */ struct sctp_association *sctp_make_temp_asoc(const struct sctp_endpoint *ep, - struct sctp_chunk *chunk, - gfp_t gfp) + struct sctp_chunk *chunk, + gfp_t gfp) { struct sctp_association *asoc; enum sctp_scope scope; @@ -1602,8 +1600,8 @@ static struct sctp_cookie_param *sctp_pack_cookie( const struct sctp_endpoint *ep, const struct sctp_association *asoc, const struct sctp_chunk *init_chunk, - int *cookie_len, - const __u8 *raw_addrs, int addrs_len) + int *cookie_len, const __u8 *raw_addrs, + int addrs_len) { struct sctp_signed_cookie *cookie; struct sctp_cookie_param *retval; @@ -1690,19 +1688,19 @@ nodata: /* Unpack the cookie from COOKIE ECHO chunk, recreating the association. */ struct sctp_association *sctp_unpack_cookie( - const struct sctp_endpoint *ep, - const struct sctp_association *asoc, - struct sctp_chunk *chunk, gfp_t gfp, - int *error, struct sctp_chunk **errp) + const struct sctp_endpoint *ep, + const struct sctp_association *asoc, + struct sctp_chunk *chunk, gfp_t gfp, + int *error, struct sctp_chunk **errp) { struct sctp_association *retval = NULL; + int headersize, bodysize, fixed_size; struct sctp_signed_cookie *cookie; + struct sk_buff *skb = chunk->skb; struct sctp_cookie *bear_cookie; - int headersize, bodysize, fixed_size; __u8 *digest = ep->digest; - unsigned int len; enum sctp_scope scope; - struct sk_buff *skb = chunk->skb; + unsigned int len; ktime_t kt; /* Header size is static data prior to the actual cookie, including @@ -1974,8 +1972,8 @@ static int sctp_process_hn_param(const struct sctp_association *asoc, static int sctp_verify_ext_param(struct net *net, union sctp_params param) { __u16 num_ext = ntohs(param.p->length) - sizeof(struct sctp_paramhdr); - int have_auth = 0; int have_asconf = 0; + int have_auth = 0; int i; for (i = 0; i < num_ext; i++) { @@ -2005,10 +2003,10 @@ static int sctp_verify_ext_param(struct net *net, union sctp_params param) } static void sctp_process_ext_param(struct sctp_association *asoc, - union sctp_params param) + union sctp_params param) { - struct net *net = sock_net(asoc->base.sk); __u16 num_ext = ntohs(param.p->length) - sizeof(struct sctp_paramhdr); + struct net *net = sock_net(asoc->base.sk); int i; for (i = 0; i < num_ext; i++) { @@ -2309,13 +2307,13 @@ int sctp_process_init(struct sctp_association *asoc, struct sctp_chunk *chunk, struct sctp_init_chunk *peer_init, gfp_t gfp) { struct net *net = sock_net(asoc->base.sk); - union sctp_params param; struct sctp_transport *transport; struct list_head *pos, *temp; - struct sctp_af *af; + union sctp_params param; union sctp_addr addr; - char *cookie; + struct sctp_af *af; int src_match = 0; + char *cookie; /* We must include the address that the INIT packet came from. * This is the only address that matters for an INIT packet. @@ -2499,16 +2497,15 @@ static int sctp_process_param(struct sctp_association *asoc, gfp_t gfp) { struct net *net = sock_net(asoc->base.sk); - union sctp_addr addr; - int i; - __u16 sat; - int retval = 1; - enum sctp_scope scope; - u32 stale; - struct sctp_af *af; + struct sctp_endpoint *ep = asoc->ep; union sctp_addr_param *addr_param; struct sctp_transport *t; - struct sctp_endpoint *ep = asoc->ep; + enum sctp_scope scope; + union sctp_addr addr; + struct sctp_af *af; + int retval = 1, i; + u32 stale; + __u16 sat; /* We maintain all INIT parameters in network byte order all the * time. This allows us to not worry about whether the parameters @@ -2806,22 +2803,20 @@ static struct sctp_chunk *sctp_make_asconf(struct sctp_association *asoc, * */ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, - union sctp_addr *laddr, - struct sockaddr *addrs, - int addrcnt, - __be16 flags) + union sctp_addr *laddr, + struct sockaddr *addrs, + int addrcnt, __be16 flags) { + union sctp_addr_param addr_param; struct sctp_addip_param param; - struct sctp_chunk *retval; - union sctp_addr_param addr_param; - union sctp_addr *addr; - void *addr_buf; - struct sctp_af *af; - int paramlen = sizeof(param); - int addr_param_len = 0; - int totallen = 0; - int i; - int del_pickup = 0; + int paramlen = sizeof(param); + struct sctp_chunk *retval; + int addr_param_len = 0; + union sctp_addr *addr; + int totallen = 0, i; + int del_pickup = 0; + struct sctp_af *af; + void *addr_buf; /* Get total length of all the address parameters. */ addr_buf = addrs; @@ -2897,12 +2892,12 @@ struct sctp_chunk *sctp_make_asconf_update_ip(struct sctp_association *asoc, struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, union sctp_addr *addr) { + struct sctp_af *af = sctp_get_af_specific(addr->v4.sin_family); + union sctp_addr_param addrparam; struct sctp_addip_param param; - struct sctp_chunk *retval; - int len = sizeof(param); - union sctp_addr_param addrparam; - int addrlen; - struct sctp_af *af = sctp_get_af_specific(addr->v4.sin_family); + struct sctp_chunk *retval; + int len = sizeof(param); + int addrlen; addrlen = af->to_addr_param(addr, &addrparam); if (!addrlen) @@ -2946,9 +2941,9 @@ struct sctp_chunk *sctp_make_asconf_set_prim(struct sctp_association *asoc, static struct sctp_chunk *sctp_make_asconf_ack(const struct sctp_association *asoc, __u32 serial, int vparam_len) { - struct sctp_addiphdr asconf; - struct sctp_chunk *retval; - int length = sizeof(asconf) + vparam_len; + struct sctp_addiphdr asconf; + struct sctp_chunk *retval; + int length = sizeof(asconf) + vparam_len; /* Create the chunk. */ retval = sctp_make_control(asoc, SCTP_CID_ASCONF_ACK, 0, length, @@ -2970,10 +2965,10 @@ static void sctp_add_asconf_response(struct sctp_chunk *chunk, __be32 crr_id, struct sctp_addip_param *asconf_param) { struct sctp_addip_param ack_param; - struct sctp_errhdr err_param; - int asconf_param_len = 0; - int err_param_len = 0; - __be16 response_type; + struct sctp_errhdr err_param; + int asconf_param_len = 0; + int err_param_len = 0; + __be16 response_type; if (SCTP_ERROR_NO_ERROR == err_code) { response_type = SCTP_PARAM_SUCCESS_REPORT; @@ -3011,10 +3006,10 @@ static __be16 sctp_process_asconf_param(struct sctp_association *asoc, struct sctp_chunk *asconf, struct sctp_addip_param *asconf_param) { + union sctp_addr_param *addr_param; struct sctp_transport *peer; - struct sctp_af *af; union sctp_addr addr; - union sctp_addr_param *addr_param; + struct sctp_af *af; addr_param = (void *)asconf_param + sizeof(*asconf_param); @@ -3142,8 +3137,8 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, struct sctp_paramhdr **errp) { struct sctp_addip_chunk *addip; - union sctp_params param; bool addr_param_seen = false; + union sctp_params param; addip = (struct sctp_addip_chunk *)chunk->chunk_hdr; sctp_walk_params(param, addip, addip_hdr.params) { @@ -3209,16 +3204,15 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, struct sctp_chunk *sctp_process_asconf(struct sctp_association *asoc, struct sctp_chunk *asconf) { + union sctp_addr_param *addr_param; struct sctp_addip_chunk *addip; + struct sctp_chunk *asconf_ack; bool all_param_pass = true; + struct sctp_addiphdr *hdr; + int length = 0, chunk_len; union sctp_params param; - struct sctp_addiphdr *hdr; - union sctp_addr_param *addr_param; - struct sctp_chunk *asconf_ack; - __be16 err_code; - int length = 0; - int chunk_len; - __u32 serial; + __be16 err_code; + __u32 serial; addip = (struct sctp_addip_chunk *)asconf->chunk_hdr; chunk_len = ntohs(asconf->chunk_hdr->length) - @@ -3295,12 +3289,12 @@ done: static void sctp_asconf_param_success(struct sctp_association *asoc, struct sctp_addip_param *asconf_param) { - struct sctp_af *af; - union sctp_addr addr; struct sctp_bind_addr *bp = &asoc->base.bind_addr; union sctp_addr_param *addr_param; - struct sctp_transport *transport; struct sctp_sockaddr_entry *saddr; + struct sctp_transport *transport; + union sctp_addr addr; + struct sctp_af *af; addr_param = (void *)asconf_param + sizeof(*asconf_param); @@ -3357,10 +3351,10 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, int no_err) { struct sctp_addip_param *asconf_ack_param; - struct sctp_errhdr *err_param; - int length; - int asconf_ack_len; - __be16 err_code; + struct sctp_errhdr *err_param; + int asconf_ack_len; + __be16 err_code; + int length; if (no_err) err_code = SCTP_ERROR_NO_ERROR; @@ -3409,15 +3403,15 @@ static __be16 sctp_get_asconf_response(struct sctp_chunk *asconf_ack, int sctp_process_asconf_ack(struct sctp_association *asoc, struct sctp_chunk *asconf_ack) { - struct sctp_chunk *asconf = asoc->addip_last_asconf; - union sctp_addr_param *addr_param; - struct sctp_addip_param *asconf_param; - int length = 0; - int asconf_len = asconf->skb->len; - int all_param_pass = 0; - int no_err = 1; - int retval = 0; - __be16 err_code = SCTP_ERROR_NO_ERROR; + struct sctp_chunk *asconf = asoc->addip_last_asconf; + struct sctp_addip_param *asconf_param; + __be16 err_code = SCTP_ERROR_NO_ERROR; + union sctp_addr_param *addr_param; + int asconf_len = asconf->skb->len; + int all_param_pass = 0; + int length = 0; + int no_err = 1; + int retval = 0; /* Skip the chunkhdr and addiphdr from the last asconf sent and store * a pointer to address parameter. @@ -3544,9 +3538,8 @@ struct sctp_chunk *sctp_make_fwdtsn(const struct sctp_association *asoc, * \ \ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -static struct sctp_chunk *sctp_make_reconf( - const struct sctp_association *asoc, - int length) +static struct sctp_chunk *sctp_make_reconf(const struct sctp_association *asoc, + int length) { struct sctp_reconf_chunk *reconf; struct sctp_chunk *retval; @@ -3597,9 +3590,9 @@ static struct sctp_chunk *sctp_make_reconf( * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct sctp_chunk *sctp_make_strreset_req( - const struct sctp_association *asoc, - __u16 stream_num, __u16 *stream_list, - bool out, bool in) + const struct sctp_association *asoc, + __u16 stream_num, __u16 *stream_list, + bool out, bool in) { struct sctp_strreset_outreq outreq; __u16 stream_len = stream_num * 2; @@ -3651,7 +3644,7 @@ struct sctp_chunk *sctp_make_strreset_req( * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct sctp_chunk *sctp_make_strreset_tsnreq( - const struct sctp_association *asoc) + const struct sctp_association *asoc) { struct sctp_strreset_tsnreq tsnreq; __u16 length = sizeof(tsnreq); @@ -3682,8 +3675,8 @@ struct sctp_chunk *sctp_make_strreset_tsnreq( * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ struct sctp_chunk *sctp_make_strreset_addstrm( - const struct sctp_association *asoc, - __u16 out, __u16 in) + const struct sctp_association *asoc, + __u16 out, __u16 in) { struct sctp_strreset_addstrm addstrm; __u16 size = sizeof(addstrm); @@ -3727,9 +3720,8 @@ struct sctp_chunk *sctp_make_strreset_addstrm( * | Result | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -struct sctp_chunk *sctp_make_strreset_resp( - const struct sctp_association *asoc, - __u32 result, __u32 sn) +struct sctp_chunk *sctp_make_strreset_resp(const struct sctp_association *asoc, + __u32 result, __u32 sn) { struct sctp_strreset_resp resp; __u16 length = sizeof(resp); @@ -3764,10 +3756,10 @@ struct sctp_chunk *sctp_make_strreset_resp( * | Receiver's Next TSN (optional) | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ -struct sctp_chunk *sctp_make_strreset_tsnresp( - struct sctp_association *asoc, - __u32 result, __u32 sn, - __u32 sender_tsn, __u32 receiver_tsn) +struct sctp_chunk *sctp_make_strreset_tsnresp(struct sctp_association *asoc, + __u32 result, __u32 sn, + __u32 sender_tsn, + __u32 receiver_tsn) { struct sctp_strreset_resptsn tsnresp; __u16 length = sizeof(tsnresp); -- cgit v1.2.3 From 3dfe55d9cae7413c1bd12cbc0f9145384f753014 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Fri, 11 Aug 2017 19:33:04 +0100 Subject: Bluetooth: kfree tmp rather than an alias to it While the kfree of dhkey_a is of the same address of tmp, it probably is clearer and more human readable if tmp is kfree'd rather than dhkey_a. Detected by CoverityScan, CID#1448650 ("Free of address-of expression") Signed-off-by: Colin Ian King Signed-off-by: Marcel Holtmann --- net/bluetooth/selftest.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bluetooth/selftest.c b/net/bluetooth/selftest.c index ee92c925ecc5..34a1227f4391 100644 --- a/net/bluetooth/selftest.c +++ b/net/bluetooth/selftest.c @@ -164,7 +164,7 @@ static int __init test_ecdh_sample(const u8 priv_a[32], const u8 priv_b[32], ret = -EINVAL; out: - kfree(dhkey_a); + kfree(tmp); return ret; } -- cgit v1.2.3 From a2e8da9378cc09e2e922a0b3d481bd9d07c3d245 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 9 Aug 2017 14:30:33 +0200 Subject: net: sched: use newly added classid identity helpers Instead of checking handle, which does not have the inner class information and drivers wrongly assume clsact->egress as ingress, use the newly introduced classid identification helpers. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c | 2 +- drivers/net/ethernet/intel/ixgbe/ixgbe_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_main.c | 2 +- drivers/net/ethernet/mellanox/mlx5/core/en_rep.c | 2 +- drivers/net/ethernet/mellanox/mlxsw/spectrum.c | 18 ++++++++++++++++-- drivers/net/ethernet/netronome/nfp/bpf/main.c | 2 +- drivers/net/ethernet/netronome/nfp/flower/offload.c | 2 +- net/dsa/slave.c | 9 ++++++++- 8 files changed, 30 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c index d80b20d695e0..afa6fd688fac 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_main.c @@ -2892,7 +2892,7 @@ static int cxgb_set_tx_maxrate(struct net_device *dev, int index, u32 rate) static int cxgb_setup_tc_cls_u32(struct net_device *dev, struct tc_cls_u32_offload *cls_u32) { - if (TC_H_MAJ(cls_u32->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + if (is_classid_clsact_ingress(cls_u32->common.classid) || cls_u32->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index c6b132476de4..f9fd8d8f1bef 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -9230,7 +9230,7 @@ static int ixgbe_setup_tc_cls_u32(struct net_device *dev, { struct ixgbe_adapter *adapter = netdev_priv(dev); - if (TC_H_MAJ(cls_u32->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + if (is_classid_clsact_ingress(cls_u32->common.classid) || cls_u32->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c index 981f8415b546..8633ca5af6ed 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_main.c @@ -3031,7 +3031,7 @@ static int mlx5e_setup_tc_cls_flower(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + if (is_classid_clsact_ingress(cls_flower->common.classid) || cls_flower->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c index f3c494a4ecdf..f34c00fbf78c 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rep.c @@ -657,7 +657,7 @@ mlx5e_rep_setup_tc_cls_flower(struct net_device *dev, { struct mlx5e_priv *priv = netdev_priv(dev); - if (TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + if (is_classid_clsact_ingress(cls_flower->common.classid) || cls_flower->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c index eb7c4549f464..a99600333a49 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum.c @@ -1696,7 +1696,14 @@ static void mlxsw_sp_port_del_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, static int mlxsw_sp_setup_tc_cls_matchall(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_cls_matchall_offload *f) { - bool ingress = TC_H_MAJ(f->common.handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress; + + if (is_classid_clsact_ingress(f->common.classid)) + ingress = true; + else if (is_classid_clsact_egress(f->common.classid)) + ingress = false; + else + return -EOPNOTSUPP; if (f->common.chain_index) return -EOPNOTSUPP; @@ -1717,7 +1724,14 @@ static int mlxsw_sp_setup_tc_cls_flower(struct mlxsw_sp_port *mlxsw_sp_port, struct tc_cls_flower_offload *f) { - bool ingress = TC_H_MAJ(f->common.handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress; + + if (is_classid_clsact_ingress(f->common.classid)) + ingress = true; + else if (is_classid_clsact_egress(f->common.classid)) + ingress = false; + else + return -EOPNOTSUPP; if (f->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/bpf/main.c b/drivers/net/ethernet/netronome/nfp/bpf/main.c index f981f60ec306..0e6864922d5c 100644 --- a/drivers/net/ethernet/netronome/nfp/bpf/main.c +++ b/drivers/net/ethernet/netronome/nfp/bpf/main.c @@ -127,7 +127,7 @@ static int nfp_bpf_setup_tc(struct nfp_app *app, struct net_device *netdev, struct nfp_net *nn = netdev_priv(netdev); if (type != TC_SETUP_CLSBPF || !nfp_net_ebpf_capable(nn) || - TC_H_MAJ(cls_bpf->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + is_classid_clsact_ingress(cls_bpf->common.classid) || cls_bpf->common.protocol != htons(ETH_P_ALL) || cls_bpf->common.chain_index) return -EOPNOTSUPP; diff --git a/drivers/net/ethernet/netronome/nfp/flower/offload.c b/drivers/net/ethernet/netronome/nfp/flower/offload.c index 01767c7376d5..3ad5aaa210a4 100644 --- a/drivers/net/ethernet/netronome/nfp/flower/offload.c +++ b/drivers/net/ethernet/netronome/nfp/flower/offload.c @@ -390,7 +390,7 @@ int nfp_flower_setup_tc(struct nfp_app *app, struct net_device *netdev, struct tc_cls_flower_offload *cls_flower = type_data; if (type != TC_SETUP_CLSFLOWER || - TC_H_MAJ(cls_flower->common.handle) != TC_H_MAJ(TC_H_INGRESS) || + is_classid_clsact_ingress(cls_flower->common.classid) || !eth_proto_is_802_3(cls_flower->common.protocol) || cls_flower->common.chain_index) return -EOPNOTSUPP; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 8c79011c5a83..78e78a6e6833 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -914,7 +914,14 @@ static void dsa_slave_del_cls_matchall(struct net_device *dev, static int dsa_slave_setup_tc_cls_matchall(struct net_device *dev, struct tc_cls_matchall_offload *cls) { - bool ingress = TC_H_MAJ(cls->common.handle) == TC_H_MAJ(TC_H_INGRESS); + bool ingress; + + if (is_classid_clsact_ingress(cls->common.classid)) + ingress = true; + else if (is_classid_clsact_egress(cls->common.classid)) + ingress = false; + else + return -EOPNOTSUPP; if (cls->common.chain_index) return -EOPNOTSUPP; -- cgit v1.2.3 From 7b06e8aed283081010596c98a67f06c595affe51 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 9 Aug 2017 14:30:35 +0200 Subject: net: sched: remove cops->tcf_cl_offload cops->tcf_cl_offload is no longer needed, as the drivers check what they can and cannot offload using the classid identify helpers. So remove this. Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/pkt_cls.h | 14 +++----------- include/net/sch_generic.h | 1 - net/sched/cls_bpf.c | 4 ++-- net/sched/cls_flower.c | 8 ++++---- net/sched/cls_matchall.c | 4 ++-- net/sched/cls_u32.c | 8 ++++---- net/sched/sch_ingress.c | 12 ------------ 7 files changed, 15 insertions(+), 36 deletions(-) (limited to 'net') diff --git a/include/net/pkt_cls.h b/include/net/pkt_cls.h index bd9dd79357fe..e80edd8879ef 100644 --- a/include/net/pkt_cls.h +++ b/include/net/pkt_cls.h @@ -457,19 +457,12 @@ struct tc_cls_u32_offload { }; }; -static inline bool tc_can_offload(const struct net_device *dev, - const struct tcf_proto *tp) +static inline bool tc_can_offload(const struct net_device *dev) { - const struct Qdisc *sch = tp->q; - const struct Qdisc_class_ops *cops = sch->ops->cl_ops; - if (!(dev->features & NETIF_F_HW_TC)) return false; if (!dev->netdev_ops->ndo_setup_tc) return false; - if (cops && cops->tcf_cl_offload) - return cops->tcf_cl_offload(tp->classid); - return true; } @@ -478,12 +471,11 @@ static inline bool tc_skip_hw(u32 flags) return (flags & TCA_CLS_FLAGS_SKIP_HW) ? true : false; } -static inline bool tc_should_offload(const struct net_device *dev, - const struct tcf_proto *tp, u32 flags) +static inline bool tc_should_offload(const struct net_device *dev, u32 flags) { if (tc_skip_hw(flags)) return false; - return tc_can_offload(dev, tp); + return tc_can_offload(dev); } static inline bool tc_skip_sw(u32 flags) diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index e79f5ad1c5f3..5865db91976b 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -156,7 +156,6 @@ struct Qdisc_class_ops { /* Filter manipulation */ struct tcf_block * (*tcf_block)(struct Qdisc *, unsigned long); - bool (*tcf_cl_offload)(u32 classid); unsigned long (*bind_tcf)(struct Qdisc *, unsigned long, u32 classid); void (*unbind_tcf)(struct Qdisc *, unsigned long); diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index db17b68df94e..6f2dffe30f25 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -178,7 +178,7 @@ static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog, (oldprog && tc_skip_sw(oldprog->gen_flags)); if (oldprog && oldprog->offloaded) { - if (tc_should_offload(dev, tp, prog->gen_flags)) { + if (tc_should_offload(dev, prog->gen_flags)) { cmd = TC_CLSBPF_REPLACE; } else if (!tc_skip_sw(prog->gen_flags)) { obj = oldprog; @@ -187,7 +187,7 @@ static int cls_bpf_offload(struct tcf_proto *tp, struct cls_bpf_prog *prog, return -EINVAL; } } else { - if (!tc_should_offload(dev, tp, prog->gen_flags)) + if (!tc_should_offload(dev, prog->gen_flags)) return skip_sw ? -EINVAL : 0; cmd = TC_CLSBPF_ADD; } diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index d2551a03c542..052e902dc71c 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -227,7 +227,7 @@ static void fl_hw_destroy_filter(struct tcf_proto *tp, struct cls_fl_filter *f) struct tc_cls_flower_offload cls_flower = {}; struct net_device *dev = f->hw_dev; - if (!tc_can_offload(dev, tp)) + if (!tc_can_offload(dev)) return; tc_cls_common_offload_init(&cls_flower.common, tp); @@ -246,9 +246,9 @@ static int fl_hw_replace_filter(struct tcf_proto *tp, struct tc_cls_flower_offload cls_flower = {}; int err; - if (!tc_can_offload(dev, tp)) { + if (!tc_can_offload(dev)) { if (tcf_exts_get_dev(dev, &f->exts, &f->hw_dev) || - (f->hw_dev && !tc_can_offload(f->hw_dev, tp))) { + (f->hw_dev && !tc_can_offload(f->hw_dev))) { f->hw_dev = dev; return tc_skip_sw(f->flags) ? -EINVAL : 0; } @@ -281,7 +281,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) struct tc_cls_flower_offload cls_flower = {}; struct net_device *dev = f->hw_dev; - if (!tc_can_offload(dev, tp)) + if (!tc_can_offload(dev)) return; tc_cls_common_offload_init(&cls_flower.common, tp); diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index d44e26fdae84..d4dc387f7a56 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -92,7 +92,7 @@ static void mall_destroy(struct tcf_proto *tp) if (!head) return; - if (tc_should_offload(dev, tp, head->flags)) + if (tc_should_offload(dev, head->flags)) mall_destroy_hw_filter(tp, head, (unsigned long) head); call_rcu(&head->rcu, mall_destroy_rcu); @@ -172,7 +172,7 @@ static int mall_change(struct net *net, struct sk_buff *in_skb, if (err) goto err_set_parms; - if (tc_should_offload(dev, tp, flags)) { + if (tc_should_offload(dev, flags)) { err = mall_replace_hw_filter(tp, new, (unsigned long) new); if (err) { if (tc_skip_sw(flags)) diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 5a3f78181526..af22742d2847 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -433,7 +433,7 @@ static void u32_remove_hw_knode(struct tcf_proto *tp, u32 handle) struct net_device *dev = tp->q->dev_queue->dev; struct tc_cls_u32_offload cls_u32 = {}; - if (!tc_should_offload(dev, tp, 0)) + if (!tc_should_offload(dev, 0)) return; tc_cls_common_offload_init(&cls_u32.common, tp); @@ -450,7 +450,7 @@ static int u32_replace_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h, struct tc_cls_u32_offload cls_u32 = {}; int err; - if (!tc_should_offload(dev, tp, flags)) + if (!tc_should_offload(dev, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; tc_cls_common_offload_init(&cls_u32.common, tp); @@ -471,7 +471,7 @@ static void u32_clear_hw_hnode(struct tcf_proto *tp, struct tc_u_hnode *h) struct net_device *dev = tp->q->dev_queue->dev; struct tc_cls_u32_offload cls_u32 = {}; - if (!tc_should_offload(dev, tp, 0)) + if (!tc_should_offload(dev, 0)) return; tc_cls_common_offload_init(&cls_u32.common, tp); @@ -490,7 +490,7 @@ static int u32_replace_hw_knode(struct tcf_proto *tp, struct tc_u_knode *n, struct tc_cls_u32_offload cls_u32 = {}; int err; - if (!tc_should_offload(dev, tp, flags)) + if (!tc_should_offload(dev, flags)) return tc_skip_sw(flags) ? -EINVAL : 0; tc_cls_common_offload_init(&cls_u32.common, tp); diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index d8a9bebcab90..a15c543c3569 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -32,11 +32,6 @@ static unsigned long ingress_get(struct Qdisc *sch, u32 classid) return TC_H_MIN(classid) + 1; } -static bool ingress_cl_offload(u32 classid) -{ - return true; -} - static unsigned long ingress_bind_filter(struct Qdisc *sch, unsigned long parent, u32 classid) { @@ -103,7 +98,6 @@ static const struct Qdisc_class_ops ingress_class_ops = { .put = ingress_put, .walk = ingress_walk, .tcf_block = ingress_tcf_block, - .tcf_cl_offload = ingress_cl_offload, .bind_tcf = ingress_bind_filter, .unbind_tcf = ingress_put, }; @@ -134,11 +128,6 @@ static unsigned long clsact_get(struct Qdisc *sch, u32 classid) } } -static bool clsact_cl_offload(u32 classid) -{ - return TC_H_MIN(classid) == TC_H_MIN(TC_H_MIN_INGRESS); -} - static unsigned long clsact_bind_filter(struct Qdisc *sch, unsigned long parent, u32 classid) { @@ -198,7 +187,6 @@ static const struct Qdisc_class_ops clsact_class_ops = { .put = ingress_put, .walk = ingress_walk, .tcf_block = clsact_tcf_block, - .tcf_cl_offload = clsact_cl_offload, .bind_tcf = clsact_bind_filter, .unbind_tcf = ingress_put, }; -- cgit v1.2.3 From 839da4d98960bcc68e6b7b945b33ad3916ec1e92 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 10 Aug 2017 13:49:10 -0700 Subject: net: ipv4: set orig_oif based on fib result for local traffic Attempts to connect to a local address with a socket bound to a device with the local address hangs if there is no listener: $ ip addr sh dev eth1 3: eth1: mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 02:e0:f9:1c:00:37 brd ff:ff:ff:ff:ff:ff inet 10.100.1.4/24 scope global eth1 valid_lft forever preferred_lft forever inet6 2001:db8:1::4/120 scope global valid_lft forever preferred_lft forever inet6 fe80::e0:f9ff:fe1c:37/64 scope link valid_lft forever preferred_lft forever $ vrf-test -I eth1 -r 10.100.1.4 (don't let the command name fool you; vrf-test works without vrfs.) The problem is that the original intended device, eth1 in this case, is lost when the tcp reset is sent, so the socket lookup does not find a match for the reset and the connect attempt hangs. Fix by adjusting orig_oif for local traffic to the device from the fib lookup result. With this patch you get the more user friendly: $ vrf-test -I eth1 -r 10.100.1.4 connect failed: 111: Connection refused orig_oif is saved to the newly created rtable as rt_iif and when set it is used as the dif for socket lookups. It is set based on flowi4_oif passed in to ip_route_output_key_hash_rcu and will be set to either the loopback device, an l3mdev device, nothing (flowi4_oif = 0 which is the case in the example above) or a netdev index depending on the lookup path. In each case, resetting orig_oif to the device in the fib result for the RTN_LOCAL case allows the actual device to be preserved as the skb tx and rx is done over the loopback or VRF device. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/route.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'net') diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 2ef46294475f..b88836e6b4a1 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2439,6 +2439,12 @@ struct rtable *ip_route_output_key_hash_rcu(struct net *net, struct flowi4 *fl4, /* L3 master device is the loopback for that domain */ dev_out = l3mdev_master_dev_rcu(FIB_RES_DEV(*res)) ? : net->loopback_dev; + + /* make sure orig_oif points to fib result device even + * though packet rx/tx happens over loopback or l3mdev + */ + orig_oif = FIB_RES_OIF(*res); + fl4->flowi4_oif = dev_out->ifindex; flags |= RTCF_LOCAL; goto make_route; -- cgit v1.2.3 From 939912216fa8f62331de7d04edff492d5dc8e6e9 Mon Sep 17 00:00:00 2001 From: Tonghao Zhang Date: Thu, 10 Aug 2017 20:16:29 -0700 Subject: net: skb_needs_check() removes CHECKSUM_UNNECESSARY check for tx. Because we remove the UFO support, we will also remove the CHECKSUM_UNNECESSARY check in skb_needs_check(). Cc: Willem de Bruijn Signed-off-by: Tonghao Zhang Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- net/core/dev.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/dev.c b/net/core/dev.c index 3f69f6e71824..1024d3741d12 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -2731,8 +2731,7 @@ EXPORT_SYMBOL(skb_mac_gso_segment); static inline bool skb_needs_check(struct sk_buff *skb, bool tx_path) { if (tx_path) - return skb->ip_summed != CHECKSUM_PARTIAL && - skb->ip_summed != CHECKSUM_UNNECESSARY; + return skb->ip_summed != CHECKSUM_PARTIAL; return skb->ip_summed == CHECKSUM_NONE; } -- cgit v1.2.3 From 0ed80da518a1f27562a013f106505e495e891fe4 Mon Sep 17 00:00:00 2001 From: Joe Perches Date: Fri, 11 Aug 2017 04:26:26 -0700 Subject: openvswitch: Remove unnecessary newlines from OVS_NLERR uses OVS_NLERR already adds a newline so these just add blank lines to the logging. Signed-off-by: Joe Perches Acked-by: Joe Stringer Signed-off-by: David S. Miller --- net/openvswitch/conntrack.c | 14 +++++--------- net/openvswitch/flow_netlink.c | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index 03859e386b47..30d632509f82 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -1180,15 +1180,13 @@ static int parse_nat(const struct nlattr *attr, int type = nla_type(a); if (type > OVS_NAT_ATTR_MAX) { - OVS_NLERR(log, - "Unknown NAT attribute (type=%d, max=%d).\n", + OVS_NLERR(log, "Unknown NAT attribute (type=%d, max=%d)", type, OVS_NAT_ATTR_MAX); return -EINVAL; } if (nla_len(a) != ovs_nat_attr_lens[type][ip_vers]) { - OVS_NLERR(log, - "NAT attribute type %d has unexpected length (%d != %d).\n", + OVS_NLERR(log, "NAT attribute type %d has unexpected length (%d != %d)", type, nla_len(a), ovs_nat_attr_lens[type][ip_vers]); return -EINVAL; @@ -1198,9 +1196,7 @@ static int parse_nat(const struct nlattr *attr, case OVS_NAT_ATTR_SRC: case OVS_NAT_ATTR_DST: if (info->nat) { - OVS_NLERR(log, - "Only one type of NAT may be specified.\n" - ); + OVS_NLERR(log, "Only one type of NAT may be specified"); return -ERANGE; } info->nat |= OVS_CT_NAT; @@ -1245,13 +1241,13 @@ static int parse_nat(const struct nlattr *attr, break; default: - OVS_NLERR(log, "Unknown nat attribute (%d).\n", type); + OVS_NLERR(log, "Unknown nat attribute (%d)", type); return -EINVAL; } } if (rem > 0) { - OVS_NLERR(log, "NAT attribute has %d unknown bytes.\n", rem); + OVS_NLERR(log, "NAT attribute has %d unknown bytes", rem); return -EINVAL; } if (!info->nat) { diff --git a/net/openvswitch/flow_netlink.c b/net/openvswitch/flow_netlink.c index f07d10ac35d8..e8eb427ce6d1 100644 --- a/net/openvswitch/flow_netlink.c +++ b/net/openvswitch/flow_netlink.c @@ -1255,7 +1255,7 @@ static int ovs_key_from_nlattrs(struct net *net, struct sw_flow_match *match, } if (!is_mask && ipv6_key->ipv6_label & htonl(0xFFF00000)) { - OVS_NLERR(log, "IPv6 flow label %x is out of range (max=%x).\n", + OVS_NLERR(log, "IPv6 flow label %x is out of range (max=%x)", ntohl(ipv6_key->ipv6_label), (1 << 20) - 1); return -EINVAL; } -- cgit v1.2.3 From d0225784be6c9bdfb05149ebc30bf9fc1fdbce3a Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Wed, 9 Aug 2017 17:39:12 +0200 Subject: rtnelink: Move link dump consistency check out of the loop Calls to rtnl_dump_ifinfo() are protected by RTNL lock. So are the {list,unlist}_netdevice() calls where we bump the net->dev_base_seq number. For this reason net->dev_base_seq can't change under out feet while we're looping over links in rtnl_dump_ifinfo(). So move the check for net->dev_base_seq change (since the last time we were called) out of the loop. This way we avoid giving a wrong impression that there are concurrent updates to the link list going on while we're iterating over them. Signed-off-by: Jakub Sitnicki Signed-off-by: David S. Miller --- net/core/rtnetlink.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 9e9f1419be60..a78fd61da0ec 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -1621,8 +1621,6 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) s_h = cb->args[0]; s_idx = cb->args[1]; - cb->seq = net->dev_base_seq; - /* A hack to preserve kernel<->userspace interface. * The correct header is ifinfomsg. It is consistent with rtnl_getlink. * However, before Linux v3.9 the code here assumed rtgenmsg and that's @@ -1668,8 +1666,6 @@ static int rtnl_dump_ifinfo(struct sk_buff *skb, struct netlink_callback *cb) goto out_err; } - - nl_dump_check_consistent(cb, nlmsg_hdr(skb)); cont: idx++; } @@ -1679,6 +1675,8 @@ out: out_err: cb->args[1] = idx; cb->args[0] = h; + cb->seq = net->dev_base_seq; + nl_dump_check_consistent(cb, nlmsg_hdr(skb)); return err; } -- cgit v1.2.3 From 7c4974786f4794178f04e96318fc3b2f2850cbc6 Mon Sep 17 00:00:00 2001 From: Jason Wang Date: Fri, 11 Aug 2017 19:41:17 +0800 Subject: net: export some generic xdp helpers This patch tries to export some generic xdp helpers to drivers. This can let driver to do XDP for a specific skb. This is useful for the case when the packet is hard to be processed at page level directly (e.g jumbo/GSO frame). With this patch, there's no need for driver to forbid the XDP set when configuration is not suitable. Instead, it can defer the XDP for packets that is hard to be processed directly after skb is created. Signed-off-by: Jason Wang Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 ++ net/core/dev.c | 14 ++++++++------ 2 files changed, 10 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1d238d54c484..0f1c4cb2441e 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -3243,6 +3243,8 @@ static inline void dev_consume_skb_any(struct sk_buff *skb) __dev_kfree_skb_any(skb, SKB_REASON_CONSUMED); } +void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog); +int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb); int netif_rx(struct sk_buff *skb); int netif_rx_ni(struct sk_buff *skb); int netif_receive_skb(struct sk_buff *skb); diff --git a/net/core/dev.c b/net/core/dev.c index 1024d3741d12..40b28e417072 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3919,7 +3919,7 @@ static u32 netif_receive_generic_xdp(struct sk_buff *skb, /* When doing generic XDP we have to bypass the qdisc layer and the * network taps in order to match in-driver-XDP behavior. */ -static void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog) +void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog) { struct net_device *dev = skb->dev; struct netdev_queue *txq; @@ -3940,13 +3940,12 @@ static void generic_xdp_tx(struct sk_buff *skb, struct bpf_prog *xdp_prog) kfree_skb(skb); } } +EXPORT_SYMBOL_GPL(generic_xdp_tx); static struct static_key generic_xdp_needed __read_mostly; -static int do_xdp_generic(struct sk_buff *skb) +int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb) { - struct bpf_prog *xdp_prog = rcu_dereference(skb->dev->xdp_prog); - if (xdp_prog) { u32 act = netif_receive_generic_xdp(skb, xdp_prog); int err; @@ -3971,6 +3970,7 @@ out_redir: kfree_skb(skb); return XDP_DROP; } +EXPORT_SYMBOL_GPL(do_xdp_generic); static int netif_rx_internal(struct sk_buff *skb) { @@ -3981,7 +3981,8 @@ static int netif_rx_internal(struct sk_buff *skb) trace_netif_rx(skb); if (static_key_false(&generic_xdp_needed)) { - int ret = do_xdp_generic(skb); + int ret = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), + skb); /* Consider XDP consuming the packet a success from * the netdev point of view we do not want to count @@ -4502,7 +4503,8 @@ static int netif_receive_skb_internal(struct sk_buff *skb) rcu_read_lock(); if (static_key_false(&generic_xdp_needed)) { - int ret = do_xdp_generic(skb); + int ret = do_xdp_generic(rcu_dereference(skb->dev->xdp_prog), + skb); if (ret != XDP_PASS) { rcu_read_unlock(); -- cgit v1.2.3 From 9438c871b2c12f5f829156149502ce3e2d0ece76 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 11 Aug 2017 17:02:02 -0700 Subject: net: ipv4: remove unnecessary check on orig_oif rt_iif is going to be set to either 0 or orig_oif. If orig_oif is 0 it amounts to the same end result so remove the check. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/route.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/route.c b/net/ipv4/route.c index b88836e6b4a1..6810d2076b1b 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -2236,7 +2236,7 @@ add: if (!rth) return ERR_PTR(-ENOBUFS); - rth->rt_iif = orig_oif ? : 0; + rth->rt_iif = orig_oif; if (res->table) rth->rt_table_id = res->table->tb_id; -- cgit v1.2.3 From 1dfa76390bf056a49c1105b11f815a35a693b77c Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 11 Aug 2017 17:11:15 -0700 Subject: net: ipv4: add check for l3slave for index returned in IP_PKTINFO Similar to the loopback device, for packets sent through a VRF device the index returned in ipi_ifindex needs to be the saved index in rt_iif. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/ip_sockglue.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/ip_sockglue.c b/net/ipv4/ip_sockglue.c index dd68a9ed5e40..e558e4f9597b 100644 --- a/net/ipv4/ip_sockglue.c +++ b/net/ipv4/ip_sockglue.c @@ -1207,6 +1207,7 @@ e_inval: void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) { struct in_pktinfo *pktinfo = PKTINFO_SKB_CB(skb); + bool l3slave = ipv4_l3mdev_skb(IPCB(skb)->flags); bool prepare = (inet_sk(sk)->cmsg_flags & IP_CMSG_PKTINFO) || ipv6_sk_rxinfo(sk); @@ -1220,7 +1221,7 @@ void ipv4_pktinfo_prepare(const struct sock *sk, struct sk_buff *skb) * (e.g., process binds socket to eth0 for Tx which is * redirected to loopback in the rtable/dst). */ - if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX) + if (pktinfo->ipi_ifindex == LOOPBACK_IFINDEX || l3slave) pktinfo->ipi_ifindex = inet_iif(skb); pktinfo->ipi_spec_dst.s_addr = fib_compute_spec_dst(skb); -- cgit v1.2.3 From fe4007999599c02598c17b643e8de43e487d48e8 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Tue, 15 Aug 2017 09:09:49 +0200 Subject: ipv6: fib: Provide offload indication using nexthop flags IPv6 routes currently lack nexthop flags as in IPv4. This has several implications. In the forwarding path, it requires us to check the carrier state of the nexthop device and potentially ignore a linkdown route, instead of checking for RTNH_F_LINKDOWN. It also requires capable drivers to use the user facing IPv6-specific route flags to provide offload indication, instead of using the nexthop flags as in IPv4. Add nexthop flags to IPv6 routes in the 40 bytes hole and use it to provide offload indication instead of the RTF_OFFLOAD flag, which is removed while it's still not part of any official kernel release. In the near future we would like to use the field for the RTNH_F_{LINKDOWN,DEAD} flags, but this change is more involved and might not be ready in time for the current cycle. Signed-off-by: Ido Schimmel Signed-off-by: Jiri Pirko Acked-by: David Ahern Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c | 8 ++++---- include/net/ip6_fib.h | 2 ++ include/uapi/linux/ipv6_route.h | 1 - net/ipv6/route.c | 7 +------ 4 files changed, 7 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c index 16676fffbf70..4895d5b8942b 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_router.c @@ -2397,7 +2397,7 @@ mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) if (fib_entry->type == MLXSW_SP_FIB_ENTRY_TYPE_LOCAL) { list_first_entry(&fib6_entry->rt6_list, struct mlxsw_sp_rt6, - list)->rt->rt6i_flags |= RTF_OFFLOAD; + list)->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD; return; } @@ -2407,9 +2407,9 @@ mlxsw_sp_fib6_entry_offload_set(struct mlxsw_sp_fib_entry *fib_entry) nh = mlxsw_sp_rt6_nexthop(nh_grp, mlxsw_sp_rt6); if (nh && nh->offloaded) - mlxsw_sp_rt6->rt->rt6i_flags |= RTF_OFFLOAD; + mlxsw_sp_rt6->rt->rt6i_nh_flags |= RTNH_F_OFFLOAD; else - mlxsw_sp_rt6->rt->rt6i_flags &= ~RTF_OFFLOAD; + mlxsw_sp_rt6->rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD; } } @@ -2424,7 +2424,7 @@ mlxsw_sp_fib6_entry_offload_unset(struct mlxsw_sp_fib_entry *fib_entry) list_for_each_entry(mlxsw_sp_rt6, &fib6_entry->rt6_list, list) { struct rt6_info *rt = mlxsw_sp_rt6->rt; - rt->rt6i_flags &= ~RTF_OFFLOAD; + rt->rt6i_nh_flags &= ~RTNH_F_OFFLOAD; } } diff --git a/include/net/ip6_fib.h b/include/net/ip6_fib.h index 1d790ea40ea7..71c1646298ae 100644 --- a/include/net/ip6_fib.h +++ b/include/net/ip6_fib.h @@ -120,6 +120,8 @@ struct rt6_info { atomic_t rt6i_ref; + unsigned int rt6i_nh_flags; + /* These are in a separate cache line. */ struct rt6key rt6i_dst ____cacheline_aligned_in_smp; u32 rt6i_flags; diff --git a/include/uapi/linux/ipv6_route.h b/include/uapi/linux/ipv6_route.h index 33e2a5732bd1..d496c02e14bc 100644 --- a/include/uapi/linux/ipv6_route.h +++ b/include/uapi/linux/ipv6_route.h @@ -35,7 +35,6 @@ #define RTF_PREF(pref) ((pref) << 27) #define RTF_PREF_MASK 0x18000000 -#define RTF_OFFLOAD 0x20000000 /* offloaded route */ #define RTF_PCPU 0x40000000 /* read-only: can not be set by user */ #define RTF_LOCAL 0x80000000 diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 035762fed07d..6793135d49db 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1820,11 +1820,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, goto out; } - if (cfg->fc_flags & RTF_OFFLOAD) { - NL_SET_ERR_MSG(extack, "Userspace can not set RTF_OFFLOAD"); - goto out; - } - if (cfg->fc_dst_len > 128) { NL_SET_ERR_MSG(extack, "Invalid prefix length"); goto out; @@ -3335,7 +3330,7 @@ static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt, goto nla_put_failure; } - if (rt->rt6i_flags & RTF_OFFLOAD) + if (rt->rt6i_nh_flags & RTNH_F_OFFLOAD) *flags |= RTNH_F_OFFLOAD; /* not needed for multipath encoding b/c it has a rtnexthop struct */ -- cgit v1.2.3 From 7324157b8af19ff797b550b7b1543aa1c9c07b48 Mon Sep 17 00:00:00 2001 From: Craig Gallek Date: Tue, 15 Aug 2017 09:43:40 -0400 Subject: dsa: fix flow disector null pointer A recent change to fix up DSA device behavior made the assumption that all skbs passing through the flow disector will be associated with a device. This does not appear to be a safe assumption. Syzkaller found the crash below by attaching a BPF socket filter that tries to find the payload offset of a packet passing between two unix sockets. kasan: GPF could be caused by NULL-ptr deref or user memory access general protection fault: 0000 [#1] SMP KASAN Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: CPU: 0 PID: 2940 Comm: syzkaller872007 Not tainted 4.13.0-rc4-next-20170811 #1 Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 01/01/2011 task: ffff8801d1b425c0 task.stack: ffff8801d0bc0000 RIP: 0010:__skb_flow_dissect+0xdcd/0x3ae0 net/core/flow_dissector.c:445 RSP: 0018:ffff8801d0bc7340 EFLAGS: 00010206 RAX: dffffc0000000000 RBX: 0000000000000000 RCX: 0000000000000000 RDX: 0000000000000060 RSI: ffffffff856dc080 RDI: 0000000000000300 RBP: ffff8801d0bc7870 R08: 0000000000000000 R09: 0000000000000000 R10: 0000000000000008 R11: ffffed003a178f1e R12: 0000000000000000 R13: 0000000000000000 R14: ffffffff856dc080 R15: ffff8801ce223140 FS: 00000000016ed880(0000) GS:ffff8801dc000000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000020008000 CR3: 00000001ce22d000 CR4: 00000000001406f0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: skb_flow_dissect_flow_keys include/linux/skbuff.h:1176 [inline] skb_get_poff+0x9a/0x1a0 net/core/flow_dissector.c:1079 ______skb_get_pay_offset net/core/filter.c:114 [inline] __skb_get_pay_offset+0x15/0x20 net/core/filter.c:112 Code: 80 3c 02 00 44 89 6d 10 0f 85 44 2b 00 00 4d 8b 67 20 48 b8 00 00 00 00 00 fc ff df 49 8d bc 24 00 03 00 00 48 89 fa 48 c1 ea 03 <80> 3c 02 00 0f 85 13 2b 00 00 4d 8b a4 24 00 03 00 00 4d 85 e4 RIP: __skb_flow_dissect+0xdcd/0x3ae0 net/core/flow_dissector.c:445 RSP: ffff8801d0bc7340 Fixes: 43e665287f93 ("net-next: dsa: fix flow dissection") Reported-by: Dmitry Vyukov Signed-off-by: Craig Gallek Reviewed-by: Andrew Lunn Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index 79b9c06c83ad..e2eaa1ff948d 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -442,7 +442,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb, nhoff = skb_network_offset(skb); hlen = skb_headlen(skb); #if IS_ENABLED(CONFIG_NET_DSA) - if (unlikely(netdev_uses_dsa(skb->dev))) { + if (unlikely(skb->dev && netdev_uses_dsa(skb->dev))) { const struct dsa_device_ops *ops; int offset; -- cgit v1.2.3 From 121622dba8da9c709b72d801eae7664fa7da7c36 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Aug 2017 16:34:42 +0200 Subject: ipv6: route: make rtm_getroute not assume rtnl is locked __dev_get_by_index assumes RTNL is held, use _rcu version instead. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/ipv6/route.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6793135d49db..60705b4d2c62 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3611,8 +3611,11 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, struct net_device *dev; int flags = 0; - dev = __dev_get_by_index(net, iif); + rcu_read_lock(); + + dev = dev_get_by_index_rcu(net, iif); if (!dev) { + rcu_read_unlock(); err = -ENODEV; goto errout; } @@ -3624,6 +3627,8 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, if (!fibmatch) dst = ip6_route_input_lookup(net, dev, &fl6, flags); + + rcu_read_unlock(); } else { fl6.flowi6_oif = oif; -- cgit v1.2.3 From e3a22b7f5cfb3b422669fbf3d668315ac7634e5a Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Aug 2017 16:34:43 +0200 Subject: ipv6: route: set ipv6 RTM_GETROUTE to not use rtnl Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/ipv6/route.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 60705b4d2c62..11ff19ba7efd 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -4107,7 +4107,8 @@ int __init ip6_route_init(void) ret = -ENOBUFS; if (__rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL, 0) || __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL, 0) || - __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, 0)) + __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL, + RTNL_FLAG_DOIT_UNLOCKED)) goto out_register_late_subsys; ret = register_netdevice_notifier(&ip6_route_dev_notifier); -- cgit v1.2.3 From 394f51abb3d04f33fb798f04b16ae6b0491ea4ec Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 15 Aug 2017 16:34:44 +0200 Subject: ipv4: route: set ipv4 RTM_GETROUTE to not use rtnl Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/ipv4/route.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/route.c b/net/ipv4/route.c index 6810d2076b1b..618bbe1405fc 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -3073,7 +3073,8 @@ int __init ip_rt_init(void) xfrm_init(); xfrm4_init(); #endif - rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, 0); + rtnl_register(PF_INET, RTM_GETROUTE, inet_rtm_getroute, NULL, + RTNL_FLAG_DOIT_UNLOCKED); #ifdef CONFIG_SYSCTL register_pernet_subsys(&sysctl_route_ops); -- cgit v1.2.3 From 959466588aa7f84ccf79ae36a1d89542eaf9aaec Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Tue, 15 Aug 2017 16:39:59 +0300 Subject: net_sched: call qlen_notify only if child qdisc is empty This callback is used for deactivating class in parent qdisc. This is cheaper to test queue length right here. Also this allows to catch draining screwed backlog and prevent second deactivation of already inactive parent class which will crash kernel for sure. Kernel with print warning at destruction of child qdisc where no packets but backlog is not zero. Signed-off-by: Konstantin Khlebnikov Signed-off-by: David S. Miller --- net/sched/sch_api.c | 10 +++++++++- net/sched/sch_cbq.c | 3 +-- net/sched/sch_drr.c | 3 +-- net/sched/sch_hfsc.c | 6 ++---- net/sched/sch_htb.c | 3 +-- net/sched/sch_qfq.c | 3 +-- 6 files changed, 15 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 361377fbd780..0fea0c50b763 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -749,6 +749,7 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n, const struct Qdisc_class_ops *cops; unsigned long cl; u32 parentid; + bool notify; int drops; if (n == 0 && len == 0) @@ -761,6 +762,13 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n, if (sch->flags & TCQ_F_NOPARENT) break; + /* Notify parent qdisc only if child qdisc becomes empty. + * + * If child was empty even before update then backlog + * counter is screwed and we skip notification because + * parent class is already passive. + */ + notify = !sch->q.qlen && !WARN_ON_ONCE(!n); /* TODO: perform the search on a per txq basis */ sch = qdisc_lookup(qdisc_dev(sch), TC_H_MAJ(parentid)); if (sch == NULL) { @@ -768,7 +776,7 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n, break; } cops = sch->ops->cl_ops; - if (cops->qlen_notify) { + if (notify && cops->qlen_notify) { cl = cops->get(sch, parentid); cops->qlen_notify(sch, cl); cops->put(sch, cl); diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 780db43300b1..1bdb0106f342 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -1385,8 +1385,7 @@ static void cbq_qlen_notify(struct Qdisc *sch, unsigned long arg) { struct cbq_class *cl = (struct cbq_class *)arg; - if (cl->q->q.qlen == 0) - cbq_deactivate_class(cl); + cbq_deactivate_class(cl); } static unsigned long cbq_get(struct Qdisc *sch, u32 classid) diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index a413dc1c2098..1d2f6235dfcf 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -246,8 +246,7 @@ static void drr_qlen_notify(struct Qdisc *csh, unsigned long arg) { struct drr_class *cl = (struct drr_class *)arg; - if (cl->qdisc->q.qlen == 0) - list_del(&cl->alist); + list_del(&cl->alist); } static int drr_dump_class(struct Qdisc *sch, unsigned long arg, diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index fd15200f8627..14c99870cdb6 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -1221,10 +1221,8 @@ hfsc_qlen_notify(struct Qdisc *sch, unsigned long arg) { struct hfsc_class *cl = (struct hfsc_class *)arg; - if (cl->qdisc->q.qlen == 0) { - update_vf(cl, 0, 0); - set_passive(cl); - } + update_vf(cl, 0, 0); + set_passive(cl); } static unsigned long diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index 5d65ec5207e9..dcf3c85e1f4f 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -1186,8 +1186,7 @@ static void htb_qlen_notify(struct Qdisc *sch, unsigned long arg) { struct htb_class *cl = (struct htb_class *)arg; - if (cl->un.leaf.q->q.qlen == 0) - htb_deactivate(qdisc_priv(sch), cl); + htb_deactivate(qdisc_priv(sch), cl); } static unsigned long htb_get(struct Qdisc *sch, u32 classid) diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 0e16dfda0bd7..9caa959f91e1 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -1428,8 +1428,7 @@ static void qfq_qlen_notify(struct Qdisc *sch, unsigned long arg) struct qfq_sched *q = qdisc_priv(sch); struct qfq_class *cl = (struct qfq_class *)arg; - if (cl->qdisc->q.qlen == 0) - qfq_deactivate_class(q, cl); + qfq_deactivate_class(q, cl); } static int qfq_init_qdisc(struct Qdisc *sch, struct nlattr *opt) -- cgit v1.2.3 From 6b0355f4a9a5cb1346f2c1e30bfee49f2b2c8631 Mon Sep 17 00:00:00 2001 From: Konstantin Khlebnikov Date: Tue, 15 Aug 2017 16:40:03 +0300 Subject: net_sched/hfsc: opencode trivial set_active() and set_passive() Any move comment abount update_vf() into right place. Signed-off-by: Konstantin Khlebnikov Signed-off-by: David S. Miller --- net/sched/sch_hfsc.c | 45 ++++++++++++++++----------------------------- 1 file changed, 16 insertions(+), 29 deletions(-) (limited to 'net') diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 14c99870cdb6..15f09cb9f1ff 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -829,28 +829,6 @@ update_vf(struct hfsc_class *cl, unsigned int len, u64 cur_time) } } -static void -set_active(struct hfsc_class *cl, unsigned int len) -{ - if (cl->cl_flags & HFSC_RSC) - init_ed(cl, len); - if (cl->cl_flags & HFSC_FSC) - init_vf(cl, len); - -} - -static void -set_passive(struct hfsc_class *cl) -{ - if (cl->cl_flags & HFSC_RSC) - eltree_remove(cl); - - /* - * vttree is now handled in update_vf() so that update_vf(cl, 0, 0) - * needs to be called explicitly to remove a class from vttree. - */ -} - static unsigned int qdisc_peek_len(struct Qdisc *sch) { @@ -1221,8 +1199,12 @@ hfsc_qlen_notify(struct Qdisc *sch, unsigned long arg) { struct hfsc_class *cl = (struct hfsc_class *)arg; + /* vttree is now handled in update_vf() so that update_vf(cl, 0, 0) + * needs to be called explicitly to remove a class from vttree. + */ update_vf(cl, 0, 0); - set_passive(cl); + if (cl->cl_flags & HFSC_RSC) + eltree_remove(cl); } static unsigned long @@ -1583,7 +1565,12 @@ hfsc_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) } if (cl->qdisc->q.qlen == 1) { - set_active(cl, qdisc_pkt_len(skb)); + unsigned int len = qdisc_pkt_len(skb); + + if (cl->cl_flags & HFSC_RSC) + init_ed(cl, len); + if (cl->cl_flags & HFSC_FSC) + init_vf(cl, len); /* * If this is the first packet, isolate the head so an eventual * head drop before the first dequeue operation has no chance @@ -1647,18 +1634,18 @@ hfsc_dequeue(struct Qdisc *sch) if (realtime) cl->cl_cumul += qdisc_pkt_len(skb); - if (cl->qdisc->q.qlen != 0) { - if (cl->cl_flags & HFSC_RSC) { + if (cl->cl_flags & HFSC_RSC) { + if (cl->qdisc->q.qlen != 0) { /* update ed */ next_len = qdisc_peek_len(cl->qdisc); if (realtime) update_ed(cl, next_len); else update_d(cl, next_len); + } else { + /* the class becomes passive */ + eltree_remove(cl); } - } else { - /* the class becomes passive */ - set_passive(cl); } qdisc_bstats_update(sch, skb); -- cgit v1.2.3 From f26de110f4f1d8d2490d1f9f003d5abbde030f03 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Tue, 15 Aug 2017 22:30:47 -0700 Subject: net: early init support for strparser It is useful to allow strparser to init sockets before the read_sock callback has been established. Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- net/strparser/strparser.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/strparser/strparser.c b/net/strparser/strparser.c index 0d18fbc6f870..434aa6637a52 100644 --- a/net/strparser/strparser.c +++ b/net/strparser/strparser.c @@ -373,6 +373,9 @@ static int strp_read_sock(struct strparser *strp) struct socket *sock = strp->sk->sk_socket; read_descriptor_t desc; + if (unlikely(!sock || !sock->ops || !sock->ops->read_sock)) + return -EBUSY; + desc.arg.data = strp; desc.error = 0; desc.count = 1; /* give more than one skb per call */ @@ -486,12 +489,7 @@ int strp_init(struct strparser *strp, struct sock *sk, * The upper layer calls strp_process for each skb to be parsed. */ - if (sk) { - struct socket *sock = sk->sk_socket; - - if (!sock->ops->read_sock || !sock->ops->peek_len) - return -EAFNOSUPPORT; - } else { + if (!sk) { if (!cb->lock || !cb->unlock) return -EINVAL; } -- cgit v1.2.3 From 45f91bdcd5c5ba559a4bb7c3a0e0709476cf570f Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Tue, 15 Aug 2017 22:31:10 -0700 Subject: net: add sendmsg_locked and sendpage_locked to af_inet6 To complete the sendmsg_locked and sendpage_locked implementation add the hooks for af_inet6 as well. Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- net/ipv6/af_inet6.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 0a7c74049a0c..3b58ee709f33 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -554,6 +554,8 @@ const struct proto_ops inet6_stream_ops = { .recvmsg = inet_recvmsg, /* ok */ .mmap = sock_no_mmap, .sendpage = inet_sendpage, + .sendmsg_locked = tcp_sendmsg_locked, + .sendpage_locked = tcp_sendpage_locked, .splice_read = tcp_splice_read, .read_sock = tcp_read_sock, .peek_len = tcp_peek_len, -- cgit v1.2.3 From db5980d804d7158917ad4b9186c78b2a3f1db4ef Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Tue, 15 Aug 2017 22:31:34 -0700 Subject: net: fixes for skb_send_sock A couple fixes to new skb_send_sock infrastructure. However, no users currently exist for this code (adding user in next handful of patches) so it should not be possible to trigger a panic with existing in-kernel code. Fixes: 306b13eb3cf9 ("proto_ops: Add locked held versions of sendmsg and sendpage") Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- net/core/skbuff.c | 2 +- net/socket.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index cb123590c674..917da73d3ab3 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -2285,7 +2285,7 @@ do_frag_list: slen = min_t(int, len, skb_headlen(skb) - offset); kv.iov_base = skb->data + offset; - kv.iov_len = len; + kv.iov_len = slen; memset(&msg, 0, sizeof(msg)); ret = kernel_sendmsg_locked(sk, &msg, &kv, 1, slen); diff --git a/net/socket.c b/net/socket.c index b332d1e8e4e4..c729625eb5d3 100644 --- a/net/socket.c +++ b/net/socket.c @@ -658,7 +658,7 @@ int kernel_sendmsg_locked(struct sock *sk, struct msghdr *msg, struct socket *sock = sk->sk_socket; if (!sock->ops->sendmsg_locked) - sock_no_sendmsg_locked(sk, msg, size); + return sock_no_sendmsg_locked(sk, msg, size); iov_iter_kvec(&msg->msg_iter, WRITE | ITER_KVEC, vec, num, size); -- cgit v1.2.3 From b005fd189cec9407b700599e1e80e0552446ee79 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Tue, 15 Aug 2017 22:31:58 -0700 Subject: bpf: introduce new program type for skbs on sockets A class of programs, run from strparser and soon from a new map type called sock map, are used with skb as the context but on established sockets. By creating a specific program type for these we can use bpf helpers that expect full sockets and get the verifier to ensure these helpers are not used out of context. The new type is BPF_PROG_TYPE_SK_SKB. This patch introduces the infrastructure and type. Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- include/linux/bpf_types.h | 1 + include/uapi/linux/bpf.h | 1 + net/core/filter.c | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 38 insertions(+) (limited to 'net') diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index b1e1035ca24b..4b72db30dacf 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -11,6 +11,7 @@ BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_IN, lwt_inout_prog_ops) BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_OUT, lwt_inout_prog_ops) BPF_PROG_TYPE(BPF_PROG_TYPE_LWT_XMIT, lwt_xmit_prog_ops) BPF_PROG_TYPE(BPF_PROG_TYPE_SOCK_OPS, sock_ops_prog_ops) +BPF_PROG_TYPE(BPF_PROG_TYPE_SK_SKB, sk_skb_prog_ops) #endif #ifdef CONFIG_BPF_EVENTS BPF_PROG_TYPE(BPF_PROG_TYPE_KPROBE, kprobe_prog_ops) diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 91da8371a2d0..2e796e384aeb 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -127,6 +127,7 @@ enum bpf_prog_type { BPF_PROG_TYPE_LWT_OUT, BPF_PROG_TYPE_LWT_XMIT, BPF_PROG_TYPE_SOCK_OPS, + BPF_PROG_TYPE_SK_SKB, }; enum bpf_attach_type { diff --git a/net/core/filter.c b/net/core/filter.c index e0688a855c47..46321033ae0e 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3234,6 +3234,20 @@ static const struct bpf_func_proto * } } +static const struct bpf_func_proto *sk_skb_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + case BPF_FUNC_skb_load_bytes: + return &bpf_skb_load_bytes_proto; + case BPF_FUNC_get_socket_cookie: + return &bpf_get_socket_cookie_proto; + case BPF_FUNC_get_socket_uid: + return &bpf_get_socket_uid_proto; + default: + return bpf_base_func_proto(func_id); + } +} + static const struct bpf_func_proto * lwt_xmit_func_proto(enum bpf_func_id func_id) { @@ -3525,6 +3539,22 @@ static bool sock_ops_is_valid_access(int off, int size, return __is_valid_sock_ops_access(off, size); } +static bool sk_skb_is_valid_access(int off, int size, + enum bpf_access_type type, + struct bpf_insn_access_aux *info) +{ + switch (off) { + case bpf_ctx_range(struct __sk_buff, data): + info->reg_type = PTR_TO_PACKET; + break; + case bpf_ctx_range(struct __sk_buff, data_end): + info->reg_type = PTR_TO_PACKET_END; + break; + } + + return bpf_skb_is_valid_access(off, size, type, info); +} + static u32 bpf_convert_ctx_access(enum bpf_access_type type, const struct bpf_insn *si, struct bpf_insn *insn_buf, @@ -3994,6 +4024,12 @@ const struct bpf_verifier_ops sock_ops_prog_ops = { .convert_ctx_access = sock_ops_convert_ctx_access, }; +const struct bpf_verifier_ops sk_skb_prog_ops = { + .get_func_proto = sk_skb_func_proto, + .is_valid_access = sk_skb_is_valid_access, + .convert_ctx_access = bpf_convert_ctx_access, +}; + int sk_detach_filter(struct sock *sk) { int ret = -ENOENT; -- cgit v1.2.3 From 174a79ff9515f400b9a6115643dafd62a635b7e6 Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Tue, 15 Aug 2017 22:32:47 -0700 Subject: bpf: sockmap with sk redirect support Recently we added a new map type called dev map used to forward XDP packets between ports (6093ec2dc313). This patches introduces a similar notion for sockets. A sockmap allows users to add participating sockets to a map. When sockets are added to the map enough context is stored with the map entry to use the entry with a new helper bpf_sk_redirect_map(map, key, flags) This helper (analogous to bpf_redirect_map in XDP) is given the map and an entry in the map. When called from a sockmap program, discussed below, the skb will be sent on the socket using skb_send_sock(). With the above we need a bpf program to call the helper from that will then implement the send logic. The initial site implemented in this series is the recv_sock hook. For this to work we implemented a map attach command to add attributes to a map. In sockmap we add two programs a parse program and a verdict program. The parse program uses strparser to build messages and pass them to the verdict program. The parse programs use the normal strparser semantics. The verdict program is of type SK_SKB. The verdict program returns a verdict SK_DROP, or SK_REDIRECT for now. Additional actions may be added later. When SK_REDIRECT is returned, expected when bpf program uses bpf_sk_redirect_map(), the sockmap logic will consult per cpu variables set by the helper routine and pull the sock entry out of the sock map. This pattern follows the existing redirect logic in cls and xdp programs. This gives the flow, recv_sock -> str_parser (parse_prog) -> verdict_prog -> skb_send_sock \ -> kfree_skb As an example use case a message based load balancer may use specific logic in the verdict program to select the sock to send on. Sample programs are provided in future patches that hopefully illustrate the user interfaces. Also selftests are in follow-on patches. Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- include/linux/bpf.h | 7 +- include/linux/bpf_types.h | 1 + include/linux/filter.h | 2 + include/uapi/linux/bpf.h | 33 +- kernel/bpf/Makefile | 2 +- kernel/bpf/sockmap.c | 792 ++++++++++++++++++++++++++++++++++++++++++++++ kernel/bpf/syscall.c | 51 ++- kernel/bpf/verifier.c | 14 + net/core/filter.c | 43 +++ 9 files changed, 940 insertions(+), 5 deletions(-) create mode 100644 kernel/bpf/sockmap.c (limited to 'net') diff --git a/include/linux/bpf.h b/include/linux/bpf.h index d6e1de8ce0fc..a4145e9c74b5 100644 --- a/include/linux/bpf.h +++ b/include/linux/bpf.h @@ -16,6 +16,7 @@ #include struct perf_event; +struct bpf_prog; struct bpf_map; /* map is generic key/value storage optionally accesible by eBPF programs */ @@ -37,6 +38,8 @@ struct bpf_map_ops { void (*map_fd_put_ptr)(void *ptr); u32 (*map_gen_lookup)(struct bpf_map *map, struct bpf_insn *insn_buf); u32 (*map_fd_sys_lookup_elem)(void *ptr); + int (*map_attach)(struct bpf_map *map, + struct bpf_prog *p1, struct bpf_prog *p2); }; struct bpf_map { @@ -138,8 +141,6 @@ enum bpf_reg_type { PTR_TO_PACKET_END, /* skb->data + headlen */ }; -struct bpf_prog; - /* The information passed from prog-specific *_is_valid_access * back to the verifier. */ @@ -312,6 +313,7 @@ int bpf_check(struct bpf_prog **fp, union bpf_attr *attr); /* Map specifics */ struct net_device *__dev_map_lookup_elem(struct bpf_map *map, u32 key); +struct sock *__sock_map_lookup_elem(struct bpf_map *map, u32 key); void __dev_map_insert_ctx(struct bpf_map *map, u32 index); void __dev_map_flush(struct bpf_map *map); @@ -391,6 +393,7 @@ extern const struct bpf_func_proto bpf_get_current_comm_proto; extern const struct bpf_func_proto bpf_skb_vlan_push_proto; extern const struct bpf_func_proto bpf_skb_vlan_pop_proto; extern const struct bpf_func_proto bpf_get_stackid_proto; +extern const struct bpf_func_proto bpf_sock_map_update_proto; /* Shared helpers among cBPF and eBPF. */ void bpf_user_rnd_init_once(void); diff --git a/include/linux/bpf_types.h b/include/linux/bpf_types.h index 4b72db30dacf..fa805074d168 100644 --- a/include/linux/bpf_types.h +++ b/include/linux/bpf_types.h @@ -38,4 +38,5 @@ BPF_MAP_TYPE(BPF_MAP_TYPE_ARRAY_OF_MAPS, array_of_maps_map_ops) BPF_MAP_TYPE(BPF_MAP_TYPE_HASH_OF_MAPS, htab_of_maps_map_ops) #ifdef CONFIG_NET BPF_MAP_TYPE(BPF_MAP_TYPE_DEVMAP, dev_map_ops) +BPF_MAP_TYPE(BPF_MAP_TYPE_SOCKMAP, sock_map_ops) #endif diff --git a/include/linux/filter.h b/include/linux/filter.h index d19ed3c15e1e..7015116331af 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -727,6 +727,8 @@ void xdp_do_flush_map(void); void bpf_warn_invalid_xdp_action(u32 act); void bpf_warn_invalid_xdp_redirect(u32 ifindex); +struct sock *do_sk_redirect_map(void); + #ifdef CONFIG_BPF_JIT extern int bpf_jit_enable; extern int bpf_jit_harden; diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 2e796e384aeb..7f774769e3f5 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -110,6 +110,7 @@ enum bpf_map_type { BPF_MAP_TYPE_ARRAY_OF_MAPS, BPF_MAP_TYPE_HASH_OF_MAPS, BPF_MAP_TYPE_DEVMAP, + BPF_MAP_TYPE_SOCKMAP, }; enum bpf_prog_type { @@ -135,11 +136,15 @@ enum bpf_attach_type { BPF_CGROUP_INET_EGRESS, BPF_CGROUP_INET_SOCK_CREATE, BPF_CGROUP_SOCK_OPS, + BPF_CGROUP_SMAP_INGRESS, __MAX_BPF_ATTACH_TYPE }; #define MAX_BPF_ATTACH_TYPE __MAX_BPF_ATTACH_TYPE +/* If BPF_SOCKMAP_STRPARSER is used sockmap will use strparser on receive */ +#define BPF_SOCKMAP_STRPARSER (1U << 0) + /* If BPF_F_ALLOW_OVERRIDE flag is used in BPF_PROG_ATTACH command * to the given target_fd cgroup the descendent cgroup will be able to * override effective bpf program that was inherited from this cgroup @@ -211,6 +216,7 @@ union bpf_attr { __u32 attach_bpf_fd; /* eBPF program to attach */ __u32 attach_type; __u32 attach_flags; + __u32 attach_bpf_fd2; }; struct { /* anonymous struct used by BPF_PROG_TEST_RUN command */ @@ -557,6 +563,23 @@ union bpf_attr { * @mode: operation mode (enum bpf_adj_room_mode) * @flags: reserved for future use * Return: 0 on success or negative error code + * + * int bpf_sk_redirect_map(map, key, flags) + * Redirect skb to a sock in map using key as a lookup key for the + * sock in map. + * @map: pointer to sockmap + * @key: key to lookup sock in map + * @flags: reserved for future use + * Return: SK_REDIRECT + * + * int bpf_sock_map_update(skops, map, key, flags, map_flags) + * @skops: pointer to bpf_sock_ops + * @map: pointer to sockmap to update + * @key: key to insert/update sock in map + * @flags: same flags as map update elem + * @map_flags: sock map specific flags + * bit 1: Enable strparser + * other bits: reserved */ #define __BPF_FUNC_MAPPER(FN) \ FN(unspec), \ @@ -610,7 +633,9 @@ union bpf_attr { FN(set_hash), \ FN(setsockopt), \ FN(skb_adjust_room), \ - FN(redirect_map), + FN(redirect_map), \ + FN(sk_redirect_map), \ + FN(sock_map_update), \ /* integer value in 'imm' field of BPF_CALL instruction selects which helper * function eBPF program intends to call @@ -747,6 +772,12 @@ struct xdp_md { __u32 data_end; }; +enum sk_action { + SK_ABORTED = 0, + SK_DROP, + SK_REDIRECT, +}; + #define BPF_TAG_SIZE 8 struct bpf_prog_info { diff --git a/kernel/bpf/Makefile b/kernel/bpf/Makefile index 2f0bcda40e90..aa24287db888 100644 --- a/kernel/bpf/Makefile +++ b/kernel/bpf/Makefile @@ -3,7 +3,7 @@ obj-y := core.o obj-$(CONFIG_BPF_SYSCALL) += syscall.o verifier.o inode.o helpers.o tnum.o obj-$(CONFIG_BPF_SYSCALL) += hashtab.o arraymap.o percpu_freelist.o bpf_lru_list.o lpm_trie.o map_in_map.o ifeq ($(CONFIG_NET),y) -obj-$(CONFIG_BPF_SYSCALL) += devmap.o +obj-$(CONFIG_BPF_SYSCALL) += devmap.o sockmap.o endif ifeq ($(CONFIG_PERF_EVENTS),y) obj-$(CONFIG_BPF_SYSCALL) += stackmap.o diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c new file mode 100644 index 000000000000..792f0addfafa --- /dev/null +++ b/kernel/bpf/sockmap.c @@ -0,0 +1,792 @@ +/* Copyright (c) 2017 Covalent IO, Inc. http://covalent.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of version 2 of the GNU General Public + * License 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. + */ + +/* A BPF sock_map is used to store sock objects. This is primarly used + * for doing socket redirect with BPF helper routines. + * + * A sock map may have two BPF programs attached to it, a program used + * to parse packets and a program to provide a verdict and redirect + * decision on the packet. If no BPF parse program is provided it is + * assumed that every skb is a "message" (skb->len). Otherwise the + * parse program is attached to strparser and used to build messages + * that may span multiple skbs. The verdict program will either select + * a socket to send/receive the skb on or provide the drop code indicating + * the skb should be dropped. More actions may be added later as needed. + * The default program will drop packets. + * + * For reference this program is similar to devmap used in XDP context + * reviewing these together may be useful. For an example please review + * ./samples/bpf/sockmap/. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct bpf_stab { + struct bpf_map map; + struct sock **sock_map; + struct bpf_prog *bpf_parse; + struct bpf_prog *bpf_verdict; + refcount_t refcnt; +}; + +enum smap_psock_state { + SMAP_TX_RUNNING, +}; + +struct smap_psock { + struct rcu_head rcu; + + /* datapath variables */ + struct sk_buff_head rxqueue; + bool strp_enabled; + + /* datapath error path cache across tx work invocations */ + int save_rem; + int save_off; + struct sk_buff *save_skb; + + struct strparser strp; + struct bpf_prog *bpf_parse; + struct bpf_prog *bpf_verdict; + struct bpf_stab *stab; + + /* Back reference used when sock callback trigger sockmap operations */ + int key; + struct sock *sock; + unsigned long state; + + struct work_struct tx_work; + struct work_struct gc_work; + + void (*save_data_ready)(struct sock *sk); + void (*save_write_space)(struct sock *sk); + void (*save_state_change)(struct sock *sk); +}; + +static inline struct smap_psock *smap_psock_sk(const struct sock *sk) +{ + return (struct smap_psock *)rcu_dereference_sk_user_data(sk); +} + +static int smap_verdict_func(struct smap_psock *psock, struct sk_buff *skb) +{ + struct bpf_prog *prog = READ_ONCE(psock->bpf_verdict); + int rc; + + if (unlikely(!prog)) + return SK_DROP; + + skb_orphan(skb); + skb->sk = psock->sock; + bpf_compute_data_end(skb); + rc = (*prog->bpf_func)(skb, prog->insnsi); + skb->sk = NULL; + + return rc; +} + +static void smap_do_verdict(struct smap_psock *psock, struct sk_buff *skb) +{ + struct sock *sock; + int rc; + + /* Because we use per cpu values to feed input from sock redirect + * in BPF program to do_sk_redirect_map() call we need to ensure we + * are not preempted. RCU read lock is not sufficient in this case + * with CONFIG_PREEMPT_RCU enabled so we must be explicit here. + */ + preempt_disable(); + rc = smap_verdict_func(psock, skb); + switch (rc) { + case SK_REDIRECT: + sock = do_sk_redirect_map(); + preempt_enable(); + if (likely(sock)) { + struct smap_psock *peer = smap_psock_sk(sock); + + if (likely(peer && + test_bit(SMAP_TX_RUNNING, &peer->state) && + sk_stream_memory_free(peer->sock))) { + peer->sock->sk_wmem_queued += skb->truesize; + sk_mem_charge(peer->sock, skb->truesize); + skb_queue_tail(&peer->rxqueue, skb); + schedule_work(&peer->tx_work); + break; + } + } + /* Fall through and free skb otherwise */ + case SK_DROP: + default: + preempt_enable(); + kfree_skb(skb); + } +} + +static void smap_report_sk_error(struct smap_psock *psock, int err) +{ + struct sock *sk = psock->sock; + + sk->sk_err = err; + sk->sk_error_report(sk); +} + +static void smap_release_sock(struct sock *sock); + +/* Called with lock_sock(sk) held */ +static void smap_state_change(struct sock *sk) +{ + struct smap_psock *psock; + struct sock *osk; + + rcu_read_lock(); + + /* Allowing transitions into an established syn_recv states allows + * for early binding sockets to a smap object before the connection + * is established. + */ + switch (sk->sk_state) { + case TCP_SYN_RECV: + case TCP_ESTABLISHED: + break; + case TCP_CLOSE_WAIT: + case TCP_CLOSING: + case TCP_LAST_ACK: + case TCP_FIN_WAIT1: + case TCP_FIN_WAIT2: + case TCP_LISTEN: + break; + case TCP_CLOSE: + /* Only release if the map entry is in fact the sock in + * question. There is a case where the operator deletes + * the sock from the map, but the TCP sock is closed before + * the psock is detached. Use cmpxchg to verify correct + * sock is removed. + */ + psock = smap_psock_sk(sk); + if (unlikely(!psock)) + break; + osk = cmpxchg(&psock->stab->sock_map[psock->key], sk, NULL); + if (osk == sk) + smap_release_sock(sk); + break; + default: + smap_report_sk_error(psock, EPIPE); + break; + } + rcu_read_unlock(); +} + +static void smap_read_sock_strparser(struct strparser *strp, + struct sk_buff *skb) +{ + struct smap_psock *psock; + + rcu_read_lock(); + psock = container_of(strp, struct smap_psock, strp); + smap_do_verdict(psock, skb); + rcu_read_unlock(); +} + +/* Called with lock held on socket */ +static void smap_data_ready(struct sock *sk) +{ + struct smap_psock *psock; + + write_lock_bh(&sk->sk_callback_lock); + psock = smap_psock_sk(sk); + if (likely(psock)) + strp_data_ready(&psock->strp); + write_unlock_bh(&sk->sk_callback_lock); +} + +static void smap_tx_work(struct work_struct *w) +{ + struct smap_psock *psock; + struct sk_buff *skb; + int rem, off, n; + + psock = container_of(w, struct smap_psock, tx_work); + + /* lock sock to avoid losing sk_socket at some point during loop */ + lock_sock(psock->sock); + if (psock->save_skb) { + skb = psock->save_skb; + rem = psock->save_rem; + off = psock->save_off; + psock->save_skb = NULL; + goto start; + } + + while ((skb = skb_dequeue(&psock->rxqueue))) { + rem = skb->len; + off = 0; +start: + do { + if (likely(psock->sock->sk_socket)) + n = skb_send_sock_locked(psock->sock, + skb, off, rem); + else + n = -EINVAL; + if (n <= 0) { + if (n == -EAGAIN) { + /* Retry when space is available */ + psock->save_skb = skb; + psock->save_rem = rem; + psock->save_off = off; + goto out; + } + /* Hard errors break pipe and stop xmit */ + smap_report_sk_error(psock, n ? -n : EPIPE); + clear_bit(SMAP_TX_RUNNING, &psock->state); + sk_mem_uncharge(psock->sock, skb->truesize); + psock->sock->sk_wmem_queued -= skb->truesize; + kfree_skb(skb); + goto out; + } + rem -= n; + off += n; + } while (rem); + sk_mem_uncharge(psock->sock, skb->truesize); + psock->sock->sk_wmem_queued -= skb->truesize; + kfree_skb(skb); + } +out: + release_sock(psock->sock); +} + +static void smap_write_space(struct sock *sk) +{ + struct smap_psock *psock; + + rcu_read_lock(); + psock = smap_psock_sk(sk); + if (likely(psock && test_bit(SMAP_TX_RUNNING, &psock->state))) + schedule_work(&psock->tx_work); + rcu_read_unlock(); +} + +static void smap_stop_sock(struct smap_psock *psock, struct sock *sk) +{ + write_lock_bh(&sk->sk_callback_lock); + if (!psock->strp_enabled) + goto out; + sk->sk_data_ready = psock->save_data_ready; + sk->sk_write_space = psock->save_write_space; + sk->sk_state_change = psock->save_state_change; + psock->save_data_ready = NULL; + psock->save_write_space = NULL; + psock->save_state_change = NULL; + strp_stop(&psock->strp); + psock->strp_enabled = false; +out: + write_unlock_bh(&sk->sk_callback_lock); +} + +static void smap_destroy_psock(struct rcu_head *rcu) +{ + struct smap_psock *psock = container_of(rcu, + struct smap_psock, rcu); + + /* Now that a grace period has passed there is no longer + * any reference to this sock in the sockmap so we can + * destroy the psock, strparser, and bpf programs. But, + * because we use workqueue sync operations we can not + * do it in rcu context + */ + schedule_work(&psock->gc_work); +} + +static void smap_release_sock(struct sock *sock) +{ + struct smap_psock *psock = smap_psock_sk(sock); + + smap_stop_sock(psock, sock); + clear_bit(SMAP_TX_RUNNING, &psock->state); + rcu_assign_sk_user_data(sock, NULL); + call_rcu_sched(&psock->rcu, smap_destroy_psock); +} + +static int smap_parse_func_strparser(struct strparser *strp, + struct sk_buff *skb) +{ + struct smap_psock *psock; + struct bpf_prog *prog; + int rc; + + rcu_read_lock(); + psock = container_of(strp, struct smap_psock, strp); + prog = READ_ONCE(psock->bpf_parse); + + if (unlikely(!prog)) { + rcu_read_unlock(); + return skb->len; + } + + /* Attach socket for bpf program to use if needed we can do this + * because strparser clones the skb before handing it to a upper + * layer, meaning skb_orphan has been called. We NULL sk on the + * way out to ensure we don't trigger a BUG_ON in skb/sk operations + * later and because we are not charging the memory of this skb to + * any socket yet. + */ + skb->sk = psock->sock; + bpf_compute_data_end(skb); + rc = (*prog->bpf_func)(skb, prog->insnsi); + skb->sk = NULL; + rcu_read_unlock(); + return rc; +} + + +static int smap_read_sock_done(struct strparser *strp, int err) +{ + return err; +} + +static int smap_init_sock(struct smap_psock *psock, + struct sock *sk) +{ + struct strp_callbacks cb; + + memset(&cb, 0, sizeof(cb)); + cb.rcv_msg = smap_read_sock_strparser; + cb.parse_msg = smap_parse_func_strparser; + cb.read_sock_done = smap_read_sock_done; + return strp_init(&psock->strp, sk, &cb); +} + +static void smap_init_progs(struct smap_psock *psock, + struct bpf_stab *stab, + struct bpf_prog *verdict, + struct bpf_prog *parse) +{ + struct bpf_prog *orig_parse, *orig_verdict; + + orig_parse = xchg(&psock->bpf_parse, parse); + orig_verdict = xchg(&psock->bpf_verdict, verdict); + + if (orig_verdict) + bpf_prog_put(orig_verdict); + if (orig_parse) + bpf_prog_put(orig_parse); +} + +static void smap_start_sock(struct smap_psock *psock, struct sock *sk) +{ + if (sk->sk_data_ready == smap_data_ready) + return; + psock->save_data_ready = sk->sk_data_ready; + psock->save_write_space = sk->sk_write_space; + psock->save_state_change = sk->sk_state_change; + sk->sk_data_ready = smap_data_ready; + sk->sk_write_space = smap_write_space; + sk->sk_state_change = smap_state_change; + psock->strp_enabled = true; +} + +static void sock_map_remove_complete(struct bpf_stab *stab) +{ + bpf_map_area_free(stab->sock_map); + kfree(stab); +} + +static void smap_gc_work(struct work_struct *w) +{ + struct smap_psock *psock; + + psock = container_of(w, struct smap_psock, gc_work); + + /* no callback lock needed because we already detached sockmap ops */ + if (psock->strp_enabled) + strp_done(&psock->strp); + + cancel_work_sync(&psock->tx_work); + __skb_queue_purge(&psock->rxqueue); + + /* At this point all strparser and xmit work must be complete */ + if (psock->bpf_parse) + bpf_prog_put(psock->bpf_parse); + if (psock->bpf_verdict) + bpf_prog_put(psock->bpf_verdict); + + if (refcount_dec_and_test(&psock->stab->refcnt)) + sock_map_remove_complete(psock->stab); + + sock_put(psock->sock); + kfree(psock); +} + +static struct smap_psock *smap_init_psock(struct sock *sock, + struct bpf_stab *stab) +{ + struct smap_psock *psock; + + psock = kzalloc(sizeof(struct smap_psock), GFP_ATOMIC | __GFP_NOWARN); + if (!psock) + return ERR_PTR(-ENOMEM); + + psock->sock = sock; + skb_queue_head_init(&psock->rxqueue); + INIT_WORK(&psock->tx_work, smap_tx_work); + INIT_WORK(&psock->gc_work, smap_gc_work); + + rcu_assign_sk_user_data(sock, psock); + sock_hold(sock); + return psock; +} + +static struct bpf_map *sock_map_alloc(union bpf_attr *attr) +{ + struct bpf_stab *stab; + int err = -EINVAL; + u64 cost; + + /* check sanity of attributes */ + if (attr->max_entries == 0 || attr->key_size != 4 || + attr->value_size != 4 || attr->map_flags) + return ERR_PTR(-EINVAL); + + if (attr->value_size > KMALLOC_MAX_SIZE) + return ERR_PTR(-E2BIG); + + stab = kzalloc(sizeof(*stab), GFP_USER); + if (!stab) + return ERR_PTR(-ENOMEM); + + /* mandatory map attributes */ + stab->map.map_type = attr->map_type; + stab->map.key_size = attr->key_size; + stab->map.value_size = attr->value_size; + stab->map.max_entries = attr->max_entries; + stab->map.map_flags = attr->map_flags; + + /* make sure page count doesn't overflow */ + cost = (u64) stab->map.max_entries * sizeof(struct sock *); + if (cost >= U32_MAX - PAGE_SIZE) + goto free_stab; + + stab->map.pages = round_up(cost, PAGE_SIZE) >> PAGE_SHIFT; + + /* if map size is larger than memlock limit, reject it early */ + err = bpf_map_precharge_memlock(stab->map.pages); + if (err) + goto free_stab; + + stab->sock_map = bpf_map_area_alloc(stab->map.max_entries * + sizeof(struct sock *)); + if (!stab->sock_map) + goto free_stab; + + refcount_set(&stab->refcnt, 1); + return &stab->map; +free_stab: + kfree(stab); + return ERR_PTR(err); +} + +static void sock_map_free(struct bpf_map *map) +{ + struct bpf_stab *stab = container_of(map, struct bpf_stab, map); + int i; + + synchronize_rcu(); + + /* At this point no update, lookup or delete operations can happen. + * However, be aware we can still get a socket state event updates, + * and data ready callabacks that reference the psock from sk_user_data + * Also psock worker threads are still in-flight. So smap_release_sock + * will only free the psock after cancel_sync on the worker threads + * and a grace period expire to ensure psock is really safe to remove. + */ + rcu_read_lock(); + for (i = 0; i < stab->map.max_entries; i++) { + struct sock *sock; + + sock = xchg(&stab->sock_map[i], NULL); + if (!sock) + continue; + + smap_release_sock(sock); + } + rcu_read_unlock(); + + if (stab->bpf_verdict) + bpf_prog_put(stab->bpf_verdict); + if (stab->bpf_parse) + bpf_prog_put(stab->bpf_parse); + + if (refcount_dec_and_test(&stab->refcnt)) + sock_map_remove_complete(stab); +} + +static int sock_map_get_next_key(struct bpf_map *map, void *key, void *next_key) +{ + struct bpf_stab *stab = container_of(map, struct bpf_stab, map); + u32 i = key ? *(u32 *)key : U32_MAX; + u32 *next = (u32 *)next_key; + + if (i >= stab->map.max_entries) { + *next = 0; + return 0; + } + + if (i == stab->map.max_entries - 1) + return -ENOENT; + + *next = i + 1; + return 0; +} + +struct sock *__sock_map_lookup_elem(struct bpf_map *map, u32 key) +{ + struct bpf_stab *stab = container_of(map, struct bpf_stab, map); + + if (key >= map->max_entries) + return NULL; + + return READ_ONCE(stab->sock_map[key]); +} + +static int sock_map_delete_elem(struct bpf_map *map, void *key) +{ + struct bpf_stab *stab = container_of(map, struct bpf_stab, map); + int k = *(u32 *)key; + struct sock *sock; + + if (k >= map->max_entries) + return -EINVAL; + + sock = xchg(&stab->sock_map[k], NULL); + if (!sock) + return -EINVAL; + + smap_release_sock(sock); + return 0; +} + +/* Locking notes: Concurrent updates, deletes, and lookups are allowed and are + * done inside rcu critical sections. This ensures on updates that the psock + * will not be released via smap_release_sock() until concurrent updates/deletes + * complete. All operations operate on sock_map using cmpxchg and xchg + * operations to ensure we do not get stale references. Any reads into the + * map must be done with READ_ONCE() because of this. + * + * A psock is destroyed via call_rcu and after any worker threads are cancelled + * and syncd so we are certain all references from the update/lookup/delete + * operations as well as references in the data path are no longer in use. + * + * A psock object holds a refcnt on the sockmap it is attached to and this is + * not decremented until after a RCU grace period and garbage collection occurs. + * This ensures the map is not free'd until psocks linked to it are removed. The + * map link is used when the independent sock events trigger map deletion. + * + * Psocks may only participate in one sockmap at a time. Users that try to + * join a single sock to multiple maps will get an error. + * + * Last, but not least, it is possible the socket is closed while running + * an update on an existing psock. This will release the psock, but again + * not until the update has completed due to rcu grace period rules. + */ +static int sock_map_ctx_update_elem(struct bpf_sock_ops_kern *skops, + struct bpf_map *map, + void *key, u64 flags, u64 map_flags) +{ + struct bpf_stab *stab = container_of(map, struct bpf_stab, map); + struct bpf_prog *verdict, *parse; + struct smap_psock *psock = NULL; + struct sock *old_sock, *sock; + u32 i = *(u32 *)key; + bool update = false; + int err = 0; + + if (unlikely(flags > BPF_EXIST)) + return -EINVAL; + + if (unlikely(i >= stab->map.max_entries)) + return -E2BIG; + + if (unlikely(map_flags > BPF_SOCKMAP_STRPARSER)) + return -EINVAL; + + verdict = parse = NULL; + sock = READ_ONCE(stab->sock_map[i]); + + if (flags == BPF_EXIST || flags == BPF_ANY) { + if (!sock && flags == BPF_EXIST) { + return -ENOENT; + } else if (sock && sock != skops->sk) { + return -EINVAL; + } else if (sock) { + psock = smap_psock_sk(sock); + if (unlikely(!psock)) + return -EBUSY; + update = true; + } + } else if (sock && BPF_NOEXIST) { + return -EEXIST; + } + + /* reserve BPF programs early so can abort easily on failures */ + if (map_flags & BPF_SOCKMAP_STRPARSER) { + verdict = READ_ONCE(stab->bpf_verdict); + parse = READ_ONCE(stab->bpf_parse); + + if (!verdict || !parse) + return -ENOENT; + + /* bpf prog refcnt may be zero if a concurrent attach operation + * removes the program after the above READ_ONCE() but before + * we increment the refcnt. If this is the case abort with an + * error. + */ + verdict = bpf_prog_inc_not_zero(stab->bpf_verdict); + if (IS_ERR(verdict)) + return PTR_ERR(verdict); + + parse = bpf_prog_inc_not_zero(stab->bpf_parse); + if (IS_ERR(parse)) { + bpf_prog_put(verdict); + return PTR_ERR(parse); + } + } + + if (!psock) { + sock = skops->sk; + if (rcu_dereference_sk_user_data(sock)) + return -EEXIST; + psock = smap_init_psock(sock, stab); + if (IS_ERR(psock)) { + if (verdict) + bpf_prog_put(verdict); + if (parse) + bpf_prog_put(parse); + return PTR_ERR(psock); + } + psock->key = i; + psock->stab = stab; + refcount_inc(&stab->refcnt); + set_bit(SMAP_TX_RUNNING, &psock->state); + } + + if (map_flags & BPF_SOCKMAP_STRPARSER) { + write_lock_bh(&sock->sk_callback_lock); + if (psock->strp_enabled) + goto start_done; + err = smap_init_sock(psock, sock); + if (err) + goto out; + smap_init_progs(psock, stab, verdict, parse); + smap_start_sock(psock, sock); +start_done: + write_unlock_bh(&sock->sk_callback_lock); + } else if (update) { + smap_stop_sock(psock, sock); + } + + if (!update) { + old_sock = xchg(&stab->sock_map[i], skops->sk); + if (old_sock) + smap_release_sock(old_sock); + } + + return 0; +out: + write_unlock_bh(&sock->sk_callback_lock); + if (!update) + smap_release_sock(sock); + return err; +} + +static int sock_map_attach_prog(struct bpf_map *map, + struct bpf_prog *parse, + struct bpf_prog *verdict) +{ + struct bpf_stab *stab = container_of(map, struct bpf_stab, map); + struct bpf_prog *_parse, *_verdict; + + _parse = xchg(&stab->bpf_parse, parse); + _verdict = xchg(&stab->bpf_verdict, verdict); + + if (_parse) + bpf_prog_put(_parse); + if (_verdict) + bpf_prog_put(_verdict); + + return 0; +} + +static void *sock_map_lookup(struct bpf_map *map, void *key) +{ + return NULL; +} + +static int sock_map_update_elem(struct bpf_map *map, + void *key, void *value, u64 flags) +{ + struct bpf_sock_ops_kern skops; + u32 fd = *(u32 *)value; + struct socket *socket; + int err; + + socket = sockfd_lookup(fd, &err); + if (!socket) + return err; + + skops.sk = socket->sk; + if (!skops.sk) { + fput(socket->file); + return -EINVAL; + } + + err = sock_map_ctx_update_elem(&skops, map, key, + flags, BPF_SOCKMAP_STRPARSER); + fput(socket->file); + return err; +} + +const struct bpf_map_ops sock_map_ops = { + .map_alloc = sock_map_alloc, + .map_free = sock_map_free, + .map_lookup_elem = sock_map_lookup, + .map_get_next_key = sock_map_get_next_key, + .map_update_elem = sock_map_update_elem, + .map_delete_elem = sock_map_delete_elem, + .map_attach = sock_map_attach_prog, +}; + +BPF_CALL_5(bpf_sock_map_update, struct bpf_sock_ops_kern *, bpf_sock, + struct bpf_map *, map, void *, key, u64, flags, u64, map_flags) +{ + WARN_ON_ONCE(!rcu_read_lock_held()); + return sock_map_ctx_update_elem(bpf_sock, map, key, flags, map_flags); +} + +const struct bpf_func_proto bpf_sock_map_update_proto = { + .func = bpf_sock_map_update, + .gpl_only = false, + .pkt_access = true, + .ret_type = RET_INTEGER, + .arg1_type = ARG_PTR_TO_CTX, + .arg2_type = ARG_CONST_MAP_PTR, + .arg3_type = ARG_PTR_TO_MAP_KEY, + .arg4_type = ARG_ANYTHING, + .arg5_type = ARG_ANYTHING, +}; diff --git a/kernel/bpf/syscall.c b/kernel/bpf/syscall.c index 17e29f596de1..d2f2bdf71ffa 100644 --- a/kernel/bpf/syscall.c +++ b/kernel/bpf/syscall.c @@ -1087,7 +1087,50 @@ static int bpf_obj_get(const union bpf_attr *attr) #ifdef CONFIG_CGROUP_BPF -#define BPF_PROG_ATTACH_LAST_FIELD attach_flags +#define BPF_PROG_ATTACH_LAST_FIELD attach_bpf_fd2 + +static int sockmap_get_from_fd(const union bpf_attr *attr, int ptype) +{ + struct bpf_prog *prog1, *prog2; + int ufd = attr->target_fd; + struct bpf_map *map; + struct fd f; + int err; + + f = fdget(ufd); + map = __bpf_map_get(f); + if (IS_ERR(map)) + return PTR_ERR(map); + + if (!map->ops->map_attach) { + fdput(f); + return -EOPNOTSUPP; + } + + prog1 = bpf_prog_get_type(attr->attach_bpf_fd, ptype); + if (IS_ERR(prog1)) { + fdput(f); + return PTR_ERR(prog1); + } + + prog2 = bpf_prog_get_type(attr->attach_bpf_fd2, ptype); + if (IS_ERR(prog2)) { + fdput(f); + bpf_prog_put(prog1); + return PTR_ERR(prog2); + } + + err = map->ops->map_attach(map, prog1, prog2); + if (err) { + fdput(f); + bpf_prog_put(prog1); + bpf_prog_put(prog2); + return PTR_ERR(map); + } + + fdput(f); + return err; +} static int bpf_prog_attach(const union bpf_attr *attr) { @@ -1116,10 +1159,16 @@ static int bpf_prog_attach(const union bpf_attr *attr) case BPF_CGROUP_SOCK_OPS: ptype = BPF_PROG_TYPE_SOCK_OPS; break; + case BPF_CGROUP_SMAP_INGRESS: + ptype = BPF_PROG_TYPE_SK_SKB; + break; default: return -EINVAL; } + if (attr->attach_type == BPF_CGROUP_SMAP_INGRESS) + return sockmap_get_from_fd(attr, ptype); + prog = bpf_prog_get_type(attr->attach_bpf_fd, ptype); if (IS_ERR(prog)) return PTR_ERR(prog); diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7dd96d064be1..a71bc0996572 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -1522,6 +1522,12 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id) case BPF_MAP_TYPE_HASH_OF_MAPS: if (func_id != BPF_FUNC_map_lookup_elem) goto error; + case BPF_MAP_TYPE_SOCKMAP: + if (func_id != BPF_FUNC_sk_redirect_map && + func_id != BPF_FUNC_sock_map_update && + func_id != BPF_FUNC_map_delete_elem) + goto error; + break; default: break; } @@ -1550,6 +1556,14 @@ static int check_map_func_compatibility(struct bpf_map *map, int func_id) if (map->map_type != BPF_MAP_TYPE_DEVMAP) goto error; break; + case BPF_FUNC_sk_redirect_map: + if (map->map_type != BPF_MAP_TYPE_SOCKMAP) + goto error; + break; + case BPF_FUNC_sock_map_update: + if (map->map_type != BPF_MAP_TYPE_SOCKMAP) + goto error; + break; default: break; } diff --git a/net/core/filter.c b/net/core/filter.c index 46321033ae0e..8e136578488c 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1858,6 +1858,45 @@ static const struct bpf_func_proto bpf_redirect_map_proto = { .arg3_type = ARG_ANYTHING, }; +BPF_CALL_3(bpf_sk_redirect_map, struct bpf_map *, map, u32, key, u64, flags) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + + if (unlikely(flags)) + return SK_ABORTED; + + ri->ifindex = key; + ri->flags = flags; + ri->map = map; + + return SK_REDIRECT; +} + +struct sock *do_sk_redirect_map(void) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + struct sock *sk = NULL; + + if (ri->map) { + sk = __sock_map_lookup_elem(ri->map, ri->ifindex); + + ri->ifindex = 0; + ri->map = NULL; + /* we do not clear flags for future lookup */ + } + + return sk; +} + +static const struct bpf_func_proto bpf_sk_redirect_map_proto = { + .func = bpf_sk_redirect_map, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_CONST_MAP_PTR, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +}; + BPF_CALL_1(bpf_get_cgroup_classid, const struct sk_buff *, skb) { return task_get_classid(skb); @@ -3229,6 +3268,8 @@ static const struct bpf_func_proto * switch (func_id) { case BPF_FUNC_setsockopt: return &bpf_setsockopt_proto; + case BPF_FUNC_sock_map_update: + return &bpf_sock_map_update_proto; default: return bpf_base_func_proto(func_id); } @@ -3243,6 +3284,8 @@ static const struct bpf_func_proto *sk_skb_func_proto(enum bpf_func_id func_id) return &bpf_get_socket_cookie_proto; case BPF_FUNC_get_socket_uid: return &bpf_get_socket_uid_proto; + case BPF_FUNC_sk_redirect_map: + return &bpf_sk_redirect_map_proto; default: return bpf_base_func_proto(func_id); } -- cgit v1.2.3 From 8a31db5615667956c513d205cfb06885c3ec6d0b Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Tue, 15 Aug 2017 22:33:09 -0700 Subject: bpf: add access to sock fields and pkt data from sk_skb programs Signed-off-by: John Fastabend Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 9 +++ kernel/bpf/verifier.c | 1 + net/core/filter.c | 169 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) (limited to 'net') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 7f774769e3f5..5ecbe812a2cc 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -712,6 +712,15 @@ struct __sk_buff { __u32 data; __u32 data_end; __u32 napi_id; + + /* accessed by BPF_PROG_TYPE_sk_skb types */ + __u32 family; + __u32 remote_ip4; /* Stored in network byte order */ + __u32 local_ip4; /* Stored in network byte order */ + __u32 remote_ip6[4]; /* Stored in network byte order */ + __u32 local_ip6[4]; /* Stored in network byte order */ + __u32 remote_port; /* Stored in network byte order */ + __u32 local_port; /* stored in host byte order */ }; struct bpf_tunnel_key { diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a71bc0996572..958ba84a9995 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -886,6 +886,7 @@ static bool may_access_direct_pkt_data(struct bpf_verifier_env *env, case BPF_PROG_TYPE_SCHED_ACT: case BPF_PROG_TYPE_XDP: case BPF_PROG_TYPE_LWT_XMIT: + case BPF_PROG_TYPE_SK_SKB: if (meta) return meta->pkt_access; diff --git a/net/core/filter.c b/net/core/filter.c index 8e136578488c..e9f8dcef6c57 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3278,8 +3278,16 @@ static const struct bpf_func_proto * static const struct bpf_func_proto *sk_skb_func_proto(enum bpf_func_id func_id) { switch (func_id) { + case BPF_FUNC_skb_store_bytes: + return &bpf_skb_store_bytes_proto; case BPF_FUNC_skb_load_bytes: return &bpf_skb_load_bytes_proto; + case BPF_FUNC_skb_pull_data: + return &bpf_skb_pull_data_proto; + case BPF_FUNC_skb_change_tail: + return &bpf_skb_change_tail_proto; + case BPF_FUNC_skb_change_head: + return &bpf_skb_change_head_proto; case BPF_FUNC_get_socket_cookie: return &bpf_get_socket_cookie_proto; case BPF_FUNC_get_socket_uid: @@ -3343,6 +3351,10 @@ static bool bpf_skb_is_valid_access(int off, int size, enum bpf_access_type type if (off + size > offsetofend(struct __sk_buff, cb[4])) return false; break; + case bpf_ctx_range_till(struct __sk_buff, remote_ip6[0], remote_ip6[3]): + case bpf_ctx_range_till(struct __sk_buff, local_ip6[0], local_ip6[3]): + case bpf_ctx_range_till(struct __sk_buff, remote_ip4, remote_ip4): + case bpf_ctx_range_till(struct __sk_buff, local_ip4, local_ip4): case bpf_ctx_range(struct __sk_buff, data): case bpf_ctx_range(struct __sk_buff, data_end): if (size != size_default) @@ -3371,6 +3383,7 @@ static bool sk_filter_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, tc_classid): case bpf_ctx_range(struct __sk_buff, data): case bpf_ctx_range(struct __sk_buff, data_end): + case bpf_ctx_range_till(struct __sk_buff, family, local_port): return false; } @@ -3392,6 +3405,7 @@ static bool lwt_is_valid_access(int off, int size, { switch (off) { case bpf_ctx_range(struct __sk_buff, tc_classid): + case bpf_ctx_range_till(struct __sk_buff, family, local_port): return false; } @@ -3505,6 +3519,8 @@ static bool tc_cls_act_is_valid_access(int off, int size, case bpf_ctx_range(struct __sk_buff, data_end): info->reg_type = PTR_TO_PACKET_END; break; + case bpf_ctx_range_till(struct __sk_buff, family, local_port): + return false; } return bpf_skb_is_valid_access(off, size, type, info); @@ -3582,11 +3598,63 @@ static bool sock_ops_is_valid_access(int off, int size, return __is_valid_sock_ops_access(off, size); } +static int sk_skb_prologue(struct bpf_insn *insn_buf, bool direct_write, + const struct bpf_prog *prog) +{ + struct bpf_insn *insn = insn_buf; + + if (!direct_write) + return 0; + + /* if (!skb->cloned) + * goto start; + * + * (Fast-path, otherwise approximation that we might be + * a clone, do the rest in helper.) + */ + *insn++ = BPF_LDX_MEM(BPF_B, BPF_REG_6, BPF_REG_1, CLONED_OFFSET()); + *insn++ = BPF_ALU32_IMM(BPF_AND, BPF_REG_6, CLONED_MASK); + *insn++ = BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 7); + + /* ret = bpf_skb_pull_data(skb, 0); */ + *insn++ = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1); + *insn++ = BPF_ALU64_REG(BPF_XOR, BPF_REG_2, BPF_REG_2); + *insn++ = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, + BPF_FUNC_skb_pull_data); + /* if (!ret) + * goto restore; + * return SK_DROP; + */ + *insn++ = BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2); + *insn++ = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, SK_DROP); + *insn++ = BPF_EXIT_INSN(); + + /* restore: */ + *insn++ = BPF_MOV64_REG(BPF_REG_1, BPF_REG_6); + /* start: */ + *insn++ = prog->insnsi[0]; + + return insn - insn_buf; +} + static bool sk_skb_is_valid_access(int off, int size, enum bpf_access_type type, struct bpf_insn_access_aux *info) { + if (type == BPF_WRITE) { + switch (off) { + case bpf_ctx_range(struct __sk_buff, mark): + case bpf_ctx_range(struct __sk_buff, tc_index): + case bpf_ctx_range(struct __sk_buff, priority): + break; + default: + return false; + } + } + switch (off) { + case bpf_ctx_range(struct __sk_buff, tc_classid): + return false; case bpf_ctx_range(struct __sk_buff, data): info->reg_type = PTR_TO_PACKET; break; @@ -3783,6 +3851,106 @@ static u32 bpf_convert_ctx_access(enum bpf_access_type type, *insn++ = BPF_MOV64_IMM(si->dst_reg, 0); #endif break; + case offsetof(struct __sk_buff, family): + BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_family) != 2); + + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), + si->dst_reg, si->src_reg, + offsetof(struct sk_buff, sk)); + *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->dst_reg, + bpf_target_off(struct sock_common, + skc_family, + 2, target_size)); + break; + case offsetof(struct __sk_buff, remote_ip4): + BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_daddr) != 4); + + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), + si->dst_reg, si->src_reg, + offsetof(struct sk_buff, sk)); + *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg, + bpf_target_off(struct sock_common, + skc_daddr, + 4, target_size)); + break; + case offsetof(struct __sk_buff, local_ip4): + BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + skc_rcv_saddr) != 4); + + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), + si->dst_reg, si->src_reg, + offsetof(struct sk_buff, sk)); + *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg, + bpf_target_off(struct sock_common, + skc_rcv_saddr, + 4, target_size)); + break; + case offsetof(struct __sk_buff, remote_ip6[0]) ... + offsetof(struct __sk_buff, remote_ip6[3]): +#if IS_ENABLED(CONFIG_IPV6) + BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + skc_v6_daddr.s6_addr32[0]) != 4); + + off = si->off; + off -= offsetof(struct __sk_buff, remote_ip6[0]); + + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), + si->dst_reg, si->src_reg, + offsetof(struct sk_buff, sk)); + *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg, + offsetof(struct sock_common, + skc_v6_daddr.s6_addr32[0]) + + off); +#else + *insn++ = BPF_MOV32_IMM(si->dst_reg, 0); +#endif + break; + case offsetof(struct __sk_buff, local_ip6[0]) ... + offsetof(struct __sk_buff, local_ip6[3]): +#if IS_ENABLED(CONFIG_IPV6) + BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, + skc_v6_rcv_saddr.s6_addr32[0]) != 4); + + off = si->off; + off -= offsetof(struct __sk_buff, local_ip6[0]); + + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), + si->dst_reg, si->src_reg, + offsetof(struct sk_buff, sk)); + *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->dst_reg, + offsetof(struct sock_common, + skc_v6_rcv_saddr.s6_addr32[0]) + + off); +#else + *insn++ = BPF_MOV32_IMM(si->dst_reg, 0); +#endif + break; + + case offsetof(struct __sk_buff, remote_port): + BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_dport) != 2); + + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), + si->dst_reg, si->src_reg, + offsetof(struct sk_buff, sk)); + *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->dst_reg, + bpf_target_off(struct sock_common, + skc_dport, + 2, target_size)); +#ifndef __BIG_ENDIAN_BITFIELD + *insn++ = BPF_ALU32_IMM(BPF_LSH, si->dst_reg, 16); +#endif + break; + + case offsetof(struct __sk_buff, local_port): + BUILD_BUG_ON(FIELD_SIZEOF(struct sock_common, skc_num) != 2); + + *insn++ = BPF_LDX_MEM(BPF_FIELD_SIZEOF(struct sk_buff, sk), + si->dst_reg, si->src_reg, + offsetof(struct sk_buff, sk)); + *insn++ = BPF_LDX_MEM(BPF_H, si->dst_reg, si->dst_reg, + bpf_target_off(struct sock_common, + skc_num, 2, target_size)); + break; } return insn - insn_buf; @@ -4071,6 +4239,7 @@ const struct bpf_verifier_ops sk_skb_prog_ops = { .get_func_proto = sk_skb_func_proto, .is_valid_access = sk_skb_is_valid_access, .convert_ctx_access = bpf_convert_ctx_access, + .gen_prologue = sk_skb_prologue, }; int sk_detach_filter(struct sock *sk) -- cgit v1.2.3 From e543002f77f463501d47fab43acf7ba881e9dcaf Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 15 Aug 2017 21:11:03 +0200 Subject: qdisc: add tracepoint qdisc:qdisc_dequeue for dequeued SKBs The main purpose of this tracepoint is to monitor bulk dequeue in the network qdisc layer, as it cannot be deducted from the existing qdisc stats. The txq_state can be used for determining the reason for zero packet dequeues, see enum netdev_queue_state_t. Notice all packets doesn't necessary activate this tracepoint. As qdiscs with flag TCQ_F_CAN_BYPASS, can directly invoke sch_direct_xmit() when qdisc_qlen is zero. Remember that perf record supports filters like: perf record -e qdisc:qdisc_dequeue \ --filter 'ifindex == 4 && (packets > 1 || txq_state > 0)' Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/trace/events/qdisc.h | 50 ++++++++++++++++++++++++++++++++++++++++++++ net/core/net-traces.c | 1 + net/sched/sch_generic.c | 8 +++++-- 3 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 include/trace/events/qdisc.h (limited to 'net') diff --git a/include/trace/events/qdisc.h b/include/trace/events/qdisc.h new file mode 100644 index 000000000000..60d0d8bd336d --- /dev/null +++ b/include/trace/events/qdisc.h @@ -0,0 +1,50 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM qdisc + +#if !defined(_TRACE_QDISC_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_QDISC_H_ + +#include +#include +#include +#include + +TRACE_EVENT(qdisc_dequeue, + + TP_PROTO(struct Qdisc *qdisc, const struct netdev_queue *txq, + int packets, struct sk_buff *skb), + + TP_ARGS(qdisc, txq, packets, skb), + + TP_STRUCT__entry( + __field( struct Qdisc *, qdisc ) + __field(const struct netdev_queue *, txq ) + __field( int, packets ) + __field( void *, skbaddr ) + __field( int, ifindex ) + __field( u32, handle ) + __field( u32, parent ) + __field( unsigned long, txq_state) + ), + + /* skb==NULL indicate packets dequeued was 0, even when packets==1 */ + TP_fast_assign( + __entry->qdisc = qdisc; + __entry->txq = txq; + __entry->packets = skb ? packets : 0; + __entry->skbaddr = skb; + __entry->ifindex = txq->dev ? txq->dev->ifindex : 0; + __entry->handle = qdisc->handle; + __entry->parent = qdisc->parent; + __entry->txq_state = txq->state; + ), + + TP_printk("dequeue ifindex=%d qdisc handle=0x%X parent=0x%X txq_state=0x%lX packets=%d skbaddr=%p", + __entry->ifindex, __entry->handle, __entry->parent, + __entry->txq_state, __entry->packets, __entry->skbaddr ) +); + +#endif /* _TRACE_QDISC_H_ */ + +/* This part must be outside protection */ +#include diff --git a/net/core/net-traces.c b/net/core/net-traces.c index 92da5e4ceb4f..4f1468ccd056 100644 --- a/net/core/net-traces.c +++ b/net/core/net-traces.c @@ -32,6 +32,7 @@ #include #include #include +#include #if IS_ENABLED(CONFIG_IPV6) #include EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup); diff --git a/net/sched/sch_generic.c b/net/sched/sch_generic.c index 57ba406f1437..c6b89a34e8d2 100644 --- a/net/sched/sch_generic.c +++ b/net/sched/sch_generic.c @@ -29,6 +29,7 @@ #include #include #include +#include /* Qdisc to use by default */ const struct Qdisc_ops *default_qdisc_ops = &pfifo_fast_ops; @@ -126,7 +127,7 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate, q->q.qlen--; } else skb = NULL; - return skb; + goto trace; } *validate = true; skb = q->skb_bad_txq; @@ -139,7 +140,8 @@ static struct sk_buff *dequeue_skb(struct Qdisc *q, bool *validate, q->q.qlen--; goto bulk; } - return NULL; + skb = NULL; + goto trace; } if (!(q->flags & TCQ_F_ONETXQUEUE) || !netif_xmit_frozen_or_stopped(txq)) @@ -151,6 +153,8 @@ bulk: else try_bulk_dequeue_skb_slow(q, skb, packets); } +trace: + trace_qdisc_dequeue(q, txq, *packets, skb); return skb; } -- cgit v1.2.3 From d978db8dbebb60a51492da966689dfc0c9216c44 Mon Sep 17 00:00:00 2001 From: Jiri Pirko Date: Wed, 16 Aug 2017 17:15:18 +0200 Subject: net: sched: cls_flower: fix ndo_setup_tc type for stats call I made a stupid mistake using TC_CLSFLOWER_STATS instead of TC_SETUP_CLSFLOWER. Funny thing is that both are defined as "2" so it actually did not cause any harm. Anyway, fixing it now. Fixes: 2572ac53c46f ("net: sched: make type an argument for ndo_setup_tc") Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 052e902dc71c..bd9dab41f8af 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -289,7 +289,7 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) cls_flower.cookie = (unsigned long) f; cls_flower.exts = &f->exts; - dev->netdev_ops->ndo_setup_tc(dev, TC_CLSFLOWER_STATS, + dev->netdev_ops->ndo_setup_tc(dev, TC_SETUP_CLSFLOWER, &cls_flower); } -- cgit v1.2.3 From 774c46732ddba4632fa735beb17589aac90d5b49 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Wed, 16 Aug 2017 15:40:44 -0700 Subject: tcp: Export tcp_{sendpage,sendmsg}_locked() for ipv6. Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'net') diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 71b25567e787..d25e3bcca66b 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1058,6 +1058,7 @@ int tcp_sendpage_locked(struct sock *sk, struct page *page, int offset, return do_tcp_sendpages(sk, page, offset, size, flags); } +EXPORT_SYMBOL_GPL(tcp_sendpage_locked); int tcp_sendpage(struct sock *sk, struct page *page, int offset, size_t size, int flags) @@ -1436,6 +1437,7 @@ out_err: } return err; } +EXPORT_SYMBOL_GPL(tcp_sendmsg_locked); int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size) { -- cgit v1.2.3 From 4d6a75b65dbfa910e84b2cbd7cd54468cf33ff9b Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 17 Aug 2017 15:07:22 +0200 Subject: bpf: no need to nullify ri->map in xdp_do_redirect We are guaranteed to have a NULL ri->map in this branch since we test for it earlier, so we don't need to reset it here. Signed-off-by: Daniel Borkmann Acked-by: John Fastabend Signed-off-by: David S. Miller --- net/core/filter.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/core/filter.c b/net/core/filter.c index e9f8dcef6c57..ea3ca34d0bf4 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2568,7 +2568,6 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, fwd = dev_get_by_index_rcu(dev_net(dev), index); ri->ifindex = 0; - ri->map = NULL; if (unlikely(!fwd)) { bpf_warn_invalid_xdp_redirect(index); return -EINVAL; -- cgit v1.2.3 From 047b0ecd683045dcea371f9d4d2917dcf3c553da Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Thu, 17 Aug 2017 17:22:37 +0200 Subject: bpf: reuse tc bpf prologue for sk skb progs Given both program types are effecitvely doing the same in the prologue, just reuse the one that we had for tc and only adapt to the corresponding drop verdict value. That way, we don't need to have the duplicate from 8a31db561566 ("bpf: add access to sock fields and pkt data from sk_skb programs") to maintain. Reported-by: Alexei Starovoitov Signed-off-by: Daniel Borkmann Acked-by: John Fastabend Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/core/filter.c | 47 ++++++++++------------------------------------- 1 file changed, 10 insertions(+), 37 deletions(-) (limited to 'net') diff --git a/net/core/filter.c b/net/core/filter.c index ea3ca34d0bf4..0f4df86d936a 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3455,8 +3455,8 @@ static bool sock_filter_is_valid_access(int off, int size, return true; } -static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write, - const struct bpf_prog *prog) +static int bpf_unclone_prologue(struct bpf_insn *insn_buf, bool direct_write, + const struct bpf_prog *prog, int drop_verdict) { struct bpf_insn *insn = insn_buf; @@ -3483,7 +3483,7 @@ static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write, * return TC_ACT_SHOT; */ *insn++ = BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2); - *insn++ = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, TC_ACT_SHOT); + *insn++ = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, drop_verdict); *insn++ = BPF_EXIT_INSN(); /* restore: */ @@ -3494,6 +3494,12 @@ static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write, return insn - insn_buf; } +static int tc_cls_act_prologue(struct bpf_insn *insn_buf, bool direct_write, + const struct bpf_prog *prog) +{ + return bpf_unclone_prologue(insn_buf, direct_write, prog, TC_ACT_SHOT); +} + static bool tc_cls_act_is_valid_access(int off, int size, enum bpf_access_type type, struct bpf_insn_access_aux *info) @@ -3600,40 +3606,7 @@ static bool sock_ops_is_valid_access(int off, int size, static int sk_skb_prologue(struct bpf_insn *insn_buf, bool direct_write, const struct bpf_prog *prog) { - struct bpf_insn *insn = insn_buf; - - if (!direct_write) - return 0; - - /* if (!skb->cloned) - * goto start; - * - * (Fast-path, otherwise approximation that we might be - * a clone, do the rest in helper.) - */ - *insn++ = BPF_LDX_MEM(BPF_B, BPF_REG_6, BPF_REG_1, CLONED_OFFSET()); - *insn++ = BPF_ALU32_IMM(BPF_AND, BPF_REG_6, CLONED_MASK); - *insn++ = BPF_JMP_IMM(BPF_JEQ, BPF_REG_6, 0, 7); - - /* ret = bpf_skb_pull_data(skb, 0); */ - *insn++ = BPF_MOV64_REG(BPF_REG_6, BPF_REG_1); - *insn++ = BPF_ALU64_REG(BPF_XOR, BPF_REG_2, BPF_REG_2); - *insn++ = BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, - BPF_FUNC_skb_pull_data); - /* if (!ret) - * goto restore; - * return SK_DROP; - */ - *insn++ = BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2); - *insn++ = BPF_ALU32_IMM(BPF_MOV, BPF_REG_0, SK_DROP); - *insn++ = BPF_EXIT_INSN(); - - /* restore: */ - *insn++ = BPF_MOV64_REG(BPF_REG_1, BPF_REG_6); - /* start: */ - *insn++ = prog->insnsi[0]; - - return insn - insn_buf; + return bpf_unclone_prologue(insn_buf, direct_write, prog, SK_DROP); } static bool sk_skb_is_valid_access(int off, int size, -- cgit v1.2.3 From 401481e06099533892b3d8f1db498cbc480b5b24 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 18 Aug 2017 13:34:22 +0200 Subject: ipv6: fix false-postive maybe-uninitialized warning Adding a lock around one of the assignments prevents gcc from tracking the state of the local 'fibmatch' variable, so it can no longer prove that 'dst' is always initialized, leading to a bogus warning: net/ipv6/route.c: In function 'inet6_rtm_getroute': net/ipv6/route.c:3659:2: error: 'dst' may be used uninitialized in this function [-Werror=maybe-uninitialized] This moves the other assignment into the same lock to shut up the warning. Fixes: 121622dba8da ("ipv6: route: make rtm_getroute not assume rtnl is locked") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- net/ipv6/route.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index dc021ed6dd37..bec12ae3e6b7 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -3624,6 +3624,8 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, if (!fibmatch) dst = ip6_route_input_lookup(net, dev, &fl6, flags); + else + dst = ip6_route_lookup(net, &fl6, 0); rcu_read_unlock(); } else { @@ -3631,10 +3633,10 @@ static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr *nlh, if (!fibmatch) dst = ip6_route_output(net, NULL, &fl6); + else + dst = ip6_route_lookup(net, &fl6, 0); } - if (fibmatch) - dst = ip6_route_lookup(net, &fl6, 0); rt = container_of(dst, struct rt6_info, dst); if (rt->dst.error) { -- cgit v1.2.3 From 9620fef27ed2cdb37bf6fd028f32bea2ef5119a8 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 18 Aug 2017 12:08:07 -0700 Subject: ipv4: convert dst_metrics.refcnt from atomic_t to refcount_t refcount_t type and corresponding API should be used instead of atomic_t when the variable is used as a reference counter. This allows to avoid accidental refcounter overflows that might lead to use-after-free situations. Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/dst.h | 3 ++- net/core/dst.c | 6 +++--- net/ipv4/fib_semantics.c | 4 ++-- net/ipv4/route.c | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) (limited to 'net') diff --git a/include/net/dst.h b/include/net/dst.h index f73611ec4017..93568bd0a352 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -107,7 +108,7 @@ struct dst_entry { struct dst_metrics { u32 metrics[RTAX_MAX]; - atomic_t refcnt; + refcount_t refcnt; }; extern const struct dst_metrics dst_default_metrics; diff --git a/net/core/dst.c b/net/core/dst.c index 00aa972ad1a1..d6ead757c258 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -55,7 +55,7 @@ const struct dst_metrics dst_default_metrics = { * We really want to avoid false sharing on this variable, and catch * any writes on it. */ - .refcnt = ATOMIC_INIT(1), + .refcnt = REFCOUNT_INIT(1), }; void dst_init(struct dst_entry *dst, struct dst_ops *ops, @@ -213,7 +213,7 @@ u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old) struct dst_metrics *old_p = (struct dst_metrics *)__DST_METRICS_PTR(old); unsigned long prev, new; - atomic_set(&p->refcnt, 1); + refcount_set(&p->refcnt, 1); memcpy(p->metrics, old_p->metrics, sizeof(p->metrics)); new = (unsigned long) p; @@ -225,7 +225,7 @@ u32 *dst_cow_metrics_generic(struct dst_entry *dst, unsigned long old) if (prev & DST_METRICS_READ_ONLY) p = NULL; } else if (prev & DST_METRICS_REFCOUNTED) { - if (atomic_dec_and_test(&old_p->refcnt)) + if (refcount_dec_and_test(&old_p->refcnt)) kfree(old_p); } } diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index d521caf57385..394d800db50c 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -220,7 +220,7 @@ static void free_fib_info_rcu(struct rcu_head *head) } endfor_nexthops(fi); m = fi->fib_metrics; - if (m != &dst_default_metrics && atomic_dec_and_test(&m->refcnt)) + if (m != &dst_default_metrics && refcount_dec_and_test(&m->refcnt)) kfree(m); kfree(fi); } @@ -1090,7 +1090,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, kfree(fi); return ERR_PTR(err); } - atomic_set(&fi->fib_metrics->refcnt, 1); + refcount_set(&fi->fib_metrics->refcnt, 1); } else { fi->fib_metrics = (struct dst_metrics *)&dst_default_metrics; } diff --git a/net/ipv4/route.c b/net/ipv4/route.c index d400c0543106..872b4cb136d3 100644 --- a/net/ipv4/route.c +++ b/net/ipv4/route.c @@ -1398,7 +1398,7 @@ static void ipv4_dst_destroy(struct dst_entry *dst) struct dst_metrics *p = (struct dst_metrics *)DST_METRICS_PTR(dst); struct rtable *rt = (struct rtable *) dst; - if (p != &dst_default_metrics && atomic_dec_and_test(&p->refcnt)) + if (p != &dst_default_metrics && refcount_dec_and_test(&p->refcnt)) kfree(p); if (!list_empty(&rt->rt_uncached)) { @@ -1456,7 +1456,7 @@ static void rt_set_nexthop(struct rtable *rt, __be32 daddr, dst_init_metrics(&rt->dst, fi->fib_metrics->metrics, true); if (fi->fib_metrics != &dst_default_metrics) { rt->dst._metrics |= DST_METRICS_REFCOUNTED; - atomic_inc(&fi->fib_metrics->refcnt); + refcount_inc(&fi->fib_metrics->refcnt); } #ifdef CONFIG_IP_ROUTE_CLASSID rt->dst.tclassid = nh->nh_tclassid; -- cgit v1.2.3 From 0888e372c37fa31882c8ed89fb2f8188b08b6718 Mon Sep 17 00:00:00 2001 From: "Levin, Alexander (Sasha Levin)" Date: Thu, 17 Aug 2017 00:35:11 +0000 Subject: net: inet: diag: expose sockets cgroup classid This is useful for directly looking up a task based on class id rather than having to scan through all open file descriptors. Signed-off-by: Sasha Levin Signed-off-by: David S. Miller --- include/uapi/linux/inet_diag.h | 1 + net/ipv4/inet_diag.c | 11 +++++++++++ 2 files changed, 12 insertions(+) (limited to 'net') diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h index bbe201047df6..678496897a68 100644 --- a/include/uapi/linux/inet_diag.h +++ b/include/uapi/linux/inet_diag.h @@ -142,6 +142,7 @@ enum { INET_DIAG_PAD, INET_DIAG_MARK, INET_DIAG_BBRINFO, + INET_DIAG_CLASS_ID, __INET_DIAG_MAX, }; diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 3828b3a805cd..67325d5832d7 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -274,6 +274,17 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, goto errout; } + if (ext & (1 << (INET_DIAG_CLASS_ID - 1))) { + u32 classid = 0; + +#ifdef CONFIG_SOCK_CGROUP_DATA + classid = sock_cgroup_classid(&sk->sk_cgrp_data); +#endif + + if (nla_put_u32(skb, INET_DIAG_CLASS_ID, classid)) + goto errout; + } + out: nlmsg_end(skb, nlh); return 0; -- cgit v1.2.3 From 4c03bdd7b5c084c3c6973cb2419edac5363c051f Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Thu, 17 Aug 2017 18:22:37 +0200 Subject: xdp: adjust xdp redirect tracepoint to include return error code The return error code need to be included in the tracepoint xdp:xdp_redirect, else its not possible to distinguish successful or failed XDP_REDIRECT transmits. XDP have no queuing mechanism. Thus, it is fairly easily to overrun a NIC transmit queue. The eBPF program invoking helpers (bpf_redirect or bpf_redirect_map) to redirect a packet doesn't get any feedback whether the packet was actually transmitted. Info on failed transmits in the tracepoint xdp:xdp_redirect, is interesting as this opens for providing a feedback-loop to the receiving XDP program. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/trace/events/xdp.h | 11 +++++++---- net/core/filter.c | 19 ++++++++++++------- 2 files changed, 19 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index 7b1eb7b4be41..0e42e69f773b 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -53,15 +53,16 @@ TRACE_EVENT(xdp_redirect, TP_PROTO(const struct net_device *from, const struct net_device *to, - const struct bpf_prog *xdp, u32 act), + const struct bpf_prog *xdp, u32 act, int err), - TP_ARGS(from, to, xdp, act), + TP_ARGS(from, to, xdp, act, err), TP_STRUCT__entry( __string(name_from, from->name) __string(name_to, to->name) __array(u8, prog_tag, 8) __field(u32, act) + __field(int, err) ), TP_fast_assign( @@ -70,12 +71,14 @@ TRACE_EVENT(xdp_redirect, __assign_str(name_from, from->name); __assign_str(name_to, to->name); __entry->act = act; + __entry->err = err; ), - TP_printk("prog=%s from=%s to=%s action=%s", + TP_printk("prog=%s from=%s to=%s action=%s err=%d", __print_hex_str(__entry->prog_tag, 8), __get_str(name_from), __get_str(name_to), - __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB)) + __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB), + __entry->err) ); #endif /* _TRACE_XDP_H */ diff --git a/net/core/filter.c b/net/core/filter.c index 0f4df86d936a..fa2115695037 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2535,14 +2535,16 @@ int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, struct bpf_map *map = ri->map; u32 index = ri->ifindex; struct net_device *fwd; - int err = -EINVAL; + int err; ri->ifindex = 0; ri->map = NULL; fwd = __dev_map_lookup_elem(map, index); - if (!fwd) + if (!fwd) { + err = -EINVAL; goto out; + } if (ri->map_to_flush && (ri->map_to_flush != map)) xdp_do_flush_map(); @@ -2552,7 +2554,7 @@ int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, ri->map_to_flush = map; out: - trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); + trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT, err); return err; } @@ -2562,6 +2564,7 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, struct redirect_info *ri = this_cpu_ptr(&redirect_info); struct net_device *fwd; u32 index = ri->ifindex; + int err; if (ri->map) return xdp_do_redirect_map(dev, xdp, xdp_prog); @@ -2570,12 +2573,14 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, ri->ifindex = 0; if (unlikely(!fwd)) { bpf_warn_invalid_xdp_redirect(index); - return -EINVAL; + err = -EINVAL; + goto out; } - trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT); - - return __bpf_tx_xdp(fwd, NULL, xdp, 0); + err = __bpf_tx_xdp(fwd, NULL, xdp, 0); +out: + trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT, err); + return err; } EXPORT_SYMBOL_GPL(xdp_do_redirect); -- cgit v1.2.3 From d0d6683716791b2a2761a1bb025c613eb73da6c3 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:19 -0700 Subject: net: don't decrement kobj reference count on init failure If kobject_init_and_add failed, then the failure path would decrement the reference count of the queue kobject whose reference count was already zero. Fixes: 114cf5802165 ("bql: Byte queue limits") Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index b4f9922b6f23..46ff41bf0210 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -917,20 +917,19 @@ static int rx_queue_add_kobject(struct net_device *dev, int index) error = kobject_init_and_add(kobj, &rx_queue_ktype, NULL, "rx-%u", index); if (error) - goto exit; + return error; if (dev->sysfs_rx_queue_group) { error = sysfs_create_group(kobj, dev->sysfs_rx_queue_group); - if (error) - goto exit; + if (error) { + kobject_put(kobj); + return error; + } } kobject_uevent(kobj, KOBJ_ADD); dev_hold(queue->dev); - return error; -exit: - kobject_put(kobj); return error; } #endif /* CONFIG_SYSFS */ @@ -1339,21 +1338,20 @@ static int netdev_queue_add_kobject(struct net_device *dev, int index) error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL, "tx-%u", index); if (error) - goto exit; + return error; #ifdef CONFIG_BQL error = sysfs_create_group(kobj, &dql_group); - if (error) - goto exit; + if (error) { + kobject_put(kobj); + return error; + } #endif kobject_uevent(kobj, KOBJ_ADD); dev_hold(queue->dev); return 0; -exit: - kobject_put(kobj); - return error; } #endif /* CONFIG_SYSFS */ -- cgit v1.2.3 From b793dc5c6edfb106fd57d12ad6aca64bf160b403 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:20 -0700 Subject: net: constify netdev_class_file These functions are wrapper arount class_create_file which can take a const attribute. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/netdevice.h | 8 ++++---- net/core/net-sysfs.c | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 0f1c4cb2441e..eaa77bd9cb80 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -4013,17 +4013,17 @@ static inline netdev_tx_t netdev_start_xmit(struct sk_buff *skb, struct net_devi return rc; } -int netdev_class_create_file_ns(struct class_attribute *class_attr, +int netdev_class_create_file_ns(const struct class_attribute *class_attr, const void *ns); -void netdev_class_remove_file_ns(struct class_attribute *class_attr, +void netdev_class_remove_file_ns(const struct class_attribute *class_attr, const void *ns); -static inline int netdev_class_create_file(struct class_attribute *class_attr) +static inline int netdev_class_create_file(const struct class_attribute *class_attr) { return netdev_class_create_file_ns(class_attr, NULL); } -static inline void netdev_class_remove_file(struct class_attribute *class_attr) +static inline void netdev_class_remove_file(const struct class_attribute *class_attr) { netdev_class_remove_file_ns(class_attr, NULL); } diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 46ff41bf0210..40937ee63f14 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1618,14 +1618,14 @@ int netdev_register_kobject(struct net_device *ndev) return error; } -int netdev_class_create_file_ns(struct class_attribute *class_attr, +int netdev_class_create_file_ns(const struct class_attribute *class_attr, const void *ns) { return class_create_file_ns(&net_class, class_attr, ns); } EXPORT_SYMBOL(netdev_class_create_file_ns); -void netdev_class_remove_file_ns(struct class_attribute *class_attr, +void netdev_class_remove_file_ns(const struct class_attribute *class_attr, const void *ns) { class_remove_file_ns(&net_class, class_attr, ns); -- cgit v1.2.3 From e6d473e63536ea1989dc63c066f0c0464a969324 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:21 -0700 Subject: net: make net_class ro_after_init The net_class in sysfs is only modified on init. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 40937ee63f14..99061b0a1ebd 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1511,7 +1511,7 @@ static const void *net_namespace(struct device *d) return dev_net(dev); } -static struct class net_class = { +static struct class net_class __ro_after_init = { .name = "net", .dev_release = netdev_release, .dev_groups = net_class_groups, -- cgit v1.2.3 From 737aec57c672c1308d602afecd841455c39561e5 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:22 -0700 Subject: net: constify net_ns_type_operations This can be const. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- include/linux/netdevice.h | 2 +- net/core/net-sysfs.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index eaa77bd9cb80..b0c928598dab 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -4028,7 +4028,7 @@ static inline void netdev_class_remove_file(const struct class_attribute *class_ netdev_class_remove_file_ns(class_attr, NULL); } -extern struct kobj_ns_type_operations net_ns_type_operations; +extern const struct kobj_ns_type_operations net_ns_type_operations; const char *netdev_drivername(const struct net_device *dev); diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 99061b0a1ebd..2de441692f28 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1461,7 +1461,7 @@ static const void *net_netlink_ns(struct sock *sk) return sock_net(sk); } -struct kobj_ns_type_operations net_ns_type_operations = { +const struct kobj_ns_type_operations net_ns_type_operations = { .type = KOBJ_NS_TYPE_NET, .current_may_mount = net_current_may_mount, .grab_current_ns = net_grab_current_ns, -- cgit v1.2.3 From ec6cc5993c0f0c1e5a7cbb630c7ecc165e6d9e84 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:23 -0700 Subject: net: make net sysfs attributes ro_after_init The attributes of net devices are immutable. Ideally, attribute groups would contain const attributes but there are too many places that do modifications of list during startup (in other code) to allow that. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 2de441692f28..76ec74d4a65b 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -508,7 +508,7 @@ static ssize_t phys_switch_id_show(struct device *dev, } static DEVICE_ATTR_RO(phys_switch_id); -static struct attribute *net_class_attrs[] = { +static struct attribute *net_class_attrs[] __ro_after_init = { &dev_attr_netdev_group.attr, &dev_attr_type.attr, &dev_attr_dev_id.attr, @@ -597,7 +597,7 @@ NETSTAT_ENTRY(rx_compressed); NETSTAT_ENTRY(tx_compressed); NETSTAT_ENTRY(rx_nohandler); -static struct attribute *netstat_attrs[] = { +static struct attribute *netstat_attrs[] __ro_after_init = { &dev_attr_rx_packets.attr, &dev_attr_tx_packets.attr, &dev_attr_rx_bytes.attr, -- cgit v1.2.3 From 718ad681eff47d3d04879ff5f5290bdab0b8bad6 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:24 -0700 Subject: net: drop unused attribute argument from sysfs queue funcs The show and store functions don't need/use the attribute. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- drivers/net/virtio_net.c | 2 +- include/linux/netdevice.h | 5 ++--- net/core/net-sysfs.c | 37 +++++++++++-------------------------- 3 files changed, 14 insertions(+), 30 deletions(-) (limited to 'net') diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 4302f313d9a7..52ae78ca3d38 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -2376,7 +2376,7 @@ err: #ifdef CONFIG_SYSFS static ssize_t mergeable_rx_buffer_size_show(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attribute, char *buf) + char *buf) { struct virtnet_info *vi = netdev_priv(queue->dev); unsigned int queue_index = get_netdev_rx_queue_index(queue); diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index b0c928598dab..c5475b37a631 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -694,10 +694,9 @@ struct netdev_rx_queue { */ struct rx_queue_attribute { struct attribute attr; - ssize_t (*show)(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attr, char *buf); + ssize_t (*show)(struct netdev_rx_queue *queue, char *buf); ssize_t (*store)(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attr, const char *buf, size_t len); + const char *buf, size_t len); }; #ifdef CONFIG_XPS diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 76ec74d4a65b..48714c8024f3 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -661,7 +661,7 @@ static ssize_t rx_queue_attr_show(struct kobject *kobj, struct attribute *attr, if (!attribute->show) return -EIO; - return attribute->show(queue, attribute, buf); + return attribute->show(queue, buf); } static ssize_t rx_queue_attr_store(struct kobject *kobj, struct attribute *attr, @@ -673,7 +673,7 @@ static ssize_t rx_queue_attr_store(struct kobject *kobj, struct attribute *attr, if (!attribute->store) return -EIO; - return attribute->store(queue, attribute, buf, count); + return attribute->store(queue, buf, count); } static const struct sysfs_ops rx_queue_sysfs_ops = { @@ -682,8 +682,7 @@ static const struct sysfs_ops rx_queue_sysfs_ops = { }; #ifdef CONFIG_RPS -static ssize_t show_rps_map(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attribute, char *buf) +static ssize_t show_rps_map(struct netdev_rx_queue *queue, char *buf) { struct rps_map *map; cpumask_var_t mask; @@ -706,8 +705,7 @@ static ssize_t show_rps_map(struct netdev_rx_queue *queue, } static ssize_t store_rps_map(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attribute, - const char *buf, size_t len) + const char *buf, size_t len) { struct rps_map *old_map, *map; cpumask_var_t mask; @@ -765,7 +763,6 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue, } static ssize_t show_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attr, char *buf) { struct rps_dev_flow_table *flow_table; @@ -788,8 +785,7 @@ static void rps_dev_flow_table_release(struct rcu_head *rcu) } static ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, - struct rx_queue_attribute *attr, - const char *buf, size_t len) + const char *buf, size_t len) { unsigned long mask, count; struct rps_dev_flow_table *table, *old_table; @@ -975,10 +971,9 @@ net_rx_queue_update_kobjects(struct net_device *dev, int old_num, int new_num) */ struct netdev_queue_attribute { struct attribute attr; - ssize_t (*show)(struct netdev_queue *queue, - struct netdev_queue_attribute *attr, char *buf); + ssize_t (*show)(struct netdev_queue *queue, char *buf); ssize_t (*store)(struct netdev_queue *queue, - struct netdev_queue_attribute *attr, const char *buf, size_t len); + const char *buf, size_t len); }; #define to_netdev_queue_attr(_attr) container_of(_attr, \ struct netdev_queue_attribute, attr) @@ -994,7 +989,7 @@ static ssize_t netdev_queue_attr_show(struct kobject *kobj, if (!attribute->show) return -EIO; - return attribute->show(queue, attribute, buf); + return attribute->show(queue, buf); } static ssize_t netdev_queue_attr_store(struct kobject *kobj, @@ -1007,7 +1002,7 @@ static ssize_t netdev_queue_attr_store(struct kobject *kobj, if (!attribute->store) return -EIO; - return attribute->store(queue, attribute, buf, count); + return attribute->store(queue, buf, count); } static const struct sysfs_ops netdev_queue_sysfs_ops = { @@ -1016,7 +1011,6 @@ static const struct sysfs_ops netdev_queue_sysfs_ops = { }; static ssize_t show_trans_timeout(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, char *buf) { unsigned long trans_timeout; @@ -1040,7 +1034,6 @@ static unsigned int get_netdev_queue_index(struct netdev_queue *queue) } static ssize_t show_traffic_class(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, char *buf) { struct net_device *dev = queue->dev; @@ -1055,14 +1048,12 @@ static ssize_t show_traffic_class(struct netdev_queue *queue, #ifdef CONFIG_XPS static ssize_t show_tx_maxrate(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, char *buf) { return sprintf(buf, "%lu\n", queue->tx_maxrate); } static ssize_t set_tx_maxrate(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, const char *buf, size_t len) { struct net_device *dev = queue->dev; @@ -1130,7 +1121,6 @@ static ssize_t bql_set(const char *buf, const size_t count, } static ssize_t bql_show_hold_time(struct netdev_queue *queue, - struct netdev_queue_attribute *attr, char *buf) { struct dql *dql = &queue->dql; @@ -1139,7 +1129,6 @@ static ssize_t bql_show_hold_time(struct netdev_queue *queue, } static ssize_t bql_set_hold_time(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, const char *buf, size_t len) { struct dql *dql = &queue->dql; @@ -1160,7 +1149,6 @@ static struct netdev_queue_attribute bql_hold_time_attribute = bql_set_hold_time); static ssize_t bql_show_inflight(struct netdev_queue *queue, - struct netdev_queue_attribute *attr, char *buf) { struct dql *dql = &queue->dql; @@ -1173,14 +1161,12 @@ static struct netdev_queue_attribute bql_inflight_attribute = #define BQL_ATTR(NAME, FIELD) \ static ssize_t bql_show_ ## NAME(struct netdev_queue *queue, \ - struct netdev_queue_attribute *attr, \ char *buf) \ { \ return bql_show(buf, queue->dql.FIELD); \ } \ \ static ssize_t bql_set_ ## NAME(struct netdev_queue *queue, \ - struct netdev_queue_attribute *attr, \ const char *buf, size_t len) \ { \ return bql_set(buf, len, &queue->dql.FIELD); \ @@ -1211,7 +1197,7 @@ static const struct attribute_group dql_group = { #ifdef CONFIG_XPS static ssize_t show_xps_map(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, char *buf) + char *buf) { struct net_device *dev = queue->dev; int cpu, len, num_tc = 1, tc = 0; @@ -1258,8 +1244,7 @@ static ssize_t show_xps_map(struct netdev_queue *queue, } static ssize_t store_xps_map(struct netdev_queue *queue, - struct netdev_queue_attribute *attribute, - const char *buf, size_t len) + const char *buf, size_t len) { struct net_device *dev = queue->dev; unsigned long index; -- cgit v1.2.3 From 170c658afc43c2d18a167783824f4e122a07abd2 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:25 -0700 Subject: net: make BQL sysfs attributes ro_after_init Also fix macro to not have ; at end. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 48714c8024f3..a4af5e2ff398 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1144,9 +1144,9 @@ static ssize_t bql_set_hold_time(struct netdev_queue *queue, return len; } -static struct netdev_queue_attribute bql_hold_time_attribute = - __ATTR(hold_time, S_IRUGO | S_IWUSR, bql_show_hold_time, - bql_set_hold_time); +static struct netdev_queue_attribute bql_hold_time_attribute __ro_after_init + = __ATTR(hold_time, S_IRUGO | S_IWUSR, + bql_show_hold_time, bql_set_hold_time); static ssize_t bql_show_inflight(struct netdev_queue *queue, char *buf) @@ -1156,7 +1156,7 @@ static ssize_t bql_show_inflight(struct netdev_queue *queue, return sprintf(buf, "%u\n", dql->num_queued - dql->num_completed); } -static struct netdev_queue_attribute bql_inflight_attribute = +static struct netdev_queue_attribute bql_inflight_attribute __ro_after_init = __ATTR(inflight, S_IRUGO, bql_show_inflight, NULL); #define BQL_ATTR(NAME, FIELD) \ @@ -1172,15 +1172,15 @@ static ssize_t bql_set_ ## NAME(struct netdev_queue *queue, \ return bql_set(buf, len, &queue->dql.FIELD); \ } \ \ -static struct netdev_queue_attribute bql_ ## NAME ## _attribute = \ - __ATTR(NAME, S_IRUGO | S_IWUSR, bql_show_ ## NAME, \ - bql_set_ ## NAME); +static struct netdev_queue_attribute bql_ ## NAME ## _attribute __ro_after_init \ + = __ATTR(NAME, S_IRUGO | S_IWUSR, \ + bql_show_ ## NAME, bql_set_ ## NAME) -BQL_ATTR(limit, limit) -BQL_ATTR(limit_max, max_limit) -BQL_ATTR(limit_min, min_limit) +BQL_ATTR(limit, limit); +BQL_ATTR(limit_max, max_limit); +BQL_ATTR(limit_min, min_limit); -static struct attribute *dql_attrs[] = { +static struct attribute *dql_attrs[] __ro_after_init = { &bql_limit_attribute.attr, &bql_limit_max_attribute.attr, &bql_limit_min_attribute.attr, -- cgit v1.2.3 From 2b9c758129d7eb2d709291426004e5a8efcba19f Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:26 -0700 Subject: net: make queue attributes ro_after_init The XPS queue attributes can be ro_after_init. Also use __ATTR_RX macros to simplify initialization. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index a4af5e2ff398..808fbb837f25 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -1010,8 +1010,7 @@ static const struct sysfs_ops netdev_queue_sysfs_ops = { .store = netdev_queue_attr_store, }; -static ssize_t show_trans_timeout(struct netdev_queue *queue, - char *buf) +static ssize_t tx_timeout_show(struct netdev_queue *queue, char *buf) { unsigned long trans_timeout; @@ -1033,7 +1032,7 @@ static unsigned int get_netdev_queue_index(struct netdev_queue *queue) return i; } -static ssize_t show_traffic_class(struct netdev_queue *queue, +static ssize_t traffic_class_show(struct netdev_queue *queue, char *buf) { struct net_device *dev = queue->dev; @@ -1047,14 +1046,14 @@ static ssize_t show_traffic_class(struct netdev_queue *queue, } #ifdef CONFIG_XPS -static ssize_t show_tx_maxrate(struct netdev_queue *queue, +static ssize_t tx_maxrate_show(struct netdev_queue *queue, char *buf) { return sprintf(buf, "%lu\n", queue->tx_maxrate); } -static ssize_t set_tx_maxrate(struct netdev_queue *queue, - const char *buf, size_t len) +static ssize_t tx_maxrate_store(struct netdev_queue *queue, + const char *buf, size_t len) { struct net_device *dev = queue->dev; int err, index = get_netdev_queue_index(queue); @@ -1079,16 +1078,15 @@ static ssize_t set_tx_maxrate(struct netdev_queue *queue, return err; } -static struct netdev_queue_attribute queue_tx_maxrate = - __ATTR(tx_maxrate, S_IRUGO | S_IWUSR, - show_tx_maxrate, set_tx_maxrate); +static struct netdev_queue_attribute queue_tx_maxrate __ro_after_init + = __ATTR_RW(tx_maxrate); #endif -static struct netdev_queue_attribute queue_trans_timeout = - __ATTR(tx_timeout, S_IRUGO, show_trans_timeout, NULL); +static struct netdev_queue_attribute queue_trans_timeout __ro_after_init + = __ATTR_RO(tx_timeout); -static struct netdev_queue_attribute queue_traffic_class = - __ATTR(traffic_class, S_IRUGO, show_traffic_class, NULL); +static struct netdev_queue_attribute queue_traffic_class __ro_after_init + = __ATTR_RO(traffic_class); #ifdef CONFIG_BQL /* @@ -1196,8 +1194,8 @@ static const struct attribute_group dql_group = { #endif /* CONFIG_BQL */ #ifdef CONFIG_XPS -static ssize_t show_xps_map(struct netdev_queue *queue, - char *buf) +static ssize_t xps_cpus_show(struct netdev_queue *queue, + char *buf) { struct net_device *dev = queue->dev; int cpu, len, num_tc = 1, tc = 0; @@ -1243,8 +1241,8 @@ static ssize_t show_xps_map(struct netdev_queue *queue, return len < PAGE_SIZE ? len : -EINVAL; } -static ssize_t store_xps_map(struct netdev_queue *queue, - const char *buf, size_t len) +static ssize_t xps_cpus_store(struct netdev_queue *queue, + const char *buf, size_t len) { struct net_device *dev = queue->dev; unsigned long index; @@ -1272,11 +1270,11 @@ static ssize_t store_xps_map(struct netdev_queue *queue, return err ? : len; } -static struct netdev_queue_attribute xps_cpus_attribute = - __ATTR(xps_cpus, S_IRUGO | S_IWUSR, show_xps_map, store_xps_map); +static struct netdev_queue_attribute xps_cpus_attribute __ro_after_init + = __ATTR_RW(xps_cpus); #endif /* CONFIG_XPS */ -static struct attribute *netdev_queue_default_attrs[] = { +static struct attribute *netdev_queue_default_attrs[] __ro_after_init = { &queue_trans_timeout.attr, &queue_traffic_class.attr, #ifdef CONFIG_XPS @@ -1306,7 +1304,7 @@ static const void *netdev_queue_namespace(struct kobject *kobj) return ns; } -static struct kobj_type netdev_queue_ktype = { +static struct kobj_type netdev_queue_ktype __ro_after_init = { .sysfs_ops = &netdev_queue_sysfs_ops, .release = netdev_queue_release, .default_attrs = netdev_queue_default_attrs, -- cgit v1.2.3 From 667e427bc356a43e130cfc03ea4273603487cc69 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:27 -0700 Subject: net: mark receive queue attributes ro_after_init Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index 808fbb837f25..ca82c4a72350 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -655,7 +655,7 @@ static const struct attribute_group wireless_group = { static ssize_t rx_queue_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { - struct rx_queue_attribute *attribute = to_rx_queue_attr(attr); + const struct rx_queue_attribute *attribute = to_rx_queue_attr(attr); struct netdev_rx_queue *queue = to_rx_queue(kobj); if (!attribute->show) @@ -667,7 +667,7 @@ static ssize_t rx_queue_attr_show(struct kobject *kobj, struct attribute *attr, static ssize_t rx_queue_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { - struct rx_queue_attribute *attribute = to_rx_queue_attr(attr); + const struct rx_queue_attribute *attribute = to_rx_queue_attr(attr); struct netdev_rx_queue *queue = to_rx_queue(kobj); if (!attribute->store) @@ -842,16 +842,15 @@ static ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, return len; } -static struct rx_queue_attribute rps_cpus_attribute = - __ATTR(rps_cpus, S_IRUGO | S_IWUSR, show_rps_map, store_rps_map); +static struct rx_queue_attribute rps_cpus_attribute __ro_after_init + = __ATTR(rps_cpus, S_IRUGO | S_IWUSR, show_rps_map, store_rps_map); - -static struct rx_queue_attribute rps_dev_flow_table_cnt_attribute = - __ATTR(rps_flow_cnt, S_IRUGO | S_IWUSR, - show_rps_dev_flow_table_cnt, store_rps_dev_flow_table_cnt); +static struct rx_queue_attribute rps_dev_flow_table_cnt_attribute __ro_after_init + = __ATTR(rps_flow_cnt, S_IRUGO | S_IWUSR, + show_rps_dev_flow_table_cnt, store_rps_dev_flow_table_cnt); #endif /* CONFIG_RPS */ -static struct attribute *rx_queue_default_attrs[] = { +static struct attribute *rx_queue_default_attrs[] __ro_after_init = { #ifdef CONFIG_RPS &rps_cpus_attribute.attr, &rps_dev_flow_table_cnt_attribute.attr, @@ -896,7 +895,7 @@ static const void *rx_queue_namespace(struct kobject *kobj) return ns; } -static struct kobj_type rx_queue_ktype = { +static struct kobj_type rx_queue_ktype __ro_after_init = { .sysfs_ops = &rx_queue_sysfs_ops, .release = rx_queue_release, .default_attrs = rx_queue_default_attrs, @@ -983,7 +982,8 @@ struct netdev_queue_attribute { static ssize_t netdev_queue_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { - struct netdev_queue_attribute *attribute = to_netdev_queue_attr(attr); + const struct netdev_queue_attribute *attribute + = to_netdev_queue_attr(attr); struct netdev_queue *queue = to_netdev_queue(kobj); if (!attribute->show) @@ -996,7 +996,8 @@ static ssize_t netdev_queue_attr_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count) { - struct netdev_queue_attribute *attribute = to_netdev_queue_attr(attr); + const struct netdev_queue_attribute *attribute + = to_netdev_queue_attr(attr); struct netdev_queue *queue = to_netdev_queue(kobj); if (!attribute->store) -- cgit v1.2.3 From 6648c65e7ea72c3b19ea908d046e4a47e90fd907 Mon Sep 17 00:00:00 2001 From: stephen hemminger Date: Fri, 18 Aug 2017 13:46:28 -0700 Subject: net: style cleanups Make code closer to current style. Mostly whitespace changes. Signed-off-by: Stephen Hemminger Signed-off-by: David S. Miller --- net/core/net-sysfs.c | 68 +++++++++++++++++++++++++++------------------------- 1 file changed, 36 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/net/core/net-sysfs.c b/net/core/net-sysfs.c index ca82c4a72350..927a6dcbad96 100644 --- a/net/core/net-sysfs.c +++ b/net/core/net-sysfs.c @@ -97,7 +97,8 @@ static ssize_t netdev_store(struct device *dev, struct device_attribute *attr, return restart_syscall(); if (dev_isalive(netdev)) { - if ((ret = (*set)(netdev, new)) == 0) + ret = (*set)(netdev, new); + if (ret == 0) ret = len; } rtnl_unlock(); @@ -160,6 +161,7 @@ static ssize_t broadcast_show(struct device *dev, struct device_attribute *attr, char *buf) { struct net_device *ndev = to_net_dev(dev); + if (dev_isalive(ndev)) return sysfs_format_mac(buf, ndev->broadcast, ndev->addr_len); return -EINVAL; @@ -170,7 +172,7 @@ static int change_carrier(struct net_device *dev, unsigned long new_carrier) { if (!netif_running(dev)) return -EINVAL; - return dev_change_carrier(dev, (bool) new_carrier); + return dev_change_carrier(dev, (bool)new_carrier); } static ssize_t carrier_store(struct device *dev, struct device_attribute *attr, @@ -183,9 +185,10 @@ static ssize_t carrier_show(struct device *dev, struct device_attribute *attr, char *buf) { struct net_device *netdev = to_net_dev(dev); - if (netif_running(netdev)) { + + if (netif_running(netdev)) return sprintf(buf, fmt_dec, !!netif_carrier_ok(netdev)); - } + return -EINVAL; } static DEVICE_ATTR_RW(carrier); @@ -290,6 +293,7 @@ static ssize_t carrier_changes_show(struct device *dev, char *buf) { struct net_device *netdev = to_net_dev(dev); + return sprintf(buf, fmt_dec, atomic_read(&netdev->carrier_changes)); } @@ -299,7 +303,7 @@ static DEVICE_ATTR_RO(carrier_changes); static int change_mtu(struct net_device *dev, unsigned long new_mtu) { - return dev_set_mtu(dev, (int) new_mtu); + return dev_set_mtu(dev, (int)new_mtu); } static ssize_t mtu_store(struct device *dev, struct device_attribute *attr, @@ -311,7 +315,7 @@ NETDEVICE_SHOW_RW(mtu, fmt_dec); static int change_flags(struct net_device *dev, unsigned long new_flags) { - return dev_change_flags(dev, (unsigned int) new_flags); + return dev_change_flags(dev, (unsigned int)new_flags); } static ssize_t flags_store(struct device *dev, struct device_attribute *attr, @@ -362,8 +366,8 @@ static int change_gro_flush_timeout(struct net_device *dev, unsigned long val) } static ssize_t gro_flush_timeout_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) + struct device_attribute *attr, + const char *buf, size_t len) { if (!capable(CAP_NET_ADMIN)) return -EPERM; @@ -412,7 +416,7 @@ static DEVICE_ATTR_RW(ifalias); static int change_group(struct net_device *dev, unsigned long new_group) { - dev_set_group(dev, (int) new_group); + dev_set_group(dev, (int)new_group); return 0; } @@ -426,7 +430,7 @@ static DEVICE_ATTR(netdev_group, S_IRUGO | S_IWUSR, group_show, group_store); static int change_proto_down(struct net_device *dev, unsigned long proto_down) { - return dev_change_proto_down(dev, (bool) proto_down); + return dev_change_proto_down(dev, (bool)proto_down); } static ssize_t proto_down_store(struct device *dev, @@ -549,14 +553,14 @@ static ssize_t netstat_show(const struct device *d, ssize_t ret = -EINVAL; WARN_ON(offset > sizeof(struct rtnl_link_stats64) || - offset % sizeof(u64) != 0); + offset % sizeof(u64) != 0); read_lock(&dev_base_lock); if (dev_isalive(dev)) { struct rtnl_link_stats64 temp; const struct rtnl_link_stats64 *stats = dev_get_stats(dev, &temp); - ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *) stats) + offset)); + ret = sprintf(buf, fmt_u64, *(u64 *)(((u8 *)stats) + offset)); } read_unlock(&dev_base_lock); return ret; @@ -565,7 +569,7 @@ static ssize_t netstat_show(const struct device *d, /* generate a read-only statistics attribute */ #define NETSTAT_ENTRY(name) \ static ssize_t name##_show(struct device *d, \ - struct device_attribute *attr, char *buf) \ + struct device_attribute *attr, char *buf) \ { \ return netstat_show(d, attr, buf, \ offsetof(struct rtnl_link_stats64, name)); \ @@ -625,7 +629,6 @@ static struct attribute *netstat_attrs[] __ro_after_init = { NULL }; - static const struct attribute_group netstat_group = { .name = "statistics", .attrs = netstat_attrs, @@ -647,8 +650,8 @@ static const struct attribute_group wireless_group = { #endif /* CONFIG_SYSFS */ #ifdef CONFIG_SYSFS -#define to_rx_queue_attr(_attr) container_of(_attr, \ - struct rx_queue_attribute, attr) +#define to_rx_queue_attr(_attr) \ + container_of(_attr, struct rx_queue_attribute, attr) #define to_rx_queue(obj) container_of(obj, struct netdev_rx_queue, kobj) @@ -725,8 +728,8 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue, } map = kzalloc(max_t(unsigned int, - RPS_MAP_SIZE(cpumask_weight(mask)), L1_CACHE_BYTES), - GFP_KERNEL); + RPS_MAP_SIZE(cpumask_weight(mask)), L1_CACHE_BYTES), + GFP_KERNEL); if (!map) { free_cpumask_var(mask); return -ENOMEM; @@ -736,9 +739,9 @@ static ssize_t store_rps_map(struct netdev_rx_queue *queue, for_each_cpu_and(cpu, mask, cpu_online_mask) map->cpus[i++] = cpu; - if (i) + if (i) { map->len = i; - else { + } else { kfree(map); map = NULL; } @@ -827,8 +830,9 @@ static ssize_t store_rps_dev_flow_table_cnt(struct netdev_rx_queue *queue, table->mask = mask; for (count = 0; count <= mask; count++) table->flows[count].cpu = RPS_NO_CPU; - } else + } else { table = NULL; + } spin_lock(&rps_dev_flow_lock); old_table = rcu_dereference_protected(queue->rps_flow_table, @@ -865,7 +869,6 @@ static void rx_queue_release(struct kobject *kobj) struct rps_map *map; struct rps_dev_flow_table *flow_table; - map = rcu_dereference_protected(queue->rps_map, 1); if (map) { RCU_INIT_POINTER(queue->rps_map, NULL); @@ -910,7 +913,7 @@ static int rx_queue_add_kobject(struct net_device *dev, int index) kobj->kset = dev->queues_kset; error = kobject_init_and_add(kobj, &rx_queue_ktype, NULL, - "rx-%u", index); + "rx-%u", index); if (error) return error; @@ -974,8 +977,8 @@ struct netdev_queue_attribute { ssize_t (*store)(struct netdev_queue *queue, const char *buf, size_t len); }; -#define to_netdev_queue_attr(_attr) container_of(_attr, \ - struct netdev_queue_attribute, attr) +#define to_netdev_queue_attr(_attr) \ + container_of(_attr, struct netdev_queue_attribute, attr) #define to_netdev_queue(obj) container_of(obj, struct netdev_queue, kobj) @@ -1104,9 +1107,9 @@ static ssize_t bql_set(const char *buf, const size_t count, unsigned int value; int err; - if (!strcmp(buf, "max") || !strcmp(buf, "max\n")) + if (!strcmp(buf, "max") || !strcmp(buf, "max\n")) { value = DQL_MAX_LIMIT; - else { + } else { err = kstrtouint(buf, 10, &value); if (err < 0) return err; @@ -1320,7 +1323,7 @@ static int netdev_queue_add_kobject(struct net_device *dev, int index) kobj->kset = dev->queues_kset; error = kobject_init_and_add(kobj, &netdev_queue_ktype, NULL, - "tx-%u", index); + "tx-%u", index); if (error) return error; @@ -1377,7 +1380,7 @@ static int register_queue_kobjects(struct net_device *dev) #ifdef CONFIG_SYSFS dev->queues_kset = kset_create_and_add("queues", - NULL, &dev->dev.kobj); + NULL, &dev->dev.kobj); if (!dev->queues_kset) return -ENOMEM; real_rx = dev->real_num_rx_queues; @@ -1467,7 +1470,8 @@ static int netdev_uevent(struct device *d, struct kobj_uevent_env *env) /* pass ifindex to uevent. * ifindex is useful as it won't change (interface name may change) - * and is what RtNetlink uses natively. */ + * and is what RtNetlink uses natively. + */ retval = add_uevent_var(env, "IFINDEX=%d", dev->ifindex); exit: @@ -1542,7 +1546,7 @@ EXPORT_SYMBOL(of_find_net_device_by_node); */ void netdev_unregister_kobject(struct net_device *ndev) { - struct device *dev = &(ndev->dev); + struct device *dev = &ndev->dev; if (!atomic_read(&dev_net(ndev)->count)) dev_set_uevent_suppress(dev, 1); @@ -1559,7 +1563,7 @@ void netdev_unregister_kobject(struct net_device *ndev) /* Create sysfs entries for network device. */ int netdev_register_kobject(struct net_device *ndev) { - struct device *dev = &(ndev->dev); + struct device *dev = &ndev->dev; const struct attribute_group **groups = ndev->sysfs_groups; int error = 0; -- cgit v1.2.3 From 9374bf18dbb533912898b07b3751fbce40fae5c4 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Sat, 19 Aug 2017 14:54:47 +0530 Subject: Bluetooth: make device_type const Make these const as they are only stored in the type field of a device structure, which is const. Done using Coccinelle. Signed-off-by: Bhumika Goyal Signed-off-by: Marcel Holtmann --- net/bluetooth/hci_sysfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/bluetooth/hci_sysfs.c b/net/bluetooth/hci_sysfs.c index ca7a35ebaefb..aa300f3a0d51 100644 --- a/net/bluetooth/hci_sysfs.c +++ b/net/bluetooth/hci_sysfs.c @@ -13,7 +13,7 @@ static void bt_link_release(struct device *dev) kfree(conn); } -static struct device_type bt_link = { +static const struct device_type bt_link = { .name = "link", .release = bt_link_release, }; @@ -86,7 +86,7 @@ static void bt_host_release(struct device *dev) module_put(THIS_MODULE); } -static struct device_type bt_host = { +static const struct device_type bt_host = { .name = "host", .release = bt_host_release, }; -- cgit v1.2.3 From a2acc543408e1cbdcd7915a268cdbc451f09832a Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Wed, 2 Aug 2017 18:14:43 +0900 Subject: netfilter: connlimit: merge root4 and root6. The root4 variable is used only when connlimit extension module has been stored by the iptables command. and the roo6 variable is used only when connlimit extension module has been stored by the ip6tables command. So the root4 and roo6 variable does not be used at the same time. Signed-off-by: Taehee Yoo Signed-off-by: Pablo Neira Ayuso --- net/netfilter/xt_connlimit.c | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/netfilter/xt_connlimit.c b/net/netfilter/xt_connlimit.c index 97589b8a2a40..ffa8eec980e9 100644 --- a/net/netfilter/xt_connlimit.c +++ b/net/netfilter/xt_connlimit.c @@ -58,8 +58,7 @@ struct xt_connlimit_rb { static spinlock_t xt_connlimit_locks[CONNLIMIT_LOCK_SLOTS] __cacheline_aligned_in_smp; struct xt_connlimit_data { - struct rb_root climit_root4[CONNLIMIT_SLOTS]; - struct rb_root climit_root6[CONNLIMIT_SLOTS]; + struct rb_root climit_root[CONNLIMIT_SLOTS]; }; static u_int32_t connlimit_rnd __read_mostly; @@ -294,13 +293,11 @@ static int count_them(struct net *net, int count; u32 hash; - if (family == NFPROTO_IPV6) { + if (family == NFPROTO_IPV6) hash = connlimit_iphash6(addr, mask); - root = &data->climit_root6[hash]; - } else { + else hash = connlimit_iphash(addr->ip & mask->ip); - root = &data->climit_root4[hash]; - } + root = &data->climit_root[hash]; spin_lock_bh(&xt_connlimit_locks[hash % CONNLIMIT_LOCK_SLOTS]); @@ -379,10 +376,8 @@ static int connlimit_mt_check(const struct xt_mtchk_param *par) return -ENOMEM; } - for (i = 0; i < ARRAY_SIZE(info->data->climit_root4); ++i) - info->data->climit_root4[i] = RB_ROOT; - for (i = 0; i < ARRAY_SIZE(info->data->climit_root6); ++i) - info->data->climit_root6[i] = RB_ROOT; + for (i = 0; i < ARRAY_SIZE(info->data->climit_root); ++i) + info->data->climit_root[i] = RB_ROOT; return 0; } @@ -413,10 +408,8 @@ static void connlimit_mt_destroy(const struct xt_mtdtor_param *par) nf_ct_netns_put(par->net, par->family); - for (i = 0; i < ARRAY_SIZE(info->data->climit_root4); ++i) - destroy_tree(&info->data->climit_root4[i]); - for (i = 0; i < ARRAY_SIZE(info->data->climit_root6); ++i) - destroy_tree(&info->data->climit_root6[i]); + for (i = 0; i < ARRAY_SIZE(info->data->climit_root); ++i) + destroy_tree(&info->data->climit_root[i]); kfree(info->data); } -- cgit v1.2.3 From 166327d79d8d44a1668eff0fda8286e9a193a251 Mon Sep 17 00:00:00 2001 From: Taehee Yoo Date: Thu, 3 Aug 2017 10:26:20 +0900 Subject: netfilter: remove prototype of netfilter_queue_init The netfilter_queue_init() has been removed. so we can remove the prototype of that. Signed-off-by: Taehee Yoo Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_internals.h | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index bfa742da83af..19f00a47a710 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -15,7 +15,6 @@ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, struct nf_hook_entry **entryp, unsigned int verdict); unsigned int nf_queue_nf_hook_drop(struct net *net); -int __init netfilter_queue_init(void); /* nf_log.c */ int __init netfilter_log_init(void); -- cgit v1.2.3 From 46b20c38f37c48bbcb832f933e1bee7d951da99b Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Mon, 7 Aug 2017 21:44:25 +0800 Subject: netfilter: use audit_log() Use audit_log() instead of open-coding it. Signed-off-by: Geliang Tang Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebtables.c | 13 ++++--------- net/netfilter/x_tables.c | 14 ++++---------- 2 files changed, 8 insertions(+), 19 deletions(-) (limited to 'net') diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 9c6e619f452b..54c7ef4e970e 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -1069,15 +1069,10 @@ static int do_replace_finish(struct net *net, struct ebt_replace *repl, #ifdef CONFIG_AUDIT if (audit_enabled) { - struct audit_buffer *ab; - - ab = audit_log_start(current->audit_context, GFP_KERNEL, - AUDIT_NETFILTER_CFG); - if (ab) { - audit_log_format(ab, "table=%s family=%u entries=%u", - repl->name, AF_BRIDGE, repl->nentries); - audit_log_end(ab); - } + audit_log(current->audit_context, GFP_KERNEL, + AUDIT_NETFILTER_CFG, + "table=%s family=%u entries=%u", + repl->name, AF_BRIDGE, repl->nentries); } #endif return ret; diff --git a/net/netfilter/x_tables.c b/net/netfilter/x_tables.c index e1648238a9c9..c83a3b5e1c6c 100644 --- a/net/netfilter/x_tables.c +++ b/net/netfilter/x_tables.c @@ -1192,16 +1192,10 @@ xt_replace_table(struct xt_table *table, #ifdef CONFIG_AUDIT if (audit_enabled) { - struct audit_buffer *ab; - - ab = audit_log_start(current->audit_context, GFP_KERNEL, - AUDIT_NETFILTER_CFG); - if (ab) { - audit_log_format(ab, "table=%s family=%u entries=%u", - table->name, table->af, - private->number); - audit_log_end(ab); - } + audit_log(current->audit_context, GFP_KERNEL, + AUDIT_NETFILTER_CFG, + "table=%s family=%u entries=%u", + table->name, table->af, private->number); } #endif -- cgit v1.2.3 From a18177008b2613f009ef210b7da695056a932321 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 8 Aug 2017 15:15:27 +0200 Subject: netfilter: exthdr: factor out tcp option access Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_exthdr.c | 33 +++++++++++++++++++++------------ 1 file changed, 21 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index 1ec49fe5845f..921c95f2c583 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -61,6 +61,26 @@ err: regs->verdict.code = NFT_BREAK; } +static void * +nft_tcp_header_pointer(const struct nft_pktinfo *pkt, + unsigned int len, void *buffer, unsigned int *tcphdr_len) +{ + struct tcphdr *tcph; + + if (!pkt->tprot_set || pkt->tprot != IPPROTO_TCP) + return NULL; + + tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, sizeof(*tcph), buffer); + if (!tcph) + return NULL; + + *tcphdr_len = __tcp_hdrlen(tcph); + if (*tcphdr_len < sizeof(*tcph) || *tcphdr_len > len) + return NULL; + + return skb_header_pointer(pkt->skb, pkt->xt.thoff, *tcphdr_len, buffer); +} + static void nft_exthdr_tcp_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -72,18 +92,7 @@ static void nft_exthdr_tcp_eval(const struct nft_expr *expr, struct tcphdr *tcph; u8 *opt; - if (!pkt->tprot_set || pkt->tprot != IPPROTO_TCP) - goto err; - - tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, sizeof(*tcph), buff); - if (!tcph) - goto err; - - tcphdr_len = __tcp_hdrlen(tcph); - if (tcphdr_len < sizeof(*tcph)) - goto err; - - tcph = skb_header_pointer(pkt->skb, pkt->xt.thoff, tcphdr_len, buff); + tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); if (!tcph) goto err; -- cgit v1.2.3 From 5e7d695a482c6e581addf42717469bd363dd734e Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 8 Aug 2017 15:15:28 +0200 Subject: netfilter: exthdr: split netlink dump function so eval and uncoming eval_set versions can reuse a common helper. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_exthdr.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index 921c95f2c583..e3a6eebe7e0c 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -180,12 +180,8 @@ static int nft_exthdr_init(const struct nft_ctx *ctx, NFT_DATA_VALUE, priv->len); } -static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr) +static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *priv) { - const struct nft_exthdr *priv = nft_expr_priv(expr); - - if (nft_dump_register(skb, NFTA_EXTHDR_DREG, priv->dreg)) - goto nla_put_failure; if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type)) goto nla_put_failure; if (nla_put_be32(skb, NFTA_EXTHDR_OFFSET, htonl(priv->offset))) @@ -202,6 +198,16 @@ nla_put_failure: return -1; } +static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_exthdr *priv = nft_expr_priv(expr); + + if (nft_dump_register(skb, NFTA_EXTHDR_DREG, priv->dreg)) + return -1; + + return nft_exthdr_dump_common(skb, priv); +} + static struct nft_expr_type nft_exthdr_type; static const struct nft_expr_ops nft_exthdr_ipv6_ops = { .type = &nft_exthdr_type, -- cgit v1.2.3 From 99d1712bc41c7c9a5a473c104a4ad15427757b22 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 8 Aug 2017 15:15:29 +0200 Subject: netfilter: exthdr: tcp option set support This allows setting 2 and 4 byte quantities in the tcp option space. Main purpose is to allow native replacement for xt_TCPMSS to work around pmtu blackholes. Writes to kind and len are now allowed at the moment, it does not seem useful to do this as it causes corruption of the tcp option space. We can always lift this restriction later if a use-case appears. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 4 +- net/netfilter/nft_exthdr.c | 164 ++++++++++++++++++++++++++++++- 2 files changed, 165 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index be25cf69295b..40fd199f7531 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -732,7 +732,8 @@ enum nft_exthdr_op { * @NFTA_EXTHDR_OFFSET: extension header offset (NLA_U32) * @NFTA_EXTHDR_LEN: extension header length (NLA_U32) * @NFTA_EXTHDR_FLAGS: extension header flags (NLA_U32) - * @NFTA_EXTHDR_OP: option match type (NLA_U8) + * @NFTA_EXTHDR_OP: option match type (NLA_U32) + * @NFTA_EXTHDR_SREG: option match type (NLA_U32) */ enum nft_exthdr_attributes { NFTA_EXTHDR_UNSPEC, @@ -742,6 +743,7 @@ enum nft_exthdr_attributes { NFTA_EXTHDR_LEN, NFTA_EXTHDR_FLAGS, NFTA_EXTHDR_OP, + NFTA_EXTHDR_SREG, __NFTA_EXTHDR_MAX }; #define NFTA_EXTHDR_MAX (__NFTA_EXTHDR_MAX - 1) diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index e3a6eebe7e0c..f5a0bf5e3bdd 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -8,6 +8,7 @@ * Development of this code funded by Astaro AG (http://www.astaro.com/) */ +#include #include #include #include @@ -23,6 +24,7 @@ struct nft_exthdr { u8 len; u8 op; enum nft_registers dreg:8; + enum nft_registers sreg:8; u8 flags; }; @@ -124,6 +126,88 @@ err: regs->verdict.code = NFT_BREAK; } +static void nft_exthdr_tcp_set_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + u8 buff[sizeof(struct tcphdr) + MAX_TCP_OPTION_SPACE]; + struct nft_exthdr *priv = nft_expr_priv(expr); + unsigned int i, optl, tcphdr_len, offset; + struct tcphdr *tcph; + u8 *opt; + u32 src; + + tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, &tcphdr_len); + if (!tcph) + return; + + opt = (u8 *)tcph; + for (i = sizeof(*tcph); i < tcphdr_len - 1; i += optl) { + union { + u8 octet; + __be16 v16; + __be32 v32; + } old, new; + + optl = optlen(opt, i); + + if (priv->type != opt[i]) + continue; + + if (i + optl > tcphdr_len || priv->len + priv->offset > optl) + return; + + if (!skb_make_writable(pkt->skb, pkt->xt.thoff + i + priv->len)) + return; + + tcph = nft_tcp_header_pointer(pkt, sizeof(buff), buff, + &tcphdr_len); + if (!tcph) + return; + + src = regs->data[priv->sreg]; + offset = i + priv->offset; + + switch (priv->len) { + case 2: + old.v16 = get_unaligned((u16 *)(opt + offset)); + new.v16 = src; + + switch (priv->type) { + case TCPOPT_MSS: + /* increase can cause connection to stall */ + if (ntohs(old.v16) <= ntohs(new.v16)) + return; + break; + } + + if (old.v16 == new.v16) + return; + + put_unaligned(new.v16, (u16*)(opt + offset)); + inet_proto_csum_replace2(&tcph->check, pkt->skb, + old.v16, new.v16, false); + break; + case 4: + new.v32 = src; + old.v32 = get_unaligned((u32 *)(opt + offset)); + + if (old.v32 == new.v32) + return; + + put_unaligned(new.v32, (u32*)(opt + offset)); + inet_proto_csum_replace4(&tcph->check, pkt->skb, + old.v32, new.v32, false); + break; + default: + WARN_ON_ONCE(1); + break; + } + + return; + } +} + static const struct nla_policy nft_exthdr_policy[NFTA_EXTHDR_MAX + 1] = { [NFTA_EXTHDR_DREG] = { .type = NLA_U32 }, [NFTA_EXTHDR_TYPE] = { .type = NLA_U8 }, @@ -180,6 +264,55 @@ static int nft_exthdr_init(const struct nft_ctx *ctx, NFT_DATA_VALUE, priv->len); } +static int nft_exthdr_tcp_set_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) +{ + struct nft_exthdr *priv = nft_expr_priv(expr); + u32 offset, len, flags = 0, op = NFT_EXTHDR_OP_IPV6; + int err; + + if (!tb[NFTA_EXTHDR_SREG] || + !tb[NFTA_EXTHDR_TYPE] || + !tb[NFTA_EXTHDR_OFFSET] || + !tb[NFTA_EXTHDR_LEN]) + return -EINVAL; + + if (tb[NFTA_EXTHDR_DREG] || tb[NFTA_EXTHDR_FLAGS]) + return -EINVAL; + + err = nft_parse_u32_check(tb[NFTA_EXTHDR_OFFSET], U8_MAX, &offset); + if (err < 0) + return err; + + err = nft_parse_u32_check(tb[NFTA_EXTHDR_LEN], U8_MAX, &len); + if (err < 0) + return err; + + if (offset < 2) + return -EOPNOTSUPP; + + switch (len) { + case 2: break; + case 4: break; + default: + return -EOPNOTSUPP; + } + + err = nft_parse_u32_check(tb[NFTA_EXTHDR_OP], U8_MAX, &op); + if (err < 0) + return err; + + priv->type = nla_get_u8(tb[NFTA_EXTHDR_TYPE]); + priv->offset = offset; + priv->len = len; + priv->sreg = nft_parse_register(tb[NFTA_EXTHDR_SREG]); + priv->flags = flags; + priv->op = op; + + return nft_validate_register_load(priv->sreg, priv->len); +} + static int nft_exthdr_dump_common(struct sk_buff *skb, const struct nft_exthdr *priv) { if (nla_put_u8(skb, NFTA_EXTHDR_TYPE, priv->type)) @@ -208,6 +341,16 @@ static int nft_exthdr_dump(struct sk_buff *skb, const struct nft_expr *expr) return nft_exthdr_dump_common(skb, priv); } +static int nft_exthdr_dump_set(struct sk_buff *skb, const struct nft_expr *expr) +{ + const struct nft_exthdr *priv = nft_expr_priv(expr); + + if (nft_dump_register(skb, NFTA_EXTHDR_SREG, priv->sreg)) + return -1; + + return nft_exthdr_dump_common(skb, priv); +} + static struct nft_expr_type nft_exthdr_type; static const struct nft_expr_ops nft_exthdr_ipv6_ops = { .type = &nft_exthdr_type, @@ -225,6 +368,14 @@ static const struct nft_expr_ops nft_exthdr_tcp_ops = { .dump = nft_exthdr_dump, }; +static const struct nft_expr_ops nft_exthdr_tcp_set_ops = { + .type = &nft_exthdr_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_exthdr)), + .eval = nft_exthdr_tcp_set_eval, + .init = nft_exthdr_tcp_set_init, + .dump = nft_exthdr_dump_set, +}; + static const struct nft_expr_ops * nft_exthdr_select_ops(const struct nft_ctx *ctx, const struct nlattr * const tb[]) @@ -234,12 +385,21 @@ nft_exthdr_select_ops(const struct nft_ctx *ctx, if (!tb[NFTA_EXTHDR_OP]) return &nft_exthdr_ipv6_ops; + if (tb[NFTA_EXTHDR_SREG] && tb[NFTA_EXTHDR_DREG]) + return ERR_PTR(-EOPNOTSUPP); + op = ntohl(nla_get_u32(tb[NFTA_EXTHDR_OP])); switch (op) { case NFT_EXTHDR_OP_TCPOPT: - return &nft_exthdr_tcp_ops; + if (tb[NFTA_EXTHDR_SREG]) + return &nft_exthdr_tcp_set_ops; + if (tb[NFTA_EXTHDR_DREG]) + return &nft_exthdr_tcp_ops; + break; case NFT_EXTHDR_OP_IPV6: - return &nft_exthdr_ipv6_ops; + if (tb[NFTA_EXTHDR_DREG]) + return &nft_exthdr_ipv6_ops; + break; } return ERR_PTR(-EOPNOTSUPP); -- cgit v1.2.3 From 6b5dc98e8fac041a3decfc3186e08c1c570ea691 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 8 Aug 2017 15:48:04 +0200 Subject: netfilter: rt: add support to fetch path mss to be used in combination with tcp option set support to mimic iptables TCPMSS --clamp-mss-to-pmtu. v2: Eric Dumazet points out dst must be initialized. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 2 + net/netfilter/nft_rt.c | 66 ++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) (limited to 'net') diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index 40fd199f7531..b49da72efa68 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -811,11 +811,13 @@ enum nft_meta_keys { * @NFT_RT_CLASSID: realm value of packet's route (skb->dst->tclassid) * @NFT_RT_NEXTHOP4: routing nexthop for IPv4 * @NFT_RT_NEXTHOP6: routing nexthop for IPv6 + * @NFT_RT_TCPMSS: fetch current path tcp mss */ enum nft_rt_keys { NFT_RT_CLASSID, NFT_RT_NEXTHOP4, NFT_RT_NEXTHOP6, + NFT_RT_TCPMSS, }; /** diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index c7383d8f88d0..e142e65d3176 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -23,6 +23,42 @@ struct nft_rt { enum nft_registers dreg:8; }; +static u16 get_tcpmss(const struct nft_pktinfo *pkt, const struct dst_entry *skbdst) +{ + u32 minlen = sizeof(struct ipv6hdr), mtu = dst_mtu(skbdst); + const struct sk_buff *skb = pkt->skb; + const struct nf_afinfo *ai; + struct flowi fl; + + memset(&fl, 0, sizeof(fl)); + + switch (nft_pf(pkt)) { + case NFPROTO_IPV4: + fl.u.ip4.daddr = ip_hdr(skb)->saddr; + minlen = sizeof(struct iphdr); + break; + case NFPROTO_IPV6: + fl.u.ip6.daddr = ipv6_hdr(skb)->saddr; + break; + } + + ai = nf_get_afinfo(nft_pf(pkt)); + if (ai) { + struct dst_entry *dst = NULL; + + ai->route(nft_net(pkt), &dst, &fl, false); + if (dst) { + mtu = min(mtu, dst_mtu(dst)); + dst_release(dst); + } + } + + if (mtu <= minlen || mtu > 0xffff) + return TCP_MSS_DEFAULT; + + return mtu - minlen; +} + static void nft_rt_get_eval(const struct nft_expr *expr, struct nft_regs *regs, const struct nft_pktinfo *pkt) @@ -57,6 +93,9 @@ static void nft_rt_get_eval(const struct nft_expr *expr, &ipv6_hdr(skb)->daddr), sizeof(struct in6_addr)); break; + case NFT_RT_TCPMSS: + nft_reg_store16(dest, get_tcpmss(pkt, dst)); + break; default: WARN_ON(1); goto err; @@ -94,6 +133,9 @@ static int nft_rt_get_init(const struct nft_ctx *ctx, case NFT_RT_NEXTHOP6: len = sizeof(struct in6_addr); break; + case NFT_RT_TCPMSS: + len = sizeof(u16); + break; default: return -EOPNOTSUPP; } @@ -118,6 +160,29 @@ nla_put_failure: return -1; } +static int nft_rt_validate(const struct nft_ctx *ctx, const struct nft_expr *expr, + const struct nft_data **data) +{ + const struct nft_rt *priv = nft_expr_priv(expr); + unsigned int hooks; + + switch (priv->key) { + case NFT_RT_NEXTHOP4: + case NFT_RT_NEXTHOP6: + case NFT_RT_CLASSID: + return 0; + case NFT_RT_TCPMSS: + hooks = (1 << NF_INET_FORWARD) | + (1 << NF_INET_LOCAL_OUT) | + (1 << NF_INET_POST_ROUTING); + break; + default: + return -EINVAL; + } + + return nft_chain_validate_hooks(ctx->chain, hooks); +} + static struct nft_expr_type nft_rt_type; static const struct nft_expr_ops nft_rt_get_ops = { .type = &nft_rt_type, @@ -125,6 +190,7 @@ static const struct nft_expr_ops nft_rt_get_ops = { .eval = nft_rt_get_eval, .init = nft_rt_get_init, .dump = nft_rt_get_dump, + .validate = nft_rt_validate, }; static struct nft_expr_type nft_rt_type __read_mostly = { -- cgit v1.2.3 From 89e49506bc62520f93e64a278293444319a6aebb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Thu, 17 Aug 2017 16:47:00 +0200 Subject: dsa: remove unused net_device arg from handlers compile tested only, but saw no warnings/errors with allmodconfig build. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/dsa.h | 6 ++---- net/dsa/dsa.c | 4 ++-- net/dsa/tag_brcm.c | 3 +-- net/dsa/tag_dsa.c | 3 +-- net/dsa/tag_edsa.c | 3 +-- net/dsa/tag_ksz.c | 3 +-- net/dsa/tag_lan9303.c | 2 +- net/dsa/tag_mtk.c | 3 +-- net/dsa/tag_qca.c | 3 +-- net/dsa/tag_trailer.c | 3 +-- 10 files changed, 12 insertions(+), 21 deletions(-) (limited to 'net') diff --git a/include/net/dsa.h b/include/net/dsa.h index 7f46b521313e..398ca8d70ccd 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -104,8 +104,7 @@ struct packet_type; struct dsa_device_ops { struct sk_buff *(*xmit)(struct sk_buff *skb, struct net_device *dev); struct sk_buff *(*rcv)(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev); + struct packet_type *pt); int (*flow_dissect)(const struct sk_buff *skb, __be16 *proto, int *offset); }; @@ -134,8 +133,7 @@ struct dsa_switch_tree { /* Copy of tag_ops->rcv for faster access in hot path */ struct sk_buff * (*rcv)(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev); + struct packet_type *pt); /* * The switch port to which the CPU is attached. diff --git a/net/dsa/dsa.c b/net/dsa/dsa.c index 99e38af85fc5..03c58b0eb082 100644 --- a/net/dsa/dsa.c +++ b/net/dsa/dsa.c @@ -186,7 +186,7 @@ struct net_device *dsa_dev_to_net_device(struct device *dev) EXPORT_SYMBOL_GPL(dsa_dev_to_net_device); static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) + struct packet_type *pt, struct net_device *unused) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct sk_buff *nskb = NULL; @@ -202,7 +202,7 @@ static int dsa_switch_rcv(struct sk_buff *skb, struct net_device *dev, if (!skb) return 0; - nskb = dst->rcv(skb, dev, pt, orig_dev); + nskb = dst->rcv(skb, dev, pt); if (!nskb) { kfree_skb(skb); return 0; diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index c697d9815177..de74c3f77818 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -89,8 +89,7 @@ static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev } static struct sk_buff *brcm_tag_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev) + struct packet_type *pt) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_port *cpu_dp = dsa_get_cpu_port(dst); diff --git a/net/dsa/tag_dsa.c b/net/dsa/tag_dsa.c index 12867a4b458f..fbf9ca954773 100644 --- a/net/dsa/tag_dsa.c +++ b/net/dsa/tag_dsa.c @@ -65,8 +65,7 @@ static struct sk_buff *dsa_xmit(struct sk_buff *skb, struct net_device *dev) } static struct sk_buff *dsa_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev) + struct packet_type *pt) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_switch *ds; diff --git a/net/dsa/tag_edsa.c b/net/dsa/tag_edsa.c index 67a9d26f9075..76367ba1b2e2 100644 --- a/net/dsa/tag_edsa.c +++ b/net/dsa/tag_edsa.c @@ -78,8 +78,7 @@ static struct sk_buff *edsa_xmit(struct sk_buff *skb, struct net_device *dev) } static struct sk_buff *edsa_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev) + struct packet_type *pt) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_switch *ds; diff --git a/net/dsa/tag_ksz.c b/net/dsa/tag_ksz.c index de66ca8e6201..17f30675c15c 100644 --- a/net/dsa/tag_ksz.c +++ b/net/dsa/tag_ksz.c @@ -76,8 +76,7 @@ static struct sk_buff *ksz_xmit(struct sk_buff *skb, struct net_device *dev) } static struct sk_buff *ksz_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev) + struct packet_type *pt) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_port *cpu_dp = dsa_get_cpu_port(dst); diff --git a/net/dsa/tag_lan9303.c b/net/dsa/tag_lan9303.c index e23e7635fa00..0b9826105e42 100644 --- a/net/dsa/tag_lan9303.c +++ b/net/dsa/tag_lan9303.c @@ -68,7 +68,7 @@ static struct sk_buff *lan9303_xmit(struct sk_buff *skb, struct net_device *dev) } static struct sk_buff *lan9303_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, struct net_device *orig_dev) + struct packet_type *pt) { u16 *lan9303_tag; struct dsa_switch_tree *dst = dev->dsa_ptr; diff --git a/net/dsa/tag_mtk.c b/net/dsa/tag_mtk.c index 02163c045a96..ec8ee5f43255 100644 --- a/net/dsa/tag_mtk.c +++ b/net/dsa/tag_mtk.c @@ -44,8 +44,7 @@ static struct sk_buff *mtk_tag_xmit(struct sk_buff *skb, } static struct sk_buff *mtk_tag_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev) + struct packet_type *pt) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_switch *ds; diff --git a/net/dsa/tag_qca.c b/net/dsa/tag_qca.c index 1867a3d11f28..1d4c70711c0f 100644 --- a/net/dsa/tag_qca.c +++ b/net/dsa/tag_qca.c @@ -63,8 +63,7 @@ static struct sk_buff *qca_tag_xmit(struct sk_buff *skb, struct net_device *dev) } static struct sk_buff *qca_tag_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev) + struct packet_type *pt) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_port *cpu_dp = dsa_get_cpu_port(dst); diff --git a/net/dsa/tag_trailer.c b/net/dsa/tag_trailer.c index b09e56214005..8707157dea32 100644 --- a/net/dsa/tag_trailer.c +++ b/net/dsa/tag_trailer.c @@ -56,8 +56,7 @@ static struct sk_buff *trailer_xmit(struct sk_buff *skb, struct net_device *dev) } static struct sk_buff *trailer_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *pt, - struct net_device *orig_dev) + struct packet_type *pt) { struct dsa_switch_tree *dst = dev->dsa_ptr; struct dsa_port *cpu_dp = dsa_get_cpu_port(dst); -- cgit v1.2.3 From 4832c30d5458387ff2533ff66fbde26ad8bb5a2d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 17 Aug 2017 12:17:20 -0700 Subject: net: ipv6: put host and anycast routes on device with address One nagging difference between ipv4 and ipv6 is host routes for ipv6 addresses are installed using the loopback device or VRF / L3 Master device. e.g., 2001:db8:1::/120 dev veth0 proto kernel metric 256 pref medium local 2001:db8:1::1 dev lo table local proto kernel metric 0 pref medium Using the loopback device is convenient -- necessary for local tx, but has some nasty side effects, most notably setting the 'lo' device down causes all host routes for all local IPv6 address to be removed from the FIB and completely breaks IPv6 networking across all interfaces. This patch puts FIB entries for IPv6 routes against the device. This simplifies the routes in the FIB, for example by making dst->dev and rt6i_idev->dev the same (a future patch can look at removing the device reference taken for rt6i_idev for FIB entries). When copies are made on FIB lookups, the cloned route has dst->dev set to loopback (or the L3 master device). This is needed for the local Tx of packets to local addresses. With fib entries allocated against the real network device, the addrconf code that reinserts host routes on admin up of 'lo' is no longer needed. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv6/addrconf.c | 42 ------------------------------------------ net/ipv6/icmp.c | 15 +++++++++++++-- net/ipv6/route.c | 46 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 47 insertions(+), 56 deletions(-) (limited to 'net') diff --git a/net/ipv6/addrconf.c b/net/ipv6/addrconf.c index 640792e1ecb7..45d0a24644de 100644 --- a/net/ipv6/addrconf.c +++ b/net/ipv6/addrconf.c @@ -3030,9 +3030,6 @@ static void sit_add_v4_addrs(struct inet6_dev *idev) static void init_loopback(struct net_device *dev) { struct inet6_dev *idev; - struct net_device *sp_dev; - struct inet6_ifaddr *sp_ifa; - struct rt6_info *sp_rt; /* ::1 */ @@ -3045,45 +3042,6 @@ static void init_loopback(struct net_device *dev) } add_addr(idev, &in6addr_loopback, 128, IFA_HOST); - - /* Add routes to other interface's IPv6 addresses */ - for_each_netdev(dev_net(dev), sp_dev) { - if (!strcmp(sp_dev->name, dev->name)) - continue; - - idev = __in6_dev_get(sp_dev); - if (!idev) - continue; - - read_lock_bh(&idev->lock); - list_for_each_entry(sp_ifa, &idev->addr_list, if_list) { - - if (sp_ifa->flags & (IFA_F_DADFAILED | IFA_F_TENTATIVE)) - continue; - - if (sp_ifa->rt) { - /* This dst has been added to garbage list when - * lo device down, release this obsolete dst and - * reallocate a new router for ifa. - */ - if (!sp_ifa->rt->rt6i_node) { - ip6_rt_put(sp_ifa->rt); - sp_ifa->rt = NULL; - } else { - continue; - } - } - - sp_rt = addrconf_dst_alloc(idev, &sp_ifa->addr, false); - - /* Failure cases are ignored */ - if (!IS_ERR(sp_rt)) { - sp_ifa->rt = sp_rt; - ip6_ins_rt(sp_rt); - } - } - read_unlock_bh(&idev->lock); - } } void addrconf_add_linklocal(struct inet6_dev *idev, diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 8d7b113958b1..4f82830fc068 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -459,9 +459,20 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, * Source addr check */ - if (__ipv6_addr_needs_scope_id(addr_type)) + if (__ipv6_addr_needs_scope_id(addr_type)) { iif = skb->dev->ifindex; - else { + + /* for local packets, get the real device index */ + if (iif == LOOPBACK_IFINDEX) { + dst = skb_dst(skb); + if (dst) { + struct rt6_info *rt; + + rt = container_of(dst, struct rt6_info, dst); + iif = rt->rt6i_idev->dev->ifindex; + } + } + } else { dst = skb_dst(skb); iif = l3mdev_master_ifindex(dst ? dst->dev : skb->dev); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index bec12ae3e6b7..9b02064c3335 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -958,10 +958,34 @@ int ip6_ins_rt(struct rt6_info *rt) return __ip6_ins_rt(rt, &info, &mxc, NULL); } +/* called with rcu_lock held */ +static struct net_device *ip6_rt_get_dev_rcu(struct rt6_info *rt) +{ + struct net_device *dev = rt->dst.dev; + + if (rt->rt6i_flags & RTF_LOCAL) { + /* for copies of local routes, dst->dev needs to be the + * device if it is a master device, the master device if + * device is enslaved, and the loopback as the default + */ + if (netif_is_l3_slave(dev) && + !rt6_need_strict(&rt->rt6i_dst.addr)) + dev = l3mdev_master_dev_rcu(dev); + else if (!netif_is_l3_master(dev)) + dev = dev_net(dev)->loopback_dev; + /* last case is netif_is_l3_master(dev) is true in which + * case we want dev returned to be dev + */ + } + + return dev; +} + static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, const struct in6_addr *daddr, const struct in6_addr *saddr) { + struct net_device *dev; struct rt6_info *rt; /* @@ -971,8 +995,10 @@ static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) ort = (struct rt6_info *)ort->dst.from; - rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0); - + rcu_read_lock(); + dev = ip6_rt_get_dev_rcu(ort); + rt = __ip6_dst_alloc(dev_net(dev), dev, 0); + rcu_read_unlock(); if (!rt) return NULL; @@ -1000,11 +1026,13 @@ static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) { + struct net_device *dev; struct rt6_info *pcpu_rt; - pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev), - rt->dst.dev, rt->dst.flags); - + rcu_read_lock(); + dev = ip6_rt_get_dev_rcu(rt); + pcpu_rt = __ip6_dst_alloc(dev_net(dev), dev, rt->dst.flags); + rcu_read_unlock(); if (!pcpu_rt) return NULL; ip6_rt_copy_init(pcpu_rt, rt); @@ -2688,15 +2716,9 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, { u32 tb_id; struct net *net = dev_net(idev->dev); - struct net_device *dev = net->loopback_dev; + struct net_device *dev = idev->dev; struct rt6_info *rt; - /* use L3 Master device as loopback for host routes if device - * is enslaved and address is not link local or multicast - */ - if (!rt6_need_strict(addr)) - dev = l3mdev_master_dev_rcu(idev->dev) ? : dev; - rt = ip6_dst_alloc(net, dev, DST_NOCOUNT); if (!rt) return ERR_PTR(-ENOMEM); -- cgit v1.2.3 From e65a4955b0bb70ab66e2fbfd5509747fe51d8bf9 Mon Sep 17 00:00:00 2001 From: David Lamparter Date: Fri, 18 Aug 2017 14:31:35 +0200 Subject: net: check type when freeing metadata dst Commit 3fcece12bc1b ("net: store port/representator id in metadata_dst") added a new type field to metadata_dst, but metadata_dst_free() wasn't updated to check it before freeing the METADATA_IP_TUNNEL specific dst cache entry. This is not currently causing problems since it's far enough back in the struct to be zeroed for the only other type currently in existance (METADATA_HW_PORT_MUX), but nevertheless it's not correct. Fixes: 3fcece12bc1b ("net: store port/representator id in metadata_dst") Signed-off-by: David Lamparter Cc: Jakub Kicinski Cc: Sridhar Samudrala Cc: Simon Horman Cc: David S. Miller Signed-off-by: David S. Miller --- net/core/dst.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/dst.c b/net/core/dst.c index d6ead757c258..a6c47da7d0f8 100644 --- a/net/core/dst.c +++ b/net/core/dst.c @@ -299,7 +299,8 @@ EXPORT_SYMBOL_GPL(metadata_dst_alloc); void metadata_dst_free(struct metadata_dst *md_dst) { #ifdef CONFIG_DST_CACHE - dst_cache_destroy(&md_dst->u.tun_info.dst_cache); + if (md_dst->type == METADATA_IP_TUNNEL) + dst_cache_destroy(&md_dst->u.tun_info.dst_cache); #endif kfree(md_dst); } -- cgit v1.2.3 From 40501f90ed5d992176ba504910d512d9dd1b2668 Mon Sep 17 00:00:00 2001 From: Jon Paul Maloy Date: Mon, 21 Aug 2017 17:59:30 +0200 Subject: tipc: don't reset stale broadcast send link When the broadcast send link after 100 attempts has failed to transfer a packet to all peers, we consider it stale, and reset it. Thereafter it needs to re-synchronize with the peers, something currently done by just resetting and re-establishing all links to all peers. This has turned out to be overkill, with potentially unwanted consequences for the remaining cluster. A closer analysis reveals that this can be done much simpler. When this kind of failure happens, for reasons that may lie outside the TIPC protocol, it is typically only one peer which is failing to receive and acknowledge packets. It is hence sufficient to identify and reset the links only to that peer to resolve the situation, without having to reset the broadcast link at all. This solution entails a much lower risk of negative consequences for the own node as well as for the overall cluster. We implement this change in this commit. Reviewed-by: Parthasarathy Bhuvaragan Acked-by: Ying Xue Signed-off-by: Jon Maloy Signed-off-by: David S. Miller --- net/tipc/bearer.c | 24 ------------------------ net/tipc/bearer.h | 1 - net/tipc/link.c | 23 +++++++++++++---------- net/tipc/node.c | 14 ++++---------- 4 files changed, 17 insertions(+), 45 deletions(-) (limited to 'net') diff --git a/net/tipc/bearer.c b/net/tipc/bearer.c index 767e0537dde5..d49598f6002b 100644 --- a/net/tipc/bearer.c +++ b/net/tipc/bearer.c @@ -365,30 +365,6 @@ static int tipc_reset_bearer(struct net *net, struct tipc_bearer *b) return 0; } -/* tipc_bearer_reset_all - reset all links on all bearers - */ -void tipc_bearer_reset_all(struct net *net) -{ - struct tipc_bearer *b; - int i; - - for (i = 0; i < MAX_BEARERS; i++) { - b = bearer_get(net, i); - if (b) - clear_bit_unlock(0, &b->up); - } - for (i = 0; i < MAX_BEARERS; i++) { - b = bearer_get(net, i); - if (b) - tipc_reset_bearer(net, b); - } - for (i = 0; i < MAX_BEARERS; i++) { - b = bearer_get(net, i); - if (b) - test_and_set_bit_lock(0, &b->up); - } -} - /** * bearer_disable * diff --git a/net/tipc/bearer.h b/net/tipc/bearer.h index 635c9086e19a..865cb0901a20 100644 --- a/net/tipc/bearer.h +++ b/net/tipc/bearer.h @@ -210,7 +210,6 @@ void tipc_bearer_remove_dest(struct net *net, u32 bearer_id, u32 dest); struct tipc_bearer *tipc_bearer_find(struct net *net, const char *name); int tipc_bearer_get_name(struct net *net, char *name, u32 bearer_id); struct tipc_media *tipc_media_find(const char *name); -void tipc_bearer_reset_all(struct net *net); int tipc_bearer_setup(void); void tipc_bearer_cleanup(void); void tipc_bearer_stop(struct net *net); diff --git a/net/tipc/link.c b/net/tipc/link.c index 60820dc35a08..ac0144f532aa 100644 --- a/net/tipc/link.c +++ b/net/tipc/link.c @@ -978,15 +978,15 @@ static void link_retransmit_failure(struct tipc_link *l, struct sk_buff *skb) struct tipc_msg *hdr = buf_msg(skb); pr_warn("Retransmission failure on link <%s>\n", l->name); - link_print(l, "Resetting link "); + link_print(l, "State of link "); pr_info("Failed msg: usr %u, typ %u, len %u, err %u\n", msg_user(hdr), msg_type(hdr), msg_size(hdr), msg_errcode(hdr)); pr_info("sqno %u, prev: %x, src: %x\n", msg_seqno(hdr), msg_prevnode(hdr), msg_orignode(hdr)); } -int tipc_link_retrans(struct tipc_link *l, u16 from, u16 to, - struct sk_buff_head *xmitq) +int tipc_link_retrans(struct tipc_link *l, struct tipc_link *nacker, + u16 from, u16 to, struct sk_buff_head *xmitq) { struct sk_buff *_skb, *skb = skb_peek(&l->transmq); struct tipc_msg *hdr; @@ -997,11 +997,14 @@ int tipc_link_retrans(struct tipc_link *l, u16 from, u16 to, return 0; /* Detect repeated retransmit failures on same packet */ - if (likely(l->last_retransm != buf_seqno(skb))) { - l->last_retransm = buf_seqno(skb); - l->stale_count = 1; - } else if (++l->stale_count > 100) { + if (nacker->last_retransm != buf_seqno(skb)) { + nacker->last_retransm = buf_seqno(skb); + nacker->stale_count = 1; + } else if (++nacker->stale_count > 100) { link_retransmit_failure(l, skb); + nacker->stale_count = 0; + if (link_is_bc_sndlink(l)) + return TIPC_LINK_DOWN_EVT; return tipc_link_fsm_evt(l, LINK_FAILURE_EVT); } @@ -1528,7 +1531,7 @@ static int tipc_link_proto_rcv(struct tipc_link *l, struct sk_buff *skb, /* If NACK, retransmit will now start at right position */ if (gap) { - rc = tipc_link_retrans(l, ack + 1, ack + gap, xmitq); + rc = tipc_link_retrans(l, l, ack + 1, ack + gap, xmitq); l->stats.recv_nacks++; } @@ -1680,7 +1683,7 @@ int tipc_link_bc_sync_rcv(struct tipc_link *l, struct tipc_msg *hdr, return rc; if (link_bc_retr_eval(snd_l, &from, &to)) - rc = tipc_link_retrans(snd_l, from, to, xmitq); + rc = tipc_link_retrans(snd_l, l, from, to, xmitq); l->snd_nxt = peers_snd_nxt; if (link_bc_rcv_gap(l)) @@ -1775,7 +1778,7 @@ int tipc_link_bc_nack_rcv(struct tipc_link *l, struct sk_buff *skb, if (dnode == tipc_own_addr(l->net)) { tipc_link_bc_ack_rcv(l, acked, xmitq); - rc = tipc_link_retrans(l->bc_sndlink, from, to, xmitq); + rc = tipc_link_retrans(l->bc_sndlink, l, from, to, xmitq); l->stats.recv_nacks++; return rc; } diff --git a/net/tipc/node.c b/net/tipc/node.c index 9b4dcb6a16b5..eb728397c810 100644 --- a/net/tipc/node.c +++ b/net/tipc/node.c @@ -1284,7 +1284,7 @@ static void tipc_node_bc_sync_rcv(struct tipc_node *n, struct tipc_msg *hdr, rc = tipc_bcast_sync_rcv(n->net, n->bc_entry.link, hdr); if (rc & TIPC_LINK_DOWN_EVT) { - tipc_bearer_reset_all(n->net); + tipc_node_reset_links(n); return; } @@ -1351,15 +1351,9 @@ static void tipc_node_bc_rcv(struct net *net, struct sk_buff *skb, int bearer_id if (!skb_queue_empty(&be->inputq1)) tipc_node_mcast_rcv(n); - if (rc & TIPC_LINK_DOWN_EVT) { - /* Reception reassembly failure => reset all links to peer */ - if (!tipc_link_is_up(be->link)) - tipc_node_reset_links(n); - - /* Retransmission failure => reset all links to all peers */ - if (!tipc_link_is_up(tipc_bc_sndlink(net))) - tipc_bearer_reset_all(net); - } + /* If reassembly or retransmission failure => reset all links to peer */ + if (rc & TIPC_LINK_DOWN_EVT) + tipc_node_reset_links(n); tipc_node_put(n); } -- cgit v1.2.3 From 49c71586a6a94de168a26ec454eb8ea282ddd827 Mon Sep 17 00:00:00 2001 From: Tonghao Zhang Date: Mon, 21 Aug 2017 23:33:48 -0700 Subject: tcp: Get a proper dst before checking it. tcp_peer_is_proven needs a proper route to make the determination, but dst always is NULL. This bug may be there at the beginning of git tree. This does not look serious enough to deserve backports to stable versions. Signed-off-by: Tonghao Zhang Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/tcp_input.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ddc854728a60..ab908949ee95 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6111,6 +6111,10 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, if (tmp_opt.tstamp_ok) tcp_rsk(req)->ts_off = af_ops->init_ts_off(net, skb); + dst = af_ops->route_req(sk, &fl, req); + if (!dst) + goto drop_and_free; + if (!want_cookie && !isn) { /* Kill the following clause, if you dislike this way. */ if (!net->ipv4.sysctl_tcp_syncookies && @@ -6131,11 +6135,6 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, isn = af_ops->init_seq(skb); } - if (!dst) { - dst = af_ops->route_req(sk, &fl, req); - if (!dst) - goto drop_and_free; - } tcp_ecn_create_request(req, skb, sk, dst); -- cgit v1.2.3 From 111993692741a7044e6c01b428cecf1071de3d0b Mon Sep 17 00:00:00 2001 From: Tonghao Zhang Date: Mon, 21 Aug 2017 23:33:49 -0700 Subject: tcp: Remove the unused parameter for tcp_try_fastopen. Signed-off-by: Tonghao Zhang Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/net/tcp.h | 3 +-- net/ipv4/tcp_fastopen.c | 6 ++---- net/ipv4/tcp_input.c | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/tcp.h b/include/net/tcp.h index afdab3781425..a995004ae946 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -1533,8 +1533,7 @@ int tcp_fastopen_reset_cipher(void *key, unsigned int len); void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb); struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, struct request_sock *req, - struct tcp_fastopen_cookie *foc, - struct dst_entry *dst); + struct tcp_fastopen_cookie *foc); void tcp_fastopen_init_key_once(bool publish); bool tcp_fastopen_cookie_check(struct sock *sk, u16 *mss, struct tcp_fastopen_cookie *cookie); diff --git a/net/ipv4/tcp_fastopen.c b/net/ipv4/tcp_fastopen.c index ce9c7fef200f..e3c33220c418 100644 --- a/net/ipv4/tcp_fastopen.c +++ b/net/ipv4/tcp_fastopen.c @@ -171,7 +171,6 @@ void tcp_fastopen_add_skb(struct sock *sk, struct sk_buff *skb) static struct sock *tcp_fastopen_create_child(struct sock *sk, struct sk_buff *skb, - struct dst_entry *dst, struct request_sock *req) { struct tcp_sock *tp; @@ -278,8 +277,7 @@ static bool tcp_fastopen_queue_check(struct sock *sk) */ struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, struct request_sock *req, - struct tcp_fastopen_cookie *foc, - struct dst_entry *dst) + struct tcp_fastopen_cookie *foc) { struct tcp_fastopen_cookie valid_foc = { .len = -1 }; bool syn_data = TCP_SKB_CB(skb)->end_seq != TCP_SKB_CB(skb)->seq + 1; @@ -312,7 +310,7 @@ struct sock *tcp_try_fastopen(struct sock *sk, struct sk_buff *skb, * data in SYN_RECV state. */ fastopen: - child = tcp_fastopen_create_child(sk, skb, dst, req); + child = tcp_fastopen_create_child(sk, skb, req); if (child) { foc->len = -1; NET_INC_STATS(sock_net(sk), diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index ab908949ee95..d3421ee9a10a 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6150,7 +6150,7 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, tcp_openreq_init_rwin(req, sk, dst); if (!want_cookie) { tcp_reqsk_record_syn(sk, req, skb); - fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc, dst); + fastopen_sk = tcp_try_fastopen(sk, skb, req, &foc); } if (fastopen_sk) { af_ops->send_synack(fastopen_sk, dst, &fl, req, -- cgit v1.2.3 From ab2fb7e3240d24c68f854aa1b972fe415d8d1b3e Mon Sep 17 00:00:00 2001 From: Willem de Bruijn Date: Tue, 22 Aug 2017 11:39:57 -0400 Subject: udp: remove unreachable ufo branches Remove two references to ufo in the udp send path that are no longer reachable now that ufo has been removed. Commit 85f1bd9a7b5a ("udp: consistently apply ufo or fragmentation") is a fix to ufo. It is safe to revert what remains of it. Also, no skb can enter ip_append_page with skb_is_gso true now that skb_shinfo(skb)->gso_type is no longer set in ip_append_page/_data. Signed-off-by: Willem de Bruijn Signed-off-by: David S. Miller --- net/ipv4/ip_output.c | 12 ++++-------- net/ipv4/udp.c | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_output.c b/net/ipv4/ip_output.c index 73b0b15245b6..e8e675be60ec 100644 --- a/net/ipv4/ip_output.c +++ b/net/ipv4/ip_output.c @@ -1223,15 +1223,11 @@ ssize_t ip_append_page(struct sock *sk, struct flowi4 *fl4, struct page *page, cork->length += size; while (size > 0) { - if (skb_is_gso(skb)) { - len = size; - } else { + /* Check if the remaining data fits into current packet. */ + len = mtu - skb->len; + if (len < size) + len = maxfraglen - skb->len; - /* Check if the remaining data fits into current packet. */ - len = mtu - skb->len; - if (len < size) - len = maxfraglen - skb->len; - } if (len <= 0) { struct sk_buff *skb_prev; int alloclen; diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 25fb14490d6a..bf6c406bf5e7 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -809,7 +809,7 @@ static int udp_send_skb(struct sk_buff *skb, struct flowi4 *fl4) if (is_udplite) /* UDP-Lite */ csum = udplite_csum(skb); - else if (sk->sk_no_check_tx && !skb_is_gso(skb)) { /* UDP csum off */ + else if (sk->sk_no_check_tx) { /* UDP csum off */ skb->ip_summed = CHECKSUM_NONE; goto send; -- cgit v1.2.3 From 84e54fe0a5eaed696dee4019c396f8396f5a908b Mon Sep 17 00:00:00 2001 From: William Tu Date: Tue, 22 Aug 2017 09:40:28 -0700 Subject: gre: introduce native tunnel support for ERSPAN The patch adds ERSPAN type II tunnel support. The implementation is based on the draft at [1]. One of the purposes is for Linux box to be able to receive ERSPAN monitoring traffic sent from the Cisco switch, by creating a ERSPAN tunnel device. In addition, the patch also adds ERSPAN TX, so Linux virtual switch can redirect monitored traffic to the ERSPAN tunnel device. The traffic will be encapsulated into ERSPAN and sent out. The implementation reuses tunnel key as ERSPAN session ID, and field 'erspan' as ERSPAN Index fields: ./ip link add dev ers11 type erspan seq key 100 erspan 123 \ local 172.16.1.200 remote 172.16.1.100 To use the above device as ERSPAN receiver, configure Nexus 5000 switch as below: monitor session 100 type erspan-source erspan-id 123 vrf default destination ip 172.16.1.200 source interface Ethernet1/11 both source interface Ethernet1/12 both no shut monitor erspan origin ip-address 172.16.1.100 global [1] https://tools.ietf.org/html/draft-foschiano-erspan-01 [2] iproute2 patch: http://marc.info/?l=linux-netdev&m=150306086924951&w=2 [3] test script: http://marc.info/?l=linux-netdev&m=150231021807304&w=2 Signed-off-by: William Tu Signed-off-by: Meenakshi Vohra Cc: Alexey Kuznetsov Cc: Hideaki YOSHIFUJI Signed-off-by: David S. Miller --- include/net/erspan.h | 61 ++++++++++ include/net/ip_tunnels.h | 3 + include/uapi/linux/if_ether.h | 1 + include/uapi/linux/if_tunnel.h | 1 + net/ipv4/ip_gre.c | 269 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 335 insertions(+) create mode 100644 include/net/erspan.h (limited to 'net') diff --git a/include/net/erspan.h b/include/net/erspan.h new file mode 100644 index 000000000000..ca94fc86865e --- /dev/null +++ b/include/net/erspan.h @@ -0,0 +1,61 @@ +#ifndef __LINUX_ERSPAN_H +#define __LINUX_ERSPAN_H + +/* + * GRE header for ERSPAN encapsulation (8 octets [34:41]) -- 8 bytes + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |0|0|0|1|0|00000|000000000|00000| Protocol Type for ERSPAN | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Sequence Number (increments per packet per session) | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * Note that in the above GRE header [RFC1701] out of the C, R, K, S, + * s, Recur, Flags, Version fields only S (bit 03) is set to 1. The + * other fields are set to zero, so only a sequence number follows. + * + * ERSPAN Type II header (8 octets [42:49]) + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Ver | VLAN | COS | En|T| Session ID | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Reserved | Index | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * + * GRE proto ERSPAN type II = 0x88BE, type III = 0x22EB + */ + +#define ERSPAN_VERSION 0x1 + +#define VER_MASK 0xf000 +#define VLAN_MASK 0x0fff +#define COS_MASK 0xe000 +#define EN_MASK 0x1800 +#define T_MASK 0x0400 +#define ID_MASK 0x03ff +#define INDEX_MASK 0xfffff + +enum erspan_encap_type { + ERSPAN_ENCAP_NOVLAN = 0x0, /* originally without VLAN tag */ + ERSPAN_ENCAP_ISL = 0x1, /* originally ISL encapsulated */ + ERSPAN_ENCAP_8021Q = 0x2, /* originally 802.1Q encapsulated */ + ERSPAN_ENCAP_INFRAME = 0x3, /* VLAN tag perserved in frame */ +}; + +struct erspan_metadata { + __be32 index; /* type II */ +}; + +struct erspanhdr { + __be16 ver_vlan; +#define VER_OFFSET 12 + __be16 session_id; +#define COS_OFFSET 13 +#define EN_OFFSET 11 +#define T_OFFSET 10 + struct erspan_metadata md; +}; + +#endif diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 520809912f03..625c29329372 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -115,6 +115,9 @@ struct ip_tunnel { u32 o_seqno; /* The last output seqno */ int tun_hlen; /* Precalculated header length */ + /* This field used only by ERSPAN */ + u32 index; /* ERSPAN type II index */ + struct dst_cache dst_cache; struct ip_tunnel_parm parms; diff --git a/include/uapi/linux/if_ether.h b/include/uapi/linux/if_ether.h index 5bc9bfd816b7..efeb1190c2ca 100644 --- a/include/uapi/linux/if_ether.h +++ b/include/uapi/linux/if_ether.h @@ -66,6 +66,7 @@ #define ETH_P_ATALK 0x809B /* Appletalk DDP */ #define ETH_P_AARP 0x80F3 /* Appletalk AARP */ #define ETH_P_8021Q 0x8100 /* 802.1Q VLAN Extended Header */ +#define ETH_P_ERSPAN 0x88BE /* ERSPAN type II */ #define ETH_P_IPX 0x8137 /* IPX over DIX */ #define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */ #define ETH_P_PAUSE 0x8808 /* IEEE Pause frames. See 802.3 31B */ diff --git a/include/uapi/linux/if_tunnel.h b/include/uapi/linux/if_tunnel.h index 6792d1967d31..2e520883c054 100644 --- a/include/uapi/linux/if_tunnel.h +++ b/include/uapi/linux/if_tunnel.h @@ -134,6 +134,7 @@ enum { IFLA_GRE_COLLECT_METADATA, IFLA_GRE_IGNORE_DF, IFLA_GRE_FWMARK, + IFLA_GRE_ERSPAN_INDEX, __IFLA_GRE_MAX, }; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 7a7829e839c2..6e8a62289e03 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -48,6 +48,7 @@ #include #include #include +#include /* Problems & solutions @@ -115,6 +116,7 @@ static int ipgre_tunnel_init(struct net_device *dev); static unsigned int ipgre_net_id __read_mostly; static unsigned int gre_tap_net_id __read_mostly; +static unsigned int erspan_net_id __read_mostly; static void ipgre_err(struct sk_buff *skb, u32 info, const struct tnl_ptk_info *tpi) @@ -246,6 +248,56 @@ static void gre_err(struct sk_buff *skb, u32 info) ipgre_err(skb, info, &tpi); } +static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, + int gre_hdr_len) +{ + struct net *net = dev_net(skb->dev); + struct metadata_dst *tun_dst = NULL; + struct ip_tunnel_net *itn; + struct ip_tunnel *tunnel; + struct erspanhdr *ershdr; + const struct iphdr *iph; + __be32 session_id; + __be32 index; + int len; + + itn = net_generic(net, erspan_net_id); + iph = ip_hdr(skb); + len = gre_hdr_len + sizeof(*ershdr); + + if (unlikely(!pskb_may_pull(skb, len))) + return -ENOMEM; + + iph = ip_hdr(skb); + ershdr = (struct erspanhdr *)(skb->data + gre_hdr_len); + + /* The original GRE header does not have key field, + * Use ERSPAN 10-bit session ID as key. + */ + session_id = cpu_to_be32(ntohs(ershdr->session_id)); + tpi->key = session_id; + index = ershdr->md.index; + tunnel = ip_tunnel_lookup(itn, skb->dev->ifindex, + tpi->flags | TUNNEL_KEY, + iph->saddr, iph->daddr, tpi->key); + + if (tunnel) { + if (__iptunnel_pull_header(skb, + gre_hdr_len + sizeof(*ershdr), + htons(ETH_P_TEB), + false, false) < 0) + goto drop; + + tunnel->index = ntohl(index); + skb_reset_mac_header(skb); + ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error); + return PACKET_RCVD; + } +drop: + kfree_skb(skb); + return PACKET_RCVD; +} + static int __ipgre_rcv(struct sk_buff *skb, const struct tnl_ptk_info *tpi, struct ip_tunnel_net *itn, int hdr_len, bool raw_proto) { @@ -328,6 +380,11 @@ static int gre_rcv(struct sk_buff *skb) if (hdr_len < 0) goto drop; + if (unlikely(tpi.proto == htons(ETH_P_ERSPAN))) { + if (erspan_rcv(skb, &tpi, hdr_len) == PACKET_RCVD) + return 0; + } + if (ipgre_rcv(skb, &tpi, hdr_len) == PACKET_RCVD) return 0; @@ -503,6 +560,81 @@ free_skb: return NETDEV_TX_OK; } +static inline u8 tos_to_cos(u8 tos) +{ + u8 dscp, cos; + + dscp = tos >> 2; + cos = dscp >> 3; + return cos; +} + +static void erspan_build_header(struct sk_buff *skb, + __be32 id, u32 index, bool truncate) +{ + struct iphdr *iphdr = ip_hdr(skb); + struct ethhdr *eth = eth_hdr(skb); + enum erspan_encap_type enc_type; + struct erspanhdr *ershdr; + struct qtag_prefix { + __be16 eth_type; + __be16 tci; + } *qp; + u16 vlan_tci = 0; + + enc_type = ERSPAN_ENCAP_NOVLAN; + + /* If mirrored packet has vlan tag, extract tci and + * perserve vlan header in the mirrored frame. + */ + if (eth->h_proto == htons(ETH_P_8021Q)) { + qp = (struct qtag_prefix *)(skb->data + 2 * ETH_ALEN); + vlan_tci = ntohs(qp->tci); + enc_type = ERSPAN_ENCAP_INFRAME; + } + + skb_push(skb, sizeof(*ershdr)); + ershdr = (struct erspanhdr *)skb->data; + memset(ershdr, 0, sizeof(*ershdr)); + + ershdr->ver_vlan = htons((vlan_tci & VLAN_MASK) | + (ERSPAN_VERSION << VER_OFFSET)); + ershdr->session_id = htons((u16)(ntohl(id) & ID_MASK) | + ((tos_to_cos(iphdr->tos) << COS_OFFSET) & COS_MASK) | + (enc_type << EN_OFFSET & EN_MASK) | + ((truncate << T_OFFSET) & T_MASK)); + ershdr->md.index = htonl(index & INDEX_MASK); +} + +static netdev_tx_t erspan_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct ip_tunnel *tunnel = netdev_priv(dev); + bool truncate = false; + + if (gre_handle_offloads(skb, false)) + goto free_skb; + + if (skb_cow_head(skb, dev->needed_headroom)) + goto free_skb; + + if (skb->len > dev->mtu) { + pskb_trim(skb, dev->mtu); + truncate = true; + } + + /* Push ERSPAN header */ + erspan_build_header(skb, tunnel->parms.o_key, tunnel->index, truncate); + tunnel->parms.o_flags &= ~TUNNEL_KEY; + __gre_xmit(skb, dev, &tunnel->parms.iph, htons(ETH_P_ERSPAN)); + return NETDEV_TX_OK; + +free_skb: + kfree_skb(skb); + dev->stats.tx_dropped++; + return NETDEV_TX_OK; +} + static netdev_tx_t gre_tap_xmit(struct sk_buff *skb, struct net_device *dev) { @@ -828,6 +960,39 @@ out: return ipgre_tunnel_validate(tb, data, extack); } +static int erspan_validate(struct nlattr *tb[], struct nlattr *data[], + struct netlink_ext_ack *extack) +{ + __be16 flags = 0; + int ret; + + if (!data) + return 0; + + ret = ipgre_tap_validate(tb, data, extack); + if (ret) + return ret; + + /* ERSPAN should only have GRE sequence and key flag */ + flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]); + flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]); + if (flags != (GRE_SEQ | GRE_KEY)) + return -EINVAL; + + /* ERSPAN Session ID only has 10-bit. Since we reuse + * 32-bit key field as ID, check it's range. + */ + if (data[IFLA_GRE_IKEY] && + (ntohl(nla_get_be32(data[IFLA_GRE_IKEY])) & ~ID_MASK)) + return -EINVAL; + + if (data[IFLA_GRE_OKEY] && + (ntohl(nla_get_be32(data[IFLA_GRE_OKEY])) & ~ID_MASK)) + return -EINVAL; + + return 0; +} + static int ipgre_netlink_parms(struct net_device *dev, struct nlattr *data[], struct nlattr *tb[], @@ -892,6 +1057,13 @@ static int ipgre_netlink_parms(struct net_device *dev, if (data[IFLA_GRE_FWMARK]) *fwmark = nla_get_u32(data[IFLA_GRE_FWMARK]); + if (data[IFLA_GRE_ERSPAN_INDEX]) { + t->index = nla_get_u32(data[IFLA_GRE_ERSPAN_INDEX]); + + if (t->index & ~INDEX_MASK) + return -EINVAL; + } + return 0; } @@ -949,6 +1121,36 @@ static const struct net_device_ops gre_tap_netdev_ops = { .ndo_fill_metadata_dst = gre_fill_metadata_dst, }; +static int erspan_tunnel_init(struct net_device *dev) +{ + struct ip_tunnel *tunnel = netdev_priv(dev); + int t_hlen; + + tunnel->tun_hlen = 8; + tunnel->parms.iph.protocol = IPPROTO_GRE; + t_hlen = tunnel->hlen + sizeof(struct iphdr) + sizeof(struct erspanhdr); + + dev->needed_headroom = LL_MAX_HEADER + t_hlen + 4; + dev->mtu = ETH_DATA_LEN - t_hlen - 4; + dev->features |= GRE_FEATURES; + dev->hw_features |= GRE_FEATURES; + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + + return ip_tunnel_init(dev); +} + +static const struct net_device_ops erspan_netdev_ops = { + .ndo_init = erspan_tunnel_init, + .ndo_uninit = ip_tunnel_uninit, + .ndo_start_xmit = erspan_xmit, + .ndo_set_mac_address = eth_mac_addr, + .ndo_validate_addr = eth_validate_addr, + .ndo_change_mtu = ip_tunnel_change_mtu, + .ndo_get_stats64 = ip_tunnel_get_stats64, + .ndo_get_iflink = ip_tunnel_get_iflink, + .ndo_fill_metadata_dst = gre_fill_metadata_dst, +}; + static void ipgre_tap_setup(struct net_device *dev) { ether_setup(dev); @@ -1041,6 +1243,8 @@ static size_t ipgre_get_size(const struct net_device *dev) nla_total_size(1) + /* IFLA_GRE_FWMARK */ nla_total_size(4) + + /* IFLA_GRE_ERSPAN_INDEX */ + nla_total_size(4) + 0; } @@ -1083,12 +1287,25 @@ static int ipgre_fill_info(struct sk_buff *skb, const struct net_device *dev) goto nla_put_failure; } + if (t->index) + if (nla_put_u32(skb, IFLA_GRE_ERSPAN_INDEX, t->index)) + goto nla_put_failure; + return 0; nla_put_failure: return -EMSGSIZE; } +static void erspan_setup(struct net_device *dev) +{ + ether_setup(dev); + dev->netdev_ops = &erspan_netdev_ops; + dev->priv_flags &= ~IFF_TX_SKB_SHARING; + dev->priv_flags |= IFF_LIVE_ADDR_CHANGE; + ip_tunnel_setup(dev, erspan_net_id); +} + static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_LINK] = { .type = NLA_U32 }, [IFLA_GRE_IFLAGS] = { .type = NLA_U16 }, @@ -1107,6 +1324,7 @@ static const struct nla_policy ipgre_policy[IFLA_GRE_MAX + 1] = { [IFLA_GRE_COLLECT_METADATA] = { .type = NLA_FLAG }, [IFLA_GRE_IGNORE_DF] = { .type = NLA_U8 }, [IFLA_GRE_FWMARK] = { .type = NLA_U32 }, + [IFLA_GRE_ERSPAN_INDEX] = { .type = NLA_U32 }, }; static struct rtnl_link_ops ipgre_link_ops __read_mostly = { @@ -1139,6 +1357,21 @@ static struct rtnl_link_ops ipgre_tap_ops __read_mostly = { .get_link_net = ip_tunnel_get_link_net, }; +static struct rtnl_link_ops erspan_link_ops __read_mostly = { + .kind = "erspan", + .maxtype = IFLA_GRE_MAX, + .policy = ipgre_policy, + .priv_size = sizeof(struct ip_tunnel), + .setup = erspan_setup, + .validate = erspan_validate, + .newlink = ipgre_newlink, + .changelink = ipgre_changelink, + .dellink = ip_tunnel_dellink, + .get_size = ipgre_get_size, + .fill_info = ipgre_fill_info, + .get_link_net = ip_tunnel_get_link_net, +}; + struct net_device *gretap_fb_dev_create(struct net *net, const char *name, u8 name_assign_type) { @@ -1202,6 +1435,26 @@ static struct pernet_operations ipgre_tap_net_ops = { .size = sizeof(struct ip_tunnel_net), }; +static int __net_init erspan_init_net(struct net *net) +{ + return ip_tunnel_init_net(net, erspan_net_id, + &erspan_link_ops, "erspan0"); +} + +static void __net_exit erspan_exit_net(struct net *net) +{ + struct ip_tunnel_net *itn = net_generic(net, erspan_net_id); + + ip_tunnel_delete_net(itn, &erspan_link_ops); +} + +static struct pernet_operations erspan_net_ops = { + .init = erspan_init_net, + .exit = erspan_exit_net, + .id = &erspan_net_id, + .size = sizeof(struct ip_tunnel_net), +}; + static int __init ipgre_init(void) { int err; @@ -1216,6 +1469,10 @@ static int __init ipgre_init(void) if (err < 0) goto pnet_tap_faied; + err = register_pernet_device(&erspan_net_ops); + if (err < 0) + goto pnet_erspan_failed; + err = gre_add_protocol(&ipgre_protocol, GREPROTO_CISCO); if (err < 0) { pr_info("%s: can't add protocol\n", __func__); @@ -1230,13 +1487,21 @@ static int __init ipgre_init(void) if (err < 0) goto tap_ops_failed; + err = rtnl_link_register(&erspan_link_ops); + if (err < 0) + goto erspan_link_failed; + return 0; +erspan_link_failed: + rtnl_link_unregister(&ipgre_tap_ops); tap_ops_failed: rtnl_link_unregister(&ipgre_link_ops); rtnl_link_failed: gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); add_proto_failed: + unregister_pernet_device(&erspan_net_ops); +pnet_erspan_failed: unregister_pernet_device(&ipgre_tap_net_ops); pnet_tap_faied: unregister_pernet_device(&ipgre_net_ops); @@ -1247,9 +1512,11 @@ static void __exit ipgre_fini(void) { rtnl_link_unregister(&ipgre_tap_ops); rtnl_link_unregister(&ipgre_link_ops); + rtnl_link_unregister(&erspan_link_ops); gre_del_protocol(&ipgre_protocol, GREPROTO_CISCO); unregister_pernet_device(&ipgre_tap_net_ops); unregister_pernet_device(&ipgre_net_ops); + unregister_pernet_device(&erspan_net_ops); } module_init(ipgre_init); @@ -1257,5 +1524,7 @@ module_exit(ipgre_fini); MODULE_LICENSE("GPL"); MODULE_ALIAS_RTNL_LINK("gre"); MODULE_ALIAS_RTNL_LINK("gretap"); +MODULE_ALIAS_RTNL_LINK("erspan"); MODULE_ALIAS_NETDEV("gre0"); MODULE_ALIAS_NETDEV("gretap0"); +MODULE_ALIAS_NETDEV("erspan0"); -- cgit v1.2.3 From 9695fe6f21e3b06a7086f62851c0df16369fc223 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 22 Aug 2017 12:26:46 -0700 Subject: net: sched: use kvmalloc() for class hash tables High order GFP_KERNEL allocations can stress the host badly. Use modern kvmalloc_array()/kvfree() instead of custom allocations. Signed-off-by: Eric Dumazet Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/sched/sch_api.c | 22 ++++------------------ 1 file changed, 4 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 0fea0c50b763..aaf552b8e120 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -621,14 +621,10 @@ EXPORT_SYMBOL(qdisc_watchdog_cancel); static struct hlist_head *qdisc_class_hash_alloc(unsigned int n) { - unsigned int size = n * sizeof(struct hlist_head), i; struct hlist_head *h; + unsigned int i; - if (size <= PAGE_SIZE) - h = kmalloc(size, GFP_KERNEL); - else - h = (struct hlist_head *) - __get_free_pages(GFP_KERNEL, get_order(size)); + h = kvmalloc_array(n, sizeof(struct hlist_head), GFP_KERNEL); if (h != NULL) { for (i = 0; i < n; i++) @@ -637,16 +633,6 @@ static struct hlist_head *qdisc_class_hash_alloc(unsigned int n) return h; } -static void qdisc_class_hash_free(struct hlist_head *h, unsigned int n) -{ - unsigned int size = n * sizeof(struct hlist_head); - - if (size <= PAGE_SIZE) - kfree(h); - else - free_pages((unsigned long)h, get_order(size)); -} - void qdisc_class_hash_grow(struct Qdisc *sch, struct Qdisc_class_hash *clhash) { struct Qdisc_class_common *cl; @@ -679,7 +665,7 @@ void qdisc_class_hash_grow(struct Qdisc *sch, struct Qdisc_class_hash *clhash) clhash->hashmask = nmask; sch_tree_unlock(sch); - qdisc_class_hash_free(ohash, osize); + kvfree(ohash); } EXPORT_SYMBOL(qdisc_class_hash_grow); @@ -699,7 +685,7 @@ EXPORT_SYMBOL(qdisc_class_hash_init); void qdisc_class_hash_destroy(struct Qdisc_class_hash *clhash) { - qdisc_class_hash_free(clhash->hash, clhash->hashsize); + kvfree(clhash->hash); } EXPORT_SYMBOL(qdisc_class_hash_destroy); -- cgit v1.2.3 From e4a8e817d3cb2a5108f8bb2e47e81eb25a2c5e30 Mon Sep 17 00:00:00 2001 From: Daniel Borkmann Date: Wed, 23 Aug 2017 01:47:53 +0200 Subject: bpf: misc xdp redirect cleanups Few cleanups including: bpf_redirect_map() is really XDP only due to the return code. Move it to a more appropriate location where we do the XDP redirect handling and change it's name into bpf_xdp_redirect_map() to make it consistent to the bpf_xdp_redirect() helper. xdp_do_redirect_map() helper can be static since only used out of filter.c file. Drop the goto in xdp_do_generic_redirect() and only return errors directly. In xdp_do_flush_map() only clear ri->map_to_flush which is the arg we're using in that function, ri->map is cleared earlier along with ri->ifindex. Signed-off-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/core/filter.c | 72 +++++++++++++++++++++++++------------------------------ 1 file changed, 32 insertions(+), 40 deletions(-) (limited to 'net') diff --git a/net/core/filter.c b/net/core/filter.c index fa2115695037..2a0d762a20d8 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -1835,29 +1835,6 @@ static const struct bpf_func_proto bpf_redirect_proto = { .arg2_type = ARG_ANYTHING, }; -BPF_CALL_3(bpf_redirect_map, struct bpf_map *, map, u32, ifindex, u64, flags) -{ - struct redirect_info *ri = this_cpu_ptr(&redirect_info); - - if (unlikely(flags)) - return XDP_ABORTED; - - ri->ifindex = ifindex; - ri->flags = flags; - ri->map = map; - - return XDP_REDIRECT; -} - -static const struct bpf_func_proto bpf_redirect_map_proto = { - .func = bpf_redirect_map, - .gpl_only = false, - .ret_type = RET_INTEGER, - .arg1_type = ARG_CONST_MAP_PTR, - .arg2_type = ARG_ANYTHING, - .arg3_type = ARG_ANYTHING, -}; - BPF_CALL_3(bpf_sk_redirect_map, struct bpf_map *, map, u32, key, u64, flags) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); @@ -2506,13 +2483,11 @@ static int __bpf_tx_xdp(struct net_device *dev, err = dev->netdev_ops->ndo_xdp_xmit(dev, xdp); if (err) return err; - if (map) __dev_map_insert_ctx(map, index); else dev->netdev_ops->ndo_xdp_flush(dev); - - return err; + return 0; } void xdp_do_flush_map(void) @@ -2520,16 +2495,14 @@ void xdp_do_flush_map(void) struct redirect_info *ri = this_cpu_ptr(&redirect_info); struct bpf_map *map = ri->map_to_flush; - ri->map = NULL; ri->map_to_flush = NULL; - if (map) __dev_map_flush(map); } EXPORT_SYMBOL_GPL(xdp_do_flush_map); -int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, - struct bpf_prog *xdp_prog) +static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, + struct bpf_prog *xdp_prog) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); struct bpf_map *map = ri->map; @@ -2545,14 +2518,12 @@ int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, err = -EINVAL; goto out; } - - if (ri->map_to_flush && (ri->map_to_flush != map)) + if (ri->map_to_flush && ri->map_to_flush != map) xdp_do_flush_map(); err = __bpf_tx_xdp(fwd, map, xdp, index); if (likely(!err)) ri->map_to_flush = map; - out: trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT, err); return err; @@ -2594,20 +2565,17 @@ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb) ri->ifindex = 0; if (unlikely(!dev)) { bpf_warn_invalid_xdp_redirect(index); - goto err; + return -EINVAL; } if (unlikely(!(dev->flags & IFF_UP))) - goto err; - + return -ENETDOWN; len = dev->mtu + dev->hard_header_len + VLAN_HLEN; if (skb->len > len) - goto err; + return -E2BIG; skb->dev = dev; return 0; -err: - return -EINVAL; } EXPORT_SYMBOL_GPL(xdp_do_generic_redirect); @@ -2620,6 +2588,7 @@ BPF_CALL_2(bpf_xdp_redirect, u32, ifindex, u64, flags) ri->ifindex = ifindex; ri->flags = flags; + return XDP_REDIRECT; } @@ -2631,6 +2600,29 @@ static const struct bpf_func_proto bpf_xdp_redirect_proto = { .arg2_type = ARG_ANYTHING, }; +BPF_CALL_3(bpf_xdp_redirect_map, struct bpf_map *, map, u32, ifindex, u64, flags) +{ + struct redirect_info *ri = this_cpu_ptr(&redirect_info); + + if (unlikely(flags)) + return XDP_ABORTED; + + ri->ifindex = ifindex; + ri->flags = flags; + ri->map = map; + + return XDP_REDIRECT; +} + +static const struct bpf_func_proto bpf_xdp_redirect_map_proto = { + .func = bpf_xdp_redirect_map, + .gpl_only = false, + .ret_type = RET_INTEGER, + .arg1_type = ARG_CONST_MAP_PTR, + .arg2_type = ARG_ANYTHING, + .arg3_type = ARG_ANYTHING, +}; + bool bpf_helper_changes_pkt_data(void *func) { if (func == bpf_skb_vlan_push || @@ -3233,7 +3225,7 @@ xdp_func_proto(enum bpf_func_id func_id) case BPF_FUNC_redirect: return &bpf_xdp_redirect_proto; case BPF_FUNC_redirect_map: - return &bpf_redirect_map_proto; + return &bpf_xdp_redirect_map_proto; default: return bpf_base_func_proto(func_id); } -- cgit v1.2.3 From e3d0328c76dde0b957f62f8c407b79f1d8fe3ef8 Mon Sep 17 00:00:00 2001 From: William Tu Date: Tue, 22 Aug 2017 17:04:05 -0700 Subject: gre: fix goto statement typo Fix typo: pnet_tap_faied. Signed-off-by: William Tu Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 6e8a62289e03..5a20ba9b9b50 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -1467,7 +1467,7 @@ static int __init ipgre_init(void) err = register_pernet_device(&ipgre_tap_net_ops); if (err < 0) - goto pnet_tap_faied; + goto pnet_tap_failed; err = register_pernet_device(&erspan_net_ops); if (err < 0) @@ -1503,7 +1503,7 @@ add_proto_failed: unregister_pernet_device(&erspan_net_ops); pnet_erspan_failed: unregister_pernet_device(&ipgre_tap_net_ops); -pnet_tap_faied: +pnet_tap_failed: unregister_pernet_device(&ipgre_net_ops); return err; } -- cgit v1.2.3 From 98aaa913b4ed250324429f0a9e6d5f77a3b5276c Mon Sep 17 00:00:00 2001 From: Mike Maloney Date: Tue, 22 Aug 2017 17:08:48 -0400 Subject: tcp: Extend SOF_TIMESTAMPING_RX_SOFTWARE to TCP recvmsg When SOF_TIMESTAMPING_RX_SOFTWARE is enabled for tcp sockets, return the timestamp corresponding to the highest sequence number data returned. Previously the skb->tstamp is overwritten when a TCP packet is placed in the out of order queue. While the packet is in the ooo queue, save the timestamp in the TCB_SKB_CB. This space is shared with the gso_* options which are only used on the tx path, and a previously unused 4 byte hole. When skbs are coalesced either in the sk_receive_queue or the out_of_order_queue always choose the timestamp of the appended skb to maintain the invariant of returning the timestamp of the last byte in the recvmsg buffer. Signed-off-by: Mike Maloney Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- include/net/tcp.h | 9 +++++++- net/ipv4/tcp.c | 65 ++++++++++++++++++++++++++++++++++++++++++++++++++++ net/ipv4/tcp_input.c | 35 ++++++++++++++++++++++++---- net/ipv4/tcp_ipv4.c | 2 ++ net/ipv6/tcp_ipv6.c | 2 ++ 5 files changed, 108 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/tcp.h b/include/net/tcp.h index a995004ae946..c614ff135b66 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -774,6 +774,12 @@ struct tcp_skb_cb { u16 tcp_gso_segs; u16 tcp_gso_size; }; + + /* Used to stash the receive timestamp while this skb is in the + * out of order queue, as skb->tstamp is overwritten by the + * rbnode. + */ + ktime_t swtstamp; }; __u8 tcp_flags; /* TCP header flags. (tcp[13]) */ @@ -790,7 +796,8 @@ struct tcp_skb_cb { __u8 ip_dsfield; /* IPv4 tos or IPv6 dsfield */ __u8 txstamp_ack:1, /* Record TX timestamp for ack? */ eor:1, /* Is skb MSG_EOR marked? */ - unused:6; + has_rxtstamp:1, /* SKB has a RX timestamp */ + unused:5; __u32 ack_seq; /* Sequence number ACK'd */ union { struct { diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index d25e3bcca66b..0cce4472b4a1 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -269,6 +269,7 @@ #include #include #include +#include #include #include @@ -1695,6 +1696,61 @@ int tcp_peek_len(struct socket *sock) } EXPORT_SYMBOL(tcp_peek_len); +static void tcp_update_recv_tstamps(struct sk_buff *skb, + struct scm_timestamping *tss) +{ + if (skb->tstamp) + tss->ts[0] = ktime_to_timespec(skb->tstamp); + else + tss->ts[0] = (struct timespec) {0}; + + if (skb_hwtstamps(skb)->hwtstamp) + tss->ts[2] = ktime_to_timespec(skb_hwtstamps(skb)->hwtstamp); + else + tss->ts[2] = (struct timespec) {0}; +} + +/* Similar to __sock_recv_timestamp, but does not require an skb */ +void tcp_recv_timestamp(struct msghdr *msg, const struct sock *sk, + struct scm_timestamping *tss) +{ + struct timeval tv; + bool has_timestamping = false; + + if (tss->ts[0].tv_sec || tss->ts[0].tv_nsec) { + if (sock_flag(sk, SOCK_RCVTSTAMP)) { + if (sock_flag(sk, SOCK_RCVTSTAMPNS)) { + put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPNS, + sizeof(tss->ts[0]), &tss->ts[0]); + } else { + tv.tv_sec = tss->ts[0].tv_sec; + tv.tv_usec = tss->ts[0].tv_nsec / 1000; + + put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMP, + sizeof(tv), &tv); + } + } + + if (sk->sk_tsflags & SOF_TIMESTAMPING_SOFTWARE) + has_timestamping = true; + else + tss->ts[0] = (struct timespec) {0}; + } + + if (tss->ts[2].tv_sec || tss->ts[2].tv_nsec) { + if (sk->sk_tsflags & SOF_TIMESTAMPING_RAW_HARDWARE) + has_timestamping = true; + else + tss->ts[2] = (struct timespec) {0}; + } + + if (has_timestamping) { + tss->ts[1] = (struct timespec) {0}; + put_cmsg(msg, SOL_SOCKET, SCM_TIMESTAMPING, + sizeof(*tss), tss); + } +} + /* * This routine copies from a sock struct into the user buffer. * @@ -1716,6 +1772,8 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, long timeo; struct sk_buff *skb, *last; u32 urg_hole = 0; + struct scm_timestamping tss; + bool has_tss = false; if (unlikely(flags & MSG_ERRQUEUE)) return inet_recv_error(sk, msg, len, addr_len); @@ -1911,6 +1969,10 @@ skip_copy: if (used + offset < skb->len) continue; + if (TCP_SKB_CB(skb)->has_rxtstamp) { + tcp_update_recv_tstamps(skb, &tss); + has_tss = true; + } if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN) goto found_fin_ok; if (!(flags & MSG_PEEK)) @@ -1929,6 +1991,9 @@ skip_copy: * on connected socket. I was just happy when found this 8) --ANK */ + if (has_tss) + tcp_recv_timestamp(msg, sk, &tss); + /* Clean up data we have read: This will do ACK frames. */ tcp_cleanup_rbuf(sk, copied); diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index d3421ee9a10a..568ccfd6dd37 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -4246,9 +4246,15 @@ static void tcp_sack_remove(struct tcp_sock *tp) tp->rx_opt.num_sacks = num_sacks; } +enum tcp_queue { + OOO_QUEUE, + RCV_QUEUE, +}; + /** * tcp_try_coalesce - try to merge skb to prior one * @sk: socket + * @dest: destination queue * @to: prior buffer * @from: buffer to add in queue * @fragstolen: pointer to boolean @@ -4260,6 +4266,7 @@ static void tcp_sack_remove(struct tcp_sock *tp) * Returns true if caller should free @from instead of queueing it */ static bool tcp_try_coalesce(struct sock *sk, + enum tcp_queue dest, struct sk_buff *to, struct sk_buff *from, bool *fragstolen) @@ -4281,6 +4288,15 @@ static bool tcp_try_coalesce(struct sock *sk, TCP_SKB_CB(to)->end_seq = TCP_SKB_CB(from)->end_seq; TCP_SKB_CB(to)->ack_seq = TCP_SKB_CB(from)->ack_seq; TCP_SKB_CB(to)->tcp_flags |= TCP_SKB_CB(from)->tcp_flags; + + if (TCP_SKB_CB(from)->has_rxtstamp) { + TCP_SKB_CB(to)->has_rxtstamp = true; + if (dest == OOO_QUEUE) + TCP_SKB_CB(to)->swtstamp = TCP_SKB_CB(from)->swtstamp; + else + to->tstamp = from->tstamp; + } + return true; } @@ -4315,6 +4331,9 @@ static void tcp_ofo_queue(struct sock *sk) } p = rb_next(p); rb_erase(&skb->rbnode, &tp->out_of_order_queue); + /* Replace tstamp which was stomped by rbnode */ + if (TCP_SKB_CB(skb)->has_rxtstamp) + skb->tstamp = TCP_SKB_CB(skb)->swtstamp; if (unlikely(!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt))) { SOCK_DEBUG(sk, "ofo packet was already received\n"); @@ -4326,7 +4345,8 @@ static void tcp_ofo_queue(struct sock *sk) TCP_SKB_CB(skb)->end_seq); tail = skb_peek_tail(&sk->sk_receive_queue); - eaten = tail && tcp_try_coalesce(sk, tail, skb, &fragstolen); + eaten = tail && tcp_try_coalesce(sk, RCV_QUEUE, + tail, skb, &fragstolen); tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq); fin = TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN; if (!eaten) @@ -4380,6 +4400,10 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) return; } + /* Stash tstamp to avoid being stomped on by rbnode */ + if (TCP_SKB_CB(skb)->has_rxtstamp) + TCP_SKB_CB(skb)->swtstamp = skb->tstamp; + inet_csk_schedule_ack(sk); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOQUEUE); @@ -4405,7 +4429,8 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) /* In the typical case, we are adding an skb to the end of the list. * Use of ooo_last_skb avoids the O(Log(N)) rbtree lookup. */ - if (tcp_try_coalesce(sk, tp->ooo_last_skb, skb, &fragstolen)) { + if (tcp_try_coalesce(sk, OOO_QUEUE, tp->ooo_last_skb, + skb, &fragstolen)) { coalesce_done: tcp_grow_window(sk, skb); kfree_skb_partial(skb, fragstolen); @@ -4455,7 +4480,8 @@ coalesce_done: __kfree_skb(skb1); goto merge_right; } - } else if (tcp_try_coalesce(sk, skb1, skb, &fragstolen)) { + } else if (tcp_try_coalesce(sk, OOO_QUEUE, skb1, + skb, &fragstolen)) { goto coalesce_done; } p = &parent->rb_right; @@ -4506,7 +4532,8 @@ static int __must_check tcp_queue_rcv(struct sock *sk, struct sk_buff *skb, int __skb_pull(skb, hdrlen); eaten = (tail && - tcp_try_coalesce(sk, tail, skb, fragstolen)) ? 1 : 0; + tcp_try_coalesce(sk, RCV_QUEUE, tail, + skb, fragstolen)) ? 1 : 0; tcp_rcv_nxt_update(tcp_sk(sk), TCP_SKB_CB(skb)->end_seq); if (!eaten) { __skb_queue_tail(&sk->sk_receive_queue, skb); diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index 5af8b809dfbc..a63486afa7a7 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1637,6 +1637,8 @@ int tcp_v4_rcv(struct sk_buff *skb) TCP_SKB_CB(skb)->tcp_tw_isn = 0; TCP_SKB_CB(skb)->ip_dsfield = ipv4_get_dsfield(iph); TCP_SKB_CB(skb)->sacked = 0; + TCP_SKB_CB(skb)->has_rxtstamp = + skb->tstamp || skb_hwtstamps(skb)->hwtstamp; lookup: sk = __inet_lookup_skb(&tcp_hashinfo, skb, __tcp_hdrlen(th), th->source, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index d79a1af3252e..abba3bc2a3d9 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1394,6 +1394,8 @@ static void tcp_v6_fill_cb(struct sk_buff *skb, const struct ipv6hdr *hdr, TCP_SKB_CB(skb)->tcp_tw_isn = 0; TCP_SKB_CB(skb)->ip_dsfield = ipv6_get_dsfield(hdr); TCP_SKB_CB(skb)->sacked = 0; + TCP_SKB_CB(skb)->has_rxtstamp = + skb->tstamp || skb_hwtstamps(skb)->hwtstamp; } static int tcp_v6_rcv(struct sk_buff *skb) -- cgit v1.2.3 From 5f9ae3d9e7e4ad6db0491abc7c4ae5452dbeadd8 Mon Sep 17 00:00:00 2001 From: Xin Long Date: Wed, 23 Aug 2017 10:07:26 +0800 Subject: ipv4: do metrics match when looking up and deleting a route Now when ipv4 route inserts a fib_info, it memcmp fib_metrics. It means ipv4 route identifies one route also with metrics. But when removing a route, it tries to find the route without caring about the metrics. It will cause that the route with right metrics can't be removed. Thomas noticed this issue when doing the testing: 1. add: # ip route append 192.168.7.0/24 dev v window 1000 # ip route append 192.168.7.0/24 dev v window 1001 # ip route append 192.168.7.0/24 dev v window 1002 # ip route append 192.168.7.0/24 dev v window 1003 2. delete: # ip route delete 192.168.7.0/24 dev v window 1002 3. show: 192.168.7.0/24 proto boot scope link window 1001 192.168.7.0/24 proto boot scope link window 1002 192.168.7.0/24 proto boot scope link window 1003 The one with window 1002 wasn't deleted but the first one was. This patch is to do metrics match when looking up and deleting one route. Reported-by: Thomas Haller Signed-off-by: Xin Long Acked-by: Hannes Frederic Sowa Signed-off-by: David S. Miller --- net/ipv4/fib_lookup.h | 1 + net/ipv4/fib_semantics.c | 34 ++++++++++++++++++++++++++++++++++ net/ipv4/fib_trie.c | 3 ++- 3 files changed, 37 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/fib_lookup.h b/net/ipv4/fib_lookup.h index 769ab87ebc4b..5b2af19cfb5b 100644 --- a/net/ipv4/fib_lookup.h +++ b/net/ipv4/fib_lookup.h @@ -32,6 +32,7 @@ struct fib_info *fib_create_info(struct fib_config *cfg, struct netlink_ext_ack *extack); int fib_nh_match(struct fib_config *cfg, struct fib_info *fi, struct netlink_ext_ack *extack); +bool fib_metrics_match(struct fib_config *cfg, struct fib_info *fi); int fib_dump_info(struct sk_buff *skb, u32 pid, u32 seq, int event, u32 tb_id, u8 type, __be32 dst, int dst_len, u8 tos, struct fib_info *fi, unsigned int); diff --git a/net/ipv4/fib_semantics.c b/net/ipv4/fib_semantics.c index 394d800db50c..57a5d48acee8 100644 --- a/net/ipv4/fib_semantics.c +++ b/net/ipv4/fib_semantics.c @@ -696,6 +696,40 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi, return 0; } +bool fib_metrics_match(struct fib_config *cfg, struct fib_info *fi) +{ + struct nlattr *nla; + int remaining; + + if (!cfg->fc_mx) + return true; + + nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { + int type = nla_type(nla); + u32 val; + + if (!type) + continue; + if (type > RTAX_MAX) + return false; + + if (type == RTAX_CC_ALGO) { + char tmp[TCP_CA_NAME_MAX]; + bool ecn_ca = false; + + nla_strlcpy(tmp, nla, sizeof(tmp)); + val = tcp_ca_get_key_by_name(tmp, &ecn_ca); + } else { + val = nla_get_u32(nla); + } + + if (fi->fib_metrics->metrics[type - 1] != val) + return false; + } + + return true; +} + /* * Picture diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 1a6ffb0dab9c..c636650a6a70 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -1563,7 +1563,8 @@ int fib_table_delete(struct net *net, struct fib_table *tb, fi->fib_prefsrc == cfg->fc_prefsrc) && (!cfg->fc_protocol || fi->fib_protocol == cfg->fc_protocol) && - fib_nh_match(cfg, fi, extack) == 0) { + fib_nh_match(cfg, fi, extack) == 0 && + fib_metrics_match(cfg, fi)) { fa_to_delete = fa; break; } -- cgit v1.2.3 From 257a73031d29447ee82fe06d2b97d8564f63276d Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Wed, 23 Aug 2017 11:57:51 +0200 Subject: net/sock: allow the user to set negative peek offset This is necessary to allow the user to disable peeking with offset once it's enabled. Unix sockets already allow the above, with this patch we permit it for udp[6] sockets, too. Fixes: 627d2d6b5500 ("udp: enable MSG_PEEK at non-zero offset") Signed-off-by: Paolo Abeni Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- net/core/sock.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'net') diff --git a/net/core/sock.c b/net/core/sock.c index 0f04d8bff607..dfdd14cac775 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -2454,9 +2454,6 @@ EXPORT_SYMBOL(__sk_mem_reclaim); int sk_set_peek_off(struct sock *sk, int val) { - if (val < 0) - return -EINVAL; - sk->sk_peek_off = val; return 0; } -- cgit v1.2.3 From 042a90106b09beff4fa9015d1940e45ce10297ab Mon Sep 17 00:00:00 2001 From: Arvind Yadav Date: Wed, 23 Aug 2017 16:22:20 +0530 Subject: net: tipc: constify genl_ops genl_ops are not supposed to change at runtime. All functions working with genl_ops provided by work with const genl_ops. So mark the non-const structs as const. Signed-off-by: Arvind Yadav Signed-off-by: David S. Miller --- net/tipc/netlink_compat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/tipc/netlink_compat.c b/net/tipc/netlink_compat.c index 750949dfc1d7..e48f0b2c01b9 100644 --- a/net/tipc/netlink_compat.c +++ b/net/tipc/netlink_compat.c @@ -1217,7 +1217,7 @@ send: return err; } -static struct genl_ops tipc_genl_compat_ops[] = { +static const struct genl_ops tipc_genl_compat_ops[] = { { .cmd = TIPC_GENL_CMD, .doit = tipc_nl_compat_recv, -- cgit v1.2.3 From 60890e046081aef61980dbc812ac5100ad078a87 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 23 Aug 2017 12:59:48 +0100 Subject: gre: remove duplicated assignment of iph iph is being assigned the same value twice; remove the redundant first assignment. (Thanks to Nikolay Aleksandrov for pointing out that the first asssignment should be removed and not the second) Fixes warning: net/ipv4/ip_gre.c:265:2: warning: Value stored to 'iph' is never read Signed-off-by: Colin Ian King Reviewed-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 1 - 1 file changed, 1 deletion(-) (limited to 'net') diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 5a20ba9b9b50..f70674799fdd 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -262,7 +262,6 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, int len; itn = net_generic(net, erspan_net_id); - iph = ip_hdr(skb); len = gre_hdr_len + sizeof(*ershdr); if (unlikely(!pskb_may_pull(skb, len))) -- cgit v1.2.3 From 1177009131bee310421f5c04c43d3777cbacbdc8 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:39:59 +0200 Subject: devlink: Add Ethernet header for dpipe This will be used by the IPv4 host table which will be introduced in the following patches. This header is global and can be reused by many drivers. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/devlink.h | 1 + include/uapi/linux/devlink.h | 8 ++++++++ net/core/devlink.c | 17 +++++++++++++++++ 3 files changed, 26 insertions(+) (limited to 'net') diff --git a/include/net/devlink.h b/include/net/devlink.h index ed7687bbf5d0..ddb8b5227580 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -328,6 +328,7 @@ int devlink_dpipe_action_put(struct sk_buff *skb, struct devlink_dpipe_action *action); int devlink_dpipe_match_put(struct sk_buff *skb, struct devlink_dpipe_match *match); +extern struct devlink_dpipe_header devlink_dpipe_header_ethernet; #else diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index b0e807ac53bb..a855f8dcc8ee 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -226,4 +226,12 @@ enum devlink_dpipe_action_type { DEVLINK_DPIPE_ACTION_TYPE_FIELD_MODIFY, }; +enum devlink_dpipe_field_ethernet_id { + DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC, +}; + +enum devlink_dpipe_header_id { + DEVLINK_DPIPE_HEADER_ETHERNET, +}; + #endif /* _UAPI_LINUX_DEVLINK_H_ */ diff --git a/net/core/devlink.c b/net/core/devlink.c index a0adfc31a3fe..4f6f3d601785 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -29,6 +29,23 @@ #define CREATE_TRACE_POINTS #include +static struct devlink_dpipe_field devlink_dpipe_fields_ethernet[] = { + { + .name = "destination_mac", + .id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC, + .bitwidth = 48, + }, +}; + +struct devlink_dpipe_header devlink_dpipe_header_ethernet = { + .name = "ethernet", + .id = DEVLINK_DPIPE_HEADER_ETHERNET, + .fields = devlink_dpipe_fields_ethernet, + .fields_count = ARRAY_SIZE(devlink_dpipe_fields_ethernet), + .global = true, +}; +EXPORT_SYMBOL(devlink_dpipe_header_ethernet); + EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg); static LIST_HEAD(devlink_list); -- cgit v1.2.3 From 3fb886ecea93605a8ea14e258ff3158b8966781e Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:00 +0200 Subject: devlink: Add IPv4 header for dpipe This will be used by the IPv4 host table which will be introduced in the following patches. This header is global and can be reused by many drivers. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/devlink.h | 1 + include/uapi/linux/devlink.h | 5 +++++ net/core/devlink.c | 17 +++++++++++++++++ 3 files changed, 23 insertions(+) (limited to 'net') diff --git a/include/net/devlink.h b/include/net/devlink.h index ddb8b5227580..8ff8a6f77f29 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -329,6 +329,7 @@ int devlink_dpipe_action_put(struct sk_buff *skb, int devlink_dpipe_match_put(struct sk_buff *skb, struct devlink_dpipe_match *match); extern struct devlink_dpipe_header devlink_dpipe_header_ethernet; +extern struct devlink_dpipe_header devlink_dpipe_header_ipv4; #else diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index a855f8dcc8ee..6c172548589d 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -230,8 +230,13 @@ enum devlink_dpipe_field_ethernet_id { DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC, }; +enum devlink_dpipe_field_ipv4_id { + DEVLINK_DPIPE_FIELD_IPV4_DST_IP, +}; + enum devlink_dpipe_header_id { DEVLINK_DPIPE_HEADER_ETHERNET, + DEVLINK_DPIPE_HEADER_IPV4, }; #endif /* _UAPI_LINUX_DEVLINK_H_ */ diff --git a/net/core/devlink.c b/net/core/devlink.c index 4f6f3d601785..fcf5dd662882 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -46,6 +46,23 @@ struct devlink_dpipe_header devlink_dpipe_header_ethernet = { }; EXPORT_SYMBOL(devlink_dpipe_header_ethernet); +static struct devlink_dpipe_field devlink_dpipe_fields_ipv4[] = { + { + .name = "destination ip", + .id = DEVLINK_DPIPE_FIELD_IPV4_DST_IP, + .bitwidth = 32, + }, +}; + +struct devlink_dpipe_header devlink_dpipe_header_ipv4 = { + .name = "ipv4", + .id = DEVLINK_DPIPE_HEADER_IPV4, + .fields = devlink_dpipe_fields_ipv4, + .fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv4), + .global = true, +}; +EXPORT_SYMBOL(devlink_dpipe_header_ipv4); + EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg); static LIST_HEAD(devlink_list); -- cgit v1.2.3 From ffd3cdccf214cf0df08856a6738544076c4cd548 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:02 +0200 Subject: devlink: Add support for dynamic table size Up until now the dpipe table's size was static and known at registration time. The host table does not have constant size and it is resized in dynamic manner. In order to support this behavior the size is changed to be obtained dynamically via an op. This patch also adjust the current dpipe table for the new API. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 13 +++++++++---- include/net/devlink.h | 7 +++---- net/core/devlink.c | 12 +++++++----- 3 files changed, 19 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index e9e1f7aeb04f..9eb265bd5430 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -301,24 +301,29 @@ static int mlxsw_sp_dpipe_table_erif_counters_update(void *priv, bool enable) return 0; } +static u64 mlxsw_sp_dpipe_table_erif_size_get(void *priv) +{ + struct mlxsw_sp *mlxsw_sp = priv; + + return MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); +} + static struct devlink_dpipe_table_ops mlxsw_sp_erif_ops = { .matches_dump = mlxsw_sp_dpipe_table_erif_matches_dump, .actions_dump = mlxsw_sp_dpipe_table_erif_actions_dump, .entries_dump = mlxsw_sp_dpipe_table_erif_entries_dump, .counters_set_update = mlxsw_sp_dpipe_table_erif_counters_update, + .size_get = mlxsw_sp_dpipe_table_erif_size_get, }; static int mlxsw_sp_dpipe_erif_table_init(struct mlxsw_sp *mlxsw_sp) { struct devlink *devlink = priv_to_devlink(mlxsw_sp->core); - u64 table_size; - table_size = MLXSW_CORE_RES_GET(mlxsw_sp->core, MAX_RIFS); return devlink_dpipe_table_register(devlink, MLXSW_SP_DPIPE_TABLE_NAME_ERIF, &mlxsw_sp_erif_ops, - mlxsw_sp, table_size, - false); + mlxsw_sp, false); } static void mlxsw_sp_dpipe_erif_table_fini(struct mlxsw_sp *mlxsw_sp) diff --git a/include/net/devlink.h b/include/net/devlink.h index 8ff8a6f77f29..e96272b2cfec 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -178,7 +178,6 @@ struct devlink_dpipe_table_ops; * struct devlink_dpipe_table - table object * @priv: private * @name: table name - * @size: maximum number of entries * @counters_enabled: indicates if counters are active * @counter_control_extern: indicates if counter control is in dpipe or * external tool @@ -189,7 +188,6 @@ struct devlink_dpipe_table { void *priv; struct list_head list; const char *name; - u64 size; bool counters_enabled; bool counter_control_extern; struct devlink_dpipe_table_ops *table_ops; @@ -204,6 +202,7 @@ struct devlink_dpipe_table { * @counters_set_update - when changing the counter status hardware sync * maybe needed to allocate/free counter related * resources + * @size_get - get size */ struct devlink_dpipe_table_ops { int (*actions_dump)(void *priv, struct sk_buff *skb); @@ -211,6 +210,7 @@ struct devlink_dpipe_table_ops { int (*entries_dump)(void *priv, bool counters_enabled, struct devlink_dpipe_dump_ctx *dump_ctx); int (*counters_set_update)(void *priv, bool enable); + u64 (*size_get)(void *priv); }; /** @@ -311,8 +311,7 @@ void devlink_sb_unregister(struct devlink *devlink, unsigned int sb_index); int devlink_dpipe_table_register(struct devlink *devlink, const char *table_name, struct devlink_dpipe_table_ops *table_ops, - void *priv, u64 size, - bool counter_control_extern); + void *priv, bool counter_control_extern); void devlink_dpipe_table_unregister(struct devlink *devlink, const char *table_name); int devlink_dpipe_headers_register(struct devlink *devlink, diff --git a/net/core/devlink.c b/net/core/devlink.c index fcf5dd662882..87062ff36ab4 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1647,13 +1647,15 @@ static int devlink_dpipe_table_put(struct sk_buff *skb, struct devlink_dpipe_table *table) { struct nlattr *table_attr; + u64 table_size; + table_size = table->table_ops->size_get(table->priv); table_attr = nla_nest_start(skb, DEVLINK_ATTR_DPIPE_TABLE); if (!table_attr) return -EMSGSIZE; if (nla_put_string(skb, DEVLINK_ATTR_DPIPE_TABLE_NAME, table->name) || - nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table->size, + nla_put_u64_64bit(skb, DEVLINK_ATTR_DPIPE_TABLE_SIZE, table_size, DEVLINK_ATTR_PAD)) goto nla_put_failure; if (nla_put_u8(skb, DEVLINK_ATTR_DPIPE_TABLE_COUNTERS_ENABLED, @@ -2718,20 +2720,21 @@ EXPORT_SYMBOL_GPL(devlink_dpipe_table_counter_enabled); * @table_name: table name * @table_ops: table ops * @priv: priv - * @size: size * @counter_control_extern: external control for counters */ int devlink_dpipe_table_register(struct devlink *devlink, const char *table_name, struct devlink_dpipe_table_ops *table_ops, - void *priv, u64 size, - bool counter_control_extern) + void *priv, bool counter_control_extern) { struct devlink_dpipe_table *table; if (devlink_dpipe_table_find(&devlink->dpipe_table_list, table_name)) return -EEXIST; + if (WARN_ON(!table_ops->size_get)) + return -EINVAL; + table = kzalloc(sizeof(*table), GFP_KERNEL); if (!table) return -ENOMEM; @@ -2739,7 +2742,6 @@ int devlink_dpipe_table_register(struct devlink *devlink, table->name = table_name; table->table_ops = table_ops; table->priv = priv; - table->size = size; table->counter_control_extern = counter_control_extern; mutex_lock(&devlink_mutex); -- cgit v1.2.3 From 3580732448f128c39e7325912bc4368ade5dce7d Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 24 Aug 2017 08:40:03 +0200 Subject: devlink: Move dpipe entry clear function into devlink The entry clear routine can be shared between the drivers, thus it is moved inside devlink. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- .../net/ethernet/mellanox/mlxsw/spectrum_dpipe.c | 24 ++-------------------- include/net/devlink.h | 6 ++++++ net/core/devlink.c | 22 ++++++++++++++++++++ 3 files changed, 30 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c index 9eb265bd5430..1305df9c6bf9 100644 --- a/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c +++ b/drivers/net/ethernet/mellanox/mlxsw/spectrum_dpipe.c @@ -114,26 +114,6 @@ static int mlxsw_sp_dpipe_table_erif_matches_dump(void *priv, return devlink_dpipe_match_put(skb, &match); } -static void mlxsw_sp_erif_entry_clear(struct devlink_dpipe_entry *entry) -{ - unsigned int value_count, value_index; - struct devlink_dpipe_value *value; - - value = entry->action_values; - value_count = entry->action_values_count; - for (value_index = 0; value_index < value_count; value_index++) { - kfree(value[value_index].value); - kfree(value[value_index].mask); - } - - value = entry->match_values; - value_count = entry->match_values_count; - for (value_index = 0; value_index < value_count; value_index++) { - kfree(value[value_index].value); - kfree(value[value_index].mask); - } -} - static void mlxsw_sp_erif_match_action_prepare(struct devlink_dpipe_match *match, struct devlink_dpipe_action *action) @@ -270,12 +250,12 @@ start_again: goto start_again; rtnl_unlock(); - mlxsw_sp_erif_entry_clear(&entry); + devlink_dpipe_entry_clear(&entry); return 0; err_entry_append: err_entry_get: rtnl_unlock(); - mlxsw_sp_erif_entry_clear(&entry); + devlink_dpipe_entry_clear(&entry); return err; } diff --git a/include/net/devlink.h b/include/net/devlink.h index e96272b2cfec..047a0c54f652 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -323,6 +323,7 @@ int devlink_dpipe_entry_ctx_prepare(struct devlink_dpipe_dump_ctx *dump_ctx); int devlink_dpipe_entry_ctx_append(struct devlink_dpipe_dump_ctx *dump_ctx, struct devlink_dpipe_entry *entry); int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx); +void devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry); int devlink_dpipe_action_put(struct sk_buff *skb, struct devlink_dpipe_action *action); int devlink_dpipe_match_put(struct sk_buff *skb, @@ -448,6 +449,11 @@ devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx) return 0; } +static inline void +devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry) +{ +} + static inline int devlink_dpipe_action_put(struct sk_buff *skb, struct devlink_dpipe_action *action) diff --git a/net/core/devlink.c b/net/core/devlink.c index 87062ff36ab4..194708aa5f11 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -1996,6 +1996,28 @@ int devlink_dpipe_entry_ctx_close(struct devlink_dpipe_dump_ctx *dump_ctx) } EXPORT_SYMBOL_GPL(devlink_dpipe_entry_ctx_close); +void devlink_dpipe_entry_clear(struct devlink_dpipe_entry *entry) + +{ + unsigned int value_count, value_index; + struct devlink_dpipe_value *value; + + value = entry->action_values; + value_count = entry->action_values_count; + for (value_index = 0; value_index < value_count; value_index++) { + kfree(value[value_index].value); + kfree(value[value_index].mask); + } + + value = entry->match_values; + value_count = entry->match_values_count; + for (value_index = 0; value_index < value_count; value_index++) { + kfree(value[value_index].value); + kfree(value[value_index].mask); + } +} +EXPORT_SYMBOL(devlink_dpipe_entry_clear); + static int devlink_dpipe_entries_fill(struct genl_info *info, enum devlink_command cmd, int flags, struct devlink_dpipe_table *table) -- cgit v1.2.3 From eee6ebbac18a189ef33d25ea9b8bcae176515e49 Mon Sep 17 00:00:00 2001 From: Nick Desaulniers Date: Fri, 11 Aug 2017 11:16:07 -0700 Subject: netfilter: nf_nat_h323: fix logical-not-parentheses warning Clang produces the following warning: net/ipv4/netfilter/nf_nat_h323.c:553:6: error: logical not is only applied to the left hand side of this comparison [-Werror,-Wlogical-not-parentheses] if (!set_h225_addr(skb, protoff, data, dataoff, taddr, ^ add parentheses after the '!' to evaluate the comparison first add parentheses around left hand side expression to silence this warning There's not necessarily a bug here, but it's cleaner to return early, ex: if (x) return ... rather than: if (x == 0) ... else return Also added a return code check that seemed to be missing in one instance. Signed-off-by: Nick Desaulniers Signed-off-by: Pablo Neira Ayuso --- net/ipv4/netfilter/nf_nat_h323.c | 57 +++++++++++++++++++++------------------- 1 file changed, 30 insertions(+), 27 deletions(-) (limited to 'net') diff --git a/net/ipv4/netfilter/nf_nat_h323.c b/net/ipv4/netfilter/nf_nat_h323.c index 574f7ebba0b6..ac8342dcb55e 100644 --- a/net/ipv4/netfilter/nf_nat_h323.c +++ b/net/ipv4/netfilter/nf_nat_h323.c @@ -252,16 +252,16 @@ static int nat_rtp_rtcp(struct sk_buff *skb, struct nf_conn *ct, if (set_h245_addr(skb, protoff, data, dataoff, taddr, &ct->tuplehash[!dir].tuple.dst.u3, htons((port & htons(1)) ? nated_port + 1 : - nated_port)) == 0) { - /* Save ports */ - info->rtp_port[i][dir] = rtp_port; - info->rtp_port[i][!dir] = htons(nated_port); - } else { + nated_port))) { nf_ct_unexpect_related(rtp_exp); nf_ct_unexpect_related(rtcp_exp); return -1; } + /* Save ports */ + info->rtp_port[i][dir] = rtp_port; + info->rtp_port[i][!dir] = htons(nated_port); + /* Success */ pr_debug("nf_nat_h323: expect RTP %pI4:%hu->%pI4:%hu\n", &rtp_exp->tuple.src.u3.ip, @@ -370,15 +370,15 @@ static int nat_h245(struct sk_buff *skb, struct nf_conn *ct, /* Modify signal */ if (set_h225_addr(skb, protoff, data, dataoff, taddr, &ct->tuplehash[!dir].tuple.dst.u3, - htons(nated_port)) == 0) { - /* Save ports */ - info->sig_port[dir] = port; - info->sig_port[!dir] = htons(nated_port); - } else { + htons(nated_port))) { nf_ct_unexpect_related(exp); return -1; } + /* Save ports */ + info->sig_port[dir] = port; + info->sig_port[!dir] = htons(nated_port); + pr_debug("nf_nat_q931: expect H.245 %pI4:%hu->%pI4:%hu\n", &exp->tuple.src.u3.ip, ntohs(exp->tuple.src.u.tcp.port), @@ -462,24 +462,27 @@ static int nat_q931(struct sk_buff *skb, struct nf_conn *ct, /* Modify signal */ if (set_h225_addr(skb, protoff, data, 0, &taddr[idx], &ct->tuplehash[!dir].tuple.dst.u3, - htons(nated_port)) == 0) { - /* Save ports */ - info->sig_port[dir] = port; - info->sig_port[!dir] = htons(nated_port); - - /* Fix for Gnomemeeting */ - if (idx > 0 && - get_h225_addr(ct, *data, &taddr[0], &addr, &port) && - (ntohl(addr.ip) & 0xff000000) == 0x7f000000) { - set_h225_addr(skb, protoff, data, 0, &taddr[0], - &ct->tuplehash[!dir].tuple.dst.u3, - info->sig_port[!dir]); - } - } else { + htons(nated_port))) { nf_ct_unexpect_related(exp); return -1; } + /* Save ports */ + info->sig_port[dir] = port; + info->sig_port[!dir] = htons(nated_port); + + /* Fix for Gnomemeeting */ + if (idx > 0 && + get_h225_addr(ct, *data, &taddr[0], &addr, &port) && + (ntohl(addr.ip) & 0xff000000) == 0x7f000000) { + if (set_h225_addr(skb, protoff, data, 0, &taddr[0], + &ct->tuplehash[!dir].tuple.dst.u3, + info->sig_port[!dir])) { + nf_ct_unexpect_related(exp); + return -1; + } + } + /* Success */ pr_debug("nf_nat_ras: expect Q.931 %pI4:%hu->%pI4:%hu\n", &exp->tuple.src.u3.ip, @@ -550,9 +553,9 @@ static int nat_callforwarding(struct sk_buff *skb, struct nf_conn *ct, } /* Modify signal */ - if (!set_h225_addr(skb, protoff, data, dataoff, taddr, - &ct->tuplehash[!dir].tuple.dst.u3, - htons(nated_port)) == 0) { + if (set_h225_addr(skb, protoff, data, dataoff, taddr, + &ct->tuplehash[!dir].tuple.dst.u3, + htons(nated_port))) { nf_ct_unexpect_related(exp); return -1; } -- cgit v1.2.3 From 0d03510038bda70b5a4a252e8216822e6ce0cbdb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 12 Aug 2017 00:57:02 +0200 Subject: netfilter: conntrack: compute l3proto nla size at compile time avoids a pointer and allows struct to be const later on. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l3proto.h | 19 ++++++++----------- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 13 +++++++------ net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 14 ++++++++------ net/netfilter/nf_conntrack_netlink.c | 3 ++- net/netfilter/nf_conntrack_proto.c | 9 +++------ 5 files changed, 28 insertions(+), 30 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index 1b8de164d744..6a27ffea7480 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -20,6 +20,9 @@ struct nf_conntrack_l3proto { /* L3 Protocol Family number. ex) PF_INET */ u_int16_t l3proto; + /* size of tuple nlattr, fills a hole */ + u16 nla_size; + /* Protocol name */ const char *name; @@ -49,23 +52,17 @@ struct nf_conntrack_l3proto { int (*get_l4proto)(const struct sk_buff *skb, unsigned int nhoff, unsigned int *dataoff, u_int8_t *protonum); +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) int (*tuple_to_nlattr)(struct sk_buff *skb, const struct nf_conntrack_tuple *t); - - /* Called when netns wants to use connection tracking */ - int (*net_ns_get)(struct net *); - void (*net_ns_put)(struct net *); - - /* - * Calculate size of tuple nlattr - */ - int (*nlattr_tuple_size)(void); - int (*nlattr_to_tuple)(struct nlattr *tb[], struct nf_conntrack_tuple *t); const struct nla_policy *nla_policy; +#endif - size_t nla_size; + /* Called when netns wants to use connection tracking */ + int (*net_ns_get)(struct net *); + void (*net_ns_put)(struct net *); /* Module (if any) which this is connected to. */ struct module *me; diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index de5f0e6ddd1b..9fb8cb033d80 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -303,11 +303,6 @@ static int ipv4_nlattr_to_tuple(struct nlattr *tb[], return 0; } - -static int ipv4_nlattr_tuple_size(void) -{ - return nla_policy_len(ipv4_nla_policy, CTA_IP_MAX + 1); -} #endif static struct nf_sockopt_ops so_getorigdst = { @@ -365,9 +360,10 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { .get_l4proto = ipv4_get_l4proto, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .tuple_to_nlattr = ipv4_tuple_to_nlattr, - .nlattr_tuple_size = ipv4_nlattr_tuple_size, .nlattr_to_tuple = ipv4_nlattr_to_tuple, .nla_policy = ipv4_nla_policy, + .nla_size = NLA_ALIGN(NLA_HDRLEN + sizeof(u32)) + /* CTA_IP_V4_SRC */ + NLA_ALIGN(NLA_HDRLEN + sizeof(u32)), /* CTA_IP_V4_DST */ #endif .net_ns_get = ipv4_hooks_register, .net_ns_put = ipv4_hooks_unregister, @@ -421,6 +417,11 @@ static int __init nf_conntrack_l3proto_ipv4_init(void) need_conntrack(); +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) + if (WARN_ON(nla_policy_len(ipv4_nla_policy, CTA_IP_MAX + 1) != + nf_conntrack_l3proto_ipv4.nla_size)) + return -EINVAL; +#endif ret = nf_register_sockopt(&so_getorigdst); if (ret < 0) { pr_err("Unable to register netfilter socket option\n"); diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index ddef5ee9e0a8..6b4d59fd0214 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -308,11 +308,6 @@ static int ipv6_nlattr_to_tuple(struct nlattr *tb[], return 0; } - -static int ipv6_nlattr_tuple_size(void) -{ - return nla_policy_len(ipv6_nla_policy, CTA_IP_MAX + 1); -} #endif static int ipv6_hooks_register(struct net *net) @@ -360,9 +355,10 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { .get_l4proto = ipv6_get_l4proto, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .tuple_to_nlattr = ipv6_tuple_to_nlattr, - .nlattr_tuple_size = ipv6_nlattr_tuple_size, .nlattr_to_tuple = ipv6_nlattr_to_tuple, .nla_policy = ipv6_nla_policy, + .nla_size = NLA_ALIGN(NLA_HDRLEN + sizeof(u32[4])) + + NLA_ALIGN(NLA_HDRLEN + sizeof(u32[4])), #endif .net_ns_get = ipv6_hooks_register, .net_ns_put = ipv6_hooks_unregister, @@ -421,6 +417,12 @@ static int __init nf_conntrack_l3proto_ipv6_init(void) need_conntrack(); +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) + if (WARN_ON(nla_policy_len(ipv6_nla_policy, CTA_IP_MAX + 1) != + nf_conntrack_l3proto_ipv6.nla_size)) + return -EINVAL; +#endif + ret = nf_register_sockopt(&so_getorigdst6); if (ret < 0) { pr_err("Unable to register netfilter socket option\n"); diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index f4ca48817f66..b59a453a0fd8 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -540,7 +540,8 @@ static inline size_t ctnetlink_proto_size(const struct nf_conn *ct) size_t len = 0; l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); - len += l3proto->nla_size; + len = l3proto->nla_size; + len *= 3u; /* ORIG, REPLY, MASTER */ l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); len += l4proto->nla_size; diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 27810cf816a6..85104a27cc89 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -214,10 +214,10 @@ int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto) if (proto->l3proto >= NFPROTO_NUMPROTO) return -EBUSY; - - if (proto->tuple_to_nlattr && !proto->nlattr_tuple_size) +#if IS_ENABLED(CONFIG_NF_CT_NETLINK) + if (proto->tuple_to_nlattr && proto->nla_size == 0) return -EINVAL; - +#endif mutex_lock(&nf_ct_proto_mutex); old = rcu_dereference_protected(nf_ct_l3protos[proto->l3proto], lockdep_is_held(&nf_ct_proto_mutex)); @@ -226,9 +226,6 @@ int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto) goto out_unlock; } - if (proto->nlattr_tuple_size) - proto->nla_size = 3 * proto->nlattr_tuple_size(); - rcu_assign_pointer(nf_ct_l3protos[proto->l3proto], proto); out_unlock: -- cgit v1.2.3 From a3134d537f8209f5b149d7ed9f287047158845f0 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 12 Aug 2017 00:57:03 +0200 Subject: netfilter: conntrack: remove protocol name from l3proto struct no need to waste storage for something that is only needed in one place and can be deduced from protocol number. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l3proto.h | 3 --- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 1 - net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 1 - net/netfilter/nf_conntrack_l3proto_generic.c | 1 - net/netfilter/nf_conntrack_standalone.c | 12 +++++++++++- 5 files changed, 11 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index 6a27ffea7480..e31861e4fa6a 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -23,9 +23,6 @@ struct nf_conntrack_l3proto { /* size of tuple nlattr, fills a hole */ u16 nla_size; - /* Protocol name */ - const char *name; - /* * Try to fill in the third arg: nhoff is offset of l3 proto * hdr. Return true if possible. diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 9fb8cb033d80..9f7ea862068c 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -353,7 +353,6 @@ static void ipv4_hooks_unregister(struct net *net) struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { .l3proto = PF_INET, - .name = "ipv4", .pkt_to_tuple = ipv4_pkt_to_tuple, .invert_tuple = ipv4_invert_tuple, .print_tuple = ipv4_print_tuple, diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 6b4d59fd0214..91d37fbe28de 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -348,7 +348,6 @@ static void ipv6_hooks_unregister(struct net *net) struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { .l3proto = PF_INET6, - .name = "ipv6", .pkt_to_tuple = ipv6_pkt_to_tuple, .invert_tuple = ipv6_invert_tuple, .print_tuple = ipv6_print_tuple, diff --git a/net/netfilter/nf_conntrack_l3proto_generic.c b/net/netfilter/nf_conntrack_l3proto_generic.c index cf9ace70bece..0387971582bc 100644 --- a/net/netfilter/nf_conntrack_l3proto_generic.c +++ b/net/netfilter/nf_conntrack_l3proto_generic.c @@ -64,7 +64,6 @@ static int generic_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, struct nf_conntrack_l3proto nf_conntrack_l3proto_generic __read_mostly = { .l3proto = PF_UNSPEC, - .name = "unknown", .pkt_to_tuple = generic_pkt_to_tuple, .invert_tuple = generic_invert_tuple, .print_tuple = generic_print_tuple, diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 5b6c675d55b1..359d7e6a4503 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -198,6 +198,16 @@ ct_show_delta_time(struct seq_file *s, const struct nf_conn *ct) } #endif +static const char* l3proto_name(u16 proto) +{ + switch (proto) { + case AF_INET: return "ipv4"; + case AF_INET6: return "ipv6"; + } + + return "unknown"; +} + /* return 0 on success, 1 in case of error */ static int ct_seq_show(struct seq_file *s, void *v) { @@ -231,7 +241,7 @@ static int ct_seq_show(struct seq_file *s, void *v) ret = -ENOSPC; seq_printf(s, "%-8s %u %-8s %u %ld ", - l3proto->name, nf_ct_l3num(ct), + l3proto_name(l3proto->l3proto), nf_ct_l3num(ct), l4proto->name, nf_ct_protonum(ct), nf_ct_expires(ct) / HZ); -- cgit v1.2.3 From 09ec82f5af99d1e35614eb0844b920fc335a313d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 12 Aug 2017 00:57:04 +0200 Subject: netfilter: conntrack: remove protocol name from l4proto struct no need to waste storage for something that is only needed in one place and can be deduced from protocol number. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 3 --- net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 1 - net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 1 - net/netfilter/nf_conntrack_proto.c | 8 ++++---- net/netfilter/nf_conntrack_proto_dccp.c | 2 -- net/netfilter/nf_conntrack_proto_generic.c | 1 - net/netfilter/nf_conntrack_proto_gre.c | 1 - net/netfilter/nf_conntrack_proto_sctp.c | 2 -- net/netfilter/nf_conntrack_proto_tcp.c | 2 -- net/netfilter/nf_conntrack_proto_udp.c | 4 ---- net/netfilter/nf_conntrack_standalone.c | 17 ++++++++++++++++- 11 files changed, 20 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index b6e27cafb1d9..47c16bae5e00 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -108,9 +108,6 @@ struct nf_conntrack_l4proto { /* Return the per-net protocol part. */ struct nf_proto_net *(*get_net_proto)(struct net *net); - /* Protocol name */ - const char *name; - /* Module (if any) which this is connected to. */ struct module *me; }; diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 73c591d8a9a8..fdbeb03e4600 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -362,7 +362,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly = { .l3proto = PF_INET, .l4proto = IPPROTO_ICMP, - .name = "icmp", .pkt_to_tuple = icmp_pkt_to_tuple, .invert_tuple = icmp_invert_tuple, .print_tuple = icmp_print_tuple, diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index d5f028e33f65..805ab122767a 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -367,7 +367,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = { .l3proto = PF_INET6, .l4proto = IPPROTO_ICMPV6, - .name = "icmpv6", .pkt_to_tuple = icmpv6_pkt_to_tuple, .invert_tuple = icmpv6_invert_tuple, .print_tuple = icmpv6_print_tuple, diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 85104a27cc89..0ecab7163d62 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -437,8 +437,8 @@ int nf_ct_l4proto_register(struct nf_conntrack_l4proto *l4proto[], } if (i != num_proto) { ver = l4proto[i]->l3proto == PF_INET6 ? 6 : 4; - pr_err("nf_conntrack_ipv%d: can't register %s%d proto.\n", - ver, l4proto[i]->name, ver); + pr_err("nf_conntrack_ipv%d: can't register l4 %d proto.\n", + ver, l4proto[i]->l4proto); nf_ct_l4proto_unregister(l4proto, i); } return ret; @@ -458,8 +458,8 @@ int nf_ct_l4proto_pernet_register(struct net *net, break; } if (i != num_proto) { - pr_err("nf_conntrack_%s%d: pernet registration failed\n", - l4proto[i]->name, + pr_err("nf_conntrack_proto_%d %d: pernet registration failed\n", + l4proto[i]->l4proto, l4proto[i]->l3proto == PF_INET6 ? 6 : 4); nf_ct_l4proto_pernet_unregister(net, l4proto, i); } diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 4707d997558a..a0492184a0a8 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -880,7 +880,6 @@ static struct nf_proto_net *dccp_get_net_proto(struct net *net) struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4 __read_mostly = { .l3proto = AF_INET, .l4proto = IPPROTO_DCCP, - .name = "dccp", .pkt_to_tuple = dccp_pkt_to_tuple, .invert_tuple = dccp_invert_tuple, .new = dccp_new, @@ -916,7 +915,6 @@ EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_dccp4); struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6 __read_mostly = { .l3proto = AF_INET6, .l4proto = IPPROTO_DCCP, - .name = "dccp", .pkt_to_tuple = dccp_pkt_to_tuple, .invert_tuple = dccp_invert_tuple, .new = dccp_new, diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index d5868bad33a7..4fe8b3312823 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -187,7 +187,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = { .l3proto = PF_UNSPEC, .l4proto = 255, - .name = "unknown", .pkt_to_tuple = generic_pkt_to_tuple, .invert_tuple = generic_invert_tuple, .print_tuple = generic_print_tuple, diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 87bb40a3feb5..984bcfdbd4d7 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -364,7 +364,6 @@ static int gre_init_net(struct net *net, u_int16_t proto) static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { .l3proto = AF_INET, .l4proto = IPPROTO_GRE, - .name = "gre", .pkt_to_tuple = gre_pkt_to_tuple, .invert_tuple = gre_invert_tuple, .print_tuple = gre_print_tuple, diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 6eef29d2eec4..1d7a995ea049 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -791,7 +791,6 @@ static struct nf_proto_net *sctp_get_net_proto(struct net *net) struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .l3proto = PF_INET, .l4proto = IPPROTO_SCTP, - .name = "sctp", .pkt_to_tuple = sctp_pkt_to_tuple, .invert_tuple = sctp_invert_tuple, .print_tuple = sctp_print_tuple, @@ -828,7 +827,6 @@ EXPORT_SYMBOL_GPL(nf_conntrack_l4proto_sctp4); struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .l3proto = PF_INET6, .l4proto = IPPROTO_SCTP, - .name = "sctp", .pkt_to_tuple = sctp_pkt_to_tuple, .invert_tuple = sctp_invert_tuple, .print_tuple = sctp_print_tuple, diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 9758a7dfd83e..e3e59e3d0592 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -1556,7 +1556,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = { .l3proto = PF_INET, .l4proto = IPPROTO_TCP, - .name = "tcp", .pkt_to_tuple = tcp_pkt_to_tuple, .invert_tuple = tcp_invert_tuple, .print_tuple = tcp_print_tuple, @@ -1594,7 +1593,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly = { .l3proto = PF_INET6, .l4proto = IPPROTO_TCP, - .name = "tcp", .pkt_to_tuple = tcp_pkt_to_tuple, .invert_tuple = tcp_invert_tuple, .print_tuple = tcp_print_tuple, diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index f6ebce6178ca..ec861a1169f1 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -313,7 +313,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = { .l3proto = PF_INET, .l4proto = IPPROTO_UDP, - .name = "udp", .allow_clash = true, .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, @@ -347,7 +346,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly = { .l3proto = PF_INET, .l4proto = IPPROTO_UDPLITE, - .name = "udplite", .allow_clash = true, .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, @@ -381,7 +379,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly = { .l3proto = PF_INET6, .l4proto = IPPROTO_UDP, - .name = "udp", .allow_clash = true, .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, @@ -415,7 +412,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = { .l3proto = PF_INET6, .l4proto = IPPROTO_UDPLITE, - .name = "udplite", .allow_clash = true, .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 359d7e6a4503..b28f9e93f574 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -208,6 +208,21 @@ static const char* l3proto_name(u16 proto) return "unknown"; } +static const char* l4proto_name(u16 proto) +{ + switch (proto) { + case IPPROTO_ICMP: return "icmp"; + case IPPROTO_TCP: return "tcp"; + case IPPROTO_UDP: return "udp"; + case IPPROTO_DCCP: return "dccp"; + case IPPROTO_GRE: return "gre"; + case IPPROTO_SCTP: return "sctp"; + case IPPROTO_UDPLITE: return "udplite"; + } + + return "unknown"; +} + /* return 0 on success, 1 in case of error */ static int ct_seq_show(struct seq_file *s, void *v) { @@ -242,7 +257,7 @@ static int ct_seq_show(struct seq_file *s, void *v) ret = -ENOSPC; seq_printf(s, "%-8s %u %-8s %u %ld ", l3proto_name(l3proto->l3proto), nf_ct_l3num(ct), - l4proto->name, nf_ct_protonum(ct), + l4proto_name(l4proto->l4proto), nf_ct_protonum(ct), nf_ct_expires(ct) / HZ); if (l4proto->print_conntrack) -- cgit v1.2.3 From 91950833dd5a34ac6336aa88da6d43aaeb56ac6d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 12 Aug 2017 00:57:06 +0200 Subject: netfilter: conntrack: place print_tuple in procfs part CONFIG_NF_CONNTRACK_PROCFS is deprecated, no need to use a function pointer in the trackers for this. Place the printf formatting in the one place that uses it. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l3proto.h | 4 -- include/net/netfilter/nf_conntrack_l4proto.h | 4 -- net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c | 8 ---- net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 11 ----- net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c | 8 ---- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 11 ----- net/netfilter/nf_conntrack_l3proto_generic.c | 6 --- net/netfilter/nf_conntrack_proto_dccp.c | 10 ----- net/netfilter/nf_conntrack_proto_generic.c | 7 ---- net/netfilter/nf_conntrack_proto_gre.c | 10 ----- net/netfilter/nf_conntrack_proto_sctp.c | 11 ----- net/netfilter/nf_conntrack_proto_tcp.c | 11 ----- net/netfilter/nf_conntrack_proto_udp.c | 13 ------ net/netfilter/nf_conntrack_standalone.c | 58 +++++++++++++++++++++++++- 14 files changed, 56 insertions(+), 116 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index e31861e4fa6a..dabb53b0913c 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -37,10 +37,6 @@ struct nf_conntrack_l3proto { bool (*invert_tuple)(struct nf_conntrack_tuple *inverse, const struct nf_conntrack_tuple *orig); - /* Print out the per-protocol part of the tuple. */ - void (*print_tuple)(struct seq_file *s, - const struct nf_conntrack_tuple *); - /* * Called before tracking. * *dataoff: offset of protocol header (TCP, UDP,...) in skb diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 15c58dd3f701..7e8da04a5eb6 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -61,10 +61,6 @@ struct nf_conntrack_l4proto { /* called by gc worker if table is full */ bool (*can_early_drop)(const struct nf_conn *ct); - /* Print out the per-protocol part of the tuple. Return like seq_* */ - void (*print_tuple)(struct seq_file *s, - const struct nf_conntrack_tuple *); - /* Print out the private part of the conntrack. */ void (*print_conntrack)(struct seq_file *s, struct nf_conn *); diff --git a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c index 9f7ea862068c..fe374da4bc13 100644 --- a/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c @@ -63,13 +63,6 @@ static bool ipv4_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -static void ipv4_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ - seq_printf(s, "src=%pI4 dst=%pI4 ", - &tuple->src.u3.ip, &tuple->dst.u3.ip); -} - static int ipv4_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, unsigned int *dataoff, u_int8_t *protonum) { @@ -355,7 +348,6 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv4 __read_mostly = { .l3proto = PF_INET, .pkt_to_tuple = ipv4_pkt_to_tuple, .invert_tuple = ipv4_invert_tuple, - .print_tuple = ipv4_print_tuple, .get_l4proto = ipv4_get_l4proto, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .tuple_to_nlattr = ipv4_tuple_to_nlattr, diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index fdbeb03e4600..434b4e20f6db 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -71,16 +71,6 @@ static bool icmp_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -/* Print out the per-protocol part of the tuple. */ -static void icmp_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ - seq_printf(s, "type=%u code=%u id=%u ", - tuple->dst.u.icmp.type, - tuple->dst.u.icmp.code, - ntohs(tuple->src.u.icmp.id)); -} - static unsigned int *icmp_get_timeouts(struct net *net) { return &icmp_pernet(net)->timeout; @@ -364,7 +354,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmp __read_mostly = .l4proto = IPPROTO_ICMP, .pkt_to_tuple = icmp_pkt_to_tuple, .invert_tuple = icmp_invert_tuple, - .print_tuple = icmp_print_tuple, .packet = icmp_packet, .get_timeouts = icmp_get_timeouts, .new = icmp_new, diff --git a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c index 91d37fbe28de..fe01dc953c56 100644 --- a/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_conntrack_l3proto_ipv6.c @@ -67,13 +67,6 @@ static bool ipv6_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -static void ipv6_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ - seq_printf(s, "src=%pI6 dst=%pI6 ", - tuple->src.u3.ip6, tuple->dst.u3.ip6); -} - static int ipv6_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, unsigned int *dataoff, u_int8_t *protonum) { @@ -350,7 +343,6 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_ipv6 __read_mostly = { .l3proto = PF_INET6, .pkt_to_tuple = ipv6_pkt_to_tuple, .invert_tuple = ipv6_invert_tuple, - .print_tuple = ipv6_print_tuple, .get_l4proto = ipv6_get_l4proto, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .tuple_to_nlattr = ipv6_tuple_to_nlattr, diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 805ab122767a..808f63e2e1ff 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -84,16 +84,6 @@ static bool icmpv6_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -/* Print out the per-protocol part of the tuple. */ -static void icmpv6_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ - seq_printf(s, "type=%u code=%u id=%u ", - tuple->dst.u.icmp.type, - tuple->dst.u.icmp.code, - ntohs(tuple->src.u.icmp.id)); -} - static unsigned int *icmpv6_get_timeouts(struct net *net) { return &icmpv6_pernet(net)->timeout; @@ -369,7 +359,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_icmpv6 __read_mostly = .l4proto = IPPROTO_ICMPV6, .pkt_to_tuple = icmpv6_pkt_to_tuple, .invert_tuple = icmpv6_invert_tuple, - .print_tuple = icmpv6_print_tuple, .packet = icmpv6_packet, .get_timeouts = icmpv6_get_timeouts, .new = icmpv6_new, diff --git a/net/netfilter/nf_conntrack_l3proto_generic.c b/net/netfilter/nf_conntrack_l3proto_generic.c index 0387971582bc..397e6911214f 100644 --- a/net/netfilter/nf_conntrack_l3proto_generic.c +++ b/net/netfilter/nf_conntrack_l3proto_generic.c @@ -49,11 +49,6 @@ static bool generic_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -static void generic_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ -} - static int generic_get_l4proto(const struct sk_buff *skb, unsigned int nhoff, unsigned int *dataoff, u_int8_t *protonum) { @@ -66,7 +61,6 @@ struct nf_conntrack_l3proto nf_conntrack_l3proto_generic __read_mostly = { .l3proto = PF_UNSPEC, .pkt_to_tuple = generic_pkt_to_tuple, .invert_tuple = generic_invert_tuple, - .print_tuple = generic_print_tuple, .get_l4proto = generic_get_l4proto, }; EXPORT_SYMBOL_GPL(nf_conntrack_l3proto_generic); diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index a0492184a0a8..d2df49ac390a 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -623,14 +623,6 @@ static bool dccp_can_early_drop(const struct nf_conn *ct) return false; } -static void dccp_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ - seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.dccp.port), - ntohs(tuple->dst.u.dccp.port)); -} - static void dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct) { seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]); @@ -887,7 +879,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4 __read_mostly = { .get_timeouts = dccp_get_timeouts, .error = dccp_error, .can_early_drop = dccp_can_early_drop, - .print_tuple = dccp_print_tuple, .print_conntrack = dccp_print_conntrack, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .to_nlattr = dccp_to_nlattr, @@ -922,7 +913,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6 __read_mostly = { .get_timeouts = dccp_get_timeouts, .error = dccp_error, .can_early_drop = dccp_can_early_drop, - .print_tuple = dccp_print_tuple, .print_conntrack = dccp_print_conntrack, #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .to_nlattr = dccp_to_nlattr, diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index 4fe8b3312823..2bc3d0c1a5bf 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -62,12 +62,6 @@ static bool generic_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -/* Print out the per-protocol part of the tuple. */ -static void generic_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ -} - static unsigned int *generic_get_timeouts(struct net *net) { return &(generic_pernet(net)->timeout); @@ -189,7 +183,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_generic __read_mostly = .l4proto = 255, .pkt_to_tuple = generic_pkt_to_tuple, .invert_tuple = generic_invert_tuple, - .print_tuple = generic_print_tuple, .packet = generic_packet, .get_timeouts = generic_get_timeouts, .new = generic_new, diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index 984bcfdbd4d7..cd28095dd7a4 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -224,15 +224,6 @@ static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, return true; } -/* print gre part of tuple */ -static void gre_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ - seq_printf(s, "srckey=0x%x dstkey=0x%x ", - ntohs(tuple->src.u.gre.key), - ntohs(tuple->dst.u.gre.key)); -} - /* print private data for conntrack */ static void gre_print_conntrack(struct seq_file *s, struct nf_conn *ct) { @@ -366,7 +357,6 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { .l4proto = IPPROTO_GRE, .pkt_to_tuple = gre_pkt_to_tuple, .invert_tuple = gre_invert_tuple, - .print_tuple = gre_print_tuple, .print_conntrack = gre_print_conntrack, .get_timeouts = gre_get_timeouts, .packet = gre_packet, diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 1d7a995ea049..da83b401be17 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -174,15 +174,6 @@ static bool sctp_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -/* Print out the per-protocol part of the tuple. */ -static void sctp_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ - seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.sctp.port), - ntohs(tuple->dst.u.sctp.port)); -} - /* Print out the private part of the conntrack. */ static void sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct) { @@ -793,7 +784,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .l4proto = IPPROTO_SCTP, .pkt_to_tuple = sctp_pkt_to_tuple, .invert_tuple = sctp_invert_tuple, - .print_tuple = sctp_print_tuple, .print_conntrack = sctp_print_conntrack, .packet = sctp_packet, .get_timeouts = sctp_get_timeouts, @@ -829,7 +819,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .l4proto = IPPROTO_SCTP, .pkt_to_tuple = sctp_pkt_to_tuple, .invert_tuple = sctp_invert_tuple, - .print_tuple = sctp_print_tuple, .print_conntrack = sctp_print_conntrack, .packet = sctp_packet, .get_timeouts = sctp_get_timeouts, diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index e3e59e3d0592..c868b36b8945 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -301,15 +301,6 @@ static bool tcp_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -/* Print out the per-protocol part of the tuple. */ -static void tcp_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ - seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.tcp.port), - ntohs(tuple->dst.u.tcp.port)); -} - /* Print out the private part of the conntrack. */ static void tcp_print_conntrack(struct seq_file *s, struct nf_conn *ct) { @@ -1558,7 +1549,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = .l4proto = IPPROTO_TCP, .pkt_to_tuple = tcp_pkt_to_tuple, .invert_tuple = tcp_invert_tuple, - .print_tuple = tcp_print_tuple, .print_conntrack = tcp_print_conntrack, .packet = tcp_packet, .get_timeouts = tcp_get_timeouts, @@ -1595,7 +1585,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly = .l4proto = IPPROTO_TCP, .pkt_to_tuple = tcp_pkt_to_tuple, .invert_tuple = tcp_invert_tuple, - .print_tuple = tcp_print_tuple, .print_conntrack = tcp_print_conntrack, .packet = tcp_packet, .get_timeouts = tcp_get_timeouts, diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index ec861a1169f1..dcf3030d2226 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -63,15 +63,6 @@ static bool udp_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } -/* Print out the per-protocol part of the tuple. */ -static void udp_print_tuple(struct seq_file *s, - const struct nf_conntrack_tuple *tuple) -{ - seq_printf(s, "sport=%hu dport=%hu ", - ntohs(tuple->src.u.udp.port), - ntohs(tuple->dst.u.udp.port)); -} - static unsigned int *udp_get_timeouts(struct net *net) { return udp_pernet(net)->timeouts; @@ -316,7 +307,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp4 __read_mostly = .allow_clash = true, .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, - .print_tuple = udp_print_tuple, .packet = udp_packet, .get_timeouts = udp_get_timeouts, .new = udp_new, @@ -349,7 +339,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite4 __read_mostly = .allow_clash = true, .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, - .print_tuple = udp_print_tuple, .packet = udp_packet, .get_timeouts = udp_get_timeouts, .new = udp_new, @@ -382,7 +371,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udp6 __read_mostly = .allow_clash = true, .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, - .print_tuple = udp_print_tuple, .packet = udp_packet, .get_timeouts = udp_get_timeouts, .new = udp_new, @@ -415,7 +403,6 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_udplite6 __read_mostly = .allow_clash = true, .pkt_to_tuple = udp_pkt_to_tuple, .invert_tuple = udp_invert_tuple, - .print_tuple = udp_print_tuple, .packet = udp_packet, .get_timeouts = udp_get_timeouts, .new = udp_new, diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index b28f9e93f574..9eb85858d764 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -41,8 +41,62 @@ print_tuple(struct seq_file *s, const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_l3proto *l3proto, const struct nf_conntrack_l4proto *l4proto) { - l3proto->print_tuple(s, tuple); - l4proto->print_tuple(s, tuple); + switch (l3proto->l3proto) { + case NFPROTO_IPV4: + seq_printf(s, "src=%pI4 dst=%pI4 ", + &tuple->src.u3.ip, &tuple->dst.u3.ip); + break; + case NFPROTO_IPV6: + seq_printf(s, "src=%pI6 dst=%pI6 ", + tuple->src.u3.ip6, tuple->dst.u3.ip6); + break; + default: + break; + } + + switch (l4proto->l4proto) { + case IPPROTO_ICMP: + seq_printf(s, "type=%u code=%u id=%u ", + tuple->dst.u.icmp.type, + tuple->dst.u.icmp.code, + ntohs(tuple->src.u.icmp.id)); + break; + case IPPROTO_TCP: + seq_printf(s, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.tcp.port), + ntohs(tuple->dst.u.tcp.port)); + break; + case IPPROTO_UDPLITE: /* fallthrough */ + case IPPROTO_UDP: + seq_printf(s, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.udp.port), + ntohs(tuple->dst.u.udp.port)); + + break; + case IPPROTO_DCCP: + seq_printf(s, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.dccp.port), + ntohs(tuple->dst.u.dccp.port)); + break; + case IPPROTO_SCTP: + seq_printf(s, "sport=%hu dport=%hu ", + ntohs(tuple->src.u.sctp.port), + ntohs(tuple->dst.u.sctp.port)); + break; + case IPPROTO_ICMPV6: + seq_printf(s, "type=%u code=%u id=%u ", + tuple->dst.u.icmp.type, + tuple->dst.u.icmp.code, + ntohs(tuple->src.u.icmp.id)); + break; + case IPPROTO_GRE: + seq_printf(s, "srckey=0x%x dstkey=0x%x ", + ntohs(tuple->src.u.gre.key), + ntohs(tuple->dst.u.gre.key)); + break; + default: + break; + } } EXPORT_SYMBOL_GPL(print_tuple); -- cgit v1.2.3 From ea48cc83cf612fddb4e8868369348b9f936cfedb Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 12 Aug 2017 00:57:07 +0200 Subject: netfilter: conntrack: print_conntrack only needed if CONFIG_NF_CONNTRACK_PROCFS Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l4proto.h | 7 ++++--- net/netfilter/nf_conntrack_proto_dccp.c | 6 ++++++ net/netfilter/nf_conntrack_proto_gre.c | 4 ++++ net/netfilter/nf_conntrack_proto_sctp.c | 6 ++++++ net/netfilter/nf_conntrack_proto_tcp.c | 6 ++++++ 5 files changed, 26 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 7e8da04a5eb6..4976ef92dc78 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -61,9 +61,6 @@ struct nf_conntrack_l4proto { /* called by gc worker if table is full */ bool (*can_early_drop)(const struct nf_conn *ct); - /* Print out the private part of the conntrack. */ - void (*print_conntrack)(struct seq_file *s, struct nf_conn *); - /* Return the array of timeouts for this protocol. */ unsigned int *(*get_timeouts)(struct net *net); @@ -96,6 +93,10 @@ struct nf_conntrack_l4proto { u16 nlattr_max; const struct nla_policy *nla_policy; } ctnl_timeout; +#endif +#ifdef CONFIG_NF_CONNTRACK_PROCFS + /* Print out the private part of the conntrack. */ + void (*print_conntrack)(struct seq_file *s, struct nf_conn *); #endif unsigned int *net_id; /* Init l4proto pernet data */ diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index d2df49ac390a..188347571fc7 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -623,10 +623,12 @@ static bool dccp_can_early_drop(const struct nf_conn *ct) return false; } +#ifdef CONFIG_NF_CONNTRACK_PROCFS static void dccp_print_conntrack(struct seq_file *s, struct nf_conn *ct) { seq_printf(s, "%s ", dccp_state_names[ct->proto.dccp.state]); } +#endif #if IS_ENABLED(CONFIG_NF_CT_NETLINK) static int dccp_to_nlattr(struct sk_buff *skb, struct nlattr *nla, @@ -879,7 +881,9 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp4 __read_mostly = { .get_timeouts = dccp_get_timeouts, .error = dccp_error, .can_early_drop = dccp_can_early_drop, +#ifdef CONFIG_NF_CONNTRACK_PROCFS .print_conntrack = dccp_print_conntrack, +#endif #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .to_nlattr = dccp_to_nlattr, .nlattr_size = dccp_nlattr_size, @@ -913,7 +917,9 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_dccp6 __read_mostly = { .get_timeouts = dccp_get_timeouts, .error = dccp_error, .can_early_drop = dccp_can_early_drop, +#ifdef CONFIG_NF_CONNTRACK_PROCFS .print_conntrack = dccp_print_conntrack, +#endif #if IS_ENABLED(CONFIG_NF_CT_NETLINK) .to_nlattr = dccp_to_nlattr, .nlattr_size = dccp_nlattr_size, diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index cd28095dd7a4..c0e3a23ac23a 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -224,6 +224,7 @@ static bool gre_pkt_to_tuple(const struct sk_buff *skb, unsigned int dataoff, return true; } +#ifdef CONFIG_NF_CONNTRACK_PROCFS /* print private data for conntrack */ static void gre_print_conntrack(struct seq_file *s, struct nf_conn *ct) { @@ -231,6 +232,7 @@ static void gre_print_conntrack(struct seq_file *s, struct nf_conn *ct) (ct->proto.gre.timeout / HZ), (ct->proto.gre.stream_timeout / HZ)); } +#endif static unsigned int *gre_get_timeouts(struct net *net) { @@ -357,7 +359,9 @@ static struct nf_conntrack_l4proto nf_conntrack_l4proto_gre4 __read_mostly = { .l4proto = IPPROTO_GRE, .pkt_to_tuple = gre_pkt_to_tuple, .invert_tuple = gre_invert_tuple, +#ifdef CONFIG_NF_CONNTRACK_PROCFS .print_conntrack = gre_print_conntrack, +#endif .get_timeouts = gre_get_timeouts, .packet = gre_packet, .new = gre_new, diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index da83b401be17..890b5c73368d 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -174,11 +174,13 @@ static bool sctp_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } +#ifdef CONFIG_NF_CONNTRACK_PROCFS /* Print out the private part of the conntrack. */ static void sctp_print_conntrack(struct seq_file *s, struct nf_conn *ct) { seq_printf(s, "%s ", sctp_conntrack_names[ct->proto.sctp.state]); } +#endif #define for_each_sctp_chunk(skb, sch, _sch, offset, dataoff, count) \ for ((offset) = (dataoff) + sizeof(struct sctphdr), (count) = 0; \ @@ -784,7 +786,9 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp4 __read_mostly = { .l4proto = IPPROTO_SCTP, .pkt_to_tuple = sctp_pkt_to_tuple, .invert_tuple = sctp_invert_tuple, +#ifdef CONFIG_NF_CONNTRACK_PROCFS .print_conntrack = sctp_print_conntrack, +#endif .packet = sctp_packet, .get_timeouts = sctp_get_timeouts, .new = sctp_new, @@ -819,7 +823,9 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_sctp6 __read_mostly = { .l4proto = IPPROTO_SCTP, .pkt_to_tuple = sctp_pkt_to_tuple, .invert_tuple = sctp_invert_tuple, +#ifdef CONFIG_NF_CONNTRACK_PROCFS .print_conntrack = sctp_print_conntrack, +#endif .packet = sctp_packet, .get_timeouts = sctp_get_timeouts, .new = sctp_new, diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index c868b36b8945..33c52d9ab2f5 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -301,11 +301,13 @@ static bool tcp_invert_tuple(struct nf_conntrack_tuple *tuple, return true; } +#ifdef CONFIG_NF_CONNTRACK_PROCFS /* Print out the private part of the conntrack. */ static void tcp_print_conntrack(struct seq_file *s, struct nf_conn *ct) { seq_printf(s, "%s ", tcp_conntrack_names[ct->proto.tcp.state]); } +#endif static unsigned int get_conntrack_index(const struct tcphdr *tcph) { @@ -1549,7 +1551,9 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp4 __read_mostly = .l4proto = IPPROTO_TCP, .pkt_to_tuple = tcp_pkt_to_tuple, .invert_tuple = tcp_invert_tuple, +#ifdef CONFIG_NF_CONNTRACK_PROCFS .print_conntrack = tcp_print_conntrack, +#endif .packet = tcp_packet, .get_timeouts = tcp_get_timeouts, .new = tcp_new, @@ -1585,7 +1589,9 @@ struct nf_conntrack_l4proto nf_conntrack_l4proto_tcp6 __read_mostly = .l4proto = IPPROTO_TCP, .pkt_to_tuple = tcp_pkt_to_tuple, .invert_tuple = tcp_invert_tuple, +#ifdef CONFIG_NF_CONNTRACK_PROCFS .print_conntrack = tcp_print_conntrack, +#endif .packet = tcp_packet, .get_timeouts = tcp_get_timeouts, .new = tcp_new, -- cgit v1.2.3 From b3480fe059ac9121b5714205b4ddae14b59ef4be Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Sat, 12 Aug 2017 00:57:08 +0200 Subject: netfilter: conntrack: make protocol tracker pointers const Doesn't change generated code, but will make it easier to eventually make the actual trackers themselvers const. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_conntrack_l3proto.h | 6 +++--- include/net/netfilter/nf_conntrack_l4proto.h | 4 ++-- include/net/netfilter/nf_conntrack_timeout.h | 2 +- net/netfilter/nf_conntrack_core.c | 12 ++++++------ net/netfilter/nf_conntrack_netlink.c | 22 +++++++++++----------- net/netfilter/nf_conntrack_proto.c | 20 ++++++++++---------- net/netfilter/nfnetlink_cttimeout.c | 14 +++++++------- net/netfilter/xt_CT.c | 2 +- net/openvswitch/conntrack.c | 4 ++-- 9 files changed, 43 insertions(+), 43 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l3proto.h b/include/net/netfilter/nf_conntrack_l3proto.h index dabb53b0913c..6269deecbee7 100644 --- a/include/net/netfilter/nf_conntrack_l3proto.h +++ b/include/net/netfilter/nf_conntrack_l3proto.h @@ -64,10 +64,10 @@ struct nf_conntrack_l3proto { extern struct nf_conntrack_l3proto __rcu *nf_ct_l3protos[NFPROTO_NUMPROTO]; /* Protocol global registration. */ -int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto); -void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto); +int nf_ct_l3proto_register(const struct nf_conntrack_l3proto *proto); +void nf_ct_l3proto_unregister(const struct nf_conntrack_l3proto *proto); -struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto); +const struct nf_conntrack_l3proto *nf_ct_l3proto_find_get(u_int16_t l3proto); /* Existing built-in protocols */ extern struct nf_conntrack_l3proto nf_conntrack_l3proto_generic; diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index 4976ef92dc78..d4933d56809d 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -114,10 +114,10 @@ extern struct nf_conntrack_l4proto nf_conntrack_l4proto_generic; #define MAX_NF_CT_PROTO 256 -struct nf_conntrack_l4proto *__nf_ct_l4proto_find(u_int16_t l3proto, +const struct nf_conntrack_l4proto *__nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto); -struct nf_conntrack_l4proto *nf_ct_l4proto_find_get(u_int16_t l3proto, +const struct nf_conntrack_l4proto *nf_ct_l4proto_find_get(u_int16_t l3proto, u_int8_t l4proto); void nf_ct_l4proto_put(const struct nf_conntrack_l4proto *p); diff --git a/include/net/netfilter/nf_conntrack_timeout.h b/include/net/netfilter/nf_conntrack_timeout.h index b222957062b5..483d104fa254 100644 --- a/include/net/netfilter/nf_conntrack_timeout.h +++ b/include/net/netfilter/nf_conntrack_timeout.h @@ -16,7 +16,7 @@ struct ctnl_timeout { refcount_t refcnt; char name[CTNL_TIMEOUT_NAME_MAX]; __u16 l3num; - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l4proto *l4proto; char data[0]; }; diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index f2f00eaf217d..c23df7c9cd59 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -250,8 +250,8 @@ bool nf_ct_get_tuplepr(const struct sk_buff *skb, unsigned int nhoff, u_int16_t l3num, struct net *net, struct nf_conntrack_tuple *tuple) { - struct nf_conntrack_l3proto *l3proto; - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l3proto *l3proto; + const struct nf_conntrack_l4proto *l4proto; unsigned int protoff; u_int8_t protonum; int ret; @@ -400,7 +400,7 @@ static void destroy_conntrack(struct nf_conntrack *nfct) { struct nf_conn *ct = (struct nf_conn *)nfct; - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l4proto *l4proto; pr_debug("destroy_conntrack(%p)\n", ct); NF_CT_ASSERT(atomic_read(&nfct->use) == 0); @@ -694,7 +694,7 @@ static int nf_ct_resolve_clash(struct net *net, struct sk_buff *skb, { /* This is the conntrack entry already in hashes that won race. */ struct nf_conn *ct = nf_ct_tuplehash_to_ctrack(h); - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l4proto *l4proto; l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); if (l4proto->allow_clash && @@ -1344,10 +1344,10 @@ unsigned int nf_conntrack_in(struct net *net, u_int8_t pf, unsigned int hooknum, struct sk_buff *skb) { + const struct nf_conntrack_l3proto *l3proto; + const struct nf_conntrack_l4proto *l4proto; struct nf_conn *ct, *tmpl; enum ip_conntrack_info ctinfo; - struct nf_conntrack_l3proto *l3proto; - struct nf_conntrack_l4proto *l4proto; unsigned int *timeouts; unsigned int dataoff; u_int8_t protonum; diff --git a/net/netfilter/nf_conntrack_netlink.c b/net/netfilter/nf_conntrack_netlink.c index b59a453a0fd8..de4053d84364 100644 --- a/net/netfilter/nf_conntrack_netlink.c +++ b/net/netfilter/nf_conntrack_netlink.c @@ -109,9 +109,9 @@ nla_put_failure: static int ctnetlink_dump_tuples(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple) { + const struct nf_conntrack_l3proto *l3proto; + const struct nf_conntrack_l4proto *l4proto; int ret; - struct nf_conntrack_l3proto *l3proto; - struct nf_conntrack_l4proto *l4proto; rcu_read_lock(); l3proto = __nf_ct_l3proto_find(tuple->src.l3num); @@ -163,7 +163,7 @@ nla_put_failure: static int ctnetlink_dump_protoinfo(struct sk_buff *skb, struct nf_conn *ct) { - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l4proto *l4proto; struct nlattr *nest_proto; int ret; @@ -535,9 +535,9 @@ nla_put_failure: static inline size_t ctnetlink_proto_size(const struct nf_conn *ct) { - struct nf_conntrack_l3proto *l3proto; - struct nf_conntrack_l4proto *l4proto; - size_t len = 0; + const struct nf_conntrack_l3proto *l3proto; + const struct nf_conntrack_l4proto *l4proto; + size_t len; l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); len = l3proto->nla_size; @@ -936,8 +936,8 @@ static const struct nla_policy proto_nla_policy[CTA_PROTO_MAX+1] = { static int ctnetlink_parse_tuple_proto(struct nlattr *attr, struct nf_conntrack_tuple *tuple) { + const struct nf_conntrack_l4proto *l4proto; struct nlattr *tb[CTA_PROTO_MAX+1]; - struct nf_conntrack_l4proto *l4proto; int ret = 0; ret = nla_parse_nested(tb, CTA_PROTO_MAX, attr, proto_nla_policy, @@ -1580,8 +1580,8 @@ static int ctnetlink_change_protoinfo(struct nf_conn *ct, const struct nlattr * const cda[]) { const struct nlattr *attr = cda[CTA_PROTOINFO]; + const struct nf_conntrack_l4proto *l4proto; struct nlattr *tb[CTA_PROTOINFO_MAX+1]; - struct nf_conntrack_l4proto *l4proto; int err = 0; err = nla_parse_nested(tb, CTA_PROTOINFO_MAX, attr, protoinfo_policy, @@ -2475,11 +2475,11 @@ static int ctnetlink_exp_dump_mask(struct sk_buff *skb, const struct nf_conntrack_tuple *tuple, const struct nf_conntrack_tuple_mask *mask) { - int ret; - struct nf_conntrack_l3proto *l3proto; - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l3proto *l3proto; + const struct nf_conntrack_l4proto *l4proto; struct nf_conntrack_tuple m; struct nlattr *nest_parms; + int ret; memset(&m, 0xFF, sizeof(m)); memcpy(&m.src.u3, &mask->src.u3, sizeof(m.src.u3)); diff --git a/net/netfilter/nf_conntrack_proto.c b/net/netfilter/nf_conntrack_proto.c index 0ecab7163d62..b3e489c859ec 100644 --- a/net/netfilter/nf_conntrack_proto.c +++ b/net/netfilter/nf_conntrack_proto.c @@ -65,7 +65,7 @@ nf_ct_unregister_sysctl(struct ctl_table_header **header, } #endif -struct nf_conntrack_l4proto * +const struct nf_conntrack_l4proto * __nf_ct_l4proto_find(u_int16_t l3proto, u_int8_t l4proto) { if (unlikely(l3proto >= NFPROTO_NUMPROTO || nf_ct_protos[l3proto] == NULL)) @@ -77,7 +77,7 @@ EXPORT_SYMBOL_GPL(__nf_ct_l4proto_find); /* this is guaranteed to always return a valid protocol helper, since * it falls back to generic_protocol */ -struct nf_conntrack_l3proto * +const struct nf_conntrack_l3proto * nf_ct_l3proto_find_get(u_int16_t l3proto) { struct nf_conntrack_l3proto *p; @@ -95,8 +95,8 @@ EXPORT_SYMBOL_GPL(nf_ct_l3proto_find_get); int nf_ct_l3proto_try_module_get(unsigned short l3proto) { + const struct nf_conntrack_l3proto *p; int ret; - struct nf_conntrack_l3proto *p; retry: p = nf_ct_l3proto_find_get(l3proto); if (p == &nf_conntrack_l3proto_generic) { @@ -173,10 +173,10 @@ void nf_ct_netns_put(struct net *net, u8 nfproto) } EXPORT_SYMBOL_GPL(nf_ct_netns_put); -struct nf_conntrack_l4proto * +const struct nf_conntrack_l4proto * nf_ct_l4proto_find_get(u_int16_t l3num, u_int8_t l4num) { - struct nf_conntrack_l4proto *p; + const struct nf_conntrack_l4proto *p; rcu_read_lock(); p = __nf_ct_l4proto_find(l3num, l4num); @@ -196,18 +196,18 @@ EXPORT_SYMBOL_GPL(nf_ct_l4proto_put); static int kill_l3proto(struct nf_conn *i, void *data) { - return nf_ct_l3num(i) == ((struct nf_conntrack_l3proto *)data)->l3proto; + return nf_ct_l3num(i) == ((const struct nf_conntrack_l3proto *)data)->l3proto; } static int kill_l4proto(struct nf_conn *i, void *data) { - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l4proto *l4proto; l4proto = data; return nf_ct_protonum(i) == l4proto->l4proto && nf_ct_l3num(i) == l4proto->l3proto; } -int nf_ct_l3proto_register(struct nf_conntrack_l3proto *proto) +int nf_ct_l3proto_register(const struct nf_conntrack_l3proto *proto) { int ret = 0; struct nf_conntrack_l3proto *old; @@ -235,7 +235,7 @@ out_unlock: } EXPORT_SYMBOL_GPL(nf_ct_l3proto_register); -void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto) +void nf_ct_l3proto_unregister(const struct nf_conntrack_l3proto *proto) { BUG_ON(proto->l3proto >= NFPROTO_NUMPROTO); @@ -249,7 +249,7 @@ void nf_ct_l3proto_unregister(struct nf_conntrack_l3proto *proto) synchronize_rcu(); /* Remove all contrack entries for this protocol */ - nf_ct_iterate_destroy(kill_l3proto, proto); + nf_ct_iterate_destroy(kill_l3proto, (void*)proto); } EXPORT_SYMBOL_GPL(nf_ct_l3proto_unregister); diff --git a/net/netfilter/nfnetlink_cttimeout.c b/net/netfilter/nfnetlink_cttimeout.c index fcabccc99f0d..32b1c0b44e79 100644 --- a/net/netfilter/nfnetlink_cttimeout.c +++ b/net/netfilter/nfnetlink_cttimeout.c @@ -75,7 +75,7 @@ static int cttimeout_new_timeout(struct net *net, struct sock *ctnl, { __u16 l3num; __u8 l4num; - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l4proto *l4proto; struct ctnl_timeout *timeout, *matching = NULL; char *name; int ret; @@ -159,7 +159,7 @@ ctnl_timeout_fill_info(struct sk_buff *skb, u32 portid, u32 seq, u32 type, struct nlmsghdr *nlh; struct nfgenmsg *nfmsg; unsigned int flags = portid ? NLM_F_MULTI : 0; - struct nf_conntrack_l4proto *l4proto = timeout->l4proto; + const struct nf_conntrack_l4proto *l4proto = timeout->l4proto; event = nfnl_msg_type(NFNL_SUBSYS_CTNETLINK_TIMEOUT, event); nlh = nlmsg_put(skb, portid, seq, event, sizeof(*nfmsg), flags); @@ -364,10 +364,10 @@ static int cttimeout_default_set(struct net *net, struct sock *ctnl, const struct nlattr * const cda[], struct netlink_ext_ack *extack) { + const struct nf_conntrack_l4proto *l4proto; + unsigned int *timeouts; __u16 l3num; __u8 l4num; - struct nf_conntrack_l4proto *l4proto; - unsigned int *timeouts; int ret; if (!cda[CTA_TIMEOUT_L3PROTO] || @@ -454,11 +454,11 @@ static int cttimeout_default_get(struct net *net, struct sock *ctnl, const struct nlattr * const cda[], struct netlink_ext_ack *extack) { - __u16 l3num; - __u8 l4num; - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l4proto *l4proto; struct sk_buff *skb2; int ret, err; + __u16 l3num; + __u8 l4num; if (!cda[CTA_TIMEOUT_L3PROTO] || !cda[CTA_TIMEOUT_L4PROTO]) return -EINVAL; diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index 623ef37de886..5a152e2acfd5 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -121,9 +121,9 @@ xt_ct_set_timeout(struct nf_conn *ct, const struct xt_tgchk_param *par, { #ifdef CONFIG_NF_CONNTRACK_TIMEOUT typeof(nf_ct_timeout_find_get_hook) timeout_find_get; + const struct nf_conntrack_l4proto *l4proto; struct ctnl_timeout *timeout; struct nf_conn_timeout *timeout_ext; - struct nf_conntrack_l4proto *l4proto; int ret = 0; u8 proto; diff --git a/net/openvswitch/conntrack.c b/net/openvswitch/conntrack.c index e3c4c6c3fef7..283363534647 100644 --- a/net/openvswitch/conntrack.c +++ b/net/openvswitch/conntrack.c @@ -579,8 +579,8 @@ static struct nf_conn * ovs_ct_find_existing(struct net *net, const struct nf_conntrack_zone *zone, u8 l3num, struct sk_buff *skb, bool natted) { - struct nf_conntrack_l3proto *l3proto; - struct nf_conntrack_l4proto *l4proto; + const struct nf_conntrack_l3proto *l3proto; + const struct nf_conntrack_l4proto *l4proto; struct nf_conntrack_tuple tuple; struct nf_conntrack_tuple_hash *h; struct nf_conn *ct; -- cgit v1.2.3 From c816c2558ed45e2db522ce8400fae82be6662415 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Tue, 15 Aug 2017 10:50:34 +0100 Subject: netfilter: ebtables: fix indent on if statements The returns on some if statements are not indented correctly, add in the missing tab. Signed-off-by: Colin Ian King Signed-off-by: Pablo Neira Ayuso --- net/bridge/netfilter/ebt_ip.c | 4 ++-- net/bridge/netfilter/ebt_ip6.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/bridge/netfilter/ebt_ip.c b/net/bridge/netfilter/ebt_ip.c index d06968bdf5ec..2b46c50abce0 100644 --- a/net/bridge/netfilter/ebt_ip.c +++ b/net/bridge/netfilter/ebt_ip.c @@ -64,14 +64,14 @@ ebt_ip_mt(const struct sk_buff *skb, struct xt_action_param *par) if (NF_INVF(info, EBT_IP_DPORT, dst < info->dport[0] || dst > info->dport[1])) - return false; + return false; } if (info->bitmask & EBT_IP_SPORT) { u32 src = ntohs(pptr->src); if (NF_INVF(info, EBT_IP_SPORT, src < info->sport[0] || src > info->sport[1])) - return false; + return false; } } return true; diff --git a/net/bridge/netfilter/ebt_ip6.c b/net/bridge/netfilter/ebt_ip6.c index 4617491be41e..2a5a52a53ec4 100644 --- a/net/bridge/netfilter/ebt_ip6.c +++ b/net/bridge/netfilter/ebt_ip6.c @@ -89,7 +89,7 @@ ebt_ip6_mt(const struct sk_buff *skb, struct xt_action_param *par) if (NF_INVF(info, EBT_IP6_SPORT, src < info->sport[0] || src > info->sport[1])) - return false; + return false; } if ((info->bitmask & EBT_IP6_ICMP6) && NF_INVF(info, EBT_IP6_ICMP6, -- cgit v1.2.3 From d08adb82fda523cb51a767e2833c5c5bb0906162 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Thu, 24 Aug 2017 12:33:03 +0200 Subject: xdp: remove bpf_warn_invalid_xdp_redirect Given there is a tracepoint that can track the error code of xdp_do_redirect calls, the WARN_ONCE in bpf_warn_invalid_xdp_redirect doesn't seem relevant any longer. Simply remove the function. Signed-off-by: Jesper Dangaard Brouer Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/core/filter.c | 8 -------- 1 file changed, 8 deletions(-) (limited to 'net') diff --git a/net/core/filter.c b/net/core/filter.c index 2a0d762a20d8..a5e5f31b41b9 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2476,7 +2476,6 @@ static int __bpf_tx_xdp(struct net_device *dev, int err; if (!dev->netdev_ops->ndo_xdp_xmit) { - bpf_warn_invalid_xdp_redirect(dev->ifindex); return -EOPNOTSUPP; } @@ -2543,7 +2542,6 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, fwd = dev_get_by_index_rcu(dev_net(dev), index); ri->ifindex = 0; if (unlikely(!fwd)) { - bpf_warn_invalid_xdp_redirect(index); err = -EINVAL; goto out; } @@ -2564,7 +2562,6 @@ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb) dev = dev_get_by_index_rcu(dev_net(dev), index); ri->ifindex = 0; if (unlikely(!dev)) { - bpf_warn_invalid_xdp_redirect(index); return -EINVAL; } @@ -3565,11 +3562,6 @@ void bpf_warn_invalid_xdp_action(u32 act) } EXPORT_SYMBOL_GPL(bpf_warn_invalid_xdp_action); -void bpf_warn_invalid_xdp_redirect(u32 ifindex) -{ - WARN_ONCE(1, "Illegal XDP redirect to unsupported device ifindex(%i)\n", ifindex); -} - static bool __is_valid_sock_ops_access(int off, int size) { if (off < 0 || off >= sizeof(struct bpf_sock_ops)) -- cgit v1.2.3 From 2facaad6000f2322eb40ca379aced31c957f0a41 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Thu, 24 Aug 2017 12:33:08 +0200 Subject: xdp: make generic xdp redirect use tracepoint trace_xdp_redirect If the xdp_do_generic_redirect() call fails, it trigger the trace_xdp_exception tracepoint. It seems better to use the same tracepoint trace_xdp_redirect, as the native xdp_do_redirect{,_map} does. Signed-off-by: Jesper Dangaard Brouer Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/linux/filter.h | 3 ++- net/core/dev.c | 4 ++-- net/core/filter.c | 35 +++++++++++++++++++++++------------ 3 files changed, 27 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/include/linux/filter.h b/include/linux/filter.h index 7015116331af..d29e58fde364 100644 --- a/include/linux/filter.h +++ b/include/linux/filter.h @@ -718,7 +718,8 @@ struct bpf_prog *bpf_patch_insn_single(struct bpf_prog *prog, u32 off, * because we only track one map and force a flush when the map changes. * This does not appear to be a real limitation for existing software. */ -int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb); +int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb, + struct bpf_prog *prog); int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, struct bpf_prog *prog); diff --git a/net/core/dev.c b/net/core/dev.c index 40b28e417072..270b54754821 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -3953,7 +3953,8 @@ int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb) if (act != XDP_PASS) { switch (act) { case XDP_REDIRECT: - err = xdp_do_generic_redirect(skb->dev, skb); + err = xdp_do_generic_redirect(skb->dev, skb, + xdp_prog); if (err) goto out_redir; /* fallthru to submit skb */ @@ -3966,7 +3967,6 @@ int do_xdp_generic(struct bpf_prog *xdp_prog, struct sk_buff *skb) } return XDP_PASS; out_redir: - trace_xdp_exception(skb->dev, xdp_prog, XDP_REDIRECT); kfree_skb(skb); return XDP_DROP; } diff --git a/net/core/filter.c b/net/core/filter.c index a5e5f31b41b9..a04680331033 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2553,26 +2553,37 @@ out: } EXPORT_SYMBOL_GPL(xdp_do_redirect); -int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb) +int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb, + struct bpf_prog *xdp_prog) { struct redirect_info *ri = this_cpu_ptr(&redirect_info); - unsigned int len; u32 index = ri->ifindex; + struct net_device *fwd; + unsigned int len; + int err = 0; - dev = dev_get_by_index_rcu(dev_net(dev), index); + fwd = dev_get_by_index_rcu(dev_net(dev), index); ri->ifindex = 0; - if (unlikely(!dev)) { - return -EINVAL; + if (unlikely(!fwd)) { + err = -EINVAL; + goto out; } - if (unlikely(!(dev->flags & IFF_UP))) - return -ENETDOWN; - len = dev->mtu + dev->hard_header_len + VLAN_HLEN; - if (skb->len > len) - return -E2BIG; + if (unlikely(!(fwd->flags & IFF_UP))) { + err = -ENETDOWN; + goto out; + } - skb->dev = dev; - return 0; + len = fwd->mtu + fwd->hard_header_len + VLAN_HLEN; + if (skb->len > len) { + err = -EMSGSIZE; + goto out; + } + + skb->dev = fwd; +out: + trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT, err); + return err; } EXPORT_SYMBOL_GPL(xdp_do_generic_redirect); -- cgit v1.2.3 From a873585587205750e7accfb2c93c29239ffa6e09 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Thu, 24 Aug 2017 12:33:18 +0200 Subject: xdp: remove net_device names from xdp_redirect tracepoint There is too much overhead in the current trace_xdp_redirect tracepoint as it does strcpy and strlen on the net_device names. Besides, exposing the ifindex/index is actually the information that is needed in the tracepoint to diagnose issues. When a lookup fails (either ifindex or devmap index) then there is a need for saying which to_index that have issues. V2: Adjust args to be aligned with trace_xdp_exception. Signed-off-by: Jesper Dangaard Brouer Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/trace/events/xdp.h | 24 ++++++++++++------------ net/core/filter.c | 6 +++--- 2 files changed, 15 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index 0e42e69f773b..cd37706c6f55 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -51,33 +51,33 @@ TRACE_EVENT(xdp_exception, TRACE_EVENT(xdp_redirect, - TP_PROTO(const struct net_device *from, - const struct net_device *to, - const struct bpf_prog *xdp, u32 act, int err), + TP_PROTO(const struct net_device *dev, + const struct bpf_prog *xdp, u32 act, + int to_index, int err), - TP_ARGS(from, to, xdp, act, err), + TP_ARGS(dev, xdp, act, to_index, err), TP_STRUCT__entry( - __string(name_from, from->name) - __string(name_to, to->name) __array(u8, prog_tag, 8) __field(u32, act) + __field(int, ifindex) + __field(int, to_index) __field(int, err) ), TP_fast_assign( BUILD_BUG_ON(sizeof(__entry->prog_tag) != sizeof(xdp->tag)); memcpy(__entry->prog_tag, xdp->tag, sizeof(xdp->tag)); - __assign_str(name_from, from->name); - __assign_str(name_to, to->name); - __entry->act = act; - __entry->err = err; + __entry->act = act; + __entry->ifindex = dev->ifindex; + __entry->to_index = to_index; + __entry->err = err; ), - TP_printk("prog=%s from=%s to=%s action=%s err=%d", + TP_printk("prog=%s action=%s ifindex=%d to_index=%d err=%d", __print_hex_str(__entry->prog_tag, 8), - __get_str(name_from), __get_str(name_to), __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB), + __entry->ifindex, __entry->to_index, __entry->err) ); #endif /* _TRACE_XDP_H */ diff --git a/net/core/filter.c b/net/core/filter.c index a04680331033..4bcd6baa80c9 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2524,7 +2524,7 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, if (likely(!err)) ri->map_to_flush = map; out: - trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT, err); + trace_xdp_redirect(dev, xdp_prog, XDP_REDIRECT, index, err); return err; } @@ -2548,7 +2548,7 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, err = __bpf_tx_xdp(fwd, NULL, xdp, 0); out: - trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT, err); + trace_xdp_redirect(dev, xdp_prog, XDP_REDIRECT, index, err); return err; } EXPORT_SYMBOL_GPL(xdp_do_redirect); @@ -2582,7 +2582,7 @@ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb, skb->dev = fwd; out: - trace_xdp_redirect(dev, fwd, xdp_prog, XDP_REDIRECT, err); + trace_xdp_redirect(dev, xdp_prog, XDP_REDIRECT, index, err); return err; } EXPORT_SYMBOL_GPL(xdp_do_generic_redirect); -- cgit v1.2.3 From 22b6722bfa591ba03d6a0c5521b600d4ab2d9a27 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Wed, 23 Aug 2017 09:55:41 +0200 Subject: ipv6: Add sysctl for per namespace flow label reflection Reflecting IPv6 Flow Label at server nodes is useful in environments that employ multipath routing to load balance the requests. As "IPv6 Flow Label Reflection" standard draft [1] points out - ICMPv6 PTB error messages generated in response to a downstream packets from the server can be routed by a load balancer back to the original server without looking at transport headers, if the server applies the flow label reflection. This enables the Path MTU Discovery past the ECMP router in load-balance or anycast environments where each server node is reachable by only one path. Introduce a sysctl to enable flow label reflection per net namespace for all newly created sockets. Same could be earlier achieved only per socket by setting the IPV6_FL_F_REFLECT flag for the IPV6_FLOWLABEL_MGR socket option. [1] https://tools.ietf.org/html/draft-wang-6man-flow-label-reflection-01 Signed-off-by: Jakub Sitnicki Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 9 +++++++++ include/net/netns/ipv6.h | 1 + net/ipv6/af_inet6.c | 1 + net/ipv6/sysctl_net_ipv6.c | 8 ++++++++ 4 files changed, 19 insertions(+) (limited to 'net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 84c9b8cee780..6b0bc0f71534 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -1350,6 +1350,15 @@ flowlabel_state_ranges - BOOLEAN FALSE: disabled Default: true +flowlabel_reflect - BOOLEAN + Automatically reflect the flow label. Needed for Path MTU + Discovery to work with Equal Cost Multipath Routing in anycast + environments. See RFC 7690 and: + https://tools.ietf.org/html/draft-wang-6man-flow-label-reflection-01 + TRUE: enabled + FALSE: disabled + Default: FALSE + anycast_src_echo_reply - BOOLEAN Controls the use of anycast addresses as source addresses for ICMPv6 echo reply diff --git a/include/net/netns/ipv6.h b/include/net/netns/ipv6.h index 0e50bf3ed097..2544f9760a42 100644 --- a/include/net/netns/ipv6.h +++ b/include/net/netns/ipv6.h @@ -36,6 +36,7 @@ struct netns_sysctl_ipv6 { int idgen_retries; int idgen_delay; int flowlabel_state_ranges; + int flowlabel_reflect; }; struct netns_ipv6 { diff --git a/net/ipv6/af_inet6.c b/net/ipv6/af_inet6.c index 3b58ee709f33..fe5262fd6aa5 100644 --- a/net/ipv6/af_inet6.c +++ b/net/ipv6/af_inet6.c @@ -211,6 +211,7 @@ lookup_protocol: np->mc_loop = 1; np->pmtudisc = IPV6_PMTUDISC_WANT; np->autoflowlabel = ip6_default_np_autolabel(net); + np->repflow = net->ipv6.sysctl.flowlabel_reflect; sk->sk_ipv6only = net->ipv6.sysctl.bindv6only; /* Init the ipv4 part of the socket since we can have sockets diff --git a/net/ipv6/sysctl_net_ipv6.c b/net/ipv6/sysctl_net_ipv6.c index 69c50e737c54..6fbf8ae5e52c 100644 --- a/net/ipv6/sysctl_net_ipv6.c +++ b/net/ipv6/sysctl_net_ipv6.c @@ -90,6 +90,13 @@ static struct ctl_table ipv6_table_template[] = { .mode = 0644, .proc_handler = proc_dointvec }, + { + .procname = "flowlabel_reflect", + .data = &init_net.ipv6.sysctl.flowlabel_reflect, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec, + }, { } }; @@ -149,6 +156,7 @@ static int __net_init ipv6_sysctl_net_init(struct net *net) ipv6_table[6].data = &net->ipv6.sysctl.idgen_delay; ipv6_table[7].data = &net->ipv6.sysctl.flowlabel_state_ranges; ipv6_table[8].data = &net->ipv6.sysctl.ip_nonlocal_bind; + ipv6_table[9].data = &net->ipv6.sysctl.flowlabel_reflect; ipv6_route_table = ipv6_route_sysctl_init(net); if (!ipv6_route_table) -- cgit v1.2.3 From 23aebdacb05dab9efdf22b9e0413491cbd5f128f Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Wed, 23 Aug 2017 09:58:29 +0200 Subject: ipv6: Compute multipath hash for ICMP errors from offending packet When forwarding or sending out an ICMPv6 error, look at the embedded packet that triggered the error and compute a flow hash over its headers. This let's us route the ICMP error together with the flow it belongs to when multipath (ECMP) routing is in use, which in turn makes Path MTU Discovery work in ECMP load-balanced or anycast setups (RFC 7690). Granted, end-hosts behind the ECMP router (aka servers) need to reflect the IPv6 Flow Label for PMTUD to work. The code is organized to be in parallel with ipv4 stack: ip_multipath_l3_keys -> ip6_multipath_l3_keys fib_multipath_hash -> rt6_multipath_hash Signed-off-by: Jakub Sitnicki Signed-off-by: David S. Miller --- include/net/ip6_route.h | 1 + net/ipv6/icmp.c | 1 + net/ipv6/route.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 52 insertions(+) (limited to 'net') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 907d39a42f6b..882bc3c7ccde 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -115,6 +115,7 @@ static inline int ip6_route_get_saddr(struct net *net, struct rt6_info *rt, struct rt6_info *rt6_lookup(struct net *net, const struct in6_addr *daddr, const struct in6_addr *saddr, int oif, int flags); +u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb); struct dst_entry *icmp6_dst_alloc(struct net_device *dev, struct flowi6 *fl6); diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index 4f82830fc068..dd7608cf1d72 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -519,6 +519,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, fl6.fl6_icmp_type = type; fl6.fl6_icmp_code = code; fl6.flowi6_uid = sock_net_uid(net, NULL); + fl6.mp_hash = rt6_multipath_hash(&fl6, skb); security_skb_classify_flow(skb, flowi6_to_flowi(&fl6)); sk = icmpv6_xmit_lock(net); diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 9b02064c3335..6c4dd5796a31 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -1214,6 +1214,54 @@ struct dst_entry *ip6_route_input_lookup(struct net *net, } EXPORT_SYMBOL_GPL(ip6_route_input_lookup); +static void ip6_multipath_l3_keys(const struct sk_buff *skb, + struct flow_keys *keys) +{ + const struct ipv6hdr *outer_iph = ipv6_hdr(skb); + const struct ipv6hdr *key_iph = outer_iph; + const struct ipv6hdr *inner_iph; + const struct icmp6hdr *icmph; + struct ipv6hdr _inner_iph; + + if (likely(outer_iph->nexthdr != IPPROTO_ICMPV6)) + goto out; + + icmph = icmp6_hdr(skb); + if (icmph->icmp6_type != ICMPV6_DEST_UNREACH && + icmph->icmp6_type != ICMPV6_PKT_TOOBIG && + icmph->icmp6_type != ICMPV6_TIME_EXCEED && + icmph->icmp6_type != ICMPV6_PARAMPROB) + goto out; + + inner_iph = skb_header_pointer(skb, + skb_transport_offset(skb) + sizeof(*icmph), + sizeof(_inner_iph), &_inner_iph); + if (!inner_iph) + goto out; + + key_iph = inner_iph; +out: + memset(keys, 0, sizeof(*keys)); + keys->control.addr_type = FLOW_DISSECTOR_KEY_IPV6_ADDRS; + keys->addrs.v6addrs.src = key_iph->saddr; + keys->addrs.v6addrs.dst = key_iph->daddr; + keys->tags.flow_label = ip6_flowinfo(key_iph); + keys->basic.ip_proto = key_iph->nexthdr; +} + +/* if skb is set it will be used and fl6 can be NULL */ +u32 rt6_multipath_hash(const struct flowi6 *fl6, const struct sk_buff *skb) +{ + struct flow_keys hash_keys; + + if (skb) { + ip6_multipath_l3_keys(skb, &hash_keys); + return flow_hash_from_keys(&hash_keys); + } + + return get_hash_from_flowi6(fl6); +} + void ip6_route_input(struct sk_buff *skb) { const struct ipv6hdr *iph = ipv6_hdr(skb); @@ -1232,6 +1280,8 @@ void ip6_route_input(struct sk_buff *skb) tun_info = skb_tunnel_info(skb); if (tun_info && !(tun_info->mode & IP_TUNNEL_INFO_TX)) fl6.flowi6_tun_key.tun_id = tun_info->key.tun_id; + if (unlikely(fl6.flowi6_proto == IPPROTO_ICMPV6)) + fl6.mp_hash = rt6_multipath_hash(&fl6, skb); skb_dst_drop(skb); skb_dst_set(skb, ip6_route_input_lookup(net, skb->dev, &fl6, flags)); } -- cgit v1.2.3 From 956b45318a27cefa5e99fbbbe703ea8a2a3aee13 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Wed, 23 Aug 2017 09:58:30 +0200 Subject: ipv6: Fold rt6_info_hash_nhsfn() into its only caller Commit 644d0e656958 ("ipv6 Use get_hash_from_flowi6 for rt6 hash") has turned rt6_info_hash_nhsfn() into a one-liner, so it no longer makes sense to keep it around. Also remove the accompanying comment that has become outdated. Signed-off-by: Jakub Sitnicki Signed-off-by: David S. Miller --- net/ipv6/route.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6c4dd5796a31..246e7d7d30a4 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -445,16 +445,6 @@ static bool rt6_check_expired(const struct rt6_info *rt) return false; } -/* Multipath route selection: - * Hash based function using packet header and flowlabel. - * Adapted from fib_info_hashfn() - */ -static int rt6_info_hash_nhsfn(unsigned int candidate_count, - const struct flowi6 *fl6) -{ - return get_hash_from_flowi6(fl6) % candidate_count; -} - static struct rt6_info *rt6_multipath_select(struct rt6_info *match, struct flowi6 *fl6, int oif, int strict) @@ -462,7 +452,7 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match, struct rt6_info *sibling, *next_sibling; int route_choosen; - route_choosen = rt6_info_hash_nhsfn(match->rt6i_nsiblings + 1, fl6); + route_choosen = get_hash_from_flowi6(fl6) % (match->rt6i_nsiblings + 1); /* Don't change the route, if route_choosen == 0 * (siblings does not include ourself) */ -- cgit v1.2.3 From b673d6cceae2b3e106addd27b79d321cc739bd43 Mon Sep 17 00:00:00 2001 From: Jakub Sitnicki Date: Wed, 23 Aug 2017 09:58:31 +0200 Subject: ipv6: Use multipath hash from flow info if available Allow our callers to influence the choice of ECMP link by honoring the hash passed together with the flow info. This allows for special treatment of ICMP errors which we would like to route over the same path as the IPv6 datagram that triggered the error. Also go through rt6_multipath_hash(), in the usual case when we aren't dealing with an ICMP error, so that there is one central place where multipath hash is computed. Signed-off-by: Jakub Sitnicki Signed-off-by: David S. Miller --- net/ipv6/route.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 246e7d7d30a4..4d0273459d49 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -452,7 +452,13 @@ static struct rt6_info *rt6_multipath_select(struct rt6_info *match, struct rt6_info *sibling, *next_sibling; int route_choosen; - route_choosen = get_hash_from_flowi6(fl6) % (match->rt6i_nsiblings + 1); + /* We might have already computed the hash for ICMPv6 errors. In such + * case it will always be non-zero. Otherwise now is the time to do it. + */ + if (!fl6->mp_hash) + fl6->mp_hash = rt6_multipath_hash(fl6, NULL); + + route_choosen = fl6->mp_hash % (match->rt6i_nsiblings + 1); /* Don't change the route, if route_choosen == 0 * (siblings does not include ourself) */ -- cgit v1.2.3 From 3fd87127073292538047adf1c9c757e9cab0dd56 Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Thu, 24 Aug 2017 14:38:51 -0700 Subject: strparser: initialize all callbacks commit bbb03029a899 ("strparser: Generalize strparser") added more function pointers to 'struct strp_callbacks'; however, kcm_attach() was not updated to initialize them. This could cause the ->lock() and/or ->unlock() function pointers to be set to garbage values, causing a crash in strp_work(). Fix the bug by moving the callback structs into static memory, so unspecified members are zeroed. Also constify them while we're at it. This bug was found by syzkaller, which encountered the following splat: IP: 0x55 PGD 3b1ca067 P4D 3b1ca067 PUD 3b12f067 PMD 0 Oops: 0010 [#1] SMP KASAN Dumping ftrace buffer: (ftrace buffer empty) Modules linked in: CPU: 2 PID: 1194 Comm: kworker/u8:1 Not tainted 4.13.0-rc4-next-20170811 #2 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS Bochs 01/01/2011 Workqueue: kstrp strp_work task: ffff88006bb0e480 task.stack: ffff88006bb10000 RIP: 0010:0x55 RSP: 0018:ffff88006bb17540 EFLAGS: 00010246 RAX: dffffc0000000000 RBX: ffff88006ce4bd60 RCX: 0000000000000000 RDX: 1ffff1000d9c97bd RSI: 0000000000000000 RDI: ffff88006ce4bc48 RBP: ffff88006bb17558 R08: ffffffff81467ab2 R09: 0000000000000000 R10: ffff88006bb17438 R11: ffff88006bb17940 R12: ffff88006ce4bc48 R13: ffff88003c683018 R14: ffff88006bb17980 R15: ffff88003c683000 FS: 0000000000000000(0000) GS:ffff88006de00000(0000) knlGS:0000000000000000 CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 CR2: 0000000000000055 CR3: 000000003c145000 CR4: 00000000000006e0 DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 DR3: 0000000000000000 DR6: 00000000fffe0ff0 DR7: 0000000000000400 Call Trace: process_one_work+0xbf3/0x1bc0 kernel/workqueue.c:2098 worker_thread+0x223/0x1860 kernel/workqueue.c:2233 kthread+0x35e/0x430 kernel/kthread.c:231 ret_from_fork+0x2a/0x40 arch/x86/entry/entry_64.S:431 Code: Bad RIP value. RIP: 0x55 RSP: ffff88006bb17540 CR2: 0000000000000055 ---[ end trace f0e4920047069cee ]--- Here is a C reproducer (requires CONFIG_BPF_SYSCALL=y and CONFIG_AF_KCM=y): #include #include #include #include #include #include #include #include static const struct bpf_insn bpf_insns[3] = { { .code = 0xb7 }, /* BPF_MOV64_IMM(0, 0) */ { .code = 0x95 }, /* BPF_EXIT_INSN() */ }; static const union bpf_attr bpf_attr = { .prog_type = 1, .insn_cnt = 2, .insns = (uintptr_t)&bpf_insns, .license = (uintptr_t)"", }; int main(void) { int bpf_fd = syscall(__NR_bpf, BPF_PROG_LOAD, &bpf_attr, sizeof(bpf_attr)); int inet_fd = socket(AF_INET, SOCK_STREAM, 0); int kcm_fd = socket(AF_KCM, SOCK_DGRAM, 0); ioctl(kcm_fd, SIOCKCMATTACH, &(struct kcm_attach) { .fd = inet_fd, .bpf_fd = bpf_fd }); } Fixes: bbb03029a899 ("strparser: Generalize strparser") Cc: Dmitry Vyukov Cc: Tom Herbert Signed-off-by: Eric Biggers Signed-off-by: David S. Miller --- Documentation/networking/strparser.txt | 2 +- include/net/strparser.h | 2 +- kernel/bpf/sockmap.c | 10 +++++----- net/kcm/kcmsock.c | 11 +++++------ net/strparser/strparser.c | 2 +- 5 files changed, 13 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/Documentation/networking/strparser.txt b/Documentation/networking/strparser.txt index fe01302471ae..13081b3decef 100644 --- a/Documentation/networking/strparser.txt +++ b/Documentation/networking/strparser.txt @@ -35,7 +35,7 @@ Functions ========= strp_init(struct strparser *strp, struct sock *sk, - struct strp_callbacks *cb) + const struct strp_callbacks *cb) Called to initialize a stream parser. strp is a struct of type strparser that is allocated by the upper layer. sk is the TCP diff --git a/include/net/strparser.h b/include/net/strparser.h index 4fe966a0ad92..7dc131d62ad5 100644 --- a/include/net/strparser.h +++ b/include/net/strparser.h @@ -138,7 +138,7 @@ void strp_done(struct strparser *strp); void strp_stop(struct strparser *strp); void strp_check_rcv(struct strparser *strp); int strp_init(struct strparser *strp, struct sock *sk, - struct strp_callbacks *cb); + const struct strp_callbacks *cb); void strp_data_ready(struct strparser *strp); int strp_process(struct strparser *strp, struct sk_buff *orig_skb, unsigned int orig_offset, size_t orig_len, diff --git a/kernel/bpf/sockmap.c b/kernel/bpf/sockmap.c index 78b2bb9370ac..617c239590c2 100644 --- a/kernel/bpf/sockmap.c +++ b/kernel/bpf/sockmap.c @@ -368,12 +368,12 @@ static int smap_read_sock_done(struct strparser *strp, int err) static int smap_init_sock(struct smap_psock *psock, struct sock *sk) { - struct strp_callbacks cb; + static const struct strp_callbacks cb = { + .rcv_msg = smap_read_sock_strparser, + .parse_msg = smap_parse_func_strparser, + .read_sock_done = smap_read_sock_done, + }; - memset(&cb, 0, sizeof(cb)); - cb.rcv_msg = smap_read_sock_strparser; - cb.parse_msg = smap_parse_func_strparser; - cb.read_sock_done = smap_read_sock_done; return strp_init(&psock->strp, sk, &cb); } diff --git a/net/kcm/kcmsock.c b/net/kcm/kcmsock.c index 88ce73288247..48e993b2dbcf 100644 --- a/net/kcm/kcmsock.c +++ b/net/kcm/kcmsock.c @@ -1376,7 +1376,11 @@ static int kcm_attach(struct socket *sock, struct socket *csock, struct kcm_psock *psock = NULL, *tpsock; struct list_head *head; int index = 0; - struct strp_callbacks cb; + static const struct strp_callbacks cb = { + .rcv_msg = kcm_rcv_strparser, + .parse_msg = kcm_parse_func_strparser, + .read_sock_done = kcm_read_sock_done, + }; int err; csk = csock->sk; @@ -1391,11 +1395,6 @@ static int kcm_attach(struct socket *sock, struct socket *csock, psock->sk = csk; psock->bpf_prog = prog; - cb.rcv_msg = kcm_rcv_strparser; - cb.abort_parser = NULL; - cb.parse_msg = kcm_parse_func_strparser; - cb.read_sock_done = kcm_read_sock_done; - err = strp_init(&psock->strp, csk, &cb); if (err) { kmem_cache_free(kcm_psockp, psock); diff --git a/net/strparser/strparser.c b/net/strparser/strparser.c index 434aa6637a52..d4ea46a5f233 100644 --- a/net/strparser/strparser.c +++ b/net/strparser/strparser.c @@ -472,7 +472,7 @@ static void strp_sock_unlock(struct strparser *strp) } int strp_init(struct strparser *strp, struct sock *sk, - struct strp_callbacks *cb) + const struct strp_callbacks *cb) { if (!cb || !cb->rcv_msg || !cb->parse_msg) -- cgit v1.2.3 From 96a1c173ea6576652df8564fabc5fe95e3d82d52 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Fri, 25 Aug 2017 19:51:43 +0530 Subject: ieee802154: 6lowpan: make header_ops const Make this const as it is only stored as a reference in a const field of a net_device structure. Signed-off-by: Bhumika Goyal Acked-by: Stefan Schmidt Signed-off-by: Marcel Holtmann --- net/ieee802154/6lowpan/core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/ieee802154/6lowpan/core.c b/net/ieee802154/6lowpan/core.c index de2661cd0328..974765b7d92a 100644 --- a/net/ieee802154/6lowpan/core.c +++ b/net/ieee802154/6lowpan/core.c @@ -54,7 +54,7 @@ static int open_count; -static struct header_ops lowpan_header_ops = { +static const struct header_ops lowpan_header_ops = { .create = lowpan_header_create, }; -- cgit v1.2.3 From 32d99d0b670299720dd0db92a974c9612c230889 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Fri, 25 Aug 2017 09:56:44 +0200 Subject: ipv6: sr: add support for ip4ip6 encapsulation This patch enables the SRv6 encapsulation mode to carry an IPv4 payload. All the infrastructure was already present, I just had to add a parameter to seg6_do_srh_encap() to specify the inner packet protocol, and perform some additional checks. Usage example: ip route add 1.2.3.4 encap seg6 mode encap segs fc00::1,fc00::2 dev eth0 Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/net/seg6.h | 3 ++- net/ipv6/seg6_iptunnel.c | 47 +++++++++++++++++++++++++++++++++++++---------- net/ipv6/seg6_local.c | 2 +- 3 files changed, 40 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/include/net/seg6.h b/include/net/seg6.h index 5379f550f521..099bad59dc90 100644 --- a/include/net/seg6.h +++ b/include/net/seg6.h @@ -60,7 +60,8 @@ extern int seg6_local_init(void); extern void seg6_local_exit(void); extern bool seg6_validate_srh(struct ipv6_sr_hdr *srh, int len); -extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh); +extern int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, + int proto); extern int seg6_do_srh_inline(struct sk_buff *skb, struct ipv6_sr_hdr *osrh); #endif diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 501233040570..5bec7817a7b9 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -91,7 +91,7 @@ static void set_tun_src(struct net *net, struct net_device *dev, } /* encapsulate an IPv6 packet within an outer IPv6 header with a given SRH */ -int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) +int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh, int proto) { struct net *net = dev_net(skb_dst(skb)->dev); struct ipv6hdr *hdr, *inner_hdr; @@ -116,15 +116,22 @@ int seg6_do_srh_encap(struct sk_buff *skb, struct ipv6_sr_hdr *osrh) * hlim will be decremented in ip6_forward() afterwards and * decapsulation will overwrite inner hlim with outer hlim */ - ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), - ip6_flowlabel(inner_hdr)); - hdr->hop_limit = inner_hdr->hop_limit; + + if (skb->protocol == htons(ETH_P_IPV6)) { + ip6_flow_hdr(hdr, ip6_tclass(ip6_flowinfo(inner_hdr)), + ip6_flowlabel(inner_hdr)); + hdr->hop_limit = inner_hdr->hop_limit; + } else { + ip6_flow_hdr(hdr, 0, 0); + hdr->hop_limit = ip6_dst_hoplimit(skb_dst(skb)); + } + hdr->nexthdr = NEXTHDR_ROUTING; isrh = (void *)hdr + sizeof(*hdr); memcpy(isrh, osrh, hdrlen); - isrh->nexthdr = NEXTHDR_IPV6; + isrh->nexthdr = proto; hdr->daddr = isrh->segments[isrh->first_segment]; set_tun_src(net, skb->dev, &hdr->daddr, &hdr->saddr); @@ -199,7 +206,7 @@ static int seg6_do_srh(struct sk_buff *skb) { struct dst_entry *dst = skb_dst(skb); struct seg6_iptunnel_encap *tinfo; - int err = 0; + int proto, err = 0; tinfo = seg6_encap_lwtunnel(dst->lwtstate); @@ -210,17 +217,31 @@ static int seg6_do_srh(struct sk_buff *skb) switch (tinfo->mode) { case SEG6_IPTUN_MODE_INLINE: + if (skb->protocol != htons(ETH_P_IPV6)) + return -EINVAL; + err = seg6_do_srh_inline(skb, tinfo->srh); + if (err) + return err; + skb_reset_inner_headers(skb); break; case SEG6_IPTUN_MODE_ENCAP: - err = seg6_do_srh_encap(skb, tinfo->srh); + if (skb->protocol == htons(ETH_P_IPV6)) + proto = IPPROTO_IPV6; + else if (skb->protocol == htons(ETH_P_IP)) + proto = IPPROTO_IPIP; + else + return -EINVAL; + + err = seg6_do_srh_encap(skb, tinfo->srh, proto); + if (err) + return err; + + skb->protocol = htons(ETH_P_IPV6); break; } - if (err) - return err; - ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); skb_set_transport_header(skb, sizeof(struct ipv6hdr)); @@ -334,6 +355,9 @@ static int seg6_build_state(struct nlattr *nla, struct seg6_lwt *slwt; int err; + if (family != AF_INET && family != AF_INET6) + return -EINVAL; + err = nla_parse_nested(tb, SEG6_IPTUNNEL_MAX, nla, seg6_iptunnel_policy, extack); @@ -356,6 +380,9 @@ static int seg6_build_state(struct nlattr *nla, switch (tuninfo->mode) { case SEG6_IPTUN_MODE_INLINE: + if (family != AF_INET6) + return -EINVAL; + break; case SEG6_IPTUN_MODE_ENCAP: break; diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 147680e7a00c..609b94e970de 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -290,7 +290,7 @@ static int input_action_end_b6_encap(struct sk_buff *skb, skb_reset_inner_headers(skb); skb->encapsulation = 1; - err = seg6_do_srh_encap(skb, slwt->srh); + err = seg6_do_srh_encap(skb, slwt->srh, IPPROTO_IPV6); if (err) goto drop; -- cgit v1.2.3 From 38ee7f2d47565689f35662d488d25e7afc43477d Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Fri, 25 Aug 2017 09:56:45 +0200 Subject: ipv6: sr: add support for encapsulation of L2 frames This patch implements the L2 frame encapsulation mechanism, referred to as T.Encaps.L2 in the SRv6 specifications [1]. A new type of SRv6 tunnel mode is added (SEG6_IPTUN_MODE_L2ENCAP). It only accepts packets with an existing MAC header (i.e., it will not work for locally generated packets). The resulting packet looks like IPv6 -> SRH -> Ethernet -> original L3 payload. The next header field of the SRH is set to NEXTHDR_NONE. [1] https://tools.ietf.org/html/draft-filsfils-spring-srv6-network-programming-01 Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- include/uapi/linux/seg6_iptunnel.h | 18 ++++++++++++++---- net/ipv6/seg6_iptunnel.c | 25 +++++++++++++++++++++++-- 2 files changed, 37 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/seg6_iptunnel.h b/include/uapi/linux/seg6_iptunnel.h index b6e5a0a1afd7..b23df9f58354 100644 --- a/include/uapi/linux/seg6_iptunnel.h +++ b/include/uapi/linux/seg6_iptunnel.h @@ -33,16 +33,26 @@ struct seg6_iptunnel_encap { enum { SEG6_IPTUN_MODE_INLINE, SEG6_IPTUN_MODE_ENCAP, + SEG6_IPTUN_MODE_L2ENCAP, }; #ifdef __KERNEL__ static inline size_t seg6_lwt_headroom(struct seg6_iptunnel_encap *tuninfo) { - int encap = (tuninfo->mode == SEG6_IPTUN_MODE_ENCAP); - - return ((tuninfo->srh->hdrlen + 1) << 3) + - (encap * sizeof(struct ipv6hdr)); + int head = 0; + + switch (tuninfo->mode) { + case SEG6_IPTUN_MODE_INLINE: + break; + case SEG6_IPTUN_MODE_ENCAP: + head = sizeof(struct ipv6hdr); + break; + case SEG6_IPTUN_MODE_L2ENCAP: + return 0; + } + + return ((tuninfo->srh->hdrlen + 1) << 3) + head; } #endif diff --git a/net/ipv6/seg6_iptunnel.c b/net/ipv6/seg6_iptunnel.c index 5bec7817a7b9..bd6cc688bd19 100644 --- a/net/ipv6/seg6_iptunnel.c +++ b/net/ipv6/seg6_iptunnel.c @@ -238,6 +238,22 @@ static int seg6_do_srh(struct sk_buff *skb) if (err) return err; + skb->protocol = htons(ETH_P_IPV6); + break; + case SEG6_IPTUN_MODE_L2ENCAP: + if (!skb_mac_header_was_set(skb)) + return -EINVAL; + + if (pskb_expand_head(skb, skb->mac_len, 0, GFP_ATOMIC) < 0) + return -ENOMEM; + + skb_mac_header_rebuild(skb); + skb_push(skb, skb->mac_len); + + err = seg6_do_srh_encap(skb, tinfo->srh, NEXTHDR_NONE); + if (err) + return err; + skb->protocol = htons(ETH_P_IPV6); break; } @@ -386,6 +402,8 @@ static int seg6_build_state(struct nlattr *nla, break; case SEG6_IPTUN_MODE_ENCAP: break; + case SEG6_IPTUN_MODE_L2ENCAP: + break; default: return -EINVAL; } @@ -409,8 +427,11 @@ static int seg6_build_state(struct nlattr *nla, memcpy(&slwt->tuninfo, tuninfo, tuninfo_len); newts->type = LWTUNNEL_ENCAP_SEG6; - newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT | - LWTUNNEL_STATE_INPUT_REDIRECT; + newts->flags |= LWTUNNEL_STATE_INPUT_REDIRECT; + + if (tuninfo->mode != SEG6_IPTUN_MODE_L2ENCAP) + newts->flags |= LWTUNNEL_STATE_OUTPUT_REDIRECT; + newts->headroom = seg6_lwt_headroom(tuninfo); *ts = newts; -- cgit v1.2.3 From 6285217f0c29e68b744533a9ddf50e110d36d65e Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Fri, 25 Aug 2017 09:56:46 +0200 Subject: ipv6: sr: enforce IPv6 packets for seg6local lwt This patch ensures that the seg6local lightweight tunnel is used solely with IPv6 routes and processes only IPv6 packets. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/seg6_local.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'net') diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 609b94e970de..c6263256fcf6 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -357,6 +357,11 @@ static int seg6_local_input(struct sk_buff *skb) struct seg6_action_desc *desc; struct seg6_local_lwt *slwt; + if (skb->protocol != htons(ETH_P_IPV6)) { + kfree_skb(skb); + return -EINVAL; + } + slwt = seg6_local_lwtunnel(orig_dst->lwtstate); desc = slwt->desc; @@ -623,6 +628,9 @@ static int seg6_local_build_state(struct nlattr *nla, unsigned int family, struct seg6_local_lwt *slwt; int err; + if (family != AF_INET6) + return -EINVAL; + err = nla_parse_nested(tb, SEG6_LOCAL_MAX, nla, seg6_local_policy, extack); -- cgit v1.2.3 From d7a669dd2f8ba07a17423f4ad586dfc0379882f7 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Fri, 25 Aug 2017 09:56:47 +0200 Subject: ipv6: sr: add helper functions for seg6local This patch adds three helper functions to be used with the seg6local packet processing actions. The decap_and_validate() function will be used by the End.D* actions, that decapsulate an SR-enabled packet. The advance_nextseg() function applies the fundamental operations to update an SRH for the next segment. The lookup_nexthop() function helps select the next-hop for the processed SR packets. It supports an optional next-hop address to route the packet specifically through it, and an optional routing table to use. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/Kconfig | 1 + net/ipv6/seg6_local.c | 189 ++++++++++++++++++++++++++------------------------ 2 files changed, 101 insertions(+), 89 deletions(-) (limited to 'net') diff --git a/net/ipv6/Kconfig b/net/ipv6/Kconfig index 0d722396dce6..ea71e4b0ab7a 100644 --- a/net/ipv6/Kconfig +++ b/net/ipv6/Kconfig @@ -308,6 +308,7 @@ config IPV6_SEG6_LWTUNNEL depends on IPV6 select LWTUNNEL select DST_CACHE + select IPV6_MULTIPLE_TABLES ---help--- Support for encapsulation of packets within an outer IPv6 header and a Segment Routing Header using the lightweight diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index c6263256fcf6..26db4d3e8755 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -99,23 +99,105 @@ static struct ipv6_sr_hdr *get_and_validate_srh(struct sk_buff *skb) return srh; } +static bool decap_and_validate(struct sk_buff *skb, int proto) +{ + struct ipv6_sr_hdr *srh; + unsigned int off = 0; + + srh = get_srh(skb); + if (srh && srh->segments_left > 0) + return false; + +#ifdef CONFIG_IPV6_SEG6_HMAC + if (srh && !seg6_hmac_validate_skb(skb)) + return false; +#endif + + if (ipv6_find_hdr(skb, &off, proto, NULL, NULL) < 0) + return false; + + if (!pskb_pull(skb, off)) + return false; + + skb_postpull_rcsum(skb, skb_network_header(skb), off); + + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb->encapsulation = 0; + + return true; +} + +static void advance_nextseg(struct ipv6_sr_hdr *srh, struct in6_addr *daddr) +{ + struct in6_addr *addr; + + srh->segments_left--; + addr = srh->segments + srh->segments_left; + *daddr = *addr; +} + +static void lookup_nexthop(struct sk_buff *skb, struct in6_addr *nhaddr, + u32 tbl_id) +{ + struct net *net = dev_net(skb->dev); + struct ipv6hdr *hdr = ipv6_hdr(skb); + int flags = RT6_LOOKUP_F_HAS_SADDR; + struct dst_entry *dst = NULL; + struct rt6_info *rt; + struct flowi6 fl6; + + fl6.flowi6_iif = skb->dev->ifindex; + fl6.daddr = nhaddr ? *nhaddr : hdr->daddr; + fl6.saddr = hdr->saddr; + fl6.flowlabel = ip6_flowinfo(hdr); + fl6.flowi6_mark = skb->mark; + fl6.flowi6_proto = hdr->nexthdr; + + if (nhaddr) + fl6.flowi6_flags = FLOWI_FLAG_KNOWN_NH; + + if (!tbl_id) { + dst = ip6_route_input_lookup(net, skb->dev, &fl6, flags); + } else { + struct fib6_table *table; + + table = fib6_get_table(net, tbl_id); + if (!table) + goto out; + + rt = ip6_pol_route(net, table, 0, &fl6, flags); + dst = &rt->dst; + } + + if (dst && dst->dev->flags & IFF_LOOPBACK && !dst->error) { + dst_release(dst); + dst = NULL; + } + +out: + if (!dst) { + rt = net->ipv6.ip6_blk_hole_entry; + dst = &rt->dst; + dst_hold(dst); + } + + skb_dst_drop(skb); + skb_dst_set(skb, dst); +} + /* regular endpoint function */ static int input_action_end(struct sk_buff *skb, struct seg6_local_lwt *slwt) { struct ipv6_sr_hdr *srh; - struct in6_addr *addr; srh = get_and_validate_srh(skb); if (!srh) goto drop; - srh->segments_left--; - addr = srh->segments + srh->segments_left; - - ipv6_hdr(skb)->daddr = *addr; + advance_nextseg(srh, &ipv6_hdr(skb)->daddr); - skb_dst_drop(skb); - ip6_route_input(skb); + lookup_nexthop(skb, NULL, 0); return dst_input(skb); @@ -127,41 +209,15 @@ drop: /* regular endpoint, and forward to specified nexthop */ static int input_action_end_x(struct sk_buff *skb, struct seg6_local_lwt *slwt) { - struct net *net = dev_net(skb->dev); struct ipv6_sr_hdr *srh; - struct dst_entry *dst; - struct in6_addr *addr; - struct ipv6hdr *hdr; - struct flowi6 fl6; - int flags; srh = get_and_validate_srh(skb); if (!srh) goto drop; - srh->segments_left--; - addr = srh->segments + srh->segments_left; - - hdr = ipv6_hdr(skb); - hdr->daddr = *addr; - - skb_dst_drop(skb); - - fl6.flowi6_iif = skb->dev->ifindex; - fl6.daddr = slwt->nh6; - fl6.saddr = hdr->saddr; - fl6.flowlabel = ip6_flowinfo(hdr); - fl6.flowi6_mark = skb->mark; - fl6.flowi6_proto = hdr->nexthdr; - - flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_IFACE | - RT6_LOOKUP_F_REACHABLE; + advance_nextseg(srh, &ipv6_hdr(skb)->daddr); - dst = ip6_route_input_lookup(net, skb->dev, &fl6, flags); - if (dst->dev->flags & IFF_LOOPBACK) - goto drop; - - skb_dst_set(skb, dst); + lookup_nexthop(skb, &slwt->nh6, 0); return dst_input(skb); @@ -174,42 +230,18 @@ drop: static int input_action_end_dx6(struct sk_buff *skb, struct seg6_local_lwt *slwt) { - struct net *net = dev_net(skb->dev); - struct ipv6hdr *inner_hdr; - struct ipv6_sr_hdr *srh; - struct dst_entry *dst; - unsigned int off = 0; - struct flowi6 fl6; - bool use_nh; - int flags; + struct in6_addr *nhaddr = NULL; /* this function accepts IPv6 encapsulated packets, with either * an SRH with SL=0, or no SRH. */ - srh = get_srh(skb); - if (srh && srh->segments_left > 0) - goto drop; - -#ifdef CONFIG_IPV6_SEG6_HMAC - if (srh && !seg6_hmac_validate_skb(skb)) + if (!decap_and_validate(skb, IPPROTO_IPV6)) goto drop; -#endif - if (ipv6_find_hdr(skb, &off, IPPROTO_IPV6, NULL, NULL) < 0) - goto drop; - - if (!pskb_pull(skb, off)) + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) goto drop; - skb_postpull_rcsum(skb, skb_network_header(skb), off); - - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb->encapsulation = 0; - - inner_hdr = ipv6_hdr(skb); - /* The inner packet is not associated to any local interface, * so we do not call netif_rx(). * @@ -217,26 +249,10 @@ static int input_action_end_dx6(struct sk_buff *skb, * inner packet's DA. Otherwise, use the specified nexthop. */ - use_nh = !ipv6_addr_any(&slwt->nh6); + if (!ipv6_addr_any(&slwt->nh6)) + nhaddr = &slwt->nh6; - skb_dst_drop(skb); - - fl6.flowi6_iif = skb->dev->ifindex; - fl6.daddr = use_nh ? slwt->nh6 : inner_hdr->daddr; - fl6.saddr = inner_hdr->saddr; - fl6.flowlabel = ip6_flowinfo(inner_hdr); - fl6.flowi6_mark = skb->mark; - fl6.flowi6_proto = inner_hdr->nexthdr; - - flags = RT6_LOOKUP_F_HAS_SADDR | RT6_LOOKUP_F_REACHABLE; - if (use_nh) - flags |= RT6_LOOKUP_F_IFACE; - - dst = ip6_route_input_lookup(net, skb->dev, &fl6, flags); - if (dst->dev->flags & IFF_LOOPBACK) - goto drop; - - skb_dst_set(skb, dst); + lookup_nexthop(skb, nhaddr, 0); return dst_input(skb); drop: @@ -261,8 +277,7 @@ static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); skb_set_transport_header(skb, sizeof(struct ipv6hdr)); - skb_dst_drop(skb); - ip6_route_input(skb); + lookup_nexthop(skb, NULL, 0); return dst_input(skb); @@ -276,16 +291,13 @@ static int input_action_end_b6_encap(struct sk_buff *skb, struct seg6_local_lwt *slwt) { struct ipv6_sr_hdr *srh; - struct in6_addr *addr; int err = -EINVAL; srh = get_and_validate_srh(skb); if (!srh) goto drop; - srh->segments_left--; - addr = srh->segments + srh->segments_left; - ipv6_hdr(skb)->daddr = *addr; + advance_nextseg(srh, &ipv6_hdr(skb)->daddr); skb_reset_inner_headers(skb); skb->encapsulation = 1; @@ -297,8 +309,7 @@ static int input_action_end_b6_encap(struct sk_buff *skb, ipv6_hdr(skb)->payload_len = htons(skb->len - sizeof(struct ipv6hdr)); skb_set_transport_header(skb, sizeof(struct ipv6hdr)); - skb_dst_drop(skb); - ip6_route_input(skb); + lookup_nexthop(skb, NULL, 0); return dst_input(skb); -- cgit v1.2.3 From 891ef8dd2a8d14e4e73a81dcdb135b574c57f556 Mon Sep 17 00:00:00 2001 From: David Lebrun Date: Fri, 25 Aug 2017 09:58:17 +0200 Subject: ipv6: sr: implement additional seg6local actions This patch implements the following seg6local actions. - SEG6_LOCAL_ACTION_END_T: regular SRH processing and forward to the next-hop looked up in the specified routing table. - SEG6_LOCAL_ACTION_END_DX2: decapsulate an L2 frame and forward it to the specified network interface. - SEG6_LOCAL_ACTION_END_DX4: decapsulate an IPv4 packet and forward it, possibly to the specified next-hop. - SEG6_LOCAL_ACTION_END_DT6: decapsulate an IPv6 packet and forward it to the next-hop looked up in the specified routing table. Signed-off-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/seg6_local.c | 147 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 147 insertions(+) (limited to 'net') diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 26db4d3e8755..9c1a885ee482 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -30,6 +30,7 @@ #ifdef CONFIG_IPV6_SEG6_HMAC #include #endif +#include struct seg6_local_lwt; @@ -226,6 +227,82 @@ drop: return -EINVAL; } +static int input_action_end_t(struct sk_buff *skb, struct seg6_local_lwt *slwt) +{ + struct ipv6_sr_hdr *srh; + + srh = get_and_validate_srh(skb); + if (!srh) + goto drop; + + advance_nextseg(srh, &ipv6_hdr(skb)->daddr); + + lookup_nexthop(skb, NULL, slwt->table); + + return dst_input(skb); + +drop: + kfree_skb(skb); + return -EINVAL; +} + +/* decapsulate and forward inner L2 frame on specified interface */ +static int input_action_end_dx2(struct sk_buff *skb, + struct seg6_local_lwt *slwt) +{ + struct net *net = dev_net(skb->dev); + struct net_device *odev; + struct ethhdr *eth; + + if (!decap_and_validate(skb, NEXTHDR_NONE)) + goto drop; + + if (!pskb_may_pull(skb, ETH_HLEN)) + goto drop; + + skb_reset_mac_header(skb); + eth = (struct ethhdr *)skb->data; + + /* To determine the frame's protocol, we assume it is 802.3. This avoids + * a call to eth_type_trans(), which is not really relevant for our + * use case. + */ + if (!eth_proto_is_802_3(eth->h_proto)) + goto drop; + + odev = dev_get_by_index_rcu(net, slwt->oif); + if (!odev) + goto drop; + + /* As we accept Ethernet frames, make sure the egress device is of + * the correct type. + */ + if (odev->type != ARPHRD_ETHER) + goto drop; + + if (!(odev->flags & IFF_UP) || !netif_carrier_ok(odev)) + goto drop; + + skb_orphan(skb); + + if (skb_warn_if_lro(skb)) + goto drop; + + skb_forward_csum(skb); + + if (skb->len - ETH_HLEN > odev->mtu) + goto drop; + + skb->dev = odev; + skb->protocol = eth->h_proto; + + return dev_queue_xmit(skb); + +drop: + kfree_skb(skb); + return -EINVAL; +} + /* decapsulate and forward to specified nexthop */ static int input_action_end_dx6(struct sk_buff *skb, struct seg6_local_lwt *slwt) @@ -260,6 +337,56 @@ drop: return -EINVAL; } +static int input_action_end_dx4(struct sk_buff *skb, + struct seg6_local_lwt *slwt) +{ + struct iphdr *iph; + __be32 nhaddr; + int err; + + if (!decap_and_validate(skb, IPPROTO_IPIP)) + goto drop; + + if (!pskb_may_pull(skb, sizeof(struct iphdr))) + goto drop; + + skb->protocol = htons(ETH_P_IP); + + iph = ip_hdr(skb); + + nhaddr = slwt->nh4.s_addr ?: iph->daddr; + + skb_dst_drop(skb); + + err = ip_route_input(skb, nhaddr, iph->saddr, 0, skb->dev); + if (err) + goto drop; + + return dst_input(skb); + +drop: + kfree_skb(skb); + return -EINVAL; +} + +static int input_action_end_dt6(struct sk_buff *skb, + struct seg6_local_lwt *slwt) +{ + if (!decap_and_validate(skb, IPPROTO_IPV6)) + goto drop; + + if (!pskb_may_pull(skb, sizeof(struct ipv6hdr))) + goto drop; + + lookup_nexthop(skb, NULL, slwt->table); + + return dst_input(skb); + +drop: + kfree_skb(skb); + return -EINVAL; +} + /* push an SRH on top of the current one */ static int input_action_end_b6(struct sk_buff *skb, struct seg6_local_lwt *slwt) { @@ -329,11 +456,31 @@ static struct seg6_action_desc seg6_action_table[] = { .attrs = (1 << SEG6_LOCAL_NH6), .input = input_action_end_x, }, + { + .action = SEG6_LOCAL_ACTION_END_T, + .attrs = (1 << SEG6_LOCAL_TABLE), + .input = input_action_end_t, + }, + { + .action = SEG6_LOCAL_ACTION_END_DX2, + .attrs = (1 << SEG6_LOCAL_OIF), + .input = input_action_end_dx2, + }, { .action = SEG6_LOCAL_ACTION_END_DX6, .attrs = (1 << SEG6_LOCAL_NH6), .input = input_action_end_dx6, }, + { + .action = SEG6_LOCAL_ACTION_END_DX4, + .attrs = (1 << SEG6_LOCAL_NH4), + .input = input_action_end_dx4, + }, + { + .action = SEG6_LOCAL_ACTION_END_DT6, + .attrs = (1 << SEG6_LOCAL_TABLE), + .input = input_action_end_dt6, + }, { .action = SEG6_LOCAL_ACTION_END_B6, .attrs = (1 << SEG6_LOCAL_SRH), -- cgit v1.2.3 From 27d7f07c49de289e2a2d48a6bec3e111ed4b1222 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 24 Aug 2017 16:51:27 -0700 Subject: net_sched: get rid of more forward declarations This is not needed if we move them up properly. Signed-off-by: Cong Wang Acked-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/sch_api.c | 348 +++++++++++++++++++++++++--------------------------- 1 file changed, 169 insertions(+), 179 deletions(-) (limited to 'net') diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index aaf552b8e120..330ffaea9974 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -36,13 +36,6 @@ #include #include -static int qdisc_notify(struct net *net, struct sk_buff *oskb, - struct nlmsghdr *n, u32 clid, - struct Qdisc *old, struct Qdisc *new); -static int tclass_notify(struct net *net, struct sk_buff *oskb, - struct nlmsghdr *n, struct Qdisc *q, - unsigned long cl, int event); - /* Short review. @@ -775,6 +768,111 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n, } EXPORT_SYMBOL(qdisc_tree_reduce_backlog); +static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, + u32 portid, u32 seq, u16 flags, int event) +{ + struct gnet_stats_basic_cpu __percpu *cpu_bstats = NULL; + struct gnet_stats_queue __percpu *cpu_qstats = NULL; + struct tcmsg *tcm; + struct nlmsghdr *nlh; + unsigned char *b = skb_tail_pointer(skb); + struct gnet_dump d; + struct qdisc_size_table *stab; + __u32 qlen; + + cond_resched(); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); + if (!nlh) + goto out_nlmsg_trim; + tcm = nlmsg_data(nlh); + tcm->tcm_family = AF_UNSPEC; + tcm->tcm__pad1 = 0; + tcm->tcm__pad2 = 0; + tcm->tcm_ifindex = qdisc_dev(q)->ifindex; + tcm->tcm_parent = clid; + tcm->tcm_handle = q->handle; + tcm->tcm_info = refcount_read(&q->refcnt); + if (nla_put_string(skb, TCA_KIND, q->ops->id)) + goto nla_put_failure; + if (q->ops->dump && q->ops->dump(q, skb) < 0) + goto nla_put_failure; + qlen = q->q.qlen; + + stab = rtnl_dereference(q->stab); + if (stab && qdisc_dump_stab(skb, stab) < 0) + goto nla_put_failure; + + if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, + NULL, &d, TCA_PAD) < 0) + goto nla_put_failure; + + if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0) + goto nla_put_failure; + + if (qdisc_is_percpu_stats(q)) { + cpu_bstats = q->cpu_bstats; + cpu_qstats = q->cpu_qstats; + } + + if (gnet_stats_copy_basic(qdisc_root_sleeping_running(q), + &d, cpu_bstats, &q->bstats) < 0 || + gnet_stats_copy_rate_est(&d, &q->rate_est) < 0 || + gnet_stats_copy_queue(&d, cpu_qstats, &q->qstats, qlen) < 0) + goto nla_put_failure; + + if (gnet_stats_finish_copy(&d) < 0) + goto nla_put_failure; + + nlh->nlmsg_len = skb_tail_pointer(skb) - b; + return skb->len; + +out_nlmsg_trim: +nla_put_failure: + nlmsg_trim(skb, b); + return -1; +} + +static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible) +{ + if (q->flags & TCQ_F_BUILTIN) + return true; + if ((q->flags & TCQ_F_INVISIBLE) && !dump_invisible) + return true; + + return false; +} + +static int qdisc_notify(struct net *net, struct sk_buff *oskb, + struct nlmsghdr *n, u32 clid, + struct Qdisc *old, struct Qdisc *new) +{ + struct sk_buff *skb; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + + skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + if (old && !tc_qdisc_dump_ignore(old, false)) { + if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq, + 0, RTM_DELQDISC) < 0) + goto err_out; + } + if (new && !tc_qdisc_dump_ignore(new, false)) { + if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq, + old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0) + goto err_out; + } + + if (skb->len) + return rtnetlink_send(skb, net, portid, RTNLGRP_TC, + n->nlmsg_flags & NLM_F_ECHO); + +err_out: + kfree_skb(skb); + return -EINVAL; +} + static void notify_and_destroy(struct net *net, struct sk_buff *skb, struct nlmsghdr *n, u32 clid, struct Qdisc *old, struct Qdisc *new) @@ -1342,111 +1440,6 @@ graft: return 0; } -static int tc_fill_qdisc(struct sk_buff *skb, struct Qdisc *q, u32 clid, - u32 portid, u32 seq, u16 flags, int event) -{ - struct gnet_stats_basic_cpu __percpu *cpu_bstats = NULL; - struct gnet_stats_queue __percpu *cpu_qstats = NULL; - struct tcmsg *tcm; - struct nlmsghdr *nlh; - unsigned char *b = skb_tail_pointer(skb); - struct gnet_dump d; - struct qdisc_size_table *stab; - __u32 qlen; - - cond_resched(); - nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); - if (!nlh) - goto out_nlmsg_trim; - tcm = nlmsg_data(nlh); - tcm->tcm_family = AF_UNSPEC; - tcm->tcm__pad1 = 0; - tcm->tcm__pad2 = 0; - tcm->tcm_ifindex = qdisc_dev(q)->ifindex; - tcm->tcm_parent = clid; - tcm->tcm_handle = q->handle; - tcm->tcm_info = refcount_read(&q->refcnt); - if (nla_put_string(skb, TCA_KIND, q->ops->id)) - goto nla_put_failure; - if (q->ops->dump && q->ops->dump(q, skb) < 0) - goto nla_put_failure; - qlen = q->q.qlen; - - stab = rtnl_dereference(q->stab); - if (stab && qdisc_dump_stab(skb, stab) < 0) - goto nla_put_failure; - - if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, - NULL, &d, TCA_PAD) < 0) - goto nla_put_failure; - - if (q->ops->dump_stats && q->ops->dump_stats(q, &d) < 0) - goto nla_put_failure; - - if (qdisc_is_percpu_stats(q)) { - cpu_bstats = q->cpu_bstats; - cpu_qstats = q->cpu_qstats; - } - - if (gnet_stats_copy_basic(qdisc_root_sleeping_running(q), - &d, cpu_bstats, &q->bstats) < 0 || - gnet_stats_copy_rate_est(&d, &q->rate_est) < 0 || - gnet_stats_copy_queue(&d, cpu_qstats, &q->qstats, qlen) < 0) - goto nla_put_failure; - - if (gnet_stats_finish_copy(&d) < 0) - goto nla_put_failure; - - nlh->nlmsg_len = skb_tail_pointer(skb) - b; - return skb->len; - -out_nlmsg_trim: -nla_put_failure: - nlmsg_trim(skb, b); - return -1; -} - -static bool tc_qdisc_dump_ignore(struct Qdisc *q, bool dump_invisible) -{ - if (q->flags & TCQ_F_BUILTIN) - return true; - if ((q->flags & TCQ_F_INVISIBLE) && !dump_invisible) - return true; - - return false; -} - -static int qdisc_notify(struct net *net, struct sk_buff *oskb, - struct nlmsghdr *n, u32 clid, - struct Qdisc *old, struct Qdisc *new) -{ - struct sk_buff *skb; - u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; - - skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb) - return -ENOBUFS; - - if (old && !tc_qdisc_dump_ignore(old, false)) { - if (tc_fill_qdisc(skb, old, clid, portid, n->nlmsg_seq, - 0, RTM_DELQDISC) < 0) - goto err_out; - } - if (new && !tc_qdisc_dump_ignore(new, false)) { - if (tc_fill_qdisc(skb, new, clid, portid, n->nlmsg_seq, - old ? NLM_F_REPLACE : 0, RTM_NEWQDISC) < 0) - goto err_out; - } - - if (skb->len) - return rtnetlink_send(skb, net, portid, RTNLGRP_TC, - n->nlmsg_flags & NLM_F_ECHO); - -err_out: - kfree_skb(skb); - return -EINVAL; -} - static int tc_dump_qdisc_root(struct Qdisc *root, struct sk_buff *skb, struct netlink_callback *cb, int *q_idx_p, int s_q_idx, bool recur, @@ -1559,7 +1552,71 @@ done: * Traffic classes manipulation. * ************************************************/ +static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, + unsigned long cl, + u32 portid, u32 seq, u16 flags, int event) +{ + struct tcmsg *tcm; + struct nlmsghdr *nlh; + unsigned char *b = skb_tail_pointer(skb); + struct gnet_dump d; + const struct Qdisc_class_ops *cl_ops = q->ops->cl_ops; + cond_resched(); + nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); + if (!nlh) + goto out_nlmsg_trim; + tcm = nlmsg_data(nlh); + tcm->tcm_family = AF_UNSPEC; + tcm->tcm__pad1 = 0; + tcm->tcm__pad2 = 0; + tcm->tcm_ifindex = qdisc_dev(q)->ifindex; + tcm->tcm_parent = q->handle; + tcm->tcm_handle = q->handle; + tcm->tcm_info = 0; + if (nla_put_string(skb, TCA_KIND, q->ops->id)) + goto nla_put_failure; + if (cl_ops->dump && cl_ops->dump(q, cl, skb, tcm) < 0) + goto nla_put_failure; + + if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, + NULL, &d, TCA_PAD) < 0) + goto nla_put_failure; + + if (cl_ops->dump_stats && cl_ops->dump_stats(q, cl, &d) < 0) + goto nla_put_failure; + + if (gnet_stats_finish_copy(&d) < 0) + goto nla_put_failure; + + nlh->nlmsg_len = skb_tail_pointer(skb) - b; + return skb->len; + +out_nlmsg_trim: +nla_put_failure: + nlmsg_trim(skb, b); + return -1; +} + +static int tclass_notify(struct net *net, struct sk_buff *oskb, + struct nlmsghdr *n, struct Qdisc *q, + unsigned long cl, int event) +{ + struct sk_buff *skb; + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + + skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event) < 0) { + kfree_skb(skb); + return -EINVAL; + } + + return rtnetlink_send(skb, net, portid, RTNLGRP_TC, + n->nlmsg_flags & NLM_F_ECHO); +} static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, struct netlink_ext_ack *extack) @@ -1695,73 +1752,6 @@ out: return err; } - -static int tc_fill_tclass(struct sk_buff *skb, struct Qdisc *q, - unsigned long cl, - u32 portid, u32 seq, u16 flags, int event) -{ - struct tcmsg *tcm; - struct nlmsghdr *nlh; - unsigned char *b = skb_tail_pointer(skb); - struct gnet_dump d; - const struct Qdisc_class_ops *cl_ops = q->ops->cl_ops; - - cond_resched(); - nlh = nlmsg_put(skb, portid, seq, event, sizeof(*tcm), flags); - if (!nlh) - goto out_nlmsg_trim; - tcm = nlmsg_data(nlh); - tcm->tcm_family = AF_UNSPEC; - tcm->tcm__pad1 = 0; - tcm->tcm__pad2 = 0; - tcm->tcm_ifindex = qdisc_dev(q)->ifindex; - tcm->tcm_parent = q->handle; - tcm->tcm_handle = q->handle; - tcm->tcm_info = 0; - if (nla_put_string(skb, TCA_KIND, q->ops->id)) - goto nla_put_failure; - if (cl_ops->dump && cl_ops->dump(q, cl, skb, tcm) < 0) - goto nla_put_failure; - - if (gnet_stats_start_copy_compat(skb, TCA_STATS2, TCA_STATS, TCA_XSTATS, - NULL, &d, TCA_PAD) < 0) - goto nla_put_failure; - - if (cl_ops->dump_stats && cl_ops->dump_stats(q, cl, &d) < 0) - goto nla_put_failure; - - if (gnet_stats_finish_copy(&d) < 0) - goto nla_put_failure; - - nlh->nlmsg_len = skb_tail_pointer(skb) - b; - return skb->len; - -out_nlmsg_trim: -nla_put_failure: - nlmsg_trim(skb, b); - return -1; -} - -static int tclass_notify(struct net *net, struct sk_buff *oskb, - struct nlmsghdr *n, struct Qdisc *q, - unsigned long cl, int event) -{ - struct sk_buff *skb; - u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; - - skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); - if (!skb) - return -ENOBUFS; - - if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, event) < 0) { - kfree_skb(skb); - return -EINVAL; - } - - return rtnetlink_send(skb, net, portid, RTNLGRP_TC, - n->nlmsg_flags & NLM_F_ECHO); -} - struct qdisc_dump_args { struct qdisc_walker w; struct sk_buff *skb; -- cgit v1.2.3 From 14546ba1e5653e5dd00d113e98bd21b5b235192b Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 24 Aug 2017 16:51:28 -0700 Subject: net_sched: introduce tclass_del_notify() Like for TC actions, ->delete() is a special case, we have to prepare and fill the notification before delete otherwise would get use-after-free after we remove the reference count. Signed-off-by: Cong Wang Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/sch_api.c | 39 +++++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 330ffaea9974..3ef4eb578739 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -1618,6 +1618,38 @@ static int tclass_notify(struct net *net, struct sk_buff *oskb, n->nlmsg_flags & NLM_F_ECHO); } +static int tclass_del_notify(struct net *net, + const struct Qdisc_class_ops *cops, + struct sk_buff *oskb, struct nlmsghdr *n, + struct Qdisc *q, unsigned long cl) +{ + u32 portid = oskb ? NETLINK_CB(oskb).portid : 0; + struct sk_buff *skb; + int err = 0; + + if (!cops->delete) + return -EOPNOTSUPP; + + skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); + if (!skb) + return -ENOBUFS; + + if (tc_fill_tclass(skb, q, cl, portid, n->nlmsg_seq, 0, + RTM_DELTCLASS) < 0) { + kfree_skb(skb); + return -EINVAL; + } + + err = cops->delete(q, cl); + if (err) { + kfree_skb(skb); + return err; + } + + return rtnetlink_send(skb, net, portid, RTNLGRP_TC, + n->nlmsg_flags & NLM_F_ECHO); +} + static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, struct netlink_ext_ack *extack) { @@ -1722,12 +1754,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, goto out; break; case RTM_DELTCLASS: - err = -EOPNOTSUPP; - if (cops->delete) - err = cops->delete(q, cl); - if (err == 0) - tclass_notify(net, skb, n, q, cl, - RTM_DELTCLASS); + err = tclass_del_notify(net, cops, skb, n, q, cl); goto out; case RTM_GETTCLASS: err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS); -- cgit v1.2.3 From 143976ce992fcf3bfc0f4d15d5726bb492dcf262 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 24 Aug 2017 16:51:29 -0700 Subject: net_sched: remove tc class reference counting For TC classes, their ->get() and ->put() are always paired, and the reference counting is completely useless, because: 1) For class modification and dumping paths, we already hold RTNL lock, so all of these ->get(),->change(),->put() are atomic. 2) For filter bindiing/unbinding, we use other reference counter than this one, and they should have RTNL lock too. 3) For ->qlen_notify(), it is special because it is called on ->enqueue() path, but we already hold qdisc tree lock there, and we hold this tree lock when graft or delete the class too, so it should not be gone or changed until we release the tree lock. Therefore, this patch removes ->get() and ->put(), but: 1) Adds a new ->find() to find the pointer to a class by classid, no refcnt. 2) Move the original class destroy upon the last refcnt into ->delete(), right after releasing tree lock. This is fine because the class is already removed from hash when holding the lock. For those who also use ->put() as ->unbind(), just rename them to reflect this change. Cc: Jamal Hadi Salim Signed-off-by: Cong Wang Acked-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/sch_generic.h | 3 +-- net/sched/cls_api.c | 17 ++++++----------- net/sched/sch_api.c | 21 ++++++++------------- net/sched/sch_atm.c | 30 ++++++++++++++++-------------- net/sched/sch_cbq.c | 41 ++++------------------------------------- net/sched/sch_drr.c | 30 +++++------------------------- net/sched/sch_dsmark.c | 17 ++++++++--------- net/sched/sch_fq_codel.c | 9 ++++----- net/sched/sch_hfsc.c | 32 +++++--------------------------- net/sched/sch_htb.c | 33 +++++++-------------------------- net/sched/sch_ingress.c | 20 +++++++++----------- net/sched/sch_mq.c | 9 ++------- net/sched/sch_mqprio.c | 9 ++------- net/sched/sch_multiq.c | 11 +++++------ net/sched/sch_netem.c | 9 ++------- net/sched/sch_prio.c | 11 +++++------ net/sched/sch_qfq.c | 30 +++++------------------------- net/sched/sch_red.c | 9 ++------- net/sched/sch_sfb.c | 9 ++++----- net/sched/sch_sfq.c | 9 ++++----- net/sched/sch_tbf.c | 9 ++------- 21 files changed, 106 insertions(+), 262 deletions(-) (limited to 'net') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index 1688f0f6c7ba..d817585aa5df 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -147,8 +147,7 @@ struct Qdisc_class_ops { void (*qlen_notify)(struct Qdisc *, unsigned long); /* Class manipulation routines */ - unsigned long (*get)(struct Qdisc *, u32 classid); - void (*put)(struct Qdisc *, unsigned long); + unsigned long (*find)(struct Qdisc *, u32 classid); int (*change)(struct Qdisc *, u32, u32, struct nlattr **, unsigned long *); int (*delete)(struct Qdisc *, unsigned long); diff --git a/net/sched/cls_api.c b/net/sched/cls_api.c index eef6b077f30e..d470a4e2de58 100644 --- a/net/sched/cls_api.c +++ b/net/sched/cls_api.c @@ -586,7 +586,7 @@ replay: /* Do we search for filter, attached to class? */ if (TC_H_MIN(parent)) { - cl = cops->get(q, parent); + cl = cops->find(q, parent); if (cl == 0) return -ENOENT; } @@ -716,8 +716,6 @@ replay: errout: if (chain) tcf_chain_put(chain); - if (cl) - cops->put(q, cl); if (err == -EAGAIN) /* Replay the request. */ goto replay; @@ -822,17 +820,17 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) goto out; cops = q->ops->cl_ops; if (!cops) - goto errout; + goto out; if (!cops->tcf_block) - goto errout; + goto out; if (TC_H_MIN(tcm->tcm_parent)) { - cl = cops->get(q, tcm->tcm_parent); + cl = cops->find(q, tcm->tcm_parent); if (cl == 0) - goto errout; + goto out; } block = cops->tcf_block(q, cl); if (!block) - goto errout; + goto out; index_start = cb->args[0]; index = 0; @@ -847,9 +845,6 @@ static int tc_dump_tfilter(struct sk_buff *skb, struct netlink_callback *cb) cb->args[0] = index; -errout: - if (cl) - cops->put(q, cl); out: return skb->len; } diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index 3ef4eb578739..e7f8e4bfd4ec 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -153,7 +153,7 @@ int register_qdisc(struct Qdisc_ops *qops) if (qops->cl_ops) { const struct Qdisc_class_ops *cops = qops->cl_ops; - if (!(cops->get && cops->put && cops->walk && cops->leaf)) + if (!(cops->find && cops->walk && cops->leaf)) goto out_einval; if (cops->tcf_block && !(cops->bind_tcf && cops->unbind_tcf)) @@ -320,12 +320,11 @@ static struct Qdisc *qdisc_leaf(struct Qdisc *p, u32 classid) if (cops == NULL) return NULL; - cl = cops->get(p, classid); + cl = cops->find(p, classid); if (cl == 0) return NULL; leaf = cops->leaf(p, cl); - cops->put(p, cl); return leaf; } @@ -756,9 +755,8 @@ void qdisc_tree_reduce_backlog(struct Qdisc *sch, unsigned int n, } cops = sch->ops->cl_ops; if (notify && cops->qlen_notify) { - cl = cops->get(sch, parentid); + cl = cops->find(sch, parentid); cops->qlen_notify(sch, cl); - cops->put(sch, cl); } sch->q.qlen -= n; sch->qstats.backlog -= len; @@ -955,11 +953,11 @@ skip: err = -EOPNOTSUPP; if (cops && cops->graft) { - unsigned long cl = cops->get(parent, classid); - if (cl) { + unsigned long cl = cops->find(parent, classid); + + if (cl) err = cops->graft(parent, cl, new, &old); - cops->put(parent, cl); - } else + else err = -ENOENT; } if (!err) @@ -1739,7 +1737,7 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, clid = TC_H_MAKE(qid, clid); if (clid) - cl = cops->get(q, clid); + cl = cops->find(q, clid); if (cl == 0) { err = -ENOENT; @@ -1773,9 +1771,6 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS); out: - if (cl) - cops->put(q, cl); - return err; } diff --git a/net/sched/sch_atm.c b/net/sched/sch_atm.c index 2732950766a9..c5fcdf1a58a0 100644 --- a/net/sched/sch_atm.c +++ b/net/sched/sch_atm.c @@ -108,23 +108,29 @@ static struct Qdisc *atm_tc_leaf(struct Qdisc *sch, unsigned long cl) return flow ? flow->q : NULL; } -static unsigned long atm_tc_get(struct Qdisc *sch, u32 classid) +static unsigned long atm_tc_find(struct Qdisc *sch, u32 classid) { struct atm_qdisc_data *p __maybe_unused = qdisc_priv(sch); struct atm_flow_data *flow; - pr_debug("atm_tc_get(sch %p,[qdisc %p],classid %x)\n", sch, p, classid); + pr_debug("%s(sch %p,[qdisc %p],classid %x)\n", __func__, sch, p, classid); flow = lookup_flow(sch, classid); - if (flow) - flow->ref++; - pr_debug("atm_tc_get: flow %p\n", flow); + pr_debug("%s: flow %p\n", __func__, flow); return (unsigned long)flow; } static unsigned long atm_tc_bind_filter(struct Qdisc *sch, unsigned long parent, u32 classid) { - return atm_tc_get(sch, classid); + struct atm_qdisc_data *p __maybe_unused = qdisc_priv(sch); + struct atm_flow_data *flow; + + pr_debug("%s(sch %p,[qdisc %p],classid %x)\n", __func__, sch, p, classid); + flow = lookup_flow(sch, classid); + if (flow) + flow->ref++; + pr_debug("%s: flow %p\n", __func__, flow); + return (unsigned long)flow; } /* @@ -234,7 +240,7 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, excess = NULL; else { excess = (struct atm_flow_data *) - atm_tc_get(sch, nla_get_u32(tb[TCA_ATM_EXCESS])); + atm_tc_find(sch, nla_get_u32(tb[TCA_ATM_EXCESS])); if (!excess) return -ENOENT; } @@ -262,10 +268,9 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, for (i = 1; i < 0x8000; i++) { classid = TC_H_MAKE(sch->handle, 0x8000 | i); - cl = atm_tc_get(sch, classid); + cl = atm_tc_find(sch, classid); if (!cl) break; - atm_tc_put(sch, cl); } } pr_debug("atm_tc_change: new id %x\n", classid); @@ -305,8 +310,6 @@ static int atm_tc_change(struct Qdisc *sch, u32 classid, u32 parent, *arg = (unsigned long)flow; return 0; err_out: - if (excess) - atm_tc_put(sch, (unsigned long)excess); sockfd_put(sock); return error; } @@ -377,7 +380,7 @@ static int atm_tc_enqueue(struct sk_buff *skb, struct Qdisc *sch, result = TC_ACT_OK; /* be nice to gcc */ flow = NULL; if (TC_H_MAJ(skb->priority) != sch->handle || - !(flow = (struct atm_flow_data *)atm_tc_get(sch, skb->priority))) { + !(flow = (struct atm_flow_data *)atm_tc_find(sch, skb->priority))) { struct tcf_proto *fl; list_for_each_entry(flow, &p->flows, list) { @@ -655,8 +658,7 @@ static int atm_tc_dump(struct Qdisc *sch, struct sk_buff *skb) static const struct Qdisc_class_ops atm_class_ops = { .graft = atm_tc_graft, .leaf = atm_tc_leaf, - .get = atm_tc_get, - .put = atm_tc_put, + .find = atm_tc_find, .change = atm_tc_change, .delete = atm_tc_delete, .walk = atm_tc_walk, diff --git a/net/sched/sch_cbq.c b/net/sched/sch_cbq.c index 1bdb0106f342..3ec8bec109bb 100644 --- a/net/sched/sch_cbq.c +++ b/net/sched/sch_cbq.c @@ -129,7 +129,6 @@ struct cbq_class { struct tcf_proto __rcu *filter_list; struct tcf_block *block; - int refcnt; int filters; struct cbq_class *defaults[TC_PRIO_MAX + 1]; @@ -1155,7 +1154,6 @@ static int cbq_init(struct Qdisc *sch, struct nlattr *opt) if (err < 0) goto put_rtab; - q->link.refcnt = 1; q->link.sibling = &q->link; q->link.common.classid = sch->handle; q->link.qdisc = sch; @@ -1388,16 +1386,11 @@ static void cbq_qlen_notify(struct Qdisc *sch, unsigned long arg) cbq_deactivate_class(cl); } -static unsigned long cbq_get(struct Qdisc *sch, u32 classid) +static unsigned long cbq_find(struct Qdisc *sch, u32 classid) { struct cbq_sched_data *q = qdisc_priv(sch); - struct cbq_class *cl = cbq_class_lookup(q, classid); - if (cl) { - cl->refcnt++; - return (unsigned long)cl; - } - return 0; + return (unsigned long)cbq_class_lookup(q, classid); } static void cbq_destroy_class(struct Qdisc *sch, struct cbq_class *cl) @@ -1443,25 +1436,6 @@ static void cbq_destroy(struct Qdisc *sch) qdisc_class_hash_destroy(&q->clhash); } -static void cbq_put(struct Qdisc *sch, unsigned long arg) -{ - struct cbq_class *cl = (struct cbq_class *)arg; - - if (--cl->refcnt == 0) { -#ifdef CONFIG_NET_CLS_ACT - spinlock_t *root_lock = qdisc_root_sleeping_lock(sch); - struct cbq_sched_data *q = qdisc_priv(sch); - - spin_lock_bh(root_lock); - if (q->rx_class == cl) - q->rx_class = NULL; - spin_unlock_bh(root_lock); -#endif - - cbq_destroy_class(sch, cl); - } -} - static int cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **tca, unsigned long *arg) @@ -1608,7 +1582,6 @@ cbq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, struct nlattr **t cl->R_tab = rtab; rtab = NULL; - cl->refcnt = 1; cl->q = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, classid); if (!cl->q) cl->q = &noop_qdisc; @@ -1689,12 +1662,7 @@ static int cbq_delete(struct Qdisc *sch, unsigned long arg) cbq_rmprio(q, cl); sch_tree_unlock(sch); - BUG_ON(--cl->refcnt == 0); - /* - * This shouldn't happen: we "hold" one cops->get() when called - * from tc_ctl_tclass; the destroy method is done from cops->put(). - */ - + cbq_destroy_class(sch, cl); return 0; } @@ -1760,8 +1728,7 @@ static const struct Qdisc_class_ops cbq_class_ops = { .graft = cbq_graft, .leaf = cbq_leaf, .qlen_notify = cbq_qlen_notify, - .get = cbq_get, - .put = cbq_put, + .find = cbq_find, .change = cbq_change_class, .delete = cbq_delete, .walk = cbq_walk, diff --git a/net/sched/sch_drr.c b/net/sched/sch_drr.c index 1d2f6235dfcf..2d0e8d4bdc29 100644 --- a/net/sched/sch_drr.c +++ b/net/sched/sch_drr.c @@ -20,7 +20,6 @@ struct drr_class { struct Qdisc_class_common common; - unsigned int refcnt; unsigned int filter_cnt; struct gnet_stats_basic_packed bstats; @@ -111,7 +110,6 @@ static int drr_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (cl == NULL) return -ENOBUFS; - cl->refcnt = 1; cl->common.classid = classid; cl->quantum = quantum; cl->qdisc = qdisc_create_dflt(sch->dev_queue, @@ -163,32 +161,15 @@ static int drr_delete_class(struct Qdisc *sch, unsigned long arg) drr_purge_queue(cl); qdisc_class_hash_remove(&q->clhash, &cl->common); - BUG_ON(--cl->refcnt == 0); - /* - * This shouldn't happen: we "hold" one cops->get() when called - * from tc_ctl_tclass; the destroy method is done from cops->put(). - */ - sch_tree_unlock(sch); - return 0; -} - -static unsigned long drr_get_class(struct Qdisc *sch, u32 classid) -{ - struct drr_class *cl = drr_find_class(sch, classid); - if (cl != NULL) - cl->refcnt++; - - return (unsigned long)cl; + drr_destroy_class(sch, cl); + return 0; } -static void drr_put_class(struct Qdisc *sch, unsigned long arg) +static unsigned long drr_search_class(struct Qdisc *sch, u32 classid) { - struct drr_class *cl = (struct drr_class *)arg; - - if (--cl->refcnt == 0) - drr_destroy_class(sch, cl); + return (unsigned long)drr_find_class(sch, classid); } static struct tcf_block *drr_tcf_block(struct Qdisc *sch, unsigned long cl) @@ -478,8 +459,7 @@ static void drr_destroy_qdisc(struct Qdisc *sch) static const struct Qdisc_class_ops drr_class_ops = { .change = drr_change_class, .delete = drr_delete_class, - .get = drr_get_class, - .put = drr_put_class, + .find = drr_search_class, .tcf_block = drr_tcf_block, .bind_tcf = drr_bind_tcf, .unbind_tcf = drr_unbind_tcf, diff --git a/net/sched/sch_dsmark.c b/net/sched/sch_dsmark.c index 6d94fcc3592a..2836c80c7aa5 100644 --- a/net/sched/sch_dsmark.c +++ b/net/sched/sch_dsmark.c @@ -85,21 +85,21 @@ static struct Qdisc *dsmark_leaf(struct Qdisc *sch, unsigned long arg) return p->q; } -static unsigned long dsmark_get(struct Qdisc *sch, u32 classid) +static unsigned long dsmark_find(struct Qdisc *sch, u32 classid) { - pr_debug("%s(sch %p,[qdisc %p],classid %x)\n", - __func__, sch, qdisc_priv(sch), classid); - return TC_H_MIN(classid) + 1; } static unsigned long dsmark_bind_filter(struct Qdisc *sch, unsigned long parent, u32 classid) { - return dsmark_get(sch, classid); + pr_debug("%s(sch %p,[qdisc %p],classid %x)\n", + __func__, sch, qdisc_priv(sch), classid); + + return dsmark_find(sch, classid); } -static void dsmark_put(struct Qdisc *sch, unsigned long cl) +static void dsmark_unbind_filter(struct Qdisc *sch, unsigned long cl) { } @@ -469,14 +469,13 @@ nla_put_failure: static const struct Qdisc_class_ops dsmark_class_ops = { .graft = dsmark_graft, .leaf = dsmark_leaf, - .get = dsmark_get, - .put = dsmark_put, + .find = dsmark_find, .change = dsmark_change, .delete = dsmark_delete, .walk = dsmark_walk, .tcf_block = dsmark_tcf_block, .bind_tcf = dsmark_bind_filter, - .unbind_tcf = dsmark_put, + .unbind_tcf = dsmark_unbind_filter, .dump = dsmark_dump_class, }; diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 337f2d6d81e4..7699b50688cd 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -579,7 +579,7 @@ static struct Qdisc *fq_codel_leaf(struct Qdisc *sch, unsigned long arg) return NULL; } -static unsigned long fq_codel_get(struct Qdisc *sch, u32 classid) +static unsigned long fq_codel_find(struct Qdisc *sch, u32 classid) { return 0; } @@ -592,7 +592,7 @@ static unsigned long fq_codel_bind(struct Qdisc *sch, unsigned long parent, return 0; } -static void fq_codel_put(struct Qdisc *q, unsigned long cl) +static void fq_codel_unbind(struct Qdisc *q, unsigned long cl) { } @@ -683,11 +683,10 @@ static void fq_codel_walk(struct Qdisc *sch, struct qdisc_walker *arg) static const struct Qdisc_class_ops fq_codel_class_ops = { .leaf = fq_codel_leaf, - .get = fq_codel_get, - .put = fq_codel_put, + .find = fq_codel_find, .tcf_block = fq_codel_tcf_block, .bind_tcf = fq_codel_bind, - .unbind_tcf = fq_codel_put, + .unbind_tcf = fq_codel_unbind, .dump = fq_codel_dump_class, .dump_stats = fq_codel_dump_class_stats, .walk = fq_codel_walk, diff --git a/net/sched/sch_hfsc.c b/net/sched/sch_hfsc.c index 15f09cb9f1ff..7c7820d0fdc7 100644 --- a/net/sched/sch_hfsc.c +++ b/net/sched/sch_hfsc.c @@ -110,7 +110,6 @@ enum hfsc_class_flags { struct hfsc_class { struct Qdisc_class_common cl_common; - unsigned int refcnt; /* usage count */ struct gnet_stats_basic_packed bstats; struct gnet_stats_queue qstats; @@ -1045,7 +1044,6 @@ hfsc_change_class(struct Qdisc *sch, u32 classid, u32 parentid, hfsc_change_usc(cl, usc, 0); cl->cl_common.classid = classid; - cl->refcnt = 1; cl->sched = q; cl->cl_parent = parent; cl->qdisc = qdisc_create_dflt(sch->dev_queue, @@ -1101,13 +1099,9 @@ hfsc_delete_class(struct Qdisc *sch, unsigned long arg) hfsc_purge_queue(sch, cl); qdisc_class_hash_remove(&q->clhash, &cl->cl_common); - BUG_ON(--cl->refcnt == 0); - /* - * This shouldn't happen: we "hold" one cops->get() when called - * from tc_ctl_tclass; the destroy method is done from cops->put(). - */ - sch_tree_unlock(sch); + + hfsc_destroy_class(sch, cl); return 0; } @@ -1208,23 +1202,9 @@ hfsc_qlen_notify(struct Qdisc *sch, unsigned long arg) } static unsigned long -hfsc_get_class(struct Qdisc *sch, u32 classid) -{ - struct hfsc_class *cl = hfsc_find_class(classid, sch); - - if (cl != NULL) - cl->refcnt++; - - return (unsigned long)cl; -} - -static void -hfsc_put_class(struct Qdisc *sch, unsigned long arg) +hfsc_search_class(struct Qdisc *sch, u32 classid) { - struct hfsc_class *cl = (struct hfsc_class *)arg; - - if (--cl->refcnt == 0) - hfsc_destroy_class(sch, cl); + return (unsigned long)hfsc_find_class(classid, sch); } static unsigned long @@ -1413,7 +1393,6 @@ hfsc_init_qdisc(struct Qdisc *sch, struct nlattr *opt) goto err_tcf; q->root.cl_common.classid = sch->handle; - q->root.refcnt = 1; q->root.sched = q; q->root.qdisc = qdisc_create_dflt(sch->dev_queue, &pfifo_qdisc_ops, sch->handle); @@ -1661,8 +1640,7 @@ static const struct Qdisc_class_ops hfsc_class_ops = { .graft = hfsc_graft_class, .leaf = hfsc_class_leaf, .qlen_notify = hfsc_qlen_notify, - .get = hfsc_get_class, - .put = hfsc_put_class, + .find = hfsc_search_class, .bind_tcf = hfsc_bind_tcf, .unbind_tcf = hfsc_unbind_tcf, .tcf_block = hfsc_tcf_block, diff --git a/net/sched/sch_htb.c b/net/sched/sch_htb.c index dcf3c85e1f4f..f955b59d3c7c 100644 --- a/net/sched/sch_htb.c +++ b/net/sched/sch_htb.c @@ -107,7 +107,6 @@ struct htb_class { struct tcf_proto __rcu *filter_list; /* class attached filters */ struct tcf_block *block; int filter_cnt; - int refcnt; /* usage count of this class */ int level; /* our level (see above) */ unsigned int children; @@ -193,6 +192,10 @@ static inline struct htb_class *htb_find(u32 handle, struct Qdisc *sch) return container_of(clc, struct htb_class, common); } +static unsigned long htb_search(struct Qdisc *sch, u32 handle) +{ + return (unsigned long)htb_find(handle, sch); +} /** * htb_classify - classify a packet into class * @@ -1189,14 +1192,6 @@ static void htb_qlen_notify(struct Qdisc *sch, unsigned long arg) htb_deactivate(qdisc_priv(sch), cl); } -static unsigned long htb_get(struct Qdisc *sch, u32 classid) -{ - struct htb_class *cl = htb_find(classid, sch); - if (cl) - cl->refcnt++; - return (unsigned long)cl; -} - static inline int htb_parent_last_child(struct htb_class *cl) { if (!cl->parent) @@ -1316,22 +1311,10 @@ static int htb_delete(struct Qdisc *sch, unsigned long arg) if (last_child) htb_parent_to_leaf(q, cl, new_q); - BUG_ON(--cl->refcnt == 0); - /* - * This shouldn't happen: we "hold" one cops->get() when called - * from tc_ctl_tclass; the destroy method is done from cops->put(). - */ - sch_tree_unlock(sch); - return 0; -} -static void htb_put(struct Qdisc *sch, unsigned long arg) -{ - struct htb_class *cl = (struct htb_class *)arg; - - if (--cl->refcnt == 0) - htb_destroy_class(sch, cl); + htb_destroy_class(sch, cl); + return 0; } static int htb_change_class(struct Qdisc *sch, u32 classid, @@ -1422,7 +1405,6 @@ static int htb_change_class(struct Qdisc *sch, u32 classid, } } - cl->refcnt = 1; cl->children = 0; INIT_LIST_HEAD(&cl->un.leaf.drop_list); RB_CLEAR_NODE(&cl->pq_node); @@ -1598,8 +1580,7 @@ static const struct Qdisc_class_ops htb_class_ops = { .graft = htb_graft, .leaf = htb_leaf, .qlen_notify = htb_qlen_notify, - .get = htb_get, - .put = htb_put, + .find = htb_search, .change = htb_change_class, .delete = htb_delete, .walk = htb_walk, diff --git a/net/sched/sch_ingress.c b/net/sched/sch_ingress.c index a15c543c3569..44de4ee51ce9 100644 --- a/net/sched/sch_ingress.c +++ b/net/sched/sch_ingress.c @@ -27,7 +27,7 @@ static struct Qdisc *ingress_leaf(struct Qdisc *sch, unsigned long arg) return NULL; } -static unsigned long ingress_get(struct Qdisc *sch, u32 classid) +static unsigned long ingress_find(struct Qdisc *sch, u32 classid) { return TC_H_MIN(classid) + 1; } @@ -35,10 +35,10 @@ static unsigned long ingress_get(struct Qdisc *sch, u32 classid) static unsigned long ingress_bind_filter(struct Qdisc *sch, unsigned long parent, u32 classid) { - return ingress_get(sch, classid); + return ingress_find(sch, classid); } -static void ingress_put(struct Qdisc *sch, unsigned long cl) +static void ingress_unbind_filter(struct Qdisc *sch, unsigned long cl) { } @@ -94,12 +94,11 @@ nla_put_failure: static const struct Qdisc_class_ops ingress_class_ops = { .leaf = ingress_leaf, - .get = ingress_get, - .put = ingress_put, + .find = ingress_find, .walk = ingress_walk, .tcf_block = ingress_tcf_block, .bind_tcf = ingress_bind_filter, - .unbind_tcf = ingress_put, + .unbind_tcf = ingress_unbind_filter, }; static struct Qdisc_ops ingress_qdisc_ops __read_mostly = { @@ -117,7 +116,7 @@ struct clsact_sched_data { struct tcf_block *egress_block; }; -static unsigned long clsact_get(struct Qdisc *sch, u32 classid) +static unsigned long clsact_find(struct Qdisc *sch, u32 classid) { switch (TC_H_MIN(classid)) { case TC_H_MIN(TC_H_MIN_INGRESS): @@ -131,7 +130,7 @@ static unsigned long clsact_get(struct Qdisc *sch, u32 classid) static unsigned long clsact_bind_filter(struct Qdisc *sch, unsigned long parent, u32 classid) { - return clsact_get(sch, classid); + return clsact_find(sch, classid); } static struct tcf_block *clsact_tcf_block(struct Qdisc *sch, unsigned long cl) @@ -183,12 +182,11 @@ static void clsact_destroy(struct Qdisc *sch) static const struct Qdisc_class_ops clsact_class_ops = { .leaf = ingress_leaf, - .get = clsact_get, - .put = ingress_put, + .find = clsact_find, .walk = ingress_walk, .tcf_block = clsact_tcf_block, .bind_tcf = clsact_bind_filter, - .unbind_tcf = ingress_put, + .unbind_tcf = ingress_unbind_filter, }; static struct Qdisc_ops clsact_qdisc_ops __read_mostly = { diff --git a/net/sched/sch_mq.c b/net/sched/sch_mq.c index cadfdd4f1e52..f3a3e507422b 100644 --- a/net/sched/sch_mq.c +++ b/net/sched/sch_mq.c @@ -165,7 +165,7 @@ static struct Qdisc *mq_leaf(struct Qdisc *sch, unsigned long cl) return dev_queue->qdisc_sleeping; } -static unsigned long mq_get(struct Qdisc *sch, u32 classid) +static unsigned long mq_find(struct Qdisc *sch, u32 classid) { unsigned int ntx = TC_H_MIN(classid); @@ -174,10 +174,6 @@ static unsigned long mq_get(struct Qdisc *sch, u32 classid) return ntx; } -static void mq_put(struct Qdisc *sch, unsigned long cl) -{ -} - static int mq_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm) { @@ -223,8 +219,7 @@ static const struct Qdisc_class_ops mq_class_ops = { .select_queue = mq_select_queue, .graft = mq_graft, .leaf = mq_leaf, - .get = mq_get, - .put = mq_put, + .find = mq_find, .walk = mq_walk, .dump = mq_dump_class, .dump_stats = mq_dump_class_stats, diff --git a/net/sched/sch_mqprio.c b/net/sched/sch_mqprio.c index 2165a05994b7..6bcdfe6e7b63 100644 --- a/net/sched/sch_mqprio.c +++ b/net/sched/sch_mqprio.c @@ -277,7 +277,7 @@ static struct Qdisc *mqprio_leaf(struct Qdisc *sch, unsigned long cl) return dev_queue->qdisc_sleeping; } -static unsigned long mqprio_get(struct Qdisc *sch, u32 classid) +static unsigned long mqprio_find(struct Qdisc *sch, u32 classid) { struct net_device *dev = qdisc_dev(sch); unsigned int ntx = TC_H_MIN(classid); @@ -287,10 +287,6 @@ static unsigned long mqprio_get(struct Qdisc *sch, u32 classid) return ntx; } -static void mqprio_put(struct Qdisc *sch, unsigned long cl) -{ -} - static int mqprio_dump_class(struct Qdisc *sch, unsigned long cl, struct sk_buff *skb, struct tcmsg *tcm) { @@ -403,8 +399,7 @@ static void mqprio_walk(struct Qdisc *sch, struct qdisc_walker *arg) static const struct Qdisc_class_ops mqprio_class_ops = { .graft = mqprio_graft, .leaf = mqprio_leaf, - .get = mqprio_get, - .put = mqprio_put, + .find = mqprio_find, .walk = mqprio_walk, .dump = mqprio_dump_class, .dump_stats = mqprio_dump_class_stats, diff --git a/net/sched/sch_multiq.c b/net/sched/sch_multiq.c index f143b7bbaa0d..a5df979b6248 100644 --- a/net/sched/sch_multiq.c +++ b/net/sched/sch_multiq.c @@ -306,7 +306,7 @@ multiq_leaf(struct Qdisc *sch, unsigned long arg) return q->queues[band]; } -static unsigned long multiq_get(struct Qdisc *sch, u32 classid) +static unsigned long multiq_find(struct Qdisc *sch, u32 classid) { struct multiq_sched_data *q = qdisc_priv(sch); unsigned long band = TC_H_MIN(classid); @@ -319,11 +319,11 @@ static unsigned long multiq_get(struct Qdisc *sch, u32 classid) static unsigned long multiq_bind(struct Qdisc *sch, unsigned long parent, u32 classid) { - return multiq_get(sch, classid); + return multiq_find(sch, classid); } -static void multiq_put(struct Qdisc *q, unsigned long cl) +static void multiq_unbind(struct Qdisc *q, unsigned long cl) { } @@ -385,12 +385,11 @@ static struct tcf_block *multiq_tcf_block(struct Qdisc *sch, unsigned long cl) static const struct Qdisc_class_ops multiq_class_ops = { .graft = multiq_graft, .leaf = multiq_leaf, - .get = multiq_get, - .put = multiq_put, + .find = multiq_find, .walk = multiq_walk, .tcf_block = multiq_tcf_block, .bind_tcf = multiq_bind, - .unbind_tcf = multiq_put, + .unbind_tcf = multiq_unbind, .dump = multiq_dump_class, .dump_stats = multiq_dump_class_stats, }; diff --git a/net/sched/sch_netem.c b/net/sched/sch_netem.c index 1b3dd6190e93..cf5aad0aabfc 100644 --- a/net/sched/sch_netem.c +++ b/net/sched/sch_netem.c @@ -1096,15 +1096,11 @@ static struct Qdisc *netem_leaf(struct Qdisc *sch, unsigned long arg) return q->qdisc; } -static unsigned long netem_get(struct Qdisc *sch, u32 classid) +static unsigned long netem_find(struct Qdisc *sch, u32 classid) { return 1; } -static void netem_put(struct Qdisc *sch, unsigned long arg) -{ -} - static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker) { if (!walker->stop) { @@ -1120,8 +1116,7 @@ static void netem_walk(struct Qdisc *sch, struct qdisc_walker *walker) static const struct Qdisc_class_ops netem_class_ops = { .graft = netem_graft, .leaf = netem_leaf, - .get = netem_get, - .put = netem_put, + .find = netem_find, .walk = netem_walk, .dump = netem_dump_class, }; diff --git a/net/sched/sch_prio.c b/net/sched/sch_prio.c index e3e364cc9a70..f31b28f788c0 100644 --- a/net/sched/sch_prio.c +++ b/net/sched/sch_prio.c @@ -260,7 +260,7 @@ prio_leaf(struct Qdisc *sch, unsigned long arg) return q->queues[band]; } -static unsigned long prio_get(struct Qdisc *sch, u32 classid) +static unsigned long prio_find(struct Qdisc *sch, u32 classid) { struct prio_sched_data *q = qdisc_priv(sch); unsigned long band = TC_H_MIN(classid); @@ -272,11 +272,11 @@ static unsigned long prio_get(struct Qdisc *sch, u32 classid) static unsigned long prio_bind(struct Qdisc *sch, unsigned long parent, u32 classid) { - return prio_get(sch, classid); + return prio_find(sch, classid); } -static void prio_put(struct Qdisc *q, unsigned long cl) +static void prio_unbind(struct Qdisc *q, unsigned long cl) { } @@ -338,12 +338,11 @@ static struct tcf_block *prio_tcf_block(struct Qdisc *sch, unsigned long cl) static const struct Qdisc_class_ops prio_class_ops = { .graft = prio_graft, .leaf = prio_leaf, - .get = prio_get, - .put = prio_put, + .find = prio_find, .walk = prio_walk, .tcf_block = prio_tcf_block, .bind_tcf = prio_bind, - .unbind_tcf = prio_put, + .unbind_tcf = prio_unbind, .dump = prio_dump_class, .dump_stats = prio_dump_class_stats, }; diff --git a/net/sched/sch_qfq.c b/net/sched/sch_qfq.c index 9caa959f91e1..cd661a7f81e6 100644 --- a/net/sched/sch_qfq.c +++ b/net/sched/sch_qfq.c @@ -132,7 +132,6 @@ struct qfq_aggregate; struct qfq_class { struct Qdisc_class_common common; - unsigned int refcnt; unsigned int filter_cnt; struct gnet_stats_basic_packed bstats; @@ -477,7 +476,6 @@ static int qfq_change_class(struct Qdisc *sch, u32 classid, u32 parentid, if (cl == NULL) return -ENOBUFS; - cl->refcnt = 1; cl->common.classid = classid; cl->deficit = lmax; @@ -555,32 +553,15 @@ static int qfq_delete_class(struct Qdisc *sch, unsigned long arg) qfq_purge_queue(cl); qdisc_class_hash_remove(&q->clhash, &cl->common); - BUG_ON(--cl->refcnt == 0); - /* - * This shouldn't happen: we "hold" one cops->get() when called - * from tc_ctl_tclass; the destroy method is done from cops->put(). - */ - sch_tree_unlock(sch); - return 0; -} - -static unsigned long qfq_get_class(struct Qdisc *sch, u32 classid) -{ - struct qfq_class *cl = qfq_find_class(sch, classid); - - if (cl != NULL) - cl->refcnt++; - return (unsigned long)cl; + qfq_destroy_class(sch, cl); + return 0; } -static void qfq_put_class(struct Qdisc *sch, unsigned long arg) +static unsigned long qfq_search_class(struct Qdisc *sch, u32 classid) { - struct qfq_class *cl = (struct qfq_class *)arg; - - if (--cl->refcnt == 0) - qfq_destroy_class(sch, cl); + return (unsigned long)qfq_find_class(sch, classid); } static struct tcf_block *qfq_tcf_block(struct Qdisc *sch, unsigned long cl) @@ -1510,8 +1491,7 @@ static void qfq_destroy_qdisc(struct Qdisc *sch) static const struct Qdisc_class_ops qfq_class_ops = { .change = qfq_change_class, .delete = qfq_delete_class, - .get = qfq_get_class, - .put = qfq_put_class, + .find = qfq_search_class, .tcf_block = qfq_tcf_block, .bind_tcf = qfq_bind_tcf, .unbind_tcf = qfq_unbind_tcf, diff --git a/net/sched/sch_red.c b/net/sched/sch_red.c index 11292adce412..93b9d70a9b28 100644 --- a/net/sched/sch_red.c +++ b/net/sched/sch_red.c @@ -311,15 +311,11 @@ static struct Qdisc *red_leaf(struct Qdisc *sch, unsigned long arg) return q->qdisc; } -static unsigned long red_get(struct Qdisc *sch, u32 classid) +static unsigned long red_find(struct Qdisc *sch, u32 classid) { return 1; } -static void red_put(struct Qdisc *sch, unsigned long arg) -{ -} - static void red_walk(struct Qdisc *sch, struct qdisc_walker *walker) { if (!walker->stop) { @@ -335,8 +331,7 @@ static void red_walk(struct Qdisc *sch, struct qdisc_walker *walker) static const struct Qdisc_class_ops red_class_ops = { .graft = red_graft, .leaf = red_leaf, - .get = red_get, - .put = red_put, + .find = red_find, .walk = red_walk, .dump = red_dump_class, }; diff --git a/net/sched/sch_sfb.c b/net/sched/sch_sfb.c index 11fb6ec878d6..cc39e170b4aa 100644 --- a/net/sched/sch_sfb.c +++ b/net/sched/sch_sfb.c @@ -632,12 +632,12 @@ static struct Qdisc *sfb_leaf(struct Qdisc *sch, unsigned long arg) return q->qdisc; } -static unsigned long sfb_get(struct Qdisc *sch, u32 classid) +static unsigned long sfb_find(struct Qdisc *sch, u32 classid) { return 1; } -static void sfb_put(struct Qdisc *sch, unsigned long arg) +static void sfb_unbind(struct Qdisc *sch, unsigned long arg) { } @@ -683,14 +683,13 @@ static unsigned long sfb_bind(struct Qdisc *sch, unsigned long parent, static const struct Qdisc_class_ops sfb_class_ops = { .graft = sfb_graft, .leaf = sfb_leaf, - .get = sfb_get, - .put = sfb_put, + .find = sfb_find, .change = sfb_change_class, .delete = sfb_delete, .walk = sfb_walk, .tcf_block = sfb_tcf_block, .bind_tcf = sfb_bind, - .unbind_tcf = sfb_put, + .unbind_tcf = sfb_unbind, .dump = sfb_dump_class, }; diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index 82469ef9655e..b46e0121dd1a 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -808,7 +808,7 @@ static struct Qdisc *sfq_leaf(struct Qdisc *sch, unsigned long arg) return NULL; } -static unsigned long sfq_get(struct Qdisc *sch, u32 classid) +static unsigned long sfq_find(struct Qdisc *sch, u32 classid) { return 0; } @@ -821,7 +821,7 @@ static unsigned long sfq_bind(struct Qdisc *sch, unsigned long parent, return 0; } -static void sfq_put(struct Qdisc *q, unsigned long cl) +static void sfq_unbind(struct Qdisc *q, unsigned long cl) { } @@ -885,11 +885,10 @@ static void sfq_walk(struct Qdisc *sch, struct qdisc_walker *arg) static const struct Qdisc_class_ops sfq_class_ops = { .leaf = sfq_leaf, - .get = sfq_get, - .put = sfq_put, + .find = sfq_find, .tcf_block = sfq_tcf_block, .bind_tcf = sfq_bind, - .unbind_tcf = sfq_put, + .unbind_tcf = sfq_unbind, .dump = sfq_dump_class, .dump_stats = sfq_dump_class_stats, .walk = sfq_walk, diff --git a/net/sched/sch_tbf.c b/net/sched/sch_tbf.c index b2e4b6ad241a..d5dba972ab06 100644 --- a/net/sched/sch_tbf.c +++ b/net/sched/sch_tbf.c @@ -510,15 +510,11 @@ static struct Qdisc *tbf_leaf(struct Qdisc *sch, unsigned long arg) return q->qdisc; } -static unsigned long tbf_get(struct Qdisc *sch, u32 classid) +static unsigned long tbf_find(struct Qdisc *sch, u32 classid) { return 1; } -static void tbf_put(struct Qdisc *sch, unsigned long arg) -{ -} - static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker) { if (!walker->stop) { @@ -534,8 +530,7 @@ static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker) static const struct Qdisc_class_ops tbf_class_ops = { .graft = tbf_graft, .leaf = tbf_leaf, - .get = tbf_get, - .put = tbf_put, + .find = tbf_find, .walk = tbf_walk, .dump = tbf_dump_class, }; -- cgit v1.2.3 From 3cd904ecbb5d0bcf36dfca7e726bdfd6d3644334 Mon Sep 17 00:00:00 2001 From: WANG Cong Date: Thu, 24 Aug 2017 16:51:30 -0700 Subject: net_sched: kill u32_node pointer in Qdisc It is ugly to hide a u32-filter-specific pointer inside Qdisc, this breaks the TC layers: 1. Qdisc is a generic representation, should not have any specific data of any type 2. Qdisc layer is above filter layer, should only save filters in the list of struct tcf_proto. This pointer is used as the head of the chain of u32 hash tables, that is struct tc_u_hnode, because u32 filter is very special, it allows to create multiple hash tables within one qdisc and across multiple u32 filters. Instead of using this ugly pointer, we can just save it in a global hash table key'ed by (dev ifindex, qdisc handle), therefore we can still treat it as a per qdisc basis data structure conceptually. Of course, because of network namespaces, this key is not unique at all, but it is fine as we already have a pointer to Qdisc in struct tc_u_common, we can just compare the pointers when collision. And this only affects slow paths, has no impact to fast path, thanks to the pointer ->tp_c. Cc: Jamal Hadi Salim Cc: Jiri Pirko Signed-off-by: Cong Wang Acked-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/sch_generic.h | 1 - net/sched/cls_u32.c | 57 +++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 53 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index d817585aa5df..c30b634c5f82 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -75,7 +75,6 @@ struct Qdisc { struct hlist_node hash; u32 handle; u32 parent; - void *u32_node; struct netdev_queue *dev_queue; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index af22742d2847..99ea4c74dd5b 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -40,6 +40,8 @@ #include #include #include +#include +#include #include #include #include @@ -92,6 +94,7 @@ struct tc_u_common { struct Qdisc *q; int refcnt; u32 hgenerator; + struct hlist_node hnode; struct rcu_head rcu; }; @@ -323,12 +326,40 @@ static u32 gen_new_htid(struct tc_u_common *tp_c) return i > 0 ? (tp_c->hgenerator|0x800)<<20 : 0; } +static struct hlist_head *tc_u_common_hash; + +#define U32_HASH_SHIFT 10 +#define U32_HASH_SIZE (1 << U32_HASH_SHIFT) + +static unsigned int tc_u_hash(const struct tcf_proto *tp) +{ + struct net_device *dev = tp->q->dev_queue->dev; + u32 qhandle = tp->q->handle; + int ifindex = dev->ifindex; + + return hash_64((u64)ifindex << 32 | qhandle, U32_HASH_SHIFT); +} + +static struct tc_u_common *tc_u_common_find(const struct tcf_proto *tp) +{ + struct tc_u_common *tc; + unsigned int h; + + h = tc_u_hash(tp); + hlist_for_each_entry(tc, &tc_u_common_hash[h], hnode) { + if (tc->q == tp->q) + return tc; + } + return NULL; +} + static int u32_init(struct tcf_proto *tp) { struct tc_u_hnode *root_ht; struct tc_u_common *tp_c; + unsigned int h; - tp_c = tp->q->u32_node; + tp_c = tc_u_common_find(tp); root_ht = kzalloc(sizeof(*root_ht), GFP_KERNEL); if (root_ht == NULL) @@ -345,7 +376,10 @@ static int u32_init(struct tcf_proto *tp) return -ENOBUFS; } tp_c->q = tp->q; - tp->q->u32_node = tp_c; + INIT_HLIST_NODE(&tp_c->hnode); + + h = tc_u_hash(tp); + hlist_add_head(&tp_c->hnode, &tc_u_common_hash[h]); } tp_c->refcnt++; @@ -585,7 +619,7 @@ static void u32_destroy(struct tcf_proto *tp) if (--tp_c->refcnt == 0) { struct tc_u_hnode *ht; - tp->q->u32_node = NULL; + hlist_del(&tp_c->hnode); for (ht = rtnl_dereference(tp_c->hlist); ht; @@ -1213,6 +1247,8 @@ static struct tcf_proto_ops cls_u32_ops __read_mostly = { static int __init init_u32(void) { + int i, ret; + pr_info("u32 classifier\n"); #ifdef CONFIG_CLS_U32_PERF pr_info(" Performance counters on\n"); @@ -1223,12 +1259,25 @@ static int __init init_u32(void) #ifdef CONFIG_NET_CLS_ACT pr_info(" Actions configured\n"); #endif - return register_tcf_proto_ops(&cls_u32_ops); + tc_u_common_hash = kvmalloc_array(U32_HASH_SIZE, + sizeof(struct hlist_head), + GFP_KERNEL); + if (!tc_u_common_hash) + return -ENOMEM; + + for (i = 0; i < U32_HASH_SIZE; i++) + INIT_HLIST_HEAD(&tc_u_common_hash[i]); + + ret = register_tcf_proto_ops(&cls_u32_ops); + if (ret) + kvfree(tc_u_common_hash); + return ret; } static void __exit exit_u32(void) { unregister_tcf_proto_ops(&cls_u32_ops); + kvfree(tc_u_common_hash); } module_init(init_u32) -- cgit v1.2.3 From bd9dfc54e39266ff67521c09d37e838077385b21 Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 25 Aug 2017 06:27:05 -0700 Subject: tcp: fix hang in tcp_sendpage_locked() syszkaller got a hang in tcp stack, related to a bug in tcp_sendpage_locked() root@syzkaller:~# cat /proc/3059/stack [] __lock_sock+0x1dc/0x2f0 [] lock_sock_nested+0xf3/0x110 [] tcp_sendmsg+0x21/0x50 [] inet_sendmsg+0x11f/0x5e0 [] sock_sendmsg+0xca/0x110 [] kernel_sendmsg+0x47/0x60 [] sock_no_sendpage+0x1cc/0x280 [] tcp_sendpage_locked+0x10b/0x160 [] tcp_sendpage+0x43/0x60 [] inet_sendpage+0x1aa/0x660 [] kernel_sendpage+0x8d/0xe0 [] sock_sendpage+0x8c/0xc0 [] pipe_to_sendpage+0x290/0x3b0 [] __splice_from_pipe+0x343/0x750 [] splice_from_pipe+0x1e9/0x330 [] generic_splice_sendpage+0x40/0x50 [] SyS_splice+0x7b7/0x1610 [] entry_SYSCALL_64_fastpath+0x1f/0xbe Fixes: 306b13eb3cf9 ("proto_ops: Add locked held versions of sendmsg and sendpage") Signed-off-by: Eric Dumazet Reported-by: Dmitry Vyukov Cc: Tom Herbert Signed-off-by: David S. Miller --- net/ipv4/tcp.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 0cce4472b4a1..566083ee2654 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1052,8 +1052,7 @@ int tcp_sendpage_locked(struct sock *sk, struct page *page, int offset, { if (!(sk->sk_route_caps & NETIF_F_SG) || !sk_check_csum_caps(sk)) - return sock_no_sendpage(sk->sk_socket, page, offset, size, - flags); + return sock_no_sendpage_locked(sk, page, offset, size, flags); tcp_rate_check_app_limited(sk); /* is sending application-limited? */ -- cgit v1.2.3 From 5fd02ebe6537a7567f060ed36fb0dda64c5b63d8 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 21 Aug 2017 16:19:26 +0200 Subject: netfilter: fix a few (harmless) sparse warnings net/netfilter/nft_payload.c:187:18: warning: incorrect type in return expression (expected bool got restricted __sum16 [usertype] check) net/netfilter/nft_exthdr.c:222:14: warning: cast to restricted __be32 net/netfilter/nft_rt.c:49:23: warning: incorrect type in assignment (different base types expected unsigned int got restricted __be32) net/netfilter/nft_rt.c:70:25: warning: symbol 'nft_rt_policy' was not declared. Should it be static? Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_exthdr.c | 2 +- net/netfilter/nft_payload.c | 2 +- net/netfilter/nft_rt.c | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/netfilter/nft_exthdr.c b/net/netfilter/nft_exthdr.c index f5a0bf5e3bdd..a0a93d987a3b 100644 --- a/net/netfilter/nft_exthdr.c +++ b/net/netfilter/nft_exthdr.c @@ -388,7 +388,7 @@ nft_exthdr_select_ops(const struct nft_ctx *ctx, if (tb[NFTA_EXTHDR_SREG] && tb[NFTA_EXTHDR_DREG]) return ERR_PTR(-EOPNOTSUPP); - op = ntohl(nla_get_u32(tb[NFTA_EXTHDR_OP])); + op = ntohl(nla_get_be32(tb[NFTA_EXTHDR_OP])); switch (op) { case NFT_EXTHDR_OP_TCPOPT: if (tb[NFTA_EXTHDR_SREG]) diff --git a/net/netfilter/nft_payload.c b/net/netfilter/nft_payload.c index 7d699bbd45b0..e110b0ebbf58 100644 --- a/net/netfilter/nft_payload.c +++ b/net/netfilter/nft_payload.c @@ -184,7 +184,7 @@ static bool nft_payload_udp_checksum(struct sk_buff *skb, unsigned int thoff) if (!uh) return false; - return uh->check; + return (__force bool)uh->check; } static int nft_payload_l4csum_offset(const struct nft_pktinfo *pkt, diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index e142e65d3176..61fd3acaa3c9 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -82,8 +82,8 @@ static void nft_rt_get_eval(const struct nft_expr *expr, if (nft_pf(pkt) != NFPROTO_IPV4) goto err; - *dest = rt_nexthop((const struct rtable *)dst, - ip_hdr(skb)->daddr); + *dest = (__force u32)rt_nexthop((const struct rtable *)dst, + ip_hdr(skb)->daddr); break; case NFT_RT_NEXTHOP6: if (nft_pf(pkt) != NFPROTO_IPV6) @@ -106,7 +106,7 @@ err: regs->verdict.code = NFT_BREAK; } -const struct nla_policy nft_rt_policy[NFTA_RT_MAX + 1] = { +static const struct nla_policy nft_rt_policy[NFTA_RT_MAX + 1] = { [NFTA_RT_DREG] = { .type = NLA_U32 }, [NFTA_RT_KEY] = { .type = NLA_U32 }, }; -- cgit v1.2.3 From 960632ece6949be1ab6f7a911faa4fa6e8305f4a Mon Sep 17 00:00:00 2001 From: Aaron Conole Date: Thu, 24 Aug 2017 00:08:32 +0200 Subject: netfilter: convert hook list to an array This converts the storage and layout of netfilter hook entries from a linked list to an array. After this commit, hook entries will be stored adjacent in memory. The next pointer is no longer required. The ops pointers are stored at the end of the array as they are only used in the register/unregister path and in the legacy br_netfilter code. nf_unregister_net_hooks() is slower than needed as it just calls nf_unregister_net_hook in a loop (i.e. at least n synchronize_net() calls), this will be addressed in followup patch. Test setup: - ixgbe 10gbit - netperf UDP_STREAM, 64 byte packets - 5 hooks: (raw + mangle prerouting, mangle+filter input, inet filter): empty mangle and raw prerouting, mangle and filter input hooks: 353.9 this patch: 364.2 Signed-off-by: Aaron Conole Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- include/linux/netdevice.h | 2 +- include/linux/netfilter.h | 45 +++--- include/linux/netfilter_ingress.h | 4 +- include/net/netfilter/nf_queue.h | 2 +- include/net/netns/netfilter.h | 2 +- net/bridge/br_netfilter_hooks.c | 19 ++- net/netfilter/core.c | 297 ++++++++++++++++++++++++++++---------- net/netfilter/nf_internals.h | 3 +- net/netfilter/nf_queue.c | 67 +++++---- 9 files changed, 307 insertions(+), 134 deletions(-) (limited to 'net') diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 614642eb7eb7..ca0a30127300 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -1811,7 +1811,7 @@ struct net_device { #endif struct netdev_queue __rcu *ingress_queue; #ifdef CONFIG_NETFILTER_INGRESS - struct nf_hook_entry __rcu *nf_hooks_ingress; + struct nf_hook_entries __rcu *nf_hooks_ingress; #endif unsigned char broadcast[MAX_ADDR_LEN]; diff --git a/include/linux/netfilter.h b/include/linux/netfilter.h index 22f081065d49..f84bca1703cd 100644 --- a/include/linux/netfilter.h +++ b/include/linux/netfilter.h @@ -72,25 +72,32 @@ struct nf_hook_ops { }; struct nf_hook_entry { - struct nf_hook_entry __rcu *next; nf_hookfn *hook; void *priv; - const struct nf_hook_ops *orig_ops; }; -static inline void -nf_hook_entry_init(struct nf_hook_entry *entry, const struct nf_hook_ops *ops) -{ - entry->next = NULL; - entry->hook = ops->hook; - entry->priv = ops->priv; - entry->orig_ops = ops; -} +struct nf_hook_entries { + u16 num_hook_entries; + /* padding */ + struct nf_hook_entry hooks[]; + + /* trailer: pointers to original orig_ops of each hook. + * + * This is not part of struct nf_hook_entry since its only + * needed in slow path (hook register/unregister). + * + * const struct nf_hook_ops *orig_ops[] + */ +}; -static inline int -nf_hook_entry_priority(const struct nf_hook_entry *entry) +static inline struct nf_hook_ops **nf_hook_entries_get_hook_ops(const struct nf_hook_entries *e) { - return entry->orig_ops->priority; + unsigned int n = e->num_hook_entries; + const void *hook_end; + + hook_end = &e->hooks[n]; /* this is *past* ->hooks[]! */ + + return (struct nf_hook_ops **)hook_end; } static inline int @@ -100,12 +107,6 @@ nf_hook_entry_hookfn(const struct nf_hook_entry *entry, struct sk_buff *skb, return entry->hook(entry->priv, skb, state); } -static inline const struct nf_hook_ops * -nf_hook_entry_ops(const struct nf_hook_entry *entry) -{ - return entry->orig_ops; -} - static inline void nf_hook_state_init(struct nf_hook_state *p, unsigned int hook, u_int8_t pf, @@ -168,7 +169,7 @@ extern struct static_key nf_hooks_needed[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; #endif int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, - struct nf_hook_entry *entry); + const struct nf_hook_entries *e, unsigned int i); /** * nf_hook - call a netfilter hook @@ -182,7 +183,7 @@ static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net, struct net_device *indev, struct net_device *outdev, int (*okfn)(struct net *, struct sock *, struct sk_buff *)) { - struct nf_hook_entry *hook_head; + struct nf_hook_entries *hook_head; int ret = 1; #ifdef HAVE_JUMP_LABEL @@ -200,7 +201,7 @@ static inline int nf_hook(u_int8_t pf, unsigned int hook, struct net *net, nf_hook_state_init(&state, hook, pf, indev, outdev, sk, net, okfn); - ret = nf_hook_slow(skb, &state, hook_head); + ret = nf_hook_slow(skb, &state, hook_head, 0); } rcu_read_unlock(); diff --git a/include/linux/netfilter_ingress.h b/include/linux/netfilter_ingress.h index 59476061de86..8d5dae1e2ff8 100644 --- a/include/linux/netfilter_ingress.h +++ b/include/linux/netfilter_ingress.h @@ -17,7 +17,7 @@ static inline bool nf_hook_ingress_active(const struct sk_buff *skb) /* caller must hold rcu_read_lock */ static inline int nf_hook_ingress(struct sk_buff *skb) { - struct nf_hook_entry *e = rcu_dereference(skb->dev->nf_hooks_ingress); + struct nf_hook_entries *e = rcu_dereference(skb->dev->nf_hooks_ingress); struct nf_hook_state state; int ret; @@ -30,7 +30,7 @@ static inline int nf_hook_ingress(struct sk_buff *skb) nf_hook_state_init(&state, NF_NETDEV_INGRESS, NFPROTO_NETDEV, skb->dev, NULL, NULL, dev_net(skb->dev), NULL); - ret = nf_hook_slow(skb, &state, e); + ret = nf_hook_slow(skb, &state, e, 0); if (ret == 0) return -1; diff --git a/include/net/netfilter/nf_queue.h b/include/net/netfilter/nf_queue.h index 4454719ff849..39468720fc19 100644 --- a/include/net/netfilter/nf_queue.h +++ b/include/net/netfilter/nf_queue.h @@ -10,9 +10,9 @@ struct nf_queue_entry { struct list_head list; struct sk_buff *skb; unsigned int id; + unsigned int hook_index; /* index in hook_entries->hook[] */ struct nf_hook_state state; - struct nf_hook_entry *hook; u16 size; /* sizeof(entry) + saved route keys */ /* extra space to store route keys */ diff --git a/include/net/netns/netfilter.h b/include/net/netns/netfilter.h index cea396b53a60..72d66c8763d0 100644 --- a/include/net/netns/netfilter.h +++ b/include/net/netns/netfilter.h @@ -16,7 +16,7 @@ struct netns_nf { #ifdef CONFIG_SYSCTL struct ctl_table_header *nf_log_dir_header; #endif - struct nf_hook_entry __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; + struct nf_hook_entries __rcu *hooks[NFPROTO_NUMPROTO][NF_MAX_HOOKS]; #if IS_ENABLED(CONFIG_NF_DEFRAG_IPV4) bool defrag_ipv4; #endif diff --git a/net/bridge/br_netfilter_hooks.c b/net/bridge/br_netfilter_hooks.c index 626f4b2cef16..c2eea1b8737a 100644 --- a/net/bridge/br_netfilter_hooks.c +++ b/net/bridge/br_netfilter_hooks.c @@ -985,22 +985,25 @@ int br_nf_hook_thresh(unsigned int hook, struct net *net, int (*okfn)(struct net *, struct sock *, struct sk_buff *)) { - struct nf_hook_entry *elem; + const struct nf_hook_entries *e; struct nf_hook_state state; + struct nf_hook_ops **ops; + unsigned int i; int ret; - for (elem = rcu_dereference(net->nf.hooks[NFPROTO_BRIDGE][hook]); - elem && nf_hook_entry_priority(elem) <= NF_BR_PRI_BRNF; - elem = rcu_dereference(elem->next)) - ; - - if (!elem) + e = rcu_dereference(net->nf.hooks[NFPROTO_BRIDGE][hook]); + if (!e) return okfn(net, sk, skb); + ops = nf_hook_entries_get_hook_ops(e); + for (i = 0; i < e->num_hook_entries && + ops[i]->priority <= NF_BR_PRI_BRNF; i++) + ; + nf_hook_state_init(&state, hook, NFPROTO_BRIDGE, indev, outdev, sk, net, okfn); - ret = nf_hook_slow(skb, &state, elem); + ret = nf_hook_slow(skb, &state, e, i); if (ret == 1) ret = okfn(net, sk, skb); diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 974cf2a3795a..1a9e23c9ab98 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -21,7 +21,7 @@ #include #include #include -#include +#include #include #include #include @@ -62,10 +62,160 @@ EXPORT_SYMBOL(nf_hooks_needed); #endif static DEFINE_MUTEX(nf_hook_mutex); + +/* max hooks per family/hooknum */ +#define MAX_HOOK_COUNT 1024 + #define nf_entry_dereference(e) \ rcu_dereference_protected(e, lockdep_is_held(&nf_hook_mutex)) -static struct nf_hook_entry __rcu **nf_hook_entry_head(struct net *net, const struct nf_hook_ops *reg) +static struct nf_hook_entries *allocate_hook_entries_size(u16 num) +{ + struct nf_hook_entries *e; + size_t alloc = sizeof(*e) + + sizeof(struct nf_hook_entry) * num + + sizeof(struct nf_hook_ops *) * num; + + if (num == 0) + return NULL; + + e = kvzalloc(alloc, GFP_KERNEL); + if (e) + e->num_hook_entries = num; + return e; +} + +static unsigned int accept_all(void *priv, + struct sk_buff *skb, + const struct nf_hook_state *state) +{ + return NF_ACCEPT; /* ACCEPT makes nf_hook_slow call next hook */ +} + +static const struct nf_hook_ops dummy_ops = { + .hook = accept_all, + .priority = INT_MIN, +}; + +static struct nf_hook_entries * +nf_hook_entries_grow(const struct nf_hook_entries *old, + const struct nf_hook_ops *reg) +{ + unsigned int i, alloc_entries, nhooks, old_entries; + struct nf_hook_ops **orig_ops = NULL; + struct nf_hook_ops **new_ops; + struct nf_hook_entries *new; + bool inserted = false; + + alloc_entries = 1; + old_entries = old ? old->num_hook_entries : 0; + + if (old) { + orig_ops = nf_hook_entries_get_hook_ops(old); + + for (i = 0; i < old_entries; i++) { + if (orig_ops[i] != &dummy_ops) + alloc_entries++; + } + } + + if (alloc_entries > MAX_HOOK_COUNT) + return ERR_PTR(-E2BIG); + + new = allocate_hook_entries_size(alloc_entries); + if (!new) + return ERR_PTR(-ENOMEM); + + new_ops = nf_hook_entries_get_hook_ops(new); + + i = 0; + nhooks = 0; + while (i < old_entries) { + if (orig_ops[i] == &dummy_ops) { + ++i; + continue; + } + if (inserted || reg->priority > orig_ops[i]->priority) { + new_ops[nhooks] = (void *)orig_ops[i]; + new->hooks[nhooks] = old->hooks[i]; + i++; + } else { + new_ops[nhooks] = (void *)reg; + new->hooks[nhooks].hook = reg->hook; + new->hooks[nhooks].priv = reg->priv; + inserted = true; + } + nhooks++; + } + + if (!inserted) { + new_ops[nhooks] = (void *)reg; + new->hooks[nhooks].hook = reg->hook; + new->hooks[nhooks].priv = reg->priv; + } + + return new; +} + +/* + * __nf_hook_entries_try_shrink - try to shrink hook array + * + * @pp -- location of hook blob + * + * Hook unregistration must always succeed, so to-be-removed hooks + * are replaced by a dummy one that will just move to next hook. + * + * This counts the current dummy hooks, attempts to allocate new blob, + * copies the live hooks, then replaces and discards old one. + * + * return values: + * + * Returns address to free, or NULL. + */ +static void *__nf_hook_entries_try_shrink(struct nf_hook_entries __rcu **pp) +{ + struct nf_hook_entries *old, *new = NULL; + unsigned int i, j, skip = 0, hook_entries; + struct nf_hook_ops **orig_ops; + struct nf_hook_ops **new_ops; + + old = nf_entry_dereference(*pp); + if (WARN_ON_ONCE(!old)) + return NULL; + + orig_ops = nf_hook_entries_get_hook_ops(old); + for (i = 0; i < old->num_hook_entries; i++) { + if (orig_ops[i] == &dummy_ops) + skip++; + } + + /* if skip == hook_entries all hooks have been removed */ + hook_entries = old->num_hook_entries; + if (skip == hook_entries) + goto out_assign; + + if (WARN_ON(skip == 0)) + return NULL; + + hook_entries -= skip; + new = allocate_hook_entries_size(hook_entries); + if (!new) + return NULL; + + new_ops = nf_hook_entries_get_hook_ops(new); + for (i = 0, j = 0; i < old->num_hook_entries; i++) { + if (orig_ops[i] == &dummy_ops) + continue; + new->hooks[j] = old->hooks[i]; + new_ops[j] = (void *)orig_ops[i]; + j++; + } +out_assign: + rcu_assign_pointer(*pp, new); + return old; +} + +static struct nf_hook_entries __rcu **nf_hook_entry_head(struct net *net, const struct nf_hook_ops *reg) { if (reg->pf != NFPROTO_NETDEV) return net->nf.hooks[reg->pf]+reg->hooknum; @@ -76,13 +226,14 @@ static struct nf_hook_entry __rcu **nf_hook_entry_head(struct net *net, const st return ®->dev->nf_hooks_ingress; } #endif + WARN_ON_ONCE(1); return NULL; } int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) { - struct nf_hook_entry __rcu **pp; - struct nf_hook_entry *entry, *p; + struct nf_hook_entries *p, *new_hooks; + struct nf_hook_entries __rcu **pp; if (reg->pf == NFPROTO_NETDEV) { #ifndef CONFIG_NETFILTER_INGRESS @@ -98,23 +249,18 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) if (!pp) return -EINVAL; - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) - return -ENOMEM; - - nf_hook_entry_init(entry, reg); - mutex_lock(&nf_hook_mutex); - /* Find the spot in the list */ - for (; (p = nf_entry_dereference(*pp)) != NULL; pp = &p->next) { - if (reg->priority < nf_hook_entry_priority(p)) - break; - } - rcu_assign_pointer(entry->next, p); - rcu_assign_pointer(*pp, entry); + p = nf_entry_dereference(*pp); + new_hooks = nf_hook_entries_grow(p, reg); + + if (!IS_ERR(new_hooks)) + rcu_assign_pointer(*pp, new_hooks); mutex_unlock(&nf_hook_mutex); + if (IS_ERR(new_hooks)) + return PTR_ERR(new_hooks); + #ifdef CONFIG_NETFILTER_INGRESS if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) net_inc_ingress_queue(); @@ -122,48 +268,74 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) #ifdef HAVE_JUMP_LABEL static_key_slow_inc(&nf_hooks_needed[reg->pf][reg->hooknum]); #endif + synchronize_net(); + BUG_ON(p == new_hooks); + kvfree(p); return 0; } EXPORT_SYMBOL(nf_register_net_hook); -static struct nf_hook_entry * -__nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) +/* + * __nf_unregister_net_hook - remove a hook from blob + * + * @oldp: current address of hook blob + * @unreg: hook to unregister + * + * This cannot fail, hook unregistration must always succeed. + * Therefore replace the to-be-removed hook with a dummy hook. + */ +static void __nf_unregister_net_hook(struct nf_hook_entries *old, + const struct nf_hook_ops *unreg) { - struct nf_hook_entry __rcu **pp; - struct nf_hook_entry *p; - - pp = nf_hook_entry_head(net, reg); - if (WARN_ON_ONCE(!pp)) - return NULL; + struct nf_hook_ops **orig_ops; + bool found = false; + unsigned int i; - mutex_lock(&nf_hook_mutex); - for (; (p = nf_entry_dereference(*pp)) != NULL; pp = &p->next) { - if (nf_hook_entry_ops(p) == reg) { - rcu_assign_pointer(*pp, p->next); - break; - } - } - mutex_unlock(&nf_hook_mutex); - if (!p) { - WARN(1, "nf_unregister_net_hook: hook not found!\n"); - return NULL; + orig_ops = nf_hook_entries_get_hook_ops(old); + for (i = 0; i < old->num_hook_entries; i++) { + if (orig_ops[i] != unreg) + continue; + WRITE_ONCE(old->hooks[i].hook, accept_all); + WRITE_ONCE(orig_ops[i], &dummy_ops); + found = true; + break; } + + if (found) { #ifdef CONFIG_NETFILTER_INGRESS - if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) - net_dec_ingress_queue(); + if (unreg->pf == NFPROTO_NETDEV && unreg->hooknum == NF_NETDEV_INGRESS) + net_dec_ingress_queue(); #endif #ifdef HAVE_JUMP_LABEL - static_key_slow_dec(&nf_hooks_needed[reg->pf][reg->hooknum]); + static_key_slow_dec(&nf_hooks_needed[unreg->pf][unreg->hooknum]); #endif - - return p; + } else { + WARN_ONCE(1, "hook not found, pf %d num %d", unreg->pf, unreg->hooknum); + } } void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) { - struct nf_hook_entry *p = __nf_unregister_net_hook(net, reg); + struct nf_hook_entries __rcu **pp; + struct nf_hook_entries *p; unsigned int nfq; + pp = nf_hook_entry_head(net, reg); + if (!pp) + return; + + mutex_lock(&nf_hook_mutex); + + p = nf_entry_dereference(*pp); + if (WARN_ON_ONCE(!p)) { + mutex_unlock(&nf_hook_mutex); + return; + } + + __nf_unregister_net_hook(p, reg); + + p = __nf_hook_entries_try_shrink(pp); + mutex_unlock(&nf_hook_mutex); if (!p) return; @@ -173,7 +345,7 @@ void nf_unregister_net_hook(struct net *net, const struct nf_hook_ops *reg) nfq = nf_queue_nf_hook_drop(net); if (nfq) synchronize_net(); - kfree(p); + kvfree(p); } EXPORT_SYMBOL(nf_unregister_net_hook); @@ -200,46 +372,25 @@ EXPORT_SYMBOL(nf_register_net_hooks); void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, unsigned int hookcount) { - struct nf_hook_entry *to_free[16]; - unsigned int i, n, nfq; - - do { - n = min_t(unsigned int, hookcount, ARRAY_SIZE(to_free)); - - for (i = 0; i < n; i++) - to_free[i] = __nf_unregister_net_hook(net, ®[i]); - - synchronize_net(); - - /* need 2nd synchronize_net() if nfqueue is used, skb - * can get reinjected right before nf_queue_hook_drop() - */ - nfq = nf_queue_nf_hook_drop(net); - if (nfq) - synchronize_net(); - - for (i = 0; i < n; i++) - kfree(to_free[i]); + unsigned int i; - reg += n; - hookcount -= n; - } while (hookcount > 0); + for (i = 0; i < hookcount; i++) + nf_unregister_net_hook(net, ®[i]); } EXPORT_SYMBOL(nf_unregister_net_hooks); /* Returns 1 if okfn() needs to be executed by the caller, * -EPERM for NF_DROP, 0 otherwise. Caller must hold rcu_read_lock. */ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, - struct nf_hook_entry *entry) + const struct nf_hook_entries *e, unsigned int s) { unsigned int verdict; int ret; - do { - verdict = nf_hook_entry_hookfn(entry, skb, state); + for (; s < e->num_hook_entries; s++) { + verdict = nf_hook_entry_hookfn(&e->hooks[s], skb, state); switch (verdict & NF_VERDICT_MASK) { case NF_ACCEPT: - entry = rcu_dereference(entry->next); break; case NF_DROP: kfree_skb(skb); @@ -248,8 +399,8 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, ret = -EPERM; return ret; case NF_QUEUE: - ret = nf_queue(skb, state, &entry, verdict); - if (ret == 1 && entry) + ret = nf_queue(skb, state, e, s, verdict); + if (ret == 1) continue; return ret; default: @@ -258,7 +409,7 @@ int nf_hook_slow(struct sk_buff *skb, struct nf_hook_state *state, */ return 0; } - } while (entry); + } return 1; } diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index 19f00a47a710..bacd6363946e 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -13,7 +13,8 @@ /* nf_queue.c */ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, - struct nf_hook_entry **entryp, unsigned int verdict); + const struct nf_hook_entries *entries, unsigned int index, + unsigned int verdict); unsigned int nf_queue_nf_hook_drop(struct net *net); /* nf_log.c */ diff --git a/net/netfilter/nf_queue.c b/net/netfilter/nf_queue.c index 4f4d80a58fb5..f7e21953b1de 100644 --- a/net/netfilter/nf_queue.c +++ b/net/netfilter/nf_queue.c @@ -112,7 +112,8 @@ unsigned int nf_queue_nf_hook_drop(struct net *net) EXPORT_SYMBOL_GPL(nf_queue_nf_hook_drop); static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, - struct nf_hook_entry *hook_entry, unsigned int queuenum) + const struct nf_hook_entries *entries, + unsigned int index, unsigned int queuenum) { int status = -ENOENT; struct nf_queue_entry *entry = NULL; @@ -140,7 +141,7 @@ static int __nf_queue(struct sk_buff *skb, const struct nf_hook_state *state, *entry = (struct nf_queue_entry) { .skb = skb, .state = *state, - .hook = hook_entry, + .hook_index = index, .size = sizeof(*entry) + afinfo->route_key_size, }; @@ -163,18 +164,16 @@ err: /* Packets leaving via this function must come back through nf_reinject(). */ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, - struct nf_hook_entry **entryp, unsigned int verdict) + const struct nf_hook_entries *entries, unsigned int index, + unsigned int verdict) { - struct nf_hook_entry *entry = *entryp; int ret; - ret = __nf_queue(skb, state, entry, verdict >> NF_VERDICT_QBITS); + ret = __nf_queue(skb, state, entries, index, verdict >> NF_VERDICT_QBITS); if (ret < 0) { if (ret == -ESRCH && - (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) { - *entryp = rcu_dereference(entry->next); + (verdict & NF_VERDICT_FLAG_QUEUE_BYPASS)) return 1; - } kfree_skb(skb); } @@ -183,33 +182,56 @@ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, static unsigned int nf_iterate(struct sk_buff *skb, struct nf_hook_state *state, - struct nf_hook_entry **entryp) + const struct nf_hook_entries *hooks, + unsigned int *index) { - unsigned int verdict; + const struct nf_hook_entry *hook; + unsigned int verdict, i = *index; - do { + while (i < hooks->num_hook_entries) { + hook = &hooks->hooks[i]; repeat: - verdict = nf_hook_entry_hookfn((*entryp), skb, state); + verdict = nf_hook_entry_hookfn(hook, skb, state); if (verdict != NF_ACCEPT) { if (verdict != NF_REPEAT) return verdict; goto repeat; } - *entryp = rcu_dereference((*entryp)->next); - } while (*entryp); + i++; + } + *index = i; return NF_ACCEPT; } +/* Caller must hold rcu read-side lock */ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) { - struct nf_hook_entry *hook_entry = entry->hook; + const struct nf_hook_entry *hook_entry; + const struct nf_hook_entries *hooks; struct sk_buff *skb = entry->skb; const struct nf_afinfo *afinfo; + const struct net *net; + unsigned int i; int err; + u8 pf; + + net = entry->state.net; + pf = entry->state.pf; + + hooks = rcu_dereference(net->nf.hooks[pf][entry->state.hook]); nf_queue_entry_release_refs(entry); + i = entry->hook_index; + if (WARN_ON_ONCE(i >= hooks->num_hook_entries)) { + kfree_skb(skb); + kfree(entry); + return; + } + + hook_entry = &hooks->hooks[i]; + /* Continue traversal iff userspace said ok... */ if (verdict == NF_REPEAT) verdict = nf_hook_entry_hookfn(hook_entry, skb, &entry->state); @@ -221,27 +243,22 @@ void nf_reinject(struct nf_queue_entry *entry, unsigned int verdict) } if (verdict == NF_ACCEPT) { - hook_entry = rcu_dereference(hook_entry->next); - if (hook_entry) next_hook: - verdict = nf_iterate(skb, &entry->state, &hook_entry); + ++i; + verdict = nf_iterate(skb, &entry->state, hooks, &i); } switch (verdict & NF_VERDICT_MASK) { case NF_ACCEPT: case NF_STOP: -okfn: local_bh_disable(); entry->state.okfn(entry->state.net, entry->state.sk, skb); local_bh_enable(); break; case NF_QUEUE: - err = nf_queue(skb, &entry->state, &hook_entry, verdict); - if (err == 1) { - if (hook_entry) - goto next_hook; - goto okfn; - } + err = nf_queue(skb, &entry->state, hooks, i, verdict); + if (err == 1) + goto next_hook; break; case NF_STOLEN: break; -- cgit v1.2.3 From 2420b79f8c18a75ee2417cace381f4604b9b4365 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 23 Aug 2017 17:26:26 +0200 Subject: netfilter: debug: check for sorted array Make sure our grow/shrink routine places them in the correct order. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/core.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) (limited to 'net') diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 1a9e23c9ab98..164ad20d0bd2 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -157,6 +157,27 @@ nf_hook_entries_grow(const struct nf_hook_entries *old, return new; } +static void hooks_validate(const struct nf_hook_entries *hooks) +{ +#ifdef CONFIG_DEBUG_KERNEL + struct nf_hook_ops **orig_ops; + int prio = INT_MIN; + size_t i = 0; + + orig_ops = nf_hook_entries_get_hook_ops(hooks); + + for (i = 0; i < hooks->num_hook_entries; i++) { + if (orig_ops[i] == &dummy_ops) + continue; + + WARN_ON(orig_ops[i]->priority < prio); + + if (orig_ops[i]->priority > prio) + prio = orig_ops[i]->priority; + } +#endif +} + /* * __nf_hook_entries_try_shrink - try to shrink hook array * @@ -210,6 +231,7 @@ static void *__nf_hook_entries_try_shrink(struct nf_hook_entries __rcu **pp) new_ops[j] = (void *)orig_ops[i]; j++; } + hooks_validate(new); out_assign: rcu_assign_pointer(*pp, new); return old; @@ -261,6 +283,7 @@ int nf_register_net_hook(struct net *net, const struct nf_hook_ops *reg) if (IS_ERR(new_hooks)) return PTR_ERR(new_hooks); + hooks_validate(new_hooks); #ifdef CONFIG_NETFILTER_INGRESS if (reg->pf == NFPROTO_NETDEV && reg->hooknum == NF_NETDEV_INGRESS) net_inc_ingress_queue(); -- cgit v1.2.3 From d3ad2c17b4047747bcec074814c08b00795afc85 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 23 Aug 2017 17:26:27 +0200 Subject: netfilter: core: batch nf_unregister_net_hooks synchronize_net calls re-add batching in nf_unregister_net_hooks(). Similar as before, just store an array with to-be-free'd rule arrays on stack, then call synchronize_net once per batch. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/core.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 56 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/netfilter/core.c b/net/netfilter/core.c index 164ad20d0bd2..04fe25abc5f6 100644 --- a/net/netfilter/core.c +++ b/net/netfilter/core.c @@ -395,10 +395,63 @@ EXPORT_SYMBOL(nf_register_net_hooks); void nf_unregister_net_hooks(struct net *net, const struct nf_hook_ops *reg, unsigned int hookcount) { - unsigned int i; + struct nf_hook_entries *to_free[16], *p; + struct nf_hook_entries __rcu **pp; + unsigned int i, j, n; + + mutex_lock(&nf_hook_mutex); + for (i = 0; i < hookcount; i++) { + pp = nf_hook_entry_head(net, ®[i]); + if (!pp) + continue; + + p = nf_entry_dereference(*pp); + if (WARN_ON_ONCE(!p)) + continue; + __nf_unregister_net_hook(p, ®[i]); + } + mutex_unlock(&nf_hook_mutex); + + do { + n = min_t(unsigned int, hookcount, ARRAY_SIZE(to_free)); + + mutex_lock(&nf_hook_mutex); + + for (i = 0, j = 0; i < hookcount && j < n; i++) { + pp = nf_hook_entry_head(net, ®[i]); + if (!pp) + continue; + + p = nf_entry_dereference(*pp); + if (!p) + continue; + + to_free[j] = __nf_hook_entries_try_shrink(pp); + if (to_free[j]) + ++j; + } + + mutex_unlock(&nf_hook_mutex); + + if (j) { + unsigned int nfq; + + synchronize_net(); + + /* need 2nd synchronize_net() if nfqueue is used, skb + * can get reinjected right before nf_queue_hook_drop() + */ + nfq = nf_queue_nf_hook_drop(net); + if (nfq) + synchronize_net(); + + for (i = 0; i < j; i++) + kvfree(to_free[i]); + } - for (i = 0; i < hookcount; i++) - nf_unregister_net_hook(net, ®[i]); + reg += n; + hookcount -= n; + } while (hookcount > 0); } EXPORT_SYMBOL(nf_unregister_net_hooks); -- cgit v1.2.3 From e2f387d2df0ece6d4418bb09bef7802cfaf7142d Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Fri, 25 Aug 2017 02:59:41 +0200 Subject: netfilter: conntrack: don't log "invalid" icmpv6 connections When enabling logging for invalid connections we currently also log most icmpv6 types, which we don't track intentionally (e.g. neigh discovery). "invalid" should really mean "invalid", i.e. short header or bad checksum. We don't do any logging for icmp(v4) either, its just useless noise. Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 5 ----- 1 file changed, 5 deletions(-) (limited to 'net') diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 808f63e2e1ff..43544b975eae 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -121,11 +121,6 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, pr_debug("icmpv6: can't create new conn with type %u\n", type + 128); nf_ct_dump_tuple_ipv6(&ct->tuplehash[0].tuple); - if (LOG_INVALID(nf_ct_net(ct), IPPROTO_ICMPV6)) - nf_log_packet(nf_ct_net(ct), PF_INET6, 0, skb, NULL, - NULL, NULL, - "nf_ct_icmpv6: invalid new with type %d ", - type + 128); return false; } return true; -- cgit v1.2.3 From b38cf90133e5b86c26633c1041c2cdc7360cb346 Mon Sep 17 00:00:00 2001 From: Varsha Rao Date: Mon, 28 Aug 2017 18:05:26 +0200 Subject: netfilter: Remove NFDEBUG() Remove NFDEBUG and use pr_debug() instead of it. Signed-off-by: Varsha Rao Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_internals.h | 6 ------ net/netfilter/nf_sockopt.c | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) (limited to 'net') diff --git a/net/netfilter/nf_internals.h b/net/netfilter/nf_internals.h index bacd6363946e..49f87ec093a3 100644 --- a/net/netfilter/nf_internals.h +++ b/net/netfilter/nf_internals.h @@ -5,12 +5,6 @@ #include #include -#ifdef CONFIG_NETFILTER_DEBUG -#define NFDEBUG(format, args...) printk(KERN_DEBUG format , ## args) -#else -#define NFDEBUG(format, args...) -#endif - /* nf_queue.c */ int nf_queue(struct sk_buff *skb, struct nf_hook_state *state, const struct nf_hook_entries *entries, unsigned int index, diff --git a/net/netfilter/nf_sockopt.c b/net/netfilter/nf_sockopt.c index c68c1e58b362..d2a9e6b5d01f 100644 --- a/net/netfilter/nf_sockopt.c +++ b/net/netfilter/nf_sockopt.c @@ -33,7 +33,7 @@ int nf_register_sockopt(struct nf_sockopt_ops *reg) reg->set_optmin, reg->set_optmax) || overlap(ops->get_optmin, ops->get_optmax, reg->get_optmin, reg->get_optmax))) { - NFDEBUG("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n", + pr_debug("nf_sock overlap: %u-%u/%u-%u v %u-%u/%u-%u\n", ops->set_optmin, ops->set_optmax, ops->get_optmin, ops->get_optmax, reg->set_optmin, reg->set_optmax, -- cgit v1.2.3 From d11dd6cdc3883f3c74f841f4d40dfe57c0b9756c Mon Sep 17 00:00:00 2001 From: Davide Caratti Date: Mon, 28 Aug 2017 11:52:07 +0200 Subject: netfilter: conntrack: remove unused code in nf_conntrack_proto_generic.c L4 protocol helpers for DCCP, SCTP and UDPlite can't be built as kernel modules anymore, so we can remove code enclosed in #ifdef CONFIG_NF_CT_PROTO_{DCCP,SCTP,UDPLITE}_MODULE Signed-off-by: Davide Caratti Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_conntrack_proto_generic.c | 12 ------------ 1 file changed, 12 deletions(-) (limited to 'net') diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index 2bc3d0c1a5bf..2993995b690d 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -17,21 +17,9 @@ static unsigned int nf_ct_generic_timeout __read_mostly = 600*HZ; static bool nf_generic_should_process(u8 proto) { switch (proto) { -#ifdef CONFIG_NF_CT_PROTO_SCTP_MODULE - case IPPROTO_SCTP: - return false; -#endif -#ifdef CONFIG_NF_CT_PROTO_DCCP_MODULE - case IPPROTO_DCCP: - return false; -#endif #ifdef CONFIG_NF_CT_PROTO_GRE_MODULE case IPPROTO_GRE: return false; -#endif -#ifdef CONFIG_NF_CT_PROTO_UDPLITE_MODULE - case IPPROTO_UDPLITE: - return false; #endif default: return true; -- cgit v1.2.3 From 1aff64715edb8565e99337b842d814d636641b50 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Mon, 28 Aug 2017 17:00:12 +0200 Subject: netfilter: rt: account for tcp header size too This needs to accout for the ipv4/ipv6 header size and the tcp header without options. Fixes: 6b5dc98e8fac0 ("netfilter: rt: add support to fetch path mss") Reported-by: Matteo Croce Signed-off-by: Florian Westphal Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_rt.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/netfilter/nft_rt.c b/net/netfilter/nft_rt.c index 61fd3acaa3c9..a6b7d05aeacf 100644 --- a/net/netfilter/nft_rt.c +++ b/net/netfilter/nft_rt.c @@ -35,10 +35,11 @@ static u16 get_tcpmss(const struct nft_pktinfo *pkt, const struct dst_entry *skb switch (nft_pf(pkt)) { case NFPROTO_IPV4: fl.u.ip4.daddr = ip_hdr(skb)->saddr; - minlen = sizeof(struct iphdr); + minlen = sizeof(struct iphdr) + sizeof(struct tcphdr); break; case NFPROTO_IPV6: fl.u.ip6.daddr = ipv6_hdr(skb)->saddr; + minlen = sizeof(struct ipv6hdr) + sizeof(struct tcphdr); break; } -- cgit v1.2.3 From 08848246639218ae58acdf3321bc7b693062f31c Mon Sep 17 00:00:00 2001 From: John Fastabend Date: Mon, 28 Aug 2017 07:12:21 -0700 Subject: bpf: sockmap requires STREAM_PARSER add Kconfig entry SOCKMAP uses strparser code (compiled with Kconfig option CONFIG_STREAM_PARSER) to run the parser BPF program. Without this config option set sockmap wont be compiled. However, at the moment the only way to pull in the strparser code is to enable KCM. To resolve this create a BPF specific config option to pull only the strparser piece in that sockmap needs. This also allows folks who want to use BPF/syscall/maps but don't need sockmap to easily opt out. Signed-off-by: John Fastabend Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/Kconfig | 12 ++++++++++++ 1 file changed, 12 insertions(+) (limited to 'net') diff --git a/net/Kconfig b/net/Kconfig index 7d57ef34b79c..17ca213e1f85 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -301,6 +301,18 @@ config BPF_JIT /proc/sys/net/core/bpf_jit_harden (optional) /proc/sys/net/core/bpf_jit_kallsyms (optional) +config BPF_STREAM_PARSER + bool "enable BPF STREAM_PARSER" + depends on BPF_SYSCALL + select STREAM_PARSER + ---help--- + Enabling this allows a stream parser to be used with + BPF_MAP_TYPE_SOCKMAP. + + BPF_MAP_TYPE_SOCKMAP provides a map type to use with network sockets. + It can be used to enforce socket policy, implement socket redirects, + etc. + config NET_FLOW_LIMIT bool depends on RPS -- cgit v1.2.3 From eb73ddebe992200586bcead3e2ff2889ef8d2575 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Fri, 25 Aug 2017 19:51:42 +0530 Subject: bridge: make ebt_table const Make this const as it is only passed to a const argument of the function ebt_register_table. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- net/bridge/netfilter/ebtable_nat.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/bridge/netfilter/ebtable_nat.c b/net/bridge/netfilter/ebtable_nat.c index 2f7a4f314406..30dedcb56ade 100644 --- a/net/bridge/netfilter/ebtable_nat.c +++ b/net/bridge/netfilter/ebtable_nat.c @@ -48,7 +48,7 @@ static int check(const struct ebt_table_info *info, unsigned int valid_hooks) return 0; } -static struct ebt_table frame_nat = { +static const struct ebt_table frame_nat = { .name = "nat", .table = &initial_table, .valid_hooks = NAT_VALID_HOOKS, -- cgit v1.2.3 From aa8db499ea67cff1f5f049033810ffede2fe5ae4 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Fri, 25 Aug 2017 19:51:44 +0530 Subject: ipv4: make net_protocol const Make these const as they are only passed to a const argument of the function inet_add_protocol. Signed-off-by: Bhumika Goyal Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index d678820e4306..19aee073ba29 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1596,7 +1596,7 @@ static const struct net_protocol igmp_protocol = { }; #endif -static struct net_protocol tcp_protocol = { +static const struct net_protocol tcp_protocol = { .early_demux = tcp_v4_early_demux, .early_demux_handler = tcp_v4_early_demux, .handler = tcp_v4_rcv, @@ -1606,7 +1606,7 @@ static struct net_protocol tcp_protocol = { .icmp_strict_tag_validation = 1, }; -static struct net_protocol udp_protocol = { +static const struct net_protocol udp_protocol = { .early_demux = udp_v4_early_demux, .early_demux_handler = udp_v4_early_demux, .handler = udp_rcv, -- cgit v1.2.3 From 8209432a5e60f6e8b5ea1eda91c51c427d57c10c Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Fri, 25 Aug 2017 19:51:45 +0530 Subject: RDS: make rhashtable_params const Make this const as it is either used during a copy operation or passed to a const argument of the function rhltable_init Signed-off-by: Bhumika Goyal Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/bind.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/rds/bind.c b/net/rds/bind.c index 3a915bedb76c..75d43dc8e96b 100644 --- a/net/rds/bind.c +++ b/net/rds/bind.c @@ -40,7 +40,7 @@ static struct rhashtable bind_hash_table; -static struct rhashtable_params ht_parms = { +static const struct rhashtable_params ht_parms = { .nelem_hint = 768, .key_len = sizeof(u64), .key_offset = offsetof(struct rds_sock, rs_bound_key), -- cgit v1.2.3 From 03157937fe0b5ef0431509106c096e50612fa70d Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 28 Aug 2017 13:23:09 -0700 Subject: Revert "ipv4: make net_protocol const" This reverts commit aa8db499ea67cff1f5f049033810ffede2fe5ae4. Early demux structs can not be made const. Doing so results in: [ 84.967355] BUG: unable to handle kernel paging request at ffffffff81684b10 [ 84.969272] IP: proc_configure_early_demux+0x1e/0x3d [ 84.970544] PGD 1a0a067 [ 84.970546] P4D 1a0a067 [ 84.971212] PUD 1a0b063 [ 84.971733] PMD 80000000016001e1 [ 84.972669] Oops: 0003 [#1] SMP [ 84.973065] Modules linked in: ip6table_filter ip6_tables veth vrf [ 84.973833] CPU: 0 PID: 955 Comm: sysctl Not tainted 4.13.0-rc6+ #22 [ 84.974612] Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.7.5-20140531_083030-gandalf 04/01/2014 [ 84.975855] task: ffff88003854ce00 task.stack: ffffc900005a4000 [ 84.976580] RIP: 0010:proc_configure_early_demux+0x1e/0x3d [ 84.977253] RSP: 0018:ffffc900005a7dd0 EFLAGS: 00010246 [ 84.977891] RAX: ffffffff81684b10 RBX: 0000000000000001 RCX: 0000000000000000 [ 84.978759] RDX: 0000000000000000 RSI: 0000000000000006 RDI: 0000000000000000 [ 84.979628] RBP: ffffc900005a7dd0 R08: 0000000000000000 R09: 0000000000000000 [ 84.980501] R10: 0000000000000001 R11: 0000000000000008 R12: 0000000000000001 [ 84.981373] R13: ffffffffffffffea R14: ffffffff81a9b4c0 R15: 0000000000000002 [ 84.982249] FS: 00007feb237b7700(0000) GS:ffff88003fc00000(0000) knlGS:0000000000000000 [ 84.983231] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 84.983941] CR2: ffffffff81684b10 CR3: 0000000038492000 CR4: 00000000000406f0 [ 84.984817] Call Trace: [ 84.985133] proc_tcp_early_demux+0x29/0x30 I think this is the second time such a patch has been reverted. Cc: Bhumika Goyal Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index 19aee073ba29..d678820e4306 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1596,7 +1596,7 @@ static const struct net_protocol igmp_protocol = { }; #endif -static const struct net_protocol tcp_protocol = { +static struct net_protocol tcp_protocol = { .early_demux = tcp_v4_early_demux, .early_demux_handler = tcp_v4_early_demux, .handler = tcp_v4_rcv, @@ -1606,7 +1606,7 @@ static const struct net_protocol tcp_protocol = { .icmp_strict_tag_validation = 1, }; -static const struct net_protocol udp_protocol = { +static struct net_protocol udp_protocol = { .early_demux = udp_v4_early_demux, .early_demux_handler = udp_v4_early_demux, .handler = udp_rcv, -- cgit v1.2.3 From 862a03c35ed76c50a562f7406ad23315f7862642 Mon Sep 17 00:00:00 2001 From: William Tu Date: Fri, 25 Aug 2017 09:21:27 -0700 Subject: gre: refactor the gre_fb_xmit The patch refactors the gre_fb_xmit function, by creating prepare_fb_xmit function for later ERSPAN collect_md mode patch. Signed-off-by: William Tu Signed-off-by: David S. Miller --- net/ipv4/ip_gre.c | 55 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 15 deletions(-) (limited to 'net') diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index f70674799fdd..453b7925b940 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -432,39 +432,33 @@ static struct rtable *gre_get_rt(struct sk_buff *skb, return ip_route_output_key(net, fl); } -static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev, - __be16 proto) +static struct rtable *prepare_fb_xmit(struct sk_buff *skb, + struct net_device *dev, + struct flowi4 *fl, + int tunnel_hlen) { struct ip_tunnel_info *tun_info; const struct ip_tunnel_key *key; struct rtable *rt = NULL; - struct flowi4 fl; int min_headroom; - int tunnel_hlen; - __be16 df, flags; bool use_cache; int err; tun_info = skb_tunnel_info(skb); - if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) || - ip_tunnel_info_af(tun_info) != AF_INET)) - goto err_free_skb; - key = &tun_info->key; use_cache = ip_tunnel_dst_cache_usable(skb, tun_info); + if (use_cache) - rt = dst_cache_get_ip4(&tun_info->dst_cache, &fl.saddr); + rt = dst_cache_get_ip4(&tun_info->dst_cache, &fl->saddr); if (!rt) { - rt = gre_get_rt(skb, dev, &fl, key); + rt = gre_get_rt(skb, dev, fl, key); if (IS_ERR(rt)) - goto err_free_skb; + goto err_free_skb; if (use_cache) dst_cache_set_ip4(&tun_info->dst_cache, &rt->dst, - fl.saddr); + fl->saddr); } - tunnel_hlen = gre_calc_hlen(key->tun_flags); - min_headroom = LL_RESERVED_SPACE(rt->dst.dev) + rt->dst.header_len + tunnel_hlen + sizeof(struct iphdr); if (skb_headroom(skb) < min_headroom || skb_header_cloned(skb)) { @@ -476,6 +470,37 @@ static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev, if (unlikely(err)) goto err_free_rt; } + return rt; + +err_free_rt: + ip_rt_put(rt); +err_free_skb: + kfree_skb(skb); + dev->stats.tx_dropped++; + return NULL; +} + +static void gre_fb_xmit(struct sk_buff *skb, struct net_device *dev, + __be16 proto) +{ + struct ip_tunnel_info *tun_info; + const struct ip_tunnel_key *key; + struct rtable *rt = NULL; + struct flowi4 fl; + int tunnel_hlen; + __be16 df, flags; + + tun_info = skb_tunnel_info(skb); + if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) || + ip_tunnel_info_af(tun_info) != AF_INET)) + goto err_free_skb; + + key = &tun_info->key; + tunnel_hlen = gre_calc_hlen(key->tun_flags); + + rt = prepare_fb_xmit(skb, dev, &fl, tunnel_hlen); + if (!rt) + return; /* Push Tunnel header. */ if (gre_handle_offloads(skb, !!(tun_info->key.tun_flags & TUNNEL_CSUM))) -- cgit v1.2.3 From 1a66a836da630cd70f3639208da549b549ce576b Mon Sep 17 00:00:00 2001 From: William Tu Date: Fri, 25 Aug 2017 09:21:28 -0700 Subject: gre: add collect_md mode to ERSPAN tunnel Similar to gre, vxlan, geneve, ipip tunnels, allow ERSPAN tunnels to operate in 'collect metadata' mode. bpf_skb_[gs]et_tunnel_key() helpers can make use of it right away. OVS can use it as well in the future. Signed-off-by: William Tu Signed-off-by: David S. Miller --- include/net/ip_tunnels.h | 4 +- net/ipv4/ip_gre.c | 102 +++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/include/net/ip_tunnels.h b/include/net/ip_tunnels.h index 625c29329372..992652856fe8 100644 --- a/include/net/ip_tunnels.h +++ b/include/net/ip_tunnels.h @@ -154,8 +154,10 @@ struct ip_tunnel { #define TUNNEL_GENEVE_OPT __cpu_to_be16(0x0800) #define TUNNEL_VXLAN_OPT __cpu_to_be16(0x1000) #define TUNNEL_NOCACHE __cpu_to_be16(0x2000) +#define TUNNEL_ERSPAN_OPT __cpu_to_be16(0x4000) -#define TUNNEL_OPTIONS_PRESENT (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT) +#define TUNNEL_OPTIONS_PRESENT \ + (TUNNEL_GENEVE_OPT | TUNNEL_VXLAN_OPT | TUNNEL_ERSPAN_OPT) struct tnl_ptk_info { __be16 flags; diff --git a/net/ipv4/ip_gre.c b/net/ipv4/ip_gre.c index 453b7925b940..0162fb955b33 100644 --- a/net/ipv4/ip_gre.c +++ b/net/ipv4/ip_gre.c @@ -113,6 +113,8 @@ MODULE_PARM_DESC(log_ecn_error, "Log packets received with corrupted ECN"); static struct rtnl_link_ops ipgre_link_ops __read_mostly; static int ipgre_tunnel_init(struct net_device *dev); +static void erspan_build_header(struct sk_buff *skb, + __be32 id, u32 index, bool truncate); static unsigned int ipgre_net_id __read_mostly; static unsigned int gre_tap_net_id __read_mostly; @@ -287,7 +289,33 @@ static int erspan_rcv(struct sk_buff *skb, struct tnl_ptk_info *tpi, false, false) < 0) goto drop; - tunnel->index = ntohl(index); + if (tunnel->collect_md) { + struct ip_tunnel_info *info; + struct erspan_metadata *md; + __be64 tun_id; + __be16 flags; + + tpi->flags |= TUNNEL_KEY; + flags = tpi->flags; + tun_id = key32_to_tunnel_id(tpi->key); + + tun_dst = ip_tun_rx_dst(skb, flags, + tun_id, sizeof(*md)); + if (!tun_dst) + return PACKET_REJECT; + + md = ip_tunnel_info_opts(&tun_dst->u.tun_info); + if (!md) + return PACKET_REJECT; + + md->index = index; + info = &tun_dst->u.tun_info; + info->key.tun_flags |= TUNNEL_ERSPAN_OPT; + info->options_len = sizeof(*md); + } else { + tunnel->index = ntohl(index); + } + skb_reset_mac_header(skb); ip_tunnel_rcv(tunnel, skb, tpi, tun_dst, log_ecn_error); return PACKET_RCVD; @@ -523,6 +551,64 @@ err_free_skb: dev->stats.tx_dropped++; } +static void erspan_fb_xmit(struct sk_buff *skb, struct net_device *dev, + __be16 proto) +{ + struct ip_tunnel *tunnel = netdev_priv(dev); + struct ip_tunnel_info *tun_info; + const struct ip_tunnel_key *key; + struct erspan_metadata *md; + struct rtable *rt = NULL; + bool truncate = false; + struct flowi4 fl; + int tunnel_hlen; + __be16 df; + + tun_info = skb_tunnel_info(skb); + if (unlikely(!tun_info || !(tun_info->mode & IP_TUNNEL_INFO_TX) || + ip_tunnel_info_af(tun_info) != AF_INET)) + goto err_free_skb; + + key = &tun_info->key; + + /* ERSPAN has fixed 8 byte GRE header */ + tunnel_hlen = 8 + sizeof(struct erspanhdr); + + rt = prepare_fb_xmit(skb, dev, &fl, tunnel_hlen); + if (!rt) + return; + + if (gre_handle_offloads(skb, false)) + goto err_free_rt; + + if (skb->len > dev->mtu) { + pskb_trim(skb, dev->mtu); + truncate = true; + } + + md = ip_tunnel_info_opts(tun_info); + if (!md) + goto err_free_rt; + + erspan_build_header(skb, tunnel_id_to_key32(key->tun_id), + ntohl(md->index), truncate); + + gre_build_header(skb, 8, TUNNEL_SEQ, + htons(ETH_P_ERSPAN), 0, htonl(tunnel->o_seqno++)); + + df = key->tun_flags & TUNNEL_DONT_FRAGMENT ? htons(IP_DF) : 0; + + iptunnel_xmit(skb->sk, rt, skb, fl.saddr, key->u.ipv4.dst, IPPROTO_GRE, + key->tos, key->ttl, df, false); + return; + +err_free_rt: + ip_rt_put(rt); +err_free_skb: + kfree_skb(skb); + dev->stats.tx_dropped++; +} + static int gre_fill_metadata_dst(struct net_device *dev, struct sk_buff *skb) { struct ip_tunnel_info *info = skb_tunnel_info(skb); @@ -636,6 +722,11 @@ static netdev_tx_t erspan_xmit(struct sk_buff *skb, struct ip_tunnel *tunnel = netdev_priv(dev); bool truncate = false; + if (tunnel->collect_md) { + erspan_fb_xmit(skb, dev, skb->protocol); + return NETDEV_TX_OK; + } + if (gre_handle_offloads(skb, false)) goto free_skb; @@ -998,9 +1089,12 @@ static int erspan_validate(struct nlattr *tb[], struct nlattr *data[], return ret; /* ERSPAN should only have GRE sequence and key flag */ - flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]); - flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]); - if (flags != (GRE_SEQ | GRE_KEY)) + if (data[IFLA_GRE_OFLAGS]) + flags |= nla_get_be16(data[IFLA_GRE_OFLAGS]); + if (data[IFLA_GRE_IFLAGS]) + flags |= nla_get_be16(data[IFLA_GRE_IFLAGS]); + if (!data[IFLA_GRE_COLLECT_METADATA] && + flags != (GRE_SEQ | GRE_KEY)) return -EINVAL; /* ERSPAN Session ID only has 10-bit. Since we reuse -- cgit v1.2.3 From a8e3bb347d8c32c25830af55466e08979df4e2be Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 28 Aug 2017 15:14:20 -0700 Subject: net: Add comment that early_demux can change via sysctl Twice patches trying to constify inet{6}_protocol have been reverted: 39294c3df2a8 ("Revert "ipv6: constify inet6_protocol structures"") to revert 3a3a4e3054137 and then 03157937fe0b5 ("Revert "ipv4: make net_protocol const"") to revert aa8db499ea67. Add a comment that the structures can not be const because the early_demux field can change based on a sysctl. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/ipv4/af_inet.c | 6 ++++++ net/ipv6/tcp_ipv6.c | 3 +++ net/ipv6/udp.c | 3 +++ 3 files changed, 12 insertions(+) (limited to 'net') diff --git a/net/ipv4/af_inet.c b/net/ipv4/af_inet.c index d678820e4306..e31108e5ef79 100644 --- a/net/ipv4/af_inet.c +++ b/net/ipv4/af_inet.c @@ -1596,6 +1596,9 @@ static const struct net_protocol igmp_protocol = { }; #endif +/* thinking of making this const? Don't. + * early_demux can change based on sysctl. + */ static struct net_protocol tcp_protocol = { .early_demux = tcp_v4_early_demux, .early_demux_handler = tcp_v4_early_demux, @@ -1606,6 +1609,9 @@ static struct net_protocol tcp_protocol = { .icmp_strict_tag_validation = 1, }; +/* thinking of making this const? Don't. + * early_demux can change based on sysctl. + */ static struct net_protocol udp_protocol = { .early_demux = udp_v4_early_demux, .early_demux_handler = udp_v4_early_demux, diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index abba3bc2a3d9..38f76d8b231e 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -1949,6 +1949,9 @@ struct proto tcpv6_prot = { .diag_destroy = tcp_abort, }; +/* thinking of making this const? Don't. + * early_demux can change based on sysctl. + */ static struct inet6_protocol tcpv6_protocol = { .early_demux = tcp_v6_early_demux, .early_demux_handler = tcp_v6_early_demux, diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index 2a15f1bb6ef8..976f30391356 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -1472,6 +1472,9 @@ int compat_udpv6_getsockopt(struct sock *sk, int level, int optname, } #endif +/* thinking of making this const? Don't. + * early_demux can change based on sysctl. + */ static struct inet6_protocol udpv6_protocol = { .early_demux = udp_v6_early_demux, .early_demux_handler = udp_v6_early_demux, -- cgit v1.2.3 From ae0078fcf0a5eb3a8623bfb5f988262e0911fdb9 Mon Sep 17 00:00:00 2001 From: Dexuan Cui Date: Sat, 26 Aug 2017 04:52:43 +0000 Subject: hv_sock: implements Hyper-V transport for Virtual Sockets (AF_VSOCK) Hyper-V Sockets (hv_sock) supplies a byte-stream based communication mechanism between the host and the guest. It uses VMBus ringbuffer as the transportation layer. With hv_sock, applications between the host (Windows 10, Windows Server 2016 or newer) and the guest can talk with each other using the traditional socket APIs. More info about Hyper-V Sockets is available here: "Make your own integration services": https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user-guide/make-integration-service The patch implements the necessary support in Linux guest by introducing a new vsock transport for AF_VSOCK. Signed-off-by: Dexuan Cui Cc: K. Y. Srinivasan Cc: Haiyang Zhang Cc: Stephen Hemminger Cc: Andy King Cc: Dmitry Torokhov Cc: George Zhang Cc: Jorgen Hansen Cc: Reilly Grant Cc: Asias He Cc: Stefan Hajnoczi Cc: Vitaly Kuznetsov Cc: Cathy Avery Cc: Rolf Neugebauer Cc: Marcelo Cerri Signed-off-by: David S. Miller --- MAINTAINERS | 1 + net/vmw_vsock/Kconfig | 12 + net/vmw_vsock/Makefile | 3 + net/vmw_vsock/hyperv_transport.c | 904 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 920 insertions(+) create mode 100644 net/vmw_vsock/hyperv_transport.c (limited to 'net') diff --git a/MAINTAINERS b/MAINTAINERS index 9d47ed980fff..6fdfe2685eed 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -6286,6 +6286,7 @@ F: drivers/net/hyperv/ F: drivers/scsi/storvsc_drv.c F: drivers/uio/uio_hv_generic.c F: drivers/video/fbdev/hyperv_fb.c +F: net/vmw_vsock/hyperv_transport.c F: include/linux/hyperv.h F: tools/hv/ F: Documentation/ABI/stable/sysfs-bus-vmbus diff --git a/net/vmw_vsock/Kconfig b/net/vmw_vsock/Kconfig index 8831e7c42167..a24369d175fd 100644 --- a/net/vmw_vsock/Kconfig +++ b/net/vmw_vsock/Kconfig @@ -46,3 +46,15 @@ config VIRTIO_VSOCKETS_COMMON This option is selected by any driver which needs to access the virtio_vsock. The module will be called vmw_vsock_virtio_transport_common. + +config HYPERV_VSOCKETS + tristate "Hyper-V transport for Virtual Sockets" + depends on VSOCKETS && HYPERV + help + This module implements a Hyper-V transport for Virtual Sockets. + + Enable this transport if your Virtual Machine host supports Virtual + Sockets over Hyper-V VMBus. + + To compile this driver as a module, choose M here: the module will be + called hv_sock. If unsure, say N. diff --git a/net/vmw_vsock/Makefile b/net/vmw_vsock/Makefile index 09fc2eb29dc8..e63d574234a9 100644 --- a/net/vmw_vsock/Makefile +++ b/net/vmw_vsock/Makefile @@ -2,6 +2,7 @@ obj-$(CONFIG_VSOCKETS) += vsock.o obj-$(CONFIG_VMWARE_VMCI_VSOCKETS) += vmw_vsock_vmci_transport.o obj-$(CONFIG_VIRTIO_VSOCKETS) += vmw_vsock_virtio_transport.o obj-$(CONFIG_VIRTIO_VSOCKETS_COMMON) += vmw_vsock_virtio_transport_common.o +obj-$(CONFIG_HYPERV_VSOCKETS) += hv_sock.o vsock-y += af_vsock.o af_vsock_tap.o vsock_addr.o @@ -11,3 +12,5 @@ vmw_vsock_vmci_transport-y += vmci_transport.o vmci_transport_notify.o \ vmw_vsock_virtio_transport-y += virtio_transport.o vmw_vsock_virtio_transport_common-y += virtio_transport_common.o + +hv_sock-y += hyperv_transport.o diff --git a/net/vmw_vsock/hyperv_transport.c b/net/vmw_vsock/hyperv_transport.c new file mode 100644 index 000000000000..14ed5a344cdf --- /dev/null +++ b/net/vmw_vsock/hyperv_transport.c @@ -0,0 +1,904 @@ +/* + * Hyper-V transport for vsock + * + * Hyper-V Sockets supplies a byte-stream based communication mechanism + * between the host and the VM. This driver implements the necessary + * support in the VM by introducing the new vsock transport. + * + * Copyright (c) 2017, Microsoft Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + */ +#include +#include +#include +#include +#include + +/* The host side's design of the feature requires 6 exact 4KB pages for + * recv/send rings respectively -- this is suboptimal considering memory + * consumption, however unluckily we have to live with it, before the + * host comes up with a better design in the future. + */ +#define PAGE_SIZE_4K 4096 +#define RINGBUFFER_HVS_RCV_SIZE (PAGE_SIZE_4K * 6) +#define RINGBUFFER_HVS_SND_SIZE (PAGE_SIZE_4K * 6) + +/* The MTU is 16KB per the host side's design */ +#define HVS_MTU_SIZE (1024 * 16) + +struct vmpipe_proto_header { + u32 pkt_type; + u32 data_size; +}; + +/* For recv, we use the VMBus in-place packet iterator APIs to directly copy + * data from the ringbuffer into the userspace buffer. + */ +struct hvs_recv_buf { + /* The header before the payload data */ + struct vmpipe_proto_header hdr; + + /* The payload */ + u8 data[HVS_MTU_SIZE]; +}; + +/* We can send up to HVS_MTU_SIZE bytes of payload to the host, but let's use + * a small size, i.e. HVS_SEND_BUF_SIZE, to minimize the dynamically-allocated + * buffer, because tests show there is no significant performance difference. + * + * Note: the buffer can be eliminated in the future when we add new VMBus + * ringbuffer APIs that allow us to directly copy data from userspace buffer + * to VMBus ringbuffer. + */ +#define HVS_SEND_BUF_SIZE (PAGE_SIZE_4K - sizeof(struct vmpipe_proto_header)) + +struct hvs_send_buf { + /* The header before the payload data */ + struct vmpipe_proto_header hdr; + + /* The payload */ + u8 data[HVS_SEND_BUF_SIZE]; +}; + +#define HVS_HEADER_LEN (sizeof(struct vmpacket_descriptor) + \ + sizeof(struct vmpipe_proto_header)) + +/* See 'prev_indices' in hv_ringbuffer_read(), hv_ringbuffer_write(), and + * __hv_pkt_iter_next(). + */ +#define VMBUS_PKT_TRAILER_SIZE (sizeof(u64)) + +#define HVS_PKT_LEN(payload_len) (HVS_HEADER_LEN + \ + ALIGN((payload_len), 8) + \ + VMBUS_PKT_TRAILER_SIZE) + +union hvs_service_id { + uuid_le srv_id; + + struct { + unsigned int svm_port; + unsigned char b[sizeof(uuid_le) - sizeof(unsigned int)]; + }; +}; + +/* Per-socket state (accessed via vsk->trans) */ +struct hvsock { + struct vsock_sock *vsk; + + uuid_le vm_srv_id; + uuid_le host_srv_id; + + struct vmbus_channel *chan; + struct vmpacket_descriptor *recv_desc; + + /* The length of the payload not delivered to userland yet */ + u32 recv_data_len; + /* The offset of the payload */ + u32 recv_data_off; + + /* Have we sent the zero-length packet (FIN)? */ + bool fin_sent; +}; + +/* In the VM, we support Hyper-V Sockets with AF_VSOCK, and the endpoint is + * (see struct sockaddr_vm). Note: cid is not really used here: + * when we write apps to connect to the host, we can only use VMADDR_CID_ANY + * or VMADDR_CID_HOST (both are equivalent) as the remote cid, and when we + * write apps to bind() & listen() in the VM, we can only use VMADDR_CID_ANY + * as the local cid. + * + * On the host, Hyper-V Sockets are supported by Winsock AF_HYPERV: + * https://docs.microsoft.com/en-us/virtualization/hyper-v-on-windows/user- + * guide/make-integration-service, and the endpoint is with + * the below sockaddr: + * + * struct SOCKADDR_HV + * { + * ADDRESS_FAMILY Family; + * USHORT Reserved; + * GUID VmId; + * GUID ServiceId; + * }; + * Note: VmID is not used by Linux VM and actually it isn't transmitted via + * VMBus, because here it's obvious the host and the VM can easily identify + * each other. Though the VmID is useful on the host, especially in the case + * of Windows container, Linux VM doesn't need it at all. + * + * To make use of the AF_VSOCK infrastructure in Linux VM, we have to limit + * the available GUID space of SOCKADDR_HV so that we can create a mapping + * between AF_VSOCK port and SOCKADDR_HV Service GUID. The rule of writing + * Hyper-V Sockets apps on the host and in Linux VM is: + * + **************************************************************************** + * The only valid Service GUIDs, from the perspectives of both the host and * + * Linux VM, that can be connected by the other end, must conform to this * + * format: -facb-11e6-bd58-64006a7986d3, and the "port" must be in * + * this range [0, 0x7FFFFFFF]. * + **************************************************************************** + * + * When we write apps on the host to connect(), the GUID ServiceID is used. + * When we write apps in Linux VM to connect(), we only need to specify the + * port and the driver will form the GUID and use that to request the host. + * + * From the perspective of Linux VM: + * 1. the local ephemeral port (i.e. the local auto-bound port when we call + * connect() without explicit bind()) is generated by __vsock_bind_stream(), + * and the range is [1024, 0xFFFFFFFF). + * 2. the remote ephemeral port (i.e. the auto-generated remote port for + * a connect request initiated by the host's connect()) is generated by + * hvs_remote_addr_init() and the range is [0x80000000, 0xFFFFFFFF). + */ + +#define MAX_LISTEN_PORT ((u32)0x7FFFFFFF) +#define MAX_VM_LISTEN_PORT MAX_LISTEN_PORT +#define MAX_HOST_LISTEN_PORT MAX_LISTEN_PORT +#define MIN_HOST_EPHEMERAL_PORT (MAX_HOST_LISTEN_PORT + 1) + +/* 00000000-facb-11e6-bd58-64006a7986d3 */ +static const uuid_le srv_id_template = + UUID_LE(0x00000000, 0xfacb, 0x11e6, 0xbd, 0x58, + 0x64, 0x00, 0x6a, 0x79, 0x86, 0xd3); + +static bool is_valid_srv_id(const uuid_le *id) +{ + return !memcmp(&id->b[4], &srv_id_template.b[4], sizeof(uuid_le) - 4); +} + +static unsigned int get_port_by_srv_id(const uuid_le *svr_id) +{ + return *((unsigned int *)svr_id); +} + +static void hvs_addr_init(struct sockaddr_vm *addr, const uuid_le *svr_id) +{ + unsigned int port = get_port_by_srv_id(svr_id); + + vsock_addr_init(addr, VMADDR_CID_ANY, port); +} + +static void hvs_remote_addr_init(struct sockaddr_vm *remote, + struct sockaddr_vm *local) +{ + static u32 host_ephemeral_port = MIN_HOST_EPHEMERAL_PORT; + struct sock *sk; + + vsock_addr_init(remote, VMADDR_CID_ANY, VMADDR_PORT_ANY); + + while (1) { + /* Wrap around ? */ + if (host_ephemeral_port < MIN_HOST_EPHEMERAL_PORT || + host_ephemeral_port == VMADDR_PORT_ANY) + host_ephemeral_port = MIN_HOST_EPHEMERAL_PORT; + + remote->svm_port = host_ephemeral_port++; + + sk = vsock_find_connected_socket(remote, local); + if (!sk) { + /* Found an available ephemeral port */ + return; + } + + /* Release refcnt got in vsock_find_connected_socket */ + sock_put(sk); + } +} + +static void hvs_set_channel_pending_send_size(struct vmbus_channel *chan) +{ + set_channel_pending_send_size(chan, + HVS_PKT_LEN(HVS_SEND_BUF_SIZE)); + + /* See hvs_stream_has_space(): we must make sure the host has seen + * the new pending send size, before we can re-check the writable + * bytes. + */ + virt_mb(); +} + +static void hvs_clear_channel_pending_send_size(struct vmbus_channel *chan) +{ + set_channel_pending_send_size(chan, 0); + + /* Ditto */ + virt_mb(); +} + +static bool hvs_channel_readable(struct vmbus_channel *chan) +{ + u32 readable = hv_get_bytes_to_read(&chan->inbound); + + /* 0-size payload means FIN */ + return readable >= HVS_PKT_LEN(0); +} + +static int hvs_channel_readable_payload(struct vmbus_channel *chan) +{ + u32 readable = hv_get_bytes_to_read(&chan->inbound); + + if (readable > HVS_PKT_LEN(0)) { + /* At least we have 1 byte to read. We don't need to return + * the exact readable bytes: see vsock_stream_recvmsg() -> + * vsock_stream_has_data(). + */ + return 1; + } + + if (readable == HVS_PKT_LEN(0)) { + /* 0-size payload means FIN */ + return 0; + } + + /* No payload or FIN */ + return -1; +} + +static size_t hvs_channel_writable_bytes(struct vmbus_channel *chan) +{ + u32 writeable = hv_get_bytes_to_write(&chan->outbound); + size_t ret; + + /* The ringbuffer mustn't be 100% full, and we should reserve a + * zero-length-payload packet for the FIN: see hv_ringbuffer_write() + * and hvs_shutdown(). + */ + if (writeable <= HVS_PKT_LEN(1) + HVS_PKT_LEN(0)) + return 0; + + ret = writeable - HVS_PKT_LEN(1) - HVS_PKT_LEN(0); + + return round_down(ret, 8); +} + +static int hvs_send_data(struct vmbus_channel *chan, + struct hvs_send_buf *send_buf, size_t to_write) +{ + send_buf->hdr.pkt_type = 1; + send_buf->hdr.data_size = to_write; + return vmbus_sendpacket(chan, &send_buf->hdr, + sizeof(send_buf->hdr) + to_write, + 0, VM_PKT_DATA_INBAND, 0); +} + +static void hvs_channel_cb(void *ctx) +{ + struct sock *sk = (struct sock *)ctx; + struct vsock_sock *vsk = vsock_sk(sk); + struct hvsock *hvs = vsk->trans; + struct vmbus_channel *chan = hvs->chan; + + if (hvs_channel_readable(chan)) + sk->sk_data_ready(sk); + + /* See hvs_stream_has_space(): when we reach here, the writable bytes + * may be already less than HVS_PKT_LEN(HVS_SEND_BUF_SIZE). + */ + if (hv_get_bytes_to_write(&chan->outbound) > 0) + sk->sk_write_space(sk); +} + +static void hvs_close_connection(struct vmbus_channel *chan) +{ + struct sock *sk = get_per_channel_state(chan); + struct vsock_sock *vsk = vsock_sk(sk); + + sk->sk_state = SS_UNCONNECTED; + sock_set_flag(sk, SOCK_DONE); + vsk->peer_shutdown |= SEND_SHUTDOWN | RCV_SHUTDOWN; + + sk->sk_state_change(sk); +} + +static void hvs_open_connection(struct vmbus_channel *chan) +{ + uuid_le *if_instance, *if_type; + unsigned char conn_from_host; + + struct sockaddr_vm addr; + struct sock *sk, *new = NULL; + struct vsock_sock *vnew; + struct hvsock *hvs, *hvs_new; + int ret; + + if_type = &chan->offermsg.offer.if_type; + if_instance = &chan->offermsg.offer.if_instance; + conn_from_host = chan->offermsg.offer.u.pipe.user_def[0]; + + /* The host or the VM should only listen on a port in + * [0, MAX_LISTEN_PORT] + */ + if (!is_valid_srv_id(if_type) || + get_port_by_srv_id(if_type) > MAX_LISTEN_PORT) + return; + + hvs_addr_init(&addr, conn_from_host ? if_type : if_instance); + sk = vsock_find_bound_socket(&addr); + if (!sk) + return; + + if ((conn_from_host && sk->sk_state != VSOCK_SS_LISTEN) || + (!conn_from_host && sk->sk_state != SS_CONNECTING)) + goto out; + + if (conn_from_host) { + if (sk->sk_ack_backlog >= sk->sk_max_ack_backlog) + goto out; + + new = __vsock_create(sock_net(sk), NULL, sk, GFP_KERNEL, + sk->sk_type, 0); + if (!new) + goto out; + + new->sk_state = SS_CONNECTING; + vnew = vsock_sk(new); + hvs_new = vnew->trans; + hvs_new->chan = chan; + } else { + hvs = vsock_sk(sk)->trans; + hvs->chan = chan; + } + + set_channel_read_mode(chan, HV_CALL_DIRECT); + ret = vmbus_open(chan, RINGBUFFER_HVS_SND_SIZE, + RINGBUFFER_HVS_RCV_SIZE, NULL, 0, + hvs_channel_cb, conn_from_host ? new : sk); + if (ret != 0) { + if (conn_from_host) { + hvs_new->chan = NULL; + sock_put(new); + } else { + hvs->chan = NULL; + } + goto out; + } + + set_per_channel_state(chan, conn_from_host ? new : sk); + vmbus_set_chn_rescind_callback(chan, hvs_close_connection); + + if (conn_from_host) { + new->sk_state = SS_CONNECTED; + sk->sk_ack_backlog++; + + hvs_addr_init(&vnew->local_addr, if_type); + hvs_remote_addr_init(&vnew->remote_addr, &vnew->local_addr); + + hvs_new->vm_srv_id = *if_type; + hvs_new->host_srv_id = *if_instance; + + vsock_insert_connected(vnew); + + lock_sock(sk); + vsock_enqueue_accept(sk, new); + release_sock(sk); + } else { + sk->sk_state = SS_CONNECTED; + sk->sk_socket->state = SS_CONNECTED; + + vsock_insert_connected(vsock_sk(sk)); + } + + sk->sk_state_change(sk); + +out: + /* Release refcnt obtained when we called vsock_find_bound_socket() */ + sock_put(sk); +} + +static u32 hvs_get_local_cid(void) +{ + return VMADDR_CID_ANY; +} + +static int hvs_sock_init(struct vsock_sock *vsk, struct vsock_sock *psk) +{ + struct hvsock *hvs; + + hvs = kzalloc(sizeof(*hvs), GFP_KERNEL); + if (!hvs) + return -ENOMEM; + + vsk->trans = hvs; + hvs->vsk = vsk; + + return 0; +} + +static int hvs_connect(struct vsock_sock *vsk) +{ + union hvs_service_id vm, host; + struct hvsock *h = vsk->trans; + + vm.srv_id = srv_id_template; + vm.svm_port = vsk->local_addr.svm_port; + h->vm_srv_id = vm.srv_id; + + host.srv_id = srv_id_template; + host.svm_port = vsk->remote_addr.svm_port; + h->host_srv_id = host.srv_id; + + return vmbus_send_tl_connect_request(&h->vm_srv_id, &h->host_srv_id); +} + +static int hvs_shutdown(struct vsock_sock *vsk, int mode) +{ + struct sock *sk = sk_vsock(vsk); + struct vmpipe_proto_header hdr; + struct hvs_send_buf *send_buf; + struct hvsock *hvs; + + if (!(mode & SEND_SHUTDOWN)) + return 0; + + lock_sock(sk); + + hvs = vsk->trans; + if (hvs->fin_sent) + goto out; + + send_buf = (struct hvs_send_buf *)&hdr; + + /* It can't fail: see hvs_channel_writable_bytes(). */ + (void)hvs_send_data(hvs->chan, send_buf, 0); + + hvs->fin_sent = true; +out: + release_sock(sk); + return 0; +} + +static void hvs_release(struct vsock_sock *vsk) +{ + struct hvsock *hvs = vsk->trans; + struct vmbus_channel *chan = hvs->chan; + + if (chan) + hvs_shutdown(vsk, RCV_SHUTDOWN | SEND_SHUTDOWN); + + vsock_remove_sock(vsk); +} + +static void hvs_destruct(struct vsock_sock *vsk) +{ + struct hvsock *hvs = vsk->trans; + struct vmbus_channel *chan = hvs->chan; + + if (chan) + vmbus_hvsock_device_unregister(chan); + + kfree(hvs); +} + +static int hvs_dgram_bind(struct vsock_sock *vsk, struct sockaddr_vm *addr) +{ + return -EOPNOTSUPP; +} + +static int hvs_dgram_dequeue(struct vsock_sock *vsk, struct msghdr *msg, + size_t len, int flags) +{ + return -EOPNOTSUPP; +} + +static int hvs_dgram_enqueue(struct vsock_sock *vsk, + struct sockaddr_vm *remote, struct msghdr *msg, + size_t dgram_len) +{ + return -EOPNOTSUPP; +} + +static bool hvs_dgram_allow(u32 cid, u32 port) +{ + return false; +} + +static int hvs_update_recv_data(struct hvsock *hvs) +{ + struct hvs_recv_buf *recv_buf; + u32 payload_len; + + recv_buf = (struct hvs_recv_buf *)(hvs->recv_desc + 1); + payload_len = recv_buf->hdr.data_size; + + if (payload_len > HVS_MTU_SIZE) + return -EIO; + + if (payload_len == 0) + hvs->vsk->peer_shutdown |= SEND_SHUTDOWN; + + hvs->recv_data_len = payload_len; + hvs->recv_data_off = 0; + + return 0; +} + +static ssize_t hvs_stream_dequeue(struct vsock_sock *vsk, struct msghdr *msg, + size_t len, int flags) +{ + struct hvsock *hvs = vsk->trans; + bool need_refill = !hvs->recv_desc; + struct hvs_recv_buf *recv_buf; + u32 to_read; + int ret; + + if (flags & MSG_PEEK) + return -EOPNOTSUPP; + + if (need_refill) { + hvs->recv_desc = hv_pkt_iter_first(hvs->chan); + ret = hvs_update_recv_data(hvs); + if (ret) + return ret; + } + + recv_buf = (struct hvs_recv_buf *)(hvs->recv_desc + 1); + to_read = min_t(u32, len, hvs->recv_data_len); + ret = memcpy_to_msg(msg, recv_buf->data + hvs->recv_data_off, to_read); + if (ret != 0) + return ret; + + hvs->recv_data_len -= to_read; + if (hvs->recv_data_len == 0) { + hvs->recv_desc = hv_pkt_iter_next(hvs->chan, hvs->recv_desc); + if (hvs->recv_desc) { + ret = hvs_update_recv_data(hvs); + if (ret) + return ret; + } + } else { + hvs->recv_data_off += to_read; + } + + return to_read; +} + +static ssize_t hvs_stream_enqueue(struct vsock_sock *vsk, struct msghdr *msg, + size_t len) +{ + struct hvsock *hvs = vsk->trans; + struct vmbus_channel *chan = hvs->chan; + struct hvs_send_buf *send_buf; + ssize_t to_write, max_writable, ret; + + BUILD_BUG_ON(sizeof(*send_buf) != PAGE_SIZE_4K); + + send_buf = kmalloc(sizeof(*send_buf), GFP_KERNEL); + if (!send_buf) + return -ENOMEM; + + max_writable = hvs_channel_writable_bytes(chan); + to_write = min_t(ssize_t, len, max_writable); + to_write = min_t(ssize_t, to_write, HVS_SEND_BUF_SIZE); + + ret = memcpy_from_msg(send_buf->data, msg, to_write); + if (ret < 0) + goto out; + + ret = hvs_send_data(hvs->chan, send_buf, to_write); + if (ret < 0) + goto out; + + ret = to_write; +out: + kfree(send_buf); + return ret; +} + +static s64 hvs_stream_has_data(struct vsock_sock *vsk) +{ + struct hvsock *hvs = vsk->trans; + s64 ret; + + if (hvs->recv_data_len > 0) + return 1; + + switch (hvs_channel_readable_payload(hvs->chan)) { + case 1: + ret = 1; + break; + case 0: + vsk->peer_shutdown |= SEND_SHUTDOWN; + ret = 0; + break; + default: /* -1 */ + ret = 0; + break; + } + + return ret; +} + +static s64 hvs_stream_has_space(struct vsock_sock *vsk) +{ + struct hvsock *hvs = vsk->trans; + struct vmbus_channel *chan = hvs->chan; + s64 ret; + + ret = hvs_channel_writable_bytes(chan); + if (ret > 0) { + hvs_clear_channel_pending_send_size(chan); + } else { + /* See hvs_channel_cb() */ + hvs_set_channel_pending_send_size(chan); + + /* Re-check the writable bytes to avoid race */ + ret = hvs_channel_writable_bytes(chan); + if (ret > 0) + hvs_clear_channel_pending_send_size(chan); + } + + return ret; +} + +static u64 hvs_stream_rcvhiwat(struct vsock_sock *vsk) +{ + return HVS_MTU_SIZE + 1; +} + +static bool hvs_stream_is_active(struct vsock_sock *vsk) +{ + struct hvsock *hvs = vsk->trans; + + return hvs->chan != NULL; +} + +static bool hvs_stream_allow(u32 cid, u32 port) +{ + /* The host's port range [MIN_HOST_EPHEMERAL_PORT, 0xFFFFFFFF) is + * reserved as ephemeral ports, which are used as the host's ports + * when the host initiates connections. + * + * Perform this check in the guest so an immediate error is produced + * instead of a timeout. + */ + if (port > MAX_HOST_LISTEN_PORT) + return false; + + if (cid == VMADDR_CID_HOST) + return true; + + return false; +} + +static +int hvs_notify_poll_in(struct vsock_sock *vsk, size_t target, bool *readable) +{ + struct hvsock *hvs = vsk->trans; + + *readable = hvs_channel_readable(hvs->chan); + return 0; +} + +static +int hvs_notify_poll_out(struct vsock_sock *vsk, size_t target, bool *writable) +{ + *writable = hvs_stream_has_space(vsk) > 0; + + return 0; +} + +static +int hvs_notify_recv_init(struct vsock_sock *vsk, size_t target, + struct vsock_transport_recv_notify_data *d) +{ + return 0; +} + +static +int hvs_notify_recv_pre_block(struct vsock_sock *vsk, size_t target, + struct vsock_transport_recv_notify_data *d) +{ + return 0; +} + +static +int hvs_notify_recv_pre_dequeue(struct vsock_sock *vsk, size_t target, + struct vsock_transport_recv_notify_data *d) +{ + return 0; +} + +static +int hvs_notify_recv_post_dequeue(struct vsock_sock *vsk, size_t target, + ssize_t copied, bool data_read, + struct vsock_transport_recv_notify_data *d) +{ + return 0; +} + +static +int hvs_notify_send_init(struct vsock_sock *vsk, + struct vsock_transport_send_notify_data *d) +{ + return 0; +} + +static +int hvs_notify_send_pre_block(struct vsock_sock *vsk, + struct vsock_transport_send_notify_data *d) +{ + return 0; +} + +static +int hvs_notify_send_pre_enqueue(struct vsock_sock *vsk, + struct vsock_transport_send_notify_data *d) +{ + return 0; +} + +static +int hvs_notify_send_post_enqueue(struct vsock_sock *vsk, ssize_t written, + struct vsock_transport_send_notify_data *d) +{ + return 0; +} + +static void hvs_set_buffer_size(struct vsock_sock *vsk, u64 val) +{ + /* Ignored. */ +} + +static void hvs_set_min_buffer_size(struct vsock_sock *vsk, u64 val) +{ + /* Ignored. */ +} + +static void hvs_set_max_buffer_size(struct vsock_sock *vsk, u64 val) +{ + /* Ignored. */ +} + +static u64 hvs_get_buffer_size(struct vsock_sock *vsk) +{ + return -ENOPROTOOPT; +} + +static u64 hvs_get_min_buffer_size(struct vsock_sock *vsk) +{ + return -ENOPROTOOPT; +} + +static u64 hvs_get_max_buffer_size(struct vsock_sock *vsk) +{ + return -ENOPROTOOPT; +} + +static struct vsock_transport hvs_transport = { + .get_local_cid = hvs_get_local_cid, + + .init = hvs_sock_init, + .destruct = hvs_destruct, + .release = hvs_release, + .connect = hvs_connect, + .shutdown = hvs_shutdown, + + .dgram_bind = hvs_dgram_bind, + .dgram_dequeue = hvs_dgram_dequeue, + .dgram_enqueue = hvs_dgram_enqueue, + .dgram_allow = hvs_dgram_allow, + + .stream_dequeue = hvs_stream_dequeue, + .stream_enqueue = hvs_stream_enqueue, + .stream_has_data = hvs_stream_has_data, + .stream_has_space = hvs_stream_has_space, + .stream_rcvhiwat = hvs_stream_rcvhiwat, + .stream_is_active = hvs_stream_is_active, + .stream_allow = hvs_stream_allow, + + .notify_poll_in = hvs_notify_poll_in, + .notify_poll_out = hvs_notify_poll_out, + .notify_recv_init = hvs_notify_recv_init, + .notify_recv_pre_block = hvs_notify_recv_pre_block, + .notify_recv_pre_dequeue = hvs_notify_recv_pre_dequeue, + .notify_recv_post_dequeue = hvs_notify_recv_post_dequeue, + .notify_send_init = hvs_notify_send_init, + .notify_send_pre_block = hvs_notify_send_pre_block, + .notify_send_pre_enqueue = hvs_notify_send_pre_enqueue, + .notify_send_post_enqueue = hvs_notify_send_post_enqueue, + + .set_buffer_size = hvs_set_buffer_size, + .set_min_buffer_size = hvs_set_min_buffer_size, + .set_max_buffer_size = hvs_set_max_buffer_size, + .get_buffer_size = hvs_get_buffer_size, + .get_min_buffer_size = hvs_get_min_buffer_size, + .get_max_buffer_size = hvs_get_max_buffer_size, +}; + +static int hvs_probe(struct hv_device *hdev, + const struct hv_vmbus_device_id *dev_id) +{ + struct vmbus_channel *chan = hdev->channel; + + hvs_open_connection(chan); + + /* Always return success to suppress the unnecessary error message + * in vmbus_probe(): on error the host will rescind the device in + * 30 seconds and we can do cleanup at that time in + * vmbus_onoffer_rescind(). + */ + return 0; +} + +static int hvs_remove(struct hv_device *hdev) +{ + struct vmbus_channel *chan = hdev->channel; + + vmbus_close(chan); + + return 0; +} + +/* This isn't really used. See vmbus_match() and vmbus_probe() */ +static const struct hv_vmbus_device_id id_table[] = { + {}, +}; + +static struct hv_driver hvs_drv = { + .name = "hv_sock", + .hvsock = true, + .id_table = id_table, + .probe = hvs_probe, + .remove = hvs_remove, +}; + +static int __init hvs_init(void) +{ + int ret; + + if (vmbus_proto_version < VERSION_WIN10) + return -ENODEV; + + ret = vmbus_driver_register(&hvs_drv); + if (ret != 0) + return ret; + + ret = vsock_core_init(&hvs_transport); + if (ret) { + vmbus_driver_unregister(&hvs_drv); + return ret; + } + + return 0; +} + +static void __exit hvs_exit(void) +{ + vsock_core_exit(); + vmbus_driver_unregister(&hvs_drv); +} + +module_init(hvs_init); +module_exit(hvs_exit); + +MODULE_DESCRIPTION("Hyper-V Sockets"); +MODULE_VERSION("1.0.0"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(PF_VSOCK); -- cgit v1.2.3 From f9ab7425b312f8a41339389e83b238c3242e8429 Mon Sep 17 00:00:00 2001 From: Gao Feng Date: Sat, 26 Aug 2017 22:58:58 +0800 Subject: sched: sfq: drop packets after root qdisc lock is released The commit 520ac30f4551 ("net_sched: drop packets after root qdisc lock is released) made a big change of tc for performance. But there are some points which are not changed in SFQ enqueue operation. 1. Fail to find the SFQ hash slot; 2. When the queue is full; Now use qdisc_drop instead free skb directly. Signed-off-by: Gao Feng Signed-off-by: David S. Miller --- net/sched/sch_sfq.c | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/sched/sch_sfq.c b/net/sched/sch_sfq.c index b46e0121dd1a..e0f029a887ac 100644 --- a/net/sched/sch_sfq.c +++ b/net/sched/sch_sfq.c @@ -292,7 +292,7 @@ static inline void slot_queue_add(struct sfq_slot *slot, struct sk_buff *skb) slot->skblist_prev = skb; } -static unsigned int sfq_drop(struct Qdisc *sch) +static unsigned int sfq_drop(struct Qdisc *sch, struct sk_buff **to_free) { struct sfq_sched_data *q = qdisc_priv(sch); sfq_index x, d = q->cur_depth; @@ -310,9 +310,8 @@ drop: slot->backlog -= len; sfq_dec(q, x); sch->q.qlen--; - qdisc_qstats_drop(sch); qdisc_qstats_backlog_dec(sch, skb); - kfree_skb(skb); + qdisc_drop(skb, sch, to_free); return len; } @@ -360,7 +359,7 @@ sfq_enqueue(struct sk_buff *skb, struct Qdisc *sch, struct sk_buff **to_free) if (hash == 0) { if (ret & __NET_XMIT_BYPASS) qdisc_qstats_drop(sch); - kfree_skb(skb); + __qdisc_drop(skb, to_free); return ret; } hash--; @@ -465,7 +464,7 @@ enqueue: return NET_XMIT_SUCCESS; qlen = slot->qlen; - dropped = sfq_drop(sch); + dropped = sfq_drop(sch, to_free); /* Return Congestion Notification only if we dropped a packet * from this flow. */ @@ -628,6 +627,8 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt) struct tc_sfq_qopt_v1 *ctl_v1 = NULL; unsigned int qlen, dropped = 0; struct red_parms *p = NULL; + struct sk_buff *to_free = NULL; + struct sk_buff *tail = NULL; if (opt->nla_len < nla_attr_size(sizeof(*ctl))) return -EINVAL; @@ -674,8 +675,13 @@ static int sfq_change(struct Qdisc *sch, struct nlattr *opt) } qlen = sch->q.qlen; - while (sch->q.qlen > q->limit) - dropped += sfq_drop(sch); + while (sch->q.qlen > q->limit) { + dropped += sfq_drop(sch, &to_free); + if (!tail) + tail = to_free; + } + + rtnl_kfree_skbs(to_free, tail); qdisc_tree_reduce_backlog(sch, qlen - sch->q.qlen, dropped); del_timer(&q->perturb_timer); -- cgit v1.2.3 From 1ca163afb6fd569b6efdc221954177cba5a02cbc Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Sun, 27 Aug 2017 17:03:31 +0200 Subject: irda: move net/irda/ to drivers/staging/irda/net/ It's time to get rid of IRDA. It's long been broken, and no one seems to use it anymore. So move it to staging and after a while, we can delete it from there. To start, move the network irda core from net/irda to drivers/staging/irda/net/ Signed-off-by: Greg Kroah-Hartman Signed-off-by: David S. Miller --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/irda/net/Kconfig | 96 + drivers/staging/irda/net/Makefile | 15 + drivers/staging/irda/net/af_irda.c | 2695 ++++++++++++++++++++ drivers/staging/irda/net/discovery.c | 417 +++ drivers/staging/irda/net/ircomm/Kconfig | 12 + drivers/staging/irda/net/ircomm/Makefile | 8 + drivers/staging/irda/net/ircomm/ircomm_core.c | 563 ++++ drivers/staging/irda/net/ircomm/ircomm_event.c | 246 ++ drivers/staging/irda/net/ircomm/ircomm_lmp.c | 350 +++ drivers/staging/irda/net/ircomm/ircomm_param.c | 501 ++++ drivers/staging/irda/net/ircomm/ircomm_ttp.c | 350 +++ drivers/staging/irda/net/ircomm/ircomm_tty.c | 1329 ++++++++++ .../staging/irda/net/ircomm/ircomm_tty_attach.c | 987 +++++++ drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c | 291 +++ drivers/staging/irda/net/irda_device.c | 316 +++ drivers/staging/irda/net/iriap.c | 1085 ++++++++ drivers/staging/irda/net/iriap_event.c | 496 ++++ drivers/staging/irda/net/irias_object.c | 555 ++++ drivers/staging/irda/net/irlan/Kconfig | 14 + drivers/staging/irda/net/irlan/Makefile | 7 + drivers/staging/irda/net/irlan/irlan_client.c | 559 ++++ .../staging/irda/net/irlan/irlan_client_event.c | 511 ++++ drivers/staging/irda/net/irlan/irlan_common.c | 1176 +++++++++ drivers/staging/irda/net/irlan/irlan_eth.c | 340 +++ drivers/staging/irda/net/irlan/irlan_event.c | 60 + drivers/staging/irda/net/irlan/irlan_filter.c | 240 ++ drivers/staging/irda/net/irlan/irlan_provider.c | 408 +++ .../staging/irda/net/irlan/irlan_provider_event.c | 233 ++ drivers/staging/irda/net/irlap.c | 1207 +++++++++ drivers/staging/irda/net/irlap_event.c | 2316 +++++++++++++++++ drivers/staging/irda/net/irlap_frame.c | 1407 ++++++++++ drivers/staging/irda/net/irlmp.c | 1996 +++++++++++++++ drivers/staging/irda/net/irlmp_event.c | 886 +++++++ drivers/staging/irda/net/irlmp_frame.c | 476 ++++ drivers/staging/irda/net/irmod.c | 199 ++ drivers/staging/irda/net/irnet/Kconfig | 13 + drivers/staging/irda/net/irnet/Makefile | 7 + drivers/staging/irda/net/irnet/irnet.h | 522 ++++ drivers/staging/irda/net/irnet/irnet_irda.c | 1885 ++++++++++++++ drivers/staging/irda/net/irnet/irnet_irda.h | 178 ++ drivers/staging/irda/net/irnet/irnet_ppp.c | 1189 +++++++++ drivers/staging/irda/net/irnet/irnet_ppp.h | 116 + drivers/staging/irda/net/irnetlink.c | 162 ++ drivers/staging/irda/net/irproc.c | 96 + drivers/staging/irda/net/irqueue.c | 911 +++++++ drivers/staging/irda/net/irsysctl.c | 258 ++ drivers/staging/irda/net/irttp.c | 1891 ++++++++++++++ drivers/staging/irda/net/parameters.c | 584 +++++ drivers/staging/irda/net/qos.c | 771 ++++++ drivers/staging/irda/net/timer.c | 231 ++ drivers/staging/irda/net/wrapper.c | 492 ++++ net/Kconfig | 1 - net/Makefile | 1 - net/irda/Kconfig | 96 - net/irda/Makefile | 15 - net/irda/af_irda.c | 2695 -------------------- net/irda/discovery.c | 417 --- net/irda/ircomm/Kconfig | 12 - net/irda/ircomm/Makefile | 8 - net/irda/ircomm/ircomm_core.c | 563 ---- net/irda/ircomm/ircomm_event.c | 246 -- net/irda/ircomm/ircomm_lmp.c | 350 --- net/irda/ircomm/ircomm_param.c | 501 ---- net/irda/ircomm/ircomm_ttp.c | 350 --- net/irda/ircomm/ircomm_tty.c | 1329 ---------- net/irda/ircomm/ircomm_tty_attach.c | 987 ------- net/irda/ircomm/ircomm_tty_ioctl.c | 291 --- net/irda/irda_device.c | 316 --- net/irda/iriap.c | 1085 -------- net/irda/iriap_event.c | 496 ---- net/irda/irias_object.c | 555 ---- net/irda/irlan/Kconfig | 14 - net/irda/irlan/Makefile | 7 - net/irda/irlan/irlan_client.c | 559 ---- net/irda/irlan/irlan_client_event.c | 511 ---- net/irda/irlan/irlan_common.c | 1176 --------- net/irda/irlan/irlan_eth.c | 340 --- net/irda/irlan/irlan_event.c | 60 - net/irda/irlan/irlan_filter.c | 240 -- net/irda/irlan/irlan_provider.c | 408 --- net/irda/irlan/irlan_provider_event.c | 233 -- net/irda/irlap.c | 1207 --------- net/irda/irlap_event.c | 2316 ----------------- net/irda/irlap_frame.c | 1407 ---------- net/irda/irlmp.c | 1996 --------------- net/irda/irlmp_event.c | 886 ------- net/irda/irlmp_frame.c | 476 ---- net/irda/irmod.c | 199 -- net/irda/irnet/Kconfig | 13 - net/irda/irnet/Makefile | 7 - net/irda/irnet/irnet.h | 522 ---- net/irda/irnet/irnet_irda.c | 1885 -------------- net/irda/irnet/irnet_irda.h | 178 -- net/irda/irnet/irnet_ppp.c | 1189 --------- net/irda/irnet/irnet_ppp.h | 116 - net/irda/irnetlink.c | 162 -- net/irda/irproc.c | 96 - net/irda/irqueue.c | 911 ------- net/irda/irsysctl.c | 258 -- net/irda/irttp.c | 1891 -------------- net/irda/parameters.c | 584 ----- net/irda/qos.c | 771 ------ net/irda/timer.c | 231 -- net/irda/wrapper.c | 492 ---- 106 files changed, 31656 insertions(+), 31655 deletions(-) create mode 100644 drivers/staging/irda/net/Kconfig create mode 100644 drivers/staging/irda/net/Makefile create mode 100644 drivers/staging/irda/net/af_irda.c create mode 100644 drivers/staging/irda/net/discovery.c create mode 100644 drivers/staging/irda/net/ircomm/Kconfig create mode 100644 drivers/staging/irda/net/ircomm/Makefile create mode 100644 drivers/staging/irda/net/ircomm/ircomm_core.c create mode 100644 drivers/staging/irda/net/ircomm/ircomm_event.c create mode 100644 drivers/staging/irda/net/ircomm/ircomm_lmp.c create mode 100644 drivers/staging/irda/net/ircomm/ircomm_param.c create mode 100644 drivers/staging/irda/net/ircomm/ircomm_ttp.c create mode 100644 drivers/staging/irda/net/ircomm/ircomm_tty.c create mode 100644 drivers/staging/irda/net/ircomm/ircomm_tty_attach.c create mode 100644 drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c create mode 100644 drivers/staging/irda/net/irda_device.c create mode 100644 drivers/staging/irda/net/iriap.c create mode 100644 drivers/staging/irda/net/iriap_event.c create mode 100644 drivers/staging/irda/net/irias_object.c create mode 100644 drivers/staging/irda/net/irlan/Kconfig create mode 100644 drivers/staging/irda/net/irlan/Makefile create mode 100644 drivers/staging/irda/net/irlan/irlan_client.c create mode 100644 drivers/staging/irda/net/irlan/irlan_client_event.c create mode 100644 drivers/staging/irda/net/irlan/irlan_common.c create mode 100644 drivers/staging/irda/net/irlan/irlan_eth.c create mode 100644 drivers/staging/irda/net/irlan/irlan_event.c create mode 100644 drivers/staging/irda/net/irlan/irlan_filter.c create mode 100644 drivers/staging/irda/net/irlan/irlan_provider.c create mode 100644 drivers/staging/irda/net/irlan/irlan_provider_event.c create mode 100644 drivers/staging/irda/net/irlap.c create mode 100644 drivers/staging/irda/net/irlap_event.c create mode 100644 drivers/staging/irda/net/irlap_frame.c create mode 100644 drivers/staging/irda/net/irlmp.c create mode 100644 drivers/staging/irda/net/irlmp_event.c create mode 100644 drivers/staging/irda/net/irlmp_frame.c create mode 100644 drivers/staging/irda/net/irmod.c create mode 100644 drivers/staging/irda/net/irnet/Kconfig create mode 100644 drivers/staging/irda/net/irnet/Makefile create mode 100644 drivers/staging/irda/net/irnet/irnet.h create mode 100644 drivers/staging/irda/net/irnet/irnet_irda.c create mode 100644 drivers/staging/irda/net/irnet/irnet_irda.h create mode 100644 drivers/staging/irda/net/irnet/irnet_ppp.c create mode 100644 drivers/staging/irda/net/irnet/irnet_ppp.h create mode 100644 drivers/staging/irda/net/irnetlink.c create mode 100644 drivers/staging/irda/net/irproc.c create mode 100644 drivers/staging/irda/net/irqueue.c create mode 100644 drivers/staging/irda/net/irsysctl.c create mode 100644 drivers/staging/irda/net/irttp.c create mode 100644 drivers/staging/irda/net/parameters.c create mode 100644 drivers/staging/irda/net/qos.c create mode 100644 drivers/staging/irda/net/timer.c create mode 100644 drivers/staging/irda/net/wrapper.c delete mode 100644 net/irda/Kconfig delete mode 100644 net/irda/Makefile delete mode 100644 net/irda/af_irda.c delete mode 100644 net/irda/discovery.c delete mode 100644 net/irda/ircomm/Kconfig delete mode 100644 net/irda/ircomm/Makefile delete mode 100644 net/irda/ircomm/ircomm_core.c delete mode 100644 net/irda/ircomm/ircomm_event.c delete mode 100644 net/irda/ircomm/ircomm_lmp.c delete mode 100644 net/irda/ircomm/ircomm_param.c delete mode 100644 net/irda/ircomm/ircomm_ttp.c delete mode 100644 net/irda/ircomm/ircomm_tty.c delete mode 100644 net/irda/ircomm/ircomm_tty_attach.c delete mode 100644 net/irda/ircomm/ircomm_tty_ioctl.c delete mode 100644 net/irda/irda_device.c delete mode 100644 net/irda/iriap.c delete mode 100644 net/irda/iriap_event.c delete mode 100644 net/irda/irias_object.c delete mode 100644 net/irda/irlan/Kconfig delete mode 100644 net/irda/irlan/Makefile delete mode 100644 net/irda/irlan/irlan_client.c delete mode 100644 net/irda/irlan/irlan_client_event.c delete mode 100644 net/irda/irlan/irlan_common.c delete mode 100644 net/irda/irlan/irlan_eth.c delete mode 100644 net/irda/irlan/irlan_event.c delete mode 100644 net/irda/irlan/irlan_filter.c delete mode 100644 net/irda/irlan/irlan_provider.c delete mode 100644 net/irda/irlan/irlan_provider_event.c delete mode 100644 net/irda/irlap.c delete mode 100644 net/irda/irlap_event.c delete mode 100644 net/irda/irlap_frame.c delete mode 100644 net/irda/irlmp.c delete mode 100644 net/irda/irlmp_event.c delete mode 100644 net/irda/irlmp_frame.c delete mode 100644 net/irda/irmod.c delete mode 100644 net/irda/irnet/Kconfig delete mode 100644 net/irda/irnet/Makefile delete mode 100644 net/irda/irnet/irnet.h delete mode 100644 net/irda/irnet/irnet_irda.c delete mode 100644 net/irda/irnet/irnet_irda.h delete mode 100644 net/irda/irnet/irnet_ppp.c delete mode 100644 net/irda/irnet/irnet_ppp.h delete mode 100644 net/irda/irnetlink.c delete mode 100644 net/irda/irproc.c delete mode 100644 net/irda/irqueue.c delete mode 100644 net/irda/irsysctl.c delete mode 100644 net/irda/irttp.c delete mode 100644 net/irda/parameters.c delete mode 100644 net/irda/qos.c delete mode 100644 net/irda/timer.c delete mode 100644 net/irda/wrapper.c (limited to 'net') diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index ef28a1cb64ae..0c5086d878c6 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -24,6 +24,8 @@ menuconfig STAGING if STAGING +source "drivers/staging/irda/net/Kconfig" + source "drivers/staging/wlan-ng/Kconfig" source "drivers/staging/comedi/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 2918580bdb9e..509fbacf9a7e 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -2,6 +2,7 @@ obj-y += media/ obj-y += typec/ +obj-$(CONFIG_IRDA) += irda/net/ obj-$(CONFIG_PRISM2_USB) += wlan-ng/ obj-$(CONFIG_COMEDI) += comedi/ obj-$(CONFIG_FB_OLPC_DCON) += olpc_dcon/ diff --git a/drivers/staging/irda/net/Kconfig b/drivers/staging/irda/net/Kconfig new file mode 100644 index 000000000000..cd775e1ea0e9 --- /dev/null +++ b/drivers/staging/irda/net/Kconfig @@ -0,0 +1,96 @@ +# +# IrDA protocol configuration +# + +menuconfig IRDA + depends on NET && !S390 + tristate "IrDA (infrared) subsystem support" + select CRC_CCITT + ---help--- + Say Y here if you want to build support for the IrDA (TM) protocols. + The Infrared Data Associations (tm) specifies standards for wireless + infrared communication and is supported by most laptops and PDA's. + + To use Linux support for the IrDA (tm) protocols, you will also need + some user-space utilities like irattach. For more information, see + the file . You also want to + read the IR-HOWTO, available at + . + + If you want to exchange bits of data (vCal, vCard) with a PDA, you + will need to install some OBEX application, such as OpenObex : + + + To compile this support as a module, choose M here: the module will + be called irda. + +comment "IrDA protocols" + depends on IRDA + +source "drivers/staging/irda/net/irlan/Kconfig" + +source "drivers/staging/irda/net/irnet/Kconfig" + +source "drivers/staging/irda/net/ircomm/Kconfig" + +config IRDA_ULTRA + bool "Ultra (connectionless) protocol" + depends on IRDA + help + Say Y here to support the connectionless Ultra IRDA protocol. + Ultra allows to exchange data over IrDA with really simple devices + (watch, beacon) without the overhead of the IrDA protocol (no handshaking, + no management frames, simple fixed header). + Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1); + +comment "IrDA options" + depends on IRDA + +config IRDA_CACHE_LAST_LSAP + bool "Cache last LSAP" + depends on IRDA + help + Say Y here if you want IrLMP to cache the last LSAP used. This + makes sense since most frames will be sent/received on the same + connection. Enabling this option will save a hash-lookup per frame. + + If unsure, say Y. + +config IRDA_FAST_RR + bool "Fast RRs (low latency)" + depends on IRDA + ---help--- + Say Y here is you want IrLAP to send fast RR (Receive Ready) frames + when acting as a primary station. + Disabling this option will make latency over IrDA very bad. Enabling + this option will make the IrDA stack send more packet than strictly + necessary, thus reduce your battery life (but not that much). + + Fast RR will make IrLAP send out a RR frame immediately when + receiving a frame if its own transmit queue is currently empty. This + will give a lot of speed improvement when receiving much data since + the secondary station will not have to wait the max. turn around + time (usually 500ms) before it is allowed to transmit the next time. + If the transmit queue of the secondary is also empty, the primary will + start backing-off before sending another RR frame, waiting longer + each time until the back-off reaches the max. turn around time. + This back-off increase in controlled via + /proc/sys/net/irda/fast_poll_increase + + If unsure, say Y. + +config IRDA_DEBUG + bool "Debug information" + depends on IRDA + help + Say Y here if you want the IrDA subsystem to write debug information + to your syslog. You can change the debug level in + /proc/sys/net/irda/debug . + When this option is enabled, the IrDA also perform many extra internal + verifications which will usually prevent the kernel to crash in case of + bugs. + + If unsure, say Y (since it makes it easier to find the bugs). + +source "drivers/net/irda/Kconfig" + diff --git a/drivers/staging/irda/net/Makefile b/drivers/staging/irda/net/Makefile new file mode 100644 index 000000000000..187f6c563a4b --- /dev/null +++ b/drivers/staging/irda/net/Makefile @@ -0,0 +1,15 @@ +# +# Makefile for the Linux IrDA protocol layer. +# + +obj-$(CONFIG_IRDA) += irda.o +obj-$(CONFIG_IRLAN) += irlan/ +obj-$(CONFIG_IRNET) += irnet/ +obj-$(CONFIG_IRCOMM) += ircomm/ + +irda-y := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \ + irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \ + irttp.o irda_device.o irias_object.o wrapper.o af_irda.o \ + discovery.o parameters.o irnetlink.o irmod.o +irda-$(CONFIG_PROC_FS) += irproc.o +irda-$(CONFIG_SYSCTL) += irsysctl.o diff --git a/drivers/staging/irda/net/af_irda.c b/drivers/staging/irda/net/af_irda.c new file mode 100644 index 000000000000..23fa7c8b09a5 --- /dev/null +++ b/drivers/staging/irda/net/af_irda.c @@ -0,0 +1,2695 @@ +/********************************************************************* + * + * Filename: af_irda.c + * Version: 0.9 + * Description: IrDA sockets implementation + * Status: Stable + * Author: Dag Brattli + * Created at: Sun May 31 10:12:43 1998 + * Modified at: Sat Dec 25 21:10:23 1999 + * Modified by: Dag Brattli + * Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc. + * + * Copyright (c) 1999 Dag Brattli + * Copyright (c) 1999-2003 Jean Tourrilhes + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + * Linux-IrDA now supports four different types of IrDA sockets: + * + * o SOCK_STREAM: TinyTP connections with SAR disabled. The + * max SDU size is 0 for conn. of this type + * o SOCK_SEQPACKET: TinyTP connections with SAR enabled. TTP may + * fragment the messages, but will preserve + * the message boundaries + * o SOCK_DGRAM: IRDAPROTO_UNITDATA: TinyTP connections with Unitdata + * (unreliable) transfers + * IRDAPROTO_ULTRA: Connectionless and unreliable data + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* TIOCOUTQ, TIOCINQ */ +#include + +#include +#include + +#include + +static int irda_create(struct net *net, struct socket *sock, int protocol, int kern); + +static const struct proto_ops irda_stream_ops; +static const struct proto_ops irda_seqpacket_ops; +static const struct proto_ops irda_dgram_ops; + +#ifdef CONFIG_IRDA_ULTRA +static const struct proto_ops irda_ultra_ops; +#define ULTRA_MAX_DATA 382 +#endif /* CONFIG_IRDA_ULTRA */ + +#define IRDA_MAX_HEADER (TTP_MAX_HEADER) + +/* + * Function irda_data_indication (instance, sap, skb) + * + * Received some data from TinyTP. Just queue it on the receive queue + * + */ +static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb) +{ + struct irda_sock *self; + struct sock *sk; + int err; + + self = instance; + sk = instance; + + err = sock_queue_rcv_skb(sk, skb); + if (err) { + pr_debug("%s(), error: no more mem!\n", __func__); + self->rx_flow = FLOW_STOP; + + /* When we return error, TTP will need to requeue the skb */ + return err; + } + + return 0; +} + +/* + * Function irda_disconnect_indication (instance, sap, reason, skb) + * + * Connection has been closed. Check reason to find out why + * + */ +static void irda_disconnect_indication(void *instance, void *sap, + LM_REASON reason, struct sk_buff *skb) +{ + struct irda_sock *self; + struct sock *sk; + + self = instance; + + pr_debug("%s(%p)\n", __func__, self); + + /* Don't care about it, but let's not leak it */ + if(skb) + dev_kfree_skb(skb); + + sk = instance; + if (sk == NULL) { + pr_debug("%s(%p) : BUG : sk is NULL\n", + __func__, self); + return; + } + + /* Prevent race conditions with irda_release() and irda_shutdown() */ + bh_lock_sock(sk); + if (!sock_flag(sk, SOCK_DEAD) && sk->sk_state != TCP_CLOSE) { + sk->sk_state = TCP_CLOSE; + sk->sk_shutdown |= SEND_SHUTDOWN; + + sk->sk_state_change(sk); + + /* Close our TSAP. + * If we leave it open, IrLMP put it back into the list of + * unconnected LSAPs. The problem is that any incoming request + * can then be matched to this socket (and it will be, because + * it is at the head of the list). This would prevent any + * listening socket waiting on the same TSAP to get those + * requests. Some apps forget to close sockets, or hang to it + * a bit too long, so we may stay in this dead state long + * enough to be noticed... + * Note : all socket function do check sk->sk_state, so we are + * safe... + * Jean II + */ + if (self->tsap) { + irttp_close_tsap(self->tsap); + self->tsap = NULL; + } + } + bh_unlock_sock(sk); + + /* Note : once we are there, there is not much you want to do + * with the socket anymore, apart from closing it. + * For example, bind() and connect() won't reset sk->sk_err, + * sk->sk_shutdown and sk->sk_flags to valid values... + * Jean II + */ +} + +/* + * Function irda_connect_confirm (instance, sap, qos, max_sdu_size, skb) + * + * Connections has been confirmed by the remote device + * + */ +static void irda_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, __u8 max_header_size, + struct sk_buff *skb) +{ + struct irda_sock *self; + struct sock *sk; + + self = instance; + + pr_debug("%s(%p)\n", __func__, self); + + sk = instance; + if (sk == NULL) { + dev_kfree_skb(skb); + return; + } + + dev_kfree_skb(skb); + // Should be ??? skb_queue_tail(&sk->sk_receive_queue, skb); + + /* How much header space do we need to reserve */ + self->max_header_size = max_header_size; + + /* IrTTP max SDU size in transmit direction */ + self->max_sdu_size_tx = max_sdu_size; + + /* Find out what the largest chunk of data that we can transmit is */ + switch (sk->sk_type) { + case SOCK_STREAM: + if (max_sdu_size != 0) { + net_err_ratelimited("%s: max_sdu_size must be 0\n", + __func__); + return; + } + self->max_data_size = irttp_get_max_seg_size(self->tsap); + break; + case SOCK_SEQPACKET: + if (max_sdu_size == 0) { + net_err_ratelimited("%s: max_sdu_size cannot be 0\n", + __func__); + return; + } + self->max_data_size = max_sdu_size; + break; + default: + self->max_data_size = irttp_get_max_seg_size(self->tsap); + } + + pr_debug("%s(), max_data_size=%d\n", __func__, + self->max_data_size); + + memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); + + /* We are now connected! */ + sk->sk_state = TCP_ESTABLISHED; + sk->sk_state_change(sk); +} + +/* + * Function irda_connect_indication(instance, sap, qos, max_sdu_size, userdata) + * + * Incoming connection + * + */ +static void irda_connect_indication(void *instance, void *sap, + struct qos_info *qos, __u32 max_sdu_size, + __u8 max_header_size, struct sk_buff *skb) +{ + struct irda_sock *self; + struct sock *sk; + + self = instance; + + pr_debug("%s(%p)\n", __func__, self); + + sk = instance; + if (sk == NULL) { + dev_kfree_skb(skb); + return; + } + + /* How much header space do we need to reserve */ + self->max_header_size = max_header_size; + + /* IrTTP max SDU size in transmit direction */ + self->max_sdu_size_tx = max_sdu_size; + + /* Find out what the largest chunk of data that we can transmit is */ + switch (sk->sk_type) { + case SOCK_STREAM: + if (max_sdu_size != 0) { + net_err_ratelimited("%s: max_sdu_size must be 0\n", + __func__); + kfree_skb(skb); + return; + } + self->max_data_size = irttp_get_max_seg_size(self->tsap); + break; + case SOCK_SEQPACKET: + if (max_sdu_size == 0) { + net_err_ratelimited("%s: max_sdu_size cannot be 0\n", + __func__); + kfree_skb(skb); + return; + } + self->max_data_size = max_sdu_size; + break; + default: + self->max_data_size = irttp_get_max_seg_size(self->tsap); + } + + pr_debug("%s(), max_data_size=%d\n", __func__, + self->max_data_size); + + memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); + + skb_queue_tail(&sk->sk_receive_queue, skb); + sk->sk_state_change(sk); +} + +/* + * Function irda_connect_response (handle) + * + * Accept incoming connection + * + */ +static void irda_connect_response(struct irda_sock *self) +{ + struct sk_buff *skb; + + skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_KERNEL); + if (skb == NULL) { + pr_debug("%s() Unable to allocate sk_buff!\n", + __func__); + return; + } + + /* Reserve space for MUX_CONTROL and LAP header */ + skb_reserve(skb, IRDA_MAX_HEADER); + + irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb); +} + +/* + * Function irda_flow_indication (instance, sap, flow) + * + * Used by TinyTP to tell us if it can accept more data or not + * + */ +static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) +{ + struct irda_sock *self; + struct sock *sk; + + self = instance; + sk = instance; + BUG_ON(sk == NULL); + + switch (flow) { + case FLOW_STOP: + pr_debug("%s(), IrTTP wants us to slow down\n", + __func__); + self->tx_flow = flow; + break; + case FLOW_START: + self->tx_flow = flow; + pr_debug("%s(), IrTTP wants us to start again\n", + __func__); + wake_up_interruptible(sk_sleep(sk)); + break; + default: + pr_debug("%s(), Unknown flow command!\n", __func__); + /* Unknown flow command, better stop */ + self->tx_flow = flow; + break; + } +} + +/* + * Function irda_getvalue_confirm (obj_id, value, priv) + * + * Got answer from remote LM-IAS, just pass object to requester... + * + * Note : duplicate from above, but we need our own version that + * doesn't touch the dtsap_sel and save the full value structure... + */ +static void irda_getvalue_confirm(int result, __u16 obj_id, + struct ias_value *value, void *priv) +{ + struct irda_sock *self; + + self = priv; + if (!self) { + net_warn_ratelimited("%s: lost myself!\n", __func__); + return; + } + + pr_debug("%s(%p)\n", __func__, self); + + /* We probably don't need to make any more queries */ + iriap_close(self->iriap); + self->iriap = NULL; + + /* Check if request succeeded */ + if (result != IAS_SUCCESS) { + pr_debug("%s(), IAS query failed! (%d)\n", __func__, + result); + + self->errno = result; /* We really need it later */ + + /* Wake up any processes waiting for result */ + wake_up_interruptible(&self->query_wait); + + return; + } + + /* Pass the object to the caller (so the caller must delete it) */ + self->ias_result = value; + self->errno = 0; + + /* Wake up any processes waiting for result */ + wake_up_interruptible(&self->query_wait); +} + +/* + * Function irda_selective_discovery_indication (discovery) + * + * Got a selective discovery indication from IrLMP. + * + * IrLMP is telling us that this node is new and matching our hint bit + * filter. Wake up any process waiting for answer... + */ +static void irda_selective_discovery_indication(discinfo_t *discovery, + DISCOVERY_MODE mode, + void *priv) +{ + struct irda_sock *self; + + self = priv; + if (!self) { + net_warn_ratelimited("%s: lost myself!\n", __func__); + return; + } + + /* Pass parameter to the caller */ + self->cachedaddr = discovery->daddr; + + /* Wake up process if its waiting for device to be discovered */ + wake_up_interruptible(&self->query_wait); +} + +/* + * Function irda_discovery_timeout (priv) + * + * Timeout in the selective discovery process + * + * We were waiting for a node to be discovered, but nothing has come up + * so far. Wake up the user and tell him that we failed... + */ +static void irda_discovery_timeout(u_long priv) +{ + struct irda_sock *self; + + self = (struct irda_sock *) priv; + BUG_ON(self == NULL); + + /* Nothing for the caller */ + self->cachelog = NULL; + self->cachedaddr = 0; + self->errno = -ETIME; + + /* Wake up process if its still waiting... */ + wake_up_interruptible(&self->query_wait); +} + +/* + * Function irda_open_tsap (self) + * + * Open local Transport Service Access Point (TSAP) + * + */ +static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name) +{ + notify_t notify; + + if (self->tsap) { + pr_debug("%s: busy!\n", __func__); + return -EBUSY; + } + + /* Initialize callbacks to be used by the IrDA stack */ + irda_notify_init(¬ify); + notify.connect_confirm = irda_connect_confirm; + notify.connect_indication = irda_connect_indication; + notify.disconnect_indication = irda_disconnect_indication; + notify.data_indication = irda_data_indication; + notify.udata_indication = irda_data_indication; + notify.flow_indication = irda_flow_indication; + notify.instance = self; + strncpy(notify.name, name, NOTIFY_MAX_NAME); + + self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT, + ¬ify); + if (self->tsap == NULL) { + pr_debug("%s(), Unable to allocate TSAP!\n", + __func__); + return -ENOMEM; + } + /* Remember which TSAP selector we actually got */ + self->stsap_sel = self->tsap->stsap_sel; + + return 0; +} + +/* + * Function irda_open_lsap (self) + * + * Open local Link Service Access Point (LSAP). Used for opening Ultra + * sockets + */ +#ifdef CONFIG_IRDA_ULTRA +static int irda_open_lsap(struct irda_sock *self, int pid) +{ + notify_t notify; + + if (self->lsap) { + net_warn_ratelimited("%s(), busy!\n", __func__); + return -EBUSY; + } + + /* Initialize callbacks to be used by the IrDA stack */ + irda_notify_init(¬ify); + notify.udata_indication = irda_data_indication; + notify.instance = self; + strncpy(notify.name, "Ultra", NOTIFY_MAX_NAME); + + self->lsap = irlmp_open_lsap(LSAP_CONNLESS, ¬ify, pid); + if (self->lsap == NULL) { + pr_debug("%s(), Unable to allocate LSAP!\n", __func__); + return -ENOMEM; + } + + return 0; +} +#endif /* CONFIG_IRDA_ULTRA */ + +/* + * Function irda_find_lsap_sel (self, name) + * + * Try to lookup LSAP selector in remote LM-IAS + * + * Basically, we start a IAP query, and then go to sleep. When the query + * return, irda_getvalue_confirm will wake us up, and we can examine the + * result of the query... + * Note that in some case, the query fail even before we go to sleep, + * creating some races... + */ +static int irda_find_lsap_sel(struct irda_sock *self, char *name) +{ + pr_debug("%s(%p, %s)\n", __func__, self, name); + + if (self->iriap) { + net_warn_ratelimited("%s(): busy with a previous query\n", + __func__); + return -EBUSY; + } + + self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, + irda_getvalue_confirm); + if(self->iriap == NULL) + return -ENOMEM; + + /* Treat unexpected wakeup as disconnect */ + self->errno = -EHOSTUNREACH; + + /* Query remote LM-IAS */ + iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr, + name, "IrDA:TinyTP:LsapSel"); + + /* Wait for answer, if not yet finished (or failed) */ + if (wait_event_interruptible(self->query_wait, (self->iriap==NULL))) + /* Treat signals as disconnect */ + return -EHOSTUNREACH; + + /* Check what happened */ + if (self->errno) + { + /* Requested object/attribute doesn't exist */ + if((self->errno == IAS_CLASS_UNKNOWN) || + (self->errno == IAS_ATTRIB_UNKNOWN)) + return -EADDRNOTAVAIL; + else + return -EHOSTUNREACH; + } + + /* Get the remote TSAP selector */ + switch (self->ias_result->type) { + case IAS_INTEGER: + pr_debug("%s() int=%d\n", + __func__, self->ias_result->t.integer); + + if (self->ias_result->t.integer != -1) + self->dtsap_sel = self->ias_result->t.integer; + else + self->dtsap_sel = 0; + break; + default: + self->dtsap_sel = 0; + pr_debug("%s(), bad type!\n", __func__); + break; + } + if (self->ias_result) + irias_delete_value(self->ias_result); + + if (self->dtsap_sel) + return 0; + + return -EADDRNOTAVAIL; +} + +/* + * Function irda_discover_daddr_and_lsap_sel (self, name) + * + * This try to find a device with the requested service. + * + * It basically look into the discovery log. For each address in the list, + * it queries the LM-IAS of the device to find if this device offer + * the requested service. + * If there is more than one node supporting the service, we complain + * to the user (it should move devices around). + * The, we set both the destination address and the lsap selector to point + * on the service on the unique device we have found. + * + * Note : this function fails if there is more than one device in range, + * because IrLMP doesn't disconnect the LAP when the last LSAP is closed. + * Moreover, we would need to wait the LAP disconnection... + */ +static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) +{ + discinfo_t *discoveries; /* Copy of the discovery log */ + int number; /* Number of nodes in the log */ + int i; + int err = -ENETUNREACH; + __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */ + __u8 dtsap_sel = 0x0; /* TSAP associated with it */ + + pr_debug("%s(), name=%s\n", __func__, name); + + /* Ask lmp for the current discovery log + * Note : we have to use irlmp_get_discoveries(), as opposed + * to play with the cachelog directly, because while we are + * making our ias query, le log might change... */ + discoveries = irlmp_get_discoveries(&number, self->mask.word, + self->nslots); + /* Check if the we got some results */ + if (discoveries == NULL) + return -ENETUNREACH; /* No nodes discovered */ + + /* + * Now, check all discovered devices (if any), and connect + * client only about the services that the client is + * interested in... + */ + for(i = 0; i < number; i++) { + /* Try the address in the log */ + self->daddr = discoveries[i].daddr; + self->saddr = 0x0; + pr_debug("%s(), trying daddr = %08x\n", + __func__, self->daddr); + + /* Query remote LM-IAS for this service */ + err = irda_find_lsap_sel(self, name); + switch (err) { + case 0: + /* We found the requested service */ + if(daddr != DEV_ADDR_ANY) { + pr_debug("%s(), discovered service ''%s'' in two different devices !!!\n", + __func__, name); + self->daddr = DEV_ADDR_ANY; + kfree(discoveries); + return -ENOTUNIQ; + } + /* First time we found that one, save it ! */ + daddr = self->daddr; + dtsap_sel = self->dtsap_sel; + break; + case -EADDRNOTAVAIL: + /* Requested service simply doesn't exist on this node */ + break; + default: + /* Something bad did happen :-( */ + pr_debug("%s(), unexpected IAS query failure\n", + __func__); + self->daddr = DEV_ADDR_ANY; + kfree(discoveries); + return -EHOSTUNREACH; + } + } + /* Cleanup our copy of the discovery log */ + kfree(discoveries); + + /* Check out what we found */ + if(daddr == DEV_ADDR_ANY) { + pr_debug("%s(), cannot discover service ''%s'' in any device !!!\n", + __func__, name); + self->daddr = DEV_ADDR_ANY; + return -EADDRNOTAVAIL; + } + + /* Revert back to discovered device & service */ + self->daddr = daddr; + self->saddr = 0x0; + self->dtsap_sel = dtsap_sel; + + pr_debug("%s(), discovered requested service ''%s'' at address %08x\n", + __func__, name, self->daddr); + + return 0; +} + +/* + * Function irda_getname (sock, uaddr, uaddr_len, peer) + * + * Return the our own, or peers socket address (sockaddr_irda) + * + */ +static int irda_getname(struct socket *sock, struct sockaddr *uaddr, + int *uaddr_len, int peer) +{ + struct sockaddr_irda saddr; + struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); + + memset(&saddr, 0, sizeof(saddr)); + if (peer) { + if (sk->sk_state != TCP_ESTABLISHED) + return -ENOTCONN; + + saddr.sir_family = AF_IRDA; + saddr.sir_lsap_sel = self->dtsap_sel; + saddr.sir_addr = self->daddr; + } else { + saddr.sir_family = AF_IRDA; + saddr.sir_lsap_sel = self->stsap_sel; + saddr.sir_addr = self->saddr; + } + + pr_debug("%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel); + pr_debug("%s(), addr = %08x\n", __func__, saddr.sir_addr); + + /* uaddr_len come to us uninitialised */ + *uaddr_len = sizeof (struct sockaddr_irda); + memcpy(uaddr, &saddr, *uaddr_len); + + return 0; +} + +/* + * Function irda_listen (sock, backlog) + * + * Just move to the listen state + * + */ +static int irda_listen(struct socket *sock, int backlog) +{ + struct sock *sk = sock->sk; + int err = -EOPNOTSUPP; + + lock_sock(sk); + + if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) && + (sk->sk_type != SOCK_DGRAM)) + goto out; + + if (sk->sk_state != TCP_LISTEN) { + sk->sk_max_ack_backlog = backlog; + sk->sk_state = TCP_LISTEN; + + err = 0; + } +out: + release_sock(sk); + + return err; +} + +/* + * Function irda_bind (sock, uaddr, addr_len) + * + * Used by servers to register their well known TSAP + * + */ +static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) +{ + struct sock *sk = sock->sk; + struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; + struct irda_sock *self = irda_sk(sk); + int err; + + pr_debug("%s(%p)\n", __func__, self); + + if (addr_len != sizeof(struct sockaddr_irda)) + return -EINVAL; + + lock_sock(sk); +#ifdef CONFIG_IRDA_ULTRA + /* Special care for Ultra sockets */ + if ((sk->sk_type == SOCK_DGRAM) && + (sk->sk_protocol == IRDAPROTO_ULTRA)) { + self->pid = addr->sir_lsap_sel; + err = -EOPNOTSUPP; + if (self->pid & 0x80) { + pr_debug("%s(), extension in PID not supp!\n", + __func__); + goto out; + } + err = irda_open_lsap(self, self->pid); + if (err < 0) + goto out; + + /* Pretend we are connected */ + sock->state = SS_CONNECTED; + sk->sk_state = TCP_ESTABLISHED; + err = 0; + + goto out; + } +#endif /* CONFIG_IRDA_ULTRA */ + + self->ias_obj = irias_new_object(addr->sir_name, jiffies); + err = -ENOMEM; + if (self->ias_obj == NULL) + goto out; + + err = irda_open_tsap(self, addr->sir_lsap_sel, addr->sir_name); + if (err < 0) { + irias_delete_object(self->ias_obj); + self->ias_obj = NULL; + goto out; + } + + /* Register with LM-IAS */ + irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel", + self->stsap_sel, IAS_KERNEL_ATTR); + irias_insert_object(self->ias_obj); + + err = 0; +out: + release_sock(sk); + return err; +} + +/* + * Function irda_accept (sock, newsock, flags) + * + * Wait for incoming connection + * + */ +static int irda_accept(struct socket *sock, struct socket *newsock, int flags, + bool kern) +{ + struct sock *sk = sock->sk; + struct irda_sock *new, *self = irda_sk(sk); + struct sock *newsk; + struct sk_buff *skb = NULL; + int err; + + err = irda_create(sock_net(sk), newsock, sk->sk_protocol, kern); + if (err) + return err; + + err = -EINVAL; + + lock_sock(sk); + if (sock->state != SS_UNCONNECTED) + goto out; + + err = -EOPNOTSUPP; + if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) && + (sk->sk_type != SOCK_DGRAM)) + goto out; + + err = -EINVAL; + if (sk->sk_state != TCP_LISTEN) + goto out; + + /* + * The read queue this time is holding sockets ready to use + * hooked into the SABM we saved + */ + + /* + * We can perform the accept only if there is incoming data + * on the listening socket. + * So, we will block the caller until we receive any data. + * If the caller was waiting on select() or poll() before + * calling us, the data is waiting for us ;-) + * Jean II + */ + while (1) { + skb = skb_dequeue(&sk->sk_receive_queue); + if (skb) + break; + + /* Non blocking operation */ + err = -EWOULDBLOCK; + if (flags & O_NONBLOCK) + goto out; + + err = wait_event_interruptible(*(sk_sleep(sk)), + skb_peek(&sk->sk_receive_queue)); + if (err) + goto out; + } + + newsk = newsock->sk; + err = -EIO; + if (newsk == NULL) + goto out; + + newsk->sk_state = TCP_ESTABLISHED; + + new = irda_sk(newsk); + + /* Now attach up the new socket */ + new->tsap = irttp_dup(self->tsap, new); + err = -EPERM; /* value does not seem to make sense. -arnd */ + if (!new->tsap) { + pr_debug("%s(), dup failed!\n", __func__); + goto out; + } + + new->stsap_sel = new->tsap->stsap_sel; + new->dtsap_sel = new->tsap->dtsap_sel; + new->saddr = irttp_get_saddr(new->tsap); + new->daddr = irttp_get_daddr(new->tsap); + + new->max_sdu_size_tx = self->max_sdu_size_tx; + new->max_sdu_size_rx = self->max_sdu_size_rx; + new->max_data_size = self->max_data_size; + new->max_header_size = self->max_header_size; + + memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info)); + + /* Clean up the original one to keep it in listen state */ + irttp_listen(self->tsap); + + sk->sk_ack_backlog--; + + newsock->state = SS_CONNECTED; + + irda_connect_response(new); + err = 0; +out: + kfree_skb(skb); + release_sock(sk); + return err; +} + +/* + * Function irda_connect (sock, uaddr, addr_len, flags) + * + * Connect to a IrDA device + * + * The main difference with a "standard" connect is that with IrDA we need + * to resolve the service name into a TSAP selector (in TCP, port number + * doesn't have to be resolved). + * Because of this service name resolution, we can offer "auto-connect", + * where we connect to a service without specifying a destination address. + * + * Note : by consulting "errno", the user space caller may learn the cause + * of the failure. Most of them are visible in the function, others may come + * from subroutines called and are listed here : + * o EBUSY : already processing a connect + * o EHOSTUNREACH : bad addr->sir_addr argument + * o EADDRNOTAVAIL : bad addr->sir_name argument + * o ENOTUNIQ : more than one node has addr->sir_name (auto-connect) + * o ENETUNREACH : no node found on the network (auto-connect) + */ +static int irda_connect(struct socket *sock, struct sockaddr *uaddr, + int addr_len, int flags) +{ + struct sock *sk = sock->sk; + struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; + struct irda_sock *self = irda_sk(sk); + int err; + + pr_debug("%s(%p)\n", __func__, self); + + lock_sock(sk); + /* Don't allow connect for Ultra sockets */ + err = -ESOCKTNOSUPPORT; + if ((sk->sk_type == SOCK_DGRAM) && (sk->sk_protocol == IRDAPROTO_ULTRA)) + goto out; + + if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { + sock->state = SS_CONNECTED; + err = 0; + goto out; /* Connect completed during a ERESTARTSYS event */ + } + + if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { + sock->state = SS_UNCONNECTED; + err = -ECONNREFUSED; + goto out; + } + + err = -EISCONN; /* No reconnect on a seqpacket socket */ + if (sk->sk_state == TCP_ESTABLISHED) + goto out; + + sk->sk_state = TCP_CLOSE; + sock->state = SS_UNCONNECTED; + + err = -EINVAL; + if (addr_len != sizeof(struct sockaddr_irda)) + goto out; + + /* Check if user supplied any destination device address */ + if ((!addr->sir_addr) || (addr->sir_addr == DEV_ADDR_ANY)) { + /* Try to find one suitable */ + err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name); + if (err) { + pr_debug("%s(), auto-connect failed!\n", __func__); + goto out; + } + } else { + /* Use the one provided by the user */ + self->daddr = addr->sir_addr; + pr_debug("%s(), daddr = %08x\n", __func__, self->daddr); + + /* If we don't have a valid service name, we assume the + * user want to connect on a specific LSAP. Prevent + * the use of invalid LSAPs (IrLMP 1.1 p10). Jean II */ + if((addr->sir_name[0] != '\0') || + (addr->sir_lsap_sel >= 0x70)) { + /* Query remote LM-IAS using service name */ + err = irda_find_lsap_sel(self, addr->sir_name); + if (err) { + pr_debug("%s(), connect failed!\n", __func__); + goto out; + } + } else { + /* Directly connect to the remote LSAP + * specified by the sir_lsap field. + * Please use with caution, in IrDA LSAPs are + * dynamic and there is no "well-known" LSAP. */ + self->dtsap_sel = addr->sir_lsap_sel; + } + } + + /* Check if we have opened a local TSAP */ + if (!self->tsap) { + err = irda_open_tsap(self, LSAP_ANY, addr->sir_name); + if (err) + goto out; + } + + /* Move to connecting socket, start sending Connect Requests */ + sock->state = SS_CONNECTING; + sk->sk_state = TCP_SYN_SENT; + + /* Connect to remote device */ + err = irttp_connect_request(self->tsap, self->dtsap_sel, + self->saddr, self->daddr, NULL, + self->max_sdu_size_rx, NULL); + if (err) { + pr_debug("%s(), connect failed!\n", __func__); + goto out; + } + + /* Now the loop */ + err = -EINPROGRESS; + if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) + goto out; + + err = -ERESTARTSYS; + if (wait_event_interruptible(*(sk_sleep(sk)), + (sk->sk_state != TCP_SYN_SENT))) + goto out; + + if (sk->sk_state != TCP_ESTABLISHED) { + sock->state = SS_UNCONNECTED; + err = sock_error(sk); + if (!err) + err = -ECONNRESET; + goto out; + } + + sock->state = SS_CONNECTED; + + /* At this point, IrLMP has assigned our source address */ + self->saddr = irttp_get_saddr(self->tsap); + err = 0; +out: + release_sock(sk); + return err; +} + +static struct proto irda_proto = { + .name = "IRDA", + .owner = THIS_MODULE, + .obj_size = sizeof(struct irda_sock), +}; + +/* + * Function irda_create (sock, protocol) + * + * Create IrDA socket + * + */ +static int irda_create(struct net *net, struct socket *sock, int protocol, + int kern) +{ + struct sock *sk; + struct irda_sock *self; + + if (protocol < 0 || protocol > SK_PROTOCOL_MAX) + return -EINVAL; + + if (net != &init_net) + return -EAFNOSUPPORT; + + /* Check for valid socket type */ + switch (sock->type) { + case SOCK_STREAM: /* For TTP connections with SAR disabled */ + case SOCK_SEQPACKET: /* For TTP connections with SAR enabled */ + case SOCK_DGRAM: /* For TTP Unitdata or LMP Ultra transfers */ + break; + default: + return -ESOCKTNOSUPPORT; + } + + /* Allocate networking socket */ + sk = sk_alloc(net, PF_IRDA, GFP_KERNEL, &irda_proto, kern); + if (sk == NULL) + return -ENOMEM; + + self = irda_sk(sk); + pr_debug("%s() : self is %p\n", __func__, self); + + init_waitqueue_head(&self->query_wait); + + switch (sock->type) { + case SOCK_STREAM: + sock->ops = &irda_stream_ops; + self->max_sdu_size_rx = TTP_SAR_DISABLE; + break; + case SOCK_SEQPACKET: + sock->ops = &irda_seqpacket_ops; + self->max_sdu_size_rx = TTP_SAR_UNBOUND; + break; + case SOCK_DGRAM: + switch (protocol) { +#ifdef CONFIG_IRDA_ULTRA + case IRDAPROTO_ULTRA: + sock->ops = &irda_ultra_ops; + /* Initialise now, because we may send on unbound + * sockets. Jean II */ + self->max_data_size = ULTRA_MAX_DATA - LMP_PID_HEADER; + self->max_header_size = IRDA_MAX_HEADER + LMP_PID_HEADER; + break; +#endif /* CONFIG_IRDA_ULTRA */ + case IRDAPROTO_UNITDATA: + sock->ops = &irda_dgram_ops; + /* We let Unitdata conn. be like seqpack conn. */ + self->max_sdu_size_rx = TTP_SAR_UNBOUND; + break; + default: + sk_free(sk); + return -ESOCKTNOSUPPORT; + } + break; + default: + sk_free(sk); + return -ESOCKTNOSUPPORT; + } + + /* Initialise networking socket struct */ + sock_init_data(sock, sk); /* Note : set sk->sk_refcnt to 1 */ + sk->sk_family = PF_IRDA; + sk->sk_protocol = protocol; + + /* Register as a client with IrLMP */ + self->ckey = irlmp_register_client(0, NULL, NULL, NULL); + self->mask.word = 0xffff; + self->rx_flow = self->tx_flow = FLOW_START; + self->nslots = DISCOVERY_DEFAULT_SLOTS; + self->daddr = DEV_ADDR_ANY; /* Until we get connected */ + self->saddr = 0x0; /* so IrLMP assign us any link */ + return 0; +} + +/* + * Function irda_destroy_socket (self) + * + * Destroy socket + * + */ +static void irda_destroy_socket(struct irda_sock *self) +{ + pr_debug("%s(%p)\n", __func__, self); + + /* Unregister with IrLMP */ + irlmp_unregister_client(self->ckey); + irlmp_unregister_service(self->skey); + + /* Unregister with LM-IAS */ + if (self->ias_obj) { + irias_delete_object(self->ias_obj); + self->ias_obj = NULL; + } + + if (self->iriap) { + iriap_close(self->iriap); + self->iriap = NULL; + } + + if (self->tsap) { + irttp_disconnect_request(self->tsap, NULL, P_NORMAL); + irttp_close_tsap(self->tsap); + self->tsap = NULL; + } +#ifdef CONFIG_IRDA_ULTRA + if (self->lsap) { + irlmp_close_lsap(self->lsap); + self->lsap = NULL; + } +#endif /* CONFIG_IRDA_ULTRA */ +} + +/* + * Function irda_release (sock) + */ +static int irda_release(struct socket *sock) +{ + struct sock *sk = sock->sk; + + if (sk == NULL) + return 0; + + lock_sock(sk); + sk->sk_state = TCP_CLOSE; + sk->sk_shutdown |= SEND_SHUTDOWN; + sk->sk_state_change(sk); + + /* Destroy IrDA socket */ + irda_destroy_socket(irda_sk(sk)); + + sock_orphan(sk); + sock->sk = NULL; + release_sock(sk); + + /* Purge queues (see sock_init_data()) */ + skb_queue_purge(&sk->sk_receive_queue); + + /* Destroy networking socket if we are the last reference on it, + * i.e. if(sk->sk_refcnt == 0) -> sk_free(sk) */ + sock_put(sk); + + /* Notes on socket locking and deallocation... - Jean II + * In theory we should put pairs of sock_hold() / sock_put() to + * prevent the socket to be destroyed whenever there is an + * outstanding request or outstanding incoming packet or event. + * + * 1) This may include IAS request, both in connect and getsockopt. + * Unfortunately, the situation is a bit more messy than it looks, + * because we close iriap and kfree(self) above. + * + * 2) This may include selective discovery in getsockopt. + * Same stuff as above, irlmp registration and self are gone. + * + * Probably 1 and 2 may not matter, because it's all triggered + * by a process and the socket layer already prevent the + * socket to go away while a process is holding it, through + * sockfd_put() and fput()... + * + * 3) This may include deferred TSAP closure. In particular, + * we may receive a late irda_disconnect_indication() + * Fortunately, (tsap_cb *)->close_pend should protect us + * from that. + * + * I did some testing on SMP, and it looks solid. And the socket + * memory leak is now gone... - Jean II + */ + + return 0; +} + +/* + * Function irda_sendmsg (sock, msg, len) + * + * Send message down to TinyTP. This function is used for both STREAM and + * SEQPACK services. This is possible since it forces the client to + * fragment the message if necessary + */ +static int irda_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) +{ + struct sock *sk = sock->sk; + struct irda_sock *self; + struct sk_buff *skb; + int err = -EPIPE; + + pr_debug("%s(), len=%zd\n", __func__, len); + + /* Note : socket.c set MSG_EOR on SEQPACKET sockets */ + if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT | + MSG_NOSIGNAL)) { + return -EINVAL; + } + + lock_sock(sk); + + if (sk->sk_shutdown & SEND_SHUTDOWN) + goto out_err; + + if (sk->sk_state != TCP_ESTABLISHED) { + err = -ENOTCONN; + goto out; + } + + self = irda_sk(sk); + + /* Check if IrTTP is wants us to slow down */ + + if (wait_event_interruptible(*(sk_sleep(sk)), + (self->tx_flow != FLOW_STOP || sk->sk_state != TCP_ESTABLISHED))) { + err = -ERESTARTSYS; + goto out; + } + + /* Check if we are still connected */ + if (sk->sk_state != TCP_ESTABLISHED) { + err = -ENOTCONN; + goto out; + } + + /* Check that we don't send out too big frames */ + if (len > self->max_data_size) { + pr_debug("%s(), Chopping frame from %zd to %d bytes!\n", + __func__, len, self->max_data_size); + len = self->max_data_size; + } + + skb = sock_alloc_send_skb(sk, len + self->max_header_size + 16, + msg->msg_flags & MSG_DONTWAIT, &err); + if (!skb) + goto out_err; + + skb_reserve(skb, self->max_header_size + 16); + skb_reset_transport_header(skb); + skb_put(skb, len); + err = memcpy_from_msg(skb_transport_header(skb), msg, len); + if (err) { + kfree_skb(skb); + goto out_err; + } + + /* + * Just send the message to TinyTP, and let it deal with possible + * errors. No need to duplicate all that here + */ + err = irttp_data_request(self->tsap, skb); + if (err) { + pr_debug("%s(), err=%d\n", __func__, err); + goto out_err; + } + + release_sock(sk); + /* Tell client how much data we actually sent */ + return len; + +out_err: + err = sk_stream_error(sk, msg->msg_flags, err); +out: + release_sock(sk); + return err; + +} + +/* + * Function irda_recvmsg_dgram (sock, msg, size, flags) + * + * Try to receive message and copy it to user. The frame is discarded + * after being read, regardless of how much the user actually read + */ +static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg, + size_t size, int flags) +{ + struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); + struct sk_buff *skb; + size_t copied; + int err; + + skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, + flags & MSG_DONTWAIT, &err); + if (!skb) + return err; + + skb_reset_transport_header(skb); + copied = skb->len; + + if (copied > size) { + pr_debug("%s(), Received truncated frame (%zd < %zd)!\n", + __func__, copied, size); + copied = size; + msg->msg_flags |= MSG_TRUNC; + } + skb_copy_datagram_msg(skb, 0, msg, copied); + + skb_free_datagram(sk, skb); + + /* + * Check if we have previously stopped IrTTP and we know + * have more free space in our rx_queue. If so tell IrTTP + * to start delivering frames again before our rx_queue gets + * empty + */ + if (self->rx_flow == FLOW_STOP) { + if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) { + pr_debug("%s(), Starting IrTTP\n", __func__); + self->rx_flow = FLOW_START; + irttp_flow_request(self->tsap, FLOW_START); + } + } + + return copied; +} + +/* + * Function irda_recvmsg_stream (sock, msg, size, flags) + */ +static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg, + size_t size, int flags) +{ + struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); + int noblock = flags & MSG_DONTWAIT; + size_t copied = 0; + int target, err; + long timeo; + + if ((err = sock_error(sk)) < 0) + return err; + + if (sock->flags & __SO_ACCEPTCON) + return -EINVAL; + + err =-EOPNOTSUPP; + if (flags & MSG_OOB) + return -EOPNOTSUPP; + + err = 0; + target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); + timeo = sock_rcvtimeo(sk, noblock); + + do { + int chunk; + struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); + + if (skb == NULL) { + DEFINE_WAIT(wait); + err = 0; + + if (copied >= target) + break; + + prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); + + /* + * POSIX 1003.1g mandates this order. + */ + err = sock_error(sk); + if (err) + ; + else if (sk->sk_shutdown & RCV_SHUTDOWN) + ; + else if (noblock) + err = -EAGAIN; + else if (signal_pending(current)) + err = sock_intr_errno(timeo); + else if (sk->sk_state != TCP_ESTABLISHED) + err = -ENOTCONN; + else if (skb_peek(&sk->sk_receive_queue) == NULL) + /* Wait process until data arrives */ + schedule(); + + finish_wait(sk_sleep(sk), &wait); + + if (err) + return err; + if (sk->sk_shutdown & RCV_SHUTDOWN) + break; + + continue; + } + + chunk = min_t(unsigned int, skb->len, size); + if (memcpy_to_msg(msg, skb->data, chunk)) { + skb_queue_head(&sk->sk_receive_queue, skb); + if (copied == 0) + copied = -EFAULT; + break; + } + copied += chunk; + size -= chunk; + + /* Mark read part of skb as used */ + if (!(flags & MSG_PEEK)) { + skb_pull(skb, chunk); + + /* put the skb back if we didn't use it up.. */ + if (skb->len) { + pr_debug("%s(), back on q!\n", + __func__); + skb_queue_head(&sk->sk_receive_queue, skb); + break; + } + + kfree_skb(skb); + } else { + pr_debug("%s() questionable!?\n", __func__); + + /* put message back and return */ + skb_queue_head(&sk->sk_receive_queue, skb); + break; + } + } while (size); + + /* + * Check if we have previously stopped IrTTP and we know + * have more free space in our rx_queue. If so tell IrTTP + * to start delivering frames again before our rx_queue gets + * empty + */ + if (self->rx_flow == FLOW_STOP) { + if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) { + pr_debug("%s(), Starting IrTTP\n", __func__); + self->rx_flow = FLOW_START; + irttp_flow_request(self->tsap, FLOW_START); + } + } + + return copied; +} + +/* + * Function irda_sendmsg_dgram (sock, msg, len) + * + * Send message down to TinyTP for the unreliable sequenced + * packet service... + * + */ +static int irda_sendmsg_dgram(struct socket *sock, struct msghdr *msg, + size_t len) +{ + struct sock *sk = sock->sk; + struct irda_sock *self; + struct sk_buff *skb; + int err; + + pr_debug("%s(), len=%zd\n", __func__, len); + + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) + return -EINVAL; + + lock_sock(sk); + + if (sk->sk_shutdown & SEND_SHUTDOWN) { + send_sig(SIGPIPE, current, 0); + err = -EPIPE; + goto out; + } + + err = -ENOTCONN; + if (sk->sk_state != TCP_ESTABLISHED) + goto out; + + self = irda_sk(sk); + + /* + * Check that we don't send out too big frames. This is an unreliable + * service, so we have no fragmentation and no coalescence + */ + if (len > self->max_data_size) { + pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n", + __func__, len, self->max_data_size); + len = self->max_data_size; + } + + skb = sock_alloc_send_skb(sk, len + self->max_header_size, + msg->msg_flags & MSG_DONTWAIT, &err); + err = -ENOBUFS; + if (!skb) + goto out; + + skb_reserve(skb, self->max_header_size); + skb_reset_transport_header(skb); + + pr_debug("%s(), appending user data\n", __func__); + skb_put(skb, len); + err = memcpy_from_msg(skb_transport_header(skb), msg, len); + if (err) { + kfree_skb(skb); + goto out; + } + + /* + * Just send the message to TinyTP, and let it deal with possible + * errors. No need to duplicate all that here + */ + err = irttp_udata_request(self->tsap, skb); + if (err) { + pr_debug("%s(), err=%d\n", __func__, err); + goto out; + } + + release_sock(sk); + return len; + +out: + release_sock(sk); + return err; +} + +/* + * Function irda_sendmsg_ultra (sock, msg, len) + * + * Send message down to IrLMP for the unreliable Ultra + * packet service... + */ +#ifdef CONFIG_IRDA_ULTRA +static int irda_sendmsg_ultra(struct socket *sock, struct msghdr *msg, + size_t len) +{ + struct sock *sk = sock->sk; + struct irda_sock *self; + __u8 pid = 0; + int bound = 0; + struct sk_buff *skb; + int err; + + pr_debug("%s(), len=%zd\n", __func__, len); + + err = -EINVAL; + if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) + return -EINVAL; + + lock_sock(sk); + + err = -EPIPE; + if (sk->sk_shutdown & SEND_SHUTDOWN) { + send_sig(SIGPIPE, current, 0); + goto out; + } + + self = irda_sk(sk); + + /* Check if an address was specified with sendto. Jean II */ + if (msg->msg_name) { + DECLARE_SOCKADDR(struct sockaddr_irda *, addr, msg->msg_name); + err = -EINVAL; + /* Check address, extract pid. Jean II */ + if (msg->msg_namelen < sizeof(*addr)) + goto out; + if (addr->sir_family != AF_IRDA) + goto out; + + pid = addr->sir_lsap_sel; + if (pid & 0x80) { + pr_debug("%s(), extension in PID not supp!\n", + __func__); + err = -EOPNOTSUPP; + goto out; + } + } else { + /* Check that the socket is properly bound to an Ultra + * port. Jean II */ + if ((self->lsap == NULL) || + (sk->sk_state != TCP_ESTABLISHED)) { + pr_debug("%s(), socket not bound to Ultra PID.\n", + __func__); + err = -ENOTCONN; + goto out; + } + /* Use PID from socket */ + bound = 1; + } + + /* + * Check that we don't send out too big frames. This is an unreliable + * service, so we have no fragmentation and no coalescence + */ + if (len > self->max_data_size) { + pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n", + __func__, len, self->max_data_size); + len = self->max_data_size; + } + + skb = sock_alloc_send_skb(sk, len + self->max_header_size, + msg->msg_flags & MSG_DONTWAIT, &err); + err = -ENOBUFS; + if (!skb) + goto out; + + skb_reserve(skb, self->max_header_size); + skb_reset_transport_header(skb); + + pr_debug("%s(), appending user data\n", __func__); + skb_put(skb, len); + err = memcpy_from_msg(skb_transport_header(skb), msg, len); + if (err) { + kfree_skb(skb); + goto out; + } + + err = irlmp_connless_data_request((bound ? self->lsap : NULL), + skb, pid); + if (err) + pr_debug("%s(), err=%d\n", __func__, err); +out: + release_sock(sk); + return err ? : len; +} +#endif /* CONFIG_IRDA_ULTRA */ + +/* + * Function irda_shutdown (sk, how) + */ +static int irda_shutdown(struct socket *sock, int how) +{ + struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); + + pr_debug("%s(%p)\n", __func__, self); + + lock_sock(sk); + + sk->sk_state = TCP_CLOSE; + sk->sk_shutdown |= SEND_SHUTDOWN; + sk->sk_state_change(sk); + + if (self->iriap) { + iriap_close(self->iriap); + self->iriap = NULL; + } + + if (self->tsap) { + irttp_disconnect_request(self->tsap, NULL, P_NORMAL); + irttp_close_tsap(self->tsap); + self->tsap = NULL; + } + + /* A few cleanup so the socket look as good as new... */ + self->rx_flow = self->tx_flow = FLOW_START; /* needed ??? */ + self->daddr = DEV_ADDR_ANY; /* Until we get re-connected */ + self->saddr = 0x0; /* so IrLMP assign us any link */ + + release_sock(sk); + + return 0; +} + +/* + * Function irda_poll (file, sock, wait) + */ +static unsigned int irda_poll(struct file * file, struct socket *sock, + poll_table *wait) +{ + struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); + unsigned int mask; + + poll_wait(file, sk_sleep(sk), wait); + mask = 0; + + /* Exceptional events? */ + if (sk->sk_err) + mask |= POLLERR; + if (sk->sk_shutdown & RCV_SHUTDOWN) { + pr_debug("%s(), POLLHUP\n", __func__); + mask |= POLLHUP; + } + + /* Readable? */ + if (!skb_queue_empty(&sk->sk_receive_queue)) { + pr_debug("Socket is readable\n"); + mask |= POLLIN | POLLRDNORM; + } + + /* Connection-based need to check for termination and startup */ + switch (sk->sk_type) { + case SOCK_STREAM: + if (sk->sk_state == TCP_CLOSE) { + pr_debug("%s(), POLLHUP\n", __func__); + mask |= POLLHUP; + } + + if (sk->sk_state == TCP_ESTABLISHED) { + if ((self->tx_flow == FLOW_START) && + sock_writeable(sk)) + { + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; + } + } + break; + case SOCK_SEQPACKET: + if ((self->tx_flow == FLOW_START) && + sock_writeable(sk)) + { + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; + } + break; + case SOCK_DGRAM: + if (sock_writeable(sk)) + mask |= POLLOUT | POLLWRNORM | POLLWRBAND; + break; + default: + break; + } + + return mask; +} + +/* + * Function irda_ioctl (sock, cmd, arg) + */ +static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + struct sock *sk = sock->sk; + int err; + + pr_debug("%s(), cmd=%#x\n", __func__, cmd); + + err = -EINVAL; + switch (cmd) { + case TIOCOUTQ: { + long amount; + + amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); + if (amount < 0) + amount = 0; + err = put_user(amount, (unsigned int __user *)arg); + break; + } + + case TIOCINQ: { + struct sk_buff *skb; + long amount = 0L; + /* These two are safe on a single CPU system as only user tasks fiddle here */ + if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) + amount = skb->len; + err = put_user(amount, (unsigned int __user *)arg); + break; + } + + case SIOCGSTAMP: + if (sk != NULL) + err = sock_get_timestamp(sk, (struct timeval __user *)arg); + break; + + case SIOCGIFADDR: + case SIOCSIFADDR: + case SIOCGIFDSTADDR: + case SIOCSIFDSTADDR: + case SIOCGIFBRDADDR: + case SIOCSIFBRDADDR: + case SIOCGIFNETMASK: + case SIOCSIFNETMASK: + case SIOCGIFMETRIC: + case SIOCSIFMETRIC: + break; + default: + pr_debug("%s(), doing device ioctl!\n", __func__); + err = -ENOIOCTLCMD; + } + + return err; +} + +#ifdef CONFIG_COMPAT +/* + * Function irda_ioctl (sock, cmd, arg) + */ +static int irda_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) +{ + /* + * All IRDA's ioctl are standard ones. + */ + return -ENOIOCTLCMD; +} +#endif + +/* + * Function irda_setsockopt (sock, level, optname, optval, optlen) + * + * Set some options for the socket + * + */ +static int irda_setsockopt(struct socket *sock, int level, int optname, + char __user *optval, unsigned int optlen) +{ + struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); + struct irda_ias_set *ias_opt; + struct ias_object *ias_obj; + struct ias_attrib * ias_attr; /* Attribute in IAS object */ + int opt, free_ias = 0, err = 0; + + pr_debug("%s(%p)\n", __func__, self); + + if (level != SOL_IRLMP) + return -ENOPROTOOPT; + + lock_sock(sk); + + switch (optname) { + case IRLMP_IAS_SET: + /* The user want to add an attribute to an existing IAS object + * (in the IAS database) or to create a new object with this + * attribute. + * We first query IAS to know if the object exist, and then + * create the right attribute... + */ + + if (optlen != sizeof(struct irda_ias_set)) { + err = -EINVAL; + goto out; + } + + /* Copy query to the driver. */ + ias_opt = memdup_user(optval, optlen); + if (IS_ERR(ias_opt)) { + err = PTR_ERR(ias_opt); + goto out; + } + + /* Find the object we target. + * If the user gives us an empty string, we use the object + * associated with this socket. This will workaround + * duplicated class name - Jean II */ + if(ias_opt->irda_class_name[0] == '\0') { + if(self->ias_obj == NULL) { + kfree(ias_opt); + err = -EINVAL; + goto out; + } + ias_obj = self->ias_obj; + } else + ias_obj = irias_find_object(ias_opt->irda_class_name); + + /* Only ROOT can mess with the global IAS database. + * Users can only add attributes to the object associated + * with the socket they own - Jean II */ + if((!capable(CAP_NET_ADMIN)) && + ((ias_obj == NULL) || (ias_obj != self->ias_obj))) { + kfree(ias_opt); + err = -EPERM; + goto out; + } + + /* If the object doesn't exist, create it */ + if(ias_obj == (struct ias_object *) NULL) { + /* Create a new object */ + ias_obj = irias_new_object(ias_opt->irda_class_name, + jiffies); + if (ias_obj == NULL) { + kfree(ias_opt); + err = -ENOMEM; + goto out; + } + free_ias = 1; + } + + /* Do we have the attribute already ? */ + if(irias_find_attrib(ias_obj, ias_opt->irda_attrib_name)) { + kfree(ias_opt); + if (free_ias) { + kfree(ias_obj->name); + kfree(ias_obj); + } + err = -EINVAL; + goto out; + } + + /* Look at the type */ + switch(ias_opt->irda_attrib_type) { + case IAS_INTEGER: + /* Add an integer attribute */ + irias_add_integer_attrib( + ias_obj, + ias_opt->irda_attrib_name, + ias_opt->attribute.irda_attrib_int, + IAS_USER_ATTR); + break; + case IAS_OCT_SEQ: + /* Check length */ + if(ias_opt->attribute.irda_attrib_octet_seq.len > + IAS_MAX_OCTET_STRING) { + kfree(ias_opt); + if (free_ias) { + kfree(ias_obj->name); + kfree(ias_obj); + } + + err = -EINVAL; + goto out; + } + /* Add an octet sequence attribute */ + irias_add_octseq_attrib( + ias_obj, + ias_opt->irda_attrib_name, + ias_opt->attribute.irda_attrib_octet_seq.octet_seq, + ias_opt->attribute.irda_attrib_octet_seq.len, + IAS_USER_ATTR); + break; + case IAS_STRING: + /* Should check charset & co */ + /* Check length */ + /* The length is encoded in a __u8, and + * IAS_MAX_STRING == 256, so there is no way + * userspace can pass us a string too large. + * Jean II */ + /* NULL terminate the string (avoid troubles) */ + ias_opt->attribute.irda_attrib_string.string[ias_opt->attribute.irda_attrib_string.len] = '\0'; + /* Add a string attribute */ + irias_add_string_attrib( + ias_obj, + ias_opt->irda_attrib_name, + ias_opt->attribute.irda_attrib_string.string, + IAS_USER_ATTR); + break; + default : + kfree(ias_opt); + if (free_ias) { + kfree(ias_obj->name); + kfree(ias_obj); + } + err = -EINVAL; + goto out; + } + irias_insert_object(ias_obj); + kfree(ias_opt); + break; + case IRLMP_IAS_DEL: + /* The user want to delete an object from our local IAS + * database. We just need to query the IAS, check is the + * object is not owned by the kernel and delete it. + */ + + if (optlen != sizeof(struct irda_ias_set)) { + err = -EINVAL; + goto out; + } + + /* Copy query to the driver. */ + ias_opt = memdup_user(optval, optlen); + if (IS_ERR(ias_opt)) { + err = PTR_ERR(ias_opt); + goto out; + } + + /* Find the object we target. + * If the user gives us an empty string, we use the object + * associated with this socket. This will workaround + * duplicated class name - Jean II */ + if(ias_opt->irda_class_name[0] == '\0') + ias_obj = self->ias_obj; + else + ias_obj = irias_find_object(ias_opt->irda_class_name); + if(ias_obj == (struct ias_object *) NULL) { + kfree(ias_opt); + err = -EINVAL; + goto out; + } + + /* Only ROOT can mess with the global IAS database. + * Users can only del attributes from the object associated + * with the socket they own - Jean II */ + if((!capable(CAP_NET_ADMIN)) && + ((ias_obj == NULL) || (ias_obj != self->ias_obj))) { + kfree(ias_opt); + err = -EPERM; + goto out; + } + + /* Find the attribute (in the object) we target */ + ias_attr = irias_find_attrib(ias_obj, + ias_opt->irda_attrib_name); + if(ias_attr == (struct ias_attrib *) NULL) { + kfree(ias_opt); + err = -EINVAL; + goto out; + } + + /* Check is the user space own the object */ + if(ias_attr->value->owner != IAS_USER_ATTR) { + pr_debug("%s(), attempting to delete a kernel attribute\n", + __func__); + kfree(ias_opt); + err = -EPERM; + goto out; + } + + /* Remove the attribute (and maybe the object) */ + irias_delete_attrib(ias_obj, ias_attr, 1); + kfree(ias_opt); + break; + case IRLMP_MAX_SDU_SIZE: + if (optlen < sizeof(int)) { + err = -EINVAL; + goto out; + } + + if (get_user(opt, (int __user *)optval)) { + err = -EFAULT; + goto out; + } + + /* Only possible for a seqpacket service (TTP with SAR) */ + if (sk->sk_type != SOCK_SEQPACKET) { + pr_debug("%s(), setting max_sdu_size = %d\n", + __func__, opt); + self->max_sdu_size_rx = opt; + } else { + net_warn_ratelimited("%s: not allowed to set MAXSDUSIZE for this socket type!\n", + __func__); + err = -ENOPROTOOPT; + goto out; + } + break; + case IRLMP_HINTS_SET: + if (optlen < sizeof(int)) { + err = -EINVAL; + goto out; + } + + /* The input is really a (__u8 hints[2]), easier as an int */ + if (get_user(opt, (int __user *)optval)) { + err = -EFAULT; + goto out; + } + + /* Unregister any old registration */ + irlmp_unregister_service(self->skey); + + self->skey = irlmp_register_service((__u16) opt); + break; + case IRLMP_HINT_MASK_SET: + /* As opposed to the previous case which set the hint bits + * that we advertise, this one set the filter we use when + * making a discovery (nodes which don't match any hint + * bit in the mask are not reported). + */ + if (optlen < sizeof(int)) { + err = -EINVAL; + goto out; + } + + /* The input is really a (__u8 hints[2]), easier as an int */ + if (get_user(opt, (int __user *)optval)) { + err = -EFAULT; + goto out; + } + + /* Set the new hint mask */ + self->mask.word = (__u16) opt; + /* Mask out extension bits */ + self->mask.word &= 0x7f7f; + /* Check if no bits */ + if(!self->mask.word) + self->mask.word = 0xFFFF; + + break; + default: + err = -ENOPROTOOPT; + break; + } + +out: + release_sock(sk); + + return err; +} + +/* + * Function irda_extract_ias_value(ias_opt, ias_value) + * + * Translate internal IAS value structure to the user space representation + * + * The external representation of IAS values, as we exchange them with + * user space program is quite different from the internal representation, + * as stored in the IAS database (because we need a flat structure for + * crossing kernel boundary). + * This function transform the former in the latter. We also check + * that the value type is valid. + */ +static int irda_extract_ias_value(struct irda_ias_set *ias_opt, + struct ias_value *ias_value) +{ + /* Look at the type */ + switch (ias_value->type) { + case IAS_INTEGER: + /* Copy the integer */ + ias_opt->attribute.irda_attrib_int = ias_value->t.integer; + break; + case IAS_OCT_SEQ: + /* Set length */ + ias_opt->attribute.irda_attrib_octet_seq.len = ias_value->len; + /* Copy over */ + memcpy(ias_opt->attribute.irda_attrib_octet_seq.octet_seq, + ias_value->t.oct_seq, ias_value->len); + break; + case IAS_STRING: + /* Set length */ + ias_opt->attribute.irda_attrib_string.len = ias_value->len; + ias_opt->attribute.irda_attrib_string.charset = ias_value->charset; + /* Copy over */ + memcpy(ias_opt->attribute.irda_attrib_string.string, + ias_value->t.string, ias_value->len); + /* NULL terminate the string (avoid troubles) */ + ias_opt->attribute.irda_attrib_string.string[ias_value->len] = '\0'; + break; + case IAS_MISSING: + default : + return -EINVAL; + } + + /* Copy type over */ + ias_opt->irda_attrib_type = ias_value->type; + + return 0; +} + +/* + * Function irda_getsockopt (sock, level, optname, optval, optlen) + */ +static int irda_getsockopt(struct socket *sock, int level, int optname, + char __user *optval, int __user *optlen) +{ + struct sock *sk = sock->sk; + struct irda_sock *self = irda_sk(sk); + struct irda_device_list list = { 0 }; + struct irda_device_info *discoveries; + struct irda_ias_set * ias_opt; /* IAS get/query params */ + struct ias_object * ias_obj; /* Object in IAS */ + struct ias_attrib * ias_attr; /* Attribute in IAS object */ + int daddr = DEV_ADDR_ANY; /* Dest address for IAS queries */ + int val = 0; + int len = 0; + int err = 0; + int offset, total; + + pr_debug("%s(%p)\n", __func__, self); + + if (level != SOL_IRLMP) + return -ENOPROTOOPT; + + if (get_user(len, optlen)) + return -EFAULT; + + if(len < 0) + return -EINVAL; + + lock_sock(sk); + + switch (optname) { + case IRLMP_ENUMDEVICES: + + /* Offset to first device entry */ + offset = sizeof(struct irda_device_list) - + sizeof(struct irda_device_info); + + if (len < offset) { + err = -EINVAL; + goto out; + } + + /* Ask lmp for the current discovery log */ + discoveries = irlmp_get_discoveries(&list.len, self->mask.word, + self->nslots); + /* Check if the we got some results */ + if (discoveries == NULL) { + err = -EAGAIN; + goto out; /* Didn't find any devices */ + } + + /* Write total list length back to client */ + if (copy_to_user(optval, &list, offset)) + err = -EFAULT; + + /* Copy the list itself - watch for overflow */ + if (list.len > 2048) { + err = -EINVAL; + goto bed; + } + total = offset + (list.len * sizeof(struct irda_device_info)); + if (total > len) + total = len; + if (copy_to_user(optval+offset, discoveries, total - offset)) + err = -EFAULT; + + /* Write total number of bytes used back to client */ + if (put_user(total, optlen)) + err = -EFAULT; +bed: + /* Free up our buffer */ + kfree(discoveries); + break; + case IRLMP_MAX_SDU_SIZE: + val = self->max_data_size; + len = sizeof(int); + if (put_user(len, optlen)) { + err = -EFAULT; + goto out; + } + + if (copy_to_user(optval, &val, len)) { + err = -EFAULT; + goto out; + } + + break; + case IRLMP_IAS_GET: + /* The user want an object from our local IAS database. + * We just need to query the IAS and return the value + * that we found */ + + /* Check that the user has allocated the right space for us */ + if (len != sizeof(struct irda_ias_set)) { + err = -EINVAL; + goto out; + } + + /* Copy query to the driver. */ + ias_opt = memdup_user(optval, len); + if (IS_ERR(ias_opt)) { + err = PTR_ERR(ias_opt); + goto out; + } + + /* Find the object we target. + * If the user gives us an empty string, we use the object + * associated with this socket. This will workaround + * duplicated class name - Jean II */ + if(ias_opt->irda_class_name[0] == '\0') + ias_obj = self->ias_obj; + else + ias_obj = irias_find_object(ias_opt->irda_class_name); + if(ias_obj == (struct ias_object *) NULL) { + kfree(ias_opt); + err = -EINVAL; + goto out; + } + + /* Find the attribute (in the object) we target */ + ias_attr = irias_find_attrib(ias_obj, + ias_opt->irda_attrib_name); + if(ias_attr == (struct ias_attrib *) NULL) { + kfree(ias_opt); + err = -EINVAL; + goto out; + } + + /* Translate from internal to user structure */ + err = irda_extract_ias_value(ias_opt, ias_attr->value); + if(err) { + kfree(ias_opt); + goto out; + } + + /* Copy reply to the user */ + if (copy_to_user(optval, ias_opt, + sizeof(struct irda_ias_set))) { + kfree(ias_opt); + err = -EFAULT; + goto out; + } + /* Note : don't need to put optlen, we checked it */ + kfree(ias_opt); + break; + case IRLMP_IAS_QUERY: + /* The user want an object from a remote IAS database. + * We need to use IAP to query the remote database and + * then wait for the answer to come back. */ + + /* Check that the user has allocated the right space for us */ + if (len != sizeof(struct irda_ias_set)) { + err = -EINVAL; + goto out; + } + + /* Copy query to the driver. */ + ias_opt = memdup_user(optval, len); + if (IS_ERR(ias_opt)) { + err = PTR_ERR(ias_opt); + goto out; + } + + /* At this point, there are two cases... + * 1) the socket is connected - that's the easy case, we + * just query the device we are connected to... + * 2) the socket is not connected - the user doesn't want + * to connect and/or may not have a valid service name + * (so can't create a fake connection). In this case, + * we assume that the user pass us a valid destination + * address in the requesting structure... + */ + if(self->daddr != DEV_ADDR_ANY) { + /* We are connected - reuse known daddr */ + daddr = self->daddr; + } else { + /* We are not connected, we must specify a valid + * destination address */ + daddr = ias_opt->daddr; + if((!daddr) || (daddr == DEV_ADDR_ANY)) { + kfree(ias_opt); + err = -EINVAL; + goto out; + } + } + + /* Check that we can proceed with IAP */ + if (self->iriap) { + net_warn_ratelimited("%s: busy with a previous query\n", + __func__); + kfree(ias_opt); + err = -EBUSY; + goto out; + } + + self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, + irda_getvalue_confirm); + + if (self->iriap == NULL) { + kfree(ias_opt); + err = -ENOMEM; + goto out; + } + + /* Treat unexpected wakeup as disconnect */ + self->errno = -EHOSTUNREACH; + + /* Query remote LM-IAS */ + iriap_getvaluebyclass_request(self->iriap, + self->saddr, daddr, + ias_opt->irda_class_name, + ias_opt->irda_attrib_name); + + /* Wait for answer, if not yet finished (or failed) */ + if (wait_event_interruptible(self->query_wait, + (self->iriap == NULL))) { + /* pending request uses copy of ias_opt-content + * we can free it regardless! */ + kfree(ias_opt); + /* Treat signals as disconnect */ + err = -EHOSTUNREACH; + goto out; + } + + /* Check what happened */ + if (self->errno) + { + kfree(ias_opt); + /* Requested object/attribute doesn't exist */ + if((self->errno == IAS_CLASS_UNKNOWN) || + (self->errno == IAS_ATTRIB_UNKNOWN)) + err = -EADDRNOTAVAIL; + else + err = -EHOSTUNREACH; + + goto out; + } + + /* Translate from internal to user structure */ + err = irda_extract_ias_value(ias_opt, self->ias_result); + if (self->ias_result) + irias_delete_value(self->ias_result); + if (err) { + kfree(ias_opt); + goto out; + } + + /* Copy reply to the user */ + if (copy_to_user(optval, ias_opt, + sizeof(struct irda_ias_set))) { + kfree(ias_opt); + err = -EFAULT; + goto out; + } + /* Note : don't need to put optlen, we checked it */ + kfree(ias_opt); + break; + case IRLMP_WAITDEVICE: + /* This function is just another way of seeing life ;-) + * IRLMP_ENUMDEVICES assumes that you have a static network, + * and that you just want to pick one of the devices present. + * On the other hand, in here we assume that no device is + * present and that at some point in the future a device will + * come into range. When this device arrive, we just wake + * up the caller, so that he has time to connect to it before + * the device goes away... + * Note : once the node has been discovered for more than a + * few second, it won't trigger this function, unless it + * goes away and come back changes its hint bits (so we + * might call it IRLMP_WAITNEWDEVICE). + */ + + /* Check that the user is passing us an int */ + if (len != sizeof(int)) { + err = -EINVAL; + goto out; + } + /* Get timeout in ms (max time we block the caller) */ + if (get_user(val, (int __user *)optval)) { + err = -EFAULT; + goto out; + } + + /* Tell IrLMP we want to be notified */ + irlmp_update_client(self->ckey, self->mask.word, + irda_selective_discovery_indication, + NULL, (void *) self); + + /* Do some discovery (and also return cached results) */ + irlmp_discovery_request(self->nslots); + + /* Wait until a node is discovered */ + if (!self->cachedaddr) { + pr_debug("%s(), nothing discovered yet, going to sleep...\n", + __func__); + + /* Set watchdog timer to expire in ms. */ + self->errno = 0; + setup_timer(&self->watchdog, irda_discovery_timeout, + (unsigned long)self); + mod_timer(&self->watchdog, + jiffies + msecs_to_jiffies(val)); + + /* Wait for IR-LMP to call us back */ + err = __wait_event_interruptible(self->query_wait, + (self->cachedaddr != 0 || self->errno == -ETIME)); + + /* If watchdog is still activated, kill it! */ + del_timer(&(self->watchdog)); + + pr_debug("%s(), ...waking up !\n", __func__); + + if (err != 0) + goto out; + } + else + pr_debug("%s(), found immediately !\n", + __func__); + + /* Tell IrLMP that we have been notified */ + irlmp_update_client(self->ckey, self->mask.word, + NULL, NULL, NULL); + + /* Check if the we got some results */ + if (!self->cachedaddr) { + err = -EAGAIN; /* Didn't find any devices */ + goto out; + } + daddr = self->cachedaddr; + /* Cleanup */ + self->cachedaddr = 0; + + /* We return the daddr of the device that trigger the + * wakeup. As irlmp pass us only the new devices, we + * are sure that it's not an old device. + * If the user want more details, he should query + * the whole discovery log and pick one device... + */ + if (put_user(daddr, (int __user *)optval)) { + err = -EFAULT; + goto out; + } + + break; + default: + err = -ENOPROTOOPT; + } + +out: + + release_sock(sk); + + return err; +} + +static const struct net_proto_family irda_family_ops = { + .family = PF_IRDA, + .create = irda_create, + .owner = THIS_MODULE, +}; + +static const struct proto_ops irda_stream_ops = { + .family = PF_IRDA, + .owner = THIS_MODULE, + .release = irda_release, + .bind = irda_bind, + .connect = irda_connect, + .socketpair = sock_no_socketpair, + .accept = irda_accept, + .getname = irda_getname, + .poll = irda_poll, + .ioctl = irda_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = irda_compat_ioctl, +#endif + .listen = irda_listen, + .shutdown = irda_shutdown, + .setsockopt = irda_setsockopt, + .getsockopt = irda_getsockopt, + .sendmsg = irda_sendmsg, + .recvmsg = irda_recvmsg_stream, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static const struct proto_ops irda_seqpacket_ops = { + .family = PF_IRDA, + .owner = THIS_MODULE, + .release = irda_release, + .bind = irda_bind, + .connect = irda_connect, + .socketpair = sock_no_socketpair, + .accept = irda_accept, + .getname = irda_getname, + .poll = datagram_poll, + .ioctl = irda_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = irda_compat_ioctl, +#endif + .listen = irda_listen, + .shutdown = irda_shutdown, + .setsockopt = irda_setsockopt, + .getsockopt = irda_getsockopt, + .sendmsg = irda_sendmsg, + .recvmsg = irda_recvmsg_dgram, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +static const struct proto_ops irda_dgram_ops = { + .family = PF_IRDA, + .owner = THIS_MODULE, + .release = irda_release, + .bind = irda_bind, + .connect = irda_connect, + .socketpair = sock_no_socketpair, + .accept = irda_accept, + .getname = irda_getname, + .poll = datagram_poll, + .ioctl = irda_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = irda_compat_ioctl, +#endif + .listen = irda_listen, + .shutdown = irda_shutdown, + .setsockopt = irda_setsockopt, + .getsockopt = irda_getsockopt, + .sendmsg = irda_sendmsg_dgram, + .recvmsg = irda_recvmsg_dgram, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; + +#ifdef CONFIG_IRDA_ULTRA +static const struct proto_ops irda_ultra_ops = { + .family = PF_IRDA, + .owner = THIS_MODULE, + .release = irda_release, + .bind = irda_bind, + .connect = sock_no_connect, + .socketpair = sock_no_socketpair, + .accept = sock_no_accept, + .getname = irda_getname, + .poll = datagram_poll, + .ioctl = irda_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = irda_compat_ioctl, +#endif + .listen = sock_no_listen, + .shutdown = irda_shutdown, + .setsockopt = irda_setsockopt, + .getsockopt = irda_getsockopt, + .sendmsg = irda_sendmsg_ultra, + .recvmsg = irda_recvmsg_dgram, + .mmap = sock_no_mmap, + .sendpage = sock_no_sendpage, +}; +#endif /* CONFIG_IRDA_ULTRA */ + +/* + * Function irsock_init (pro) + * + * Initialize IrDA protocol + * + */ +int __init irsock_init(void) +{ + int rc = proto_register(&irda_proto, 0); + + if (rc == 0) + rc = sock_register(&irda_family_ops); + + return rc; +} + +/* + * Function irsock_cleanup (void) + * + * Remove IrDA protocol + * + */ +void irsock_cleanup(void) +{ + sock_unregister(PF_IRDA); + proto_unregister(&irda_proto); +} diff --git a/drivers/staging/irda/net/discovery.c b/drivers/staging/irda/net/discovery.c new file mode 100644 index 000000000000..364d70aed068 --- /dev/null +++ b/drivers/staging/irda/net/discovery.c @@ -0,0 +1,417 @@ +/********************************************************************* + * + * Filename: discovery.c + * Version: 0.1 + * Description: Routines for handling discoveries at the IrLMP layer + * Status: Experimental. + * Author: Dag Brattli + * Created at: Tue Apr 6 15:33:50 1999 + * Modified at: Sat Oct 9 17:11:31 1999 + * Modified by: Dag Brattli + * Modified at: Fri May 28 3:11 CST 1999 + * Modified by: Horst von Brand + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include + +/* + * Function irlmp_add_discovery (cachelog, discovery) + * + * Add a new discovery to the cachelog, and remove any old discoveries + * from the same device + * + * Note : we try to preserve the time this device was *first* discovered + * (as opposed to the time of last discovery used for cleanup). This is + * used by clients waiting for discovery events to tell if the device + * discovered is "new" or just the same old one. They can't rely there + * on a binary flag (new/old), because not all discovery events are + * propagated to them, and they might not always listen, so they would + * miss some new devices popping up... + * Jean II + */ +void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new) +{ + discovery_t *discovery, *node; + unsigned long flags; + + /* Set time of first discovery if node is new (see below) */ + new->firststamp = new->timestamp; + + spin_lock_irqsave(&cachelog->hb_spinlock, flags); + + /* + * Remove all discoveries of devices that has previously been + * discovered on the same link with the same name (info), or the + * same daddr. We do this since some devices (mostly PDAs) change + * their device address between every discovery. + */ + discovery = (discovery_t *) hashbin_get_first(cachelog); + while (discovery != NULL ) { + node = discovery; + + /* Be sure to stay one item ahead */ + discovery = (discovery_t *) hashbin_get_next(cachelog); + + if ((node->data.saddr == new->data.saddr) && + ((node->data.daddr == new->data.daddr) || + (strcmp(node->data.info, new->data.info) == 0))) + { + /* This discovery is a previous discovery + * from the same device, so just remove it + */ + hashbin_remove_this(cachelog, (irda_queue_t *) node); + /* Check if hints bits are unchanged */ + if (get_unaligned((__u16 *)node->data.hints) == get_unaligned((__u16 *)new->data.hints)) + /* Set time of first discovery for this node */ + new->firststamp = node->firststamp; + kfree(node); + } + } + + /* Insert the new and updated version */ + hashbin_insert(cachelog, (irda_queue_t *) new, new->data.daddr, NULL); + + spin_unlock_irqrestore(&cachelog->hb_spinlock, flags); +} + +/* + * Function irlmp_add_discovery_log (cachelog, log) + * + * Merge a disovery log into the cachelog. + * + */ +void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log) +{ + discovery_t *discovery; + + /* + * If log is missing this means that IrLAP was unable to perform the + * discovery, so restart discovery again with just the half timeout + * of the normal one. + */ + /* Well... It means that there was nobody out there - Jean II */ + if (log == NULL) { + /* irlmp_start_discovery_timer(irlmp, 150); */ + return; + } + + /* + * Locking : we are the only owner of this discovery log, so + * no need to lock it. + * We just need to lock the global log in irlmp_add_discovery(). + */ + discovery = (discovery_t *) hashbin_remove_first(log); + while (discovery != NULL) { + irlmp_add_discovery(cachelog, discovery); + + discovery = (discovery_t *) hashbin_remove_first(log); + } + + /* Delete the now empty log */ + hashbin_delete(log, (FREE_FUNC) kfree); +} + +/* + * Function irlmp_expire_discoveries (log, saddr, force) + * + * Go through all discoveries and expire all that has stayed too long + * + * Note : this assume that IrLAP won't change its saddr, which + * currently is a valid assumption... + */ +void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force) +{ + discovery_t * discovery; + discovery_t * curr; + unsigned long flags; + discinfo_t * buffer = NULL; + int n; /* Size of the full log */ + int i = 0; /* How many we expired */ + + IRDA_ASSERT(log != NULL, return;); + spin_lock_irqsave(&log->hb_spinlock, flags); + + discovery = (discovery_t *) hashbin_get_first(log); + while (discovery != NULL) { + /* Be sure to be one item ahead */ + curr = discovery; + discovery = (discovery_t *) hashbin_get_next(log); + + /* Test if it's time to expire this discovery */ + if ((curr->data.saddr == saddr) && + (force || + ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT))) + { + /* Create buffer as needed. + * As this function get called a lot and most time + * we don't have anything to put in the log (we are + * quite picky), we can save a lot of overhead + * by not calling kmalloc. Jean II */ + if(buffer == NULL) { + /* Create the client specific buffer */ + n = HASHBIN_GET_SIZE(log); + buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC); + if (buffer == NULL) { + spin_unlock_irqrestore(&log->hb_spinlock, flags); + return; + } + + } + + /* Copy discovery information */ + memcpy(&(buffer[i]), &(curr->data), + sizeof(discinfo_t)); + i++; + + /* Remove it from the log */ + curr = hashbin_remove_this(log, (irda_queue_t *) curr); + kfree(curr); + } + } + + /* Drop the spinlock before calling the higher layers, as + * we can't guarantee they won't call us back and create a + * deadlock. We will work on our own private data, so we + * don't care to be interrupted. - Jean II */ + spin_unlock_irqrestore(&log->hb_spinlock, flags); + + if(buffer == NULL) + return; + + /* Tell IrLMP and registered clients about it */ + irlmp_discovery_expiry(buffer, i); + + /* Free up our buffer */ + kfree(buffer); +} + +#if 0 +/* + * Function irlmp_dump_discoveries (log) + * + * Print out all discoveries in log + * + */ +void irlmp_dump_discoveries(hashbin_t *log) +{ + discovery_t *discovery; + + IRDA_ASSERT(log != NULL, return;); + + discovery = (discovery_t *) hashbin_get_first(log); + while (discovery != NULL) { + pr_debug("Discovery:\n"); + pr_debug(" daddr=%08x\n", discovery->data.daddr); + pr_debug(" saddr=%08x\n", discovery->data.saddr); + pr_debug(" nickname=%s\n", discovery->data.info); + + discovery = (discovery_t *) hashbin_get_next(log); + } +} +#endif + +/* + * Function irlmp_copy_discoveries (log, pn, mask) + * + * Copy all discoveries in a buffer + * + * This function implement a safe way for lmp clients to access the + * discovery log. The basic problem is that we don't want the log + * to change (add/remove) while the client is reading it. If the + * lmp client manipulate directly the hashbin, he is sure to get + * into troubles... + * The idea is that we copy all the current discovery log in a buffer + * which is specific to the client and pass this copy to him. As we + * do this operation with the spinlock grabbed, we are safe... + * Note : we don't want those clients to grab the spinlock, because + * we have no control on how long they will hold it... + * Note : we choose to copy the log in "struct irda_device_info" to + * save space... + * Note : the client must kfree himself() the log... + * Jean II + */ +struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, + __u16 mask, int old_entries) +{ + discovery_t * discovery; + unsigned long flags; + discinfo_t * buffer = NULL; + int j_timeout = (sysctl_discovery_timeout * HZ); + int n; /* Size of the full log */ + int i = 0; /* How many we picked */ + + IRDA_ASSERT(pn != NULL, return NULL;); + IRDA_ASSERT(log != NULL, return NULL;); + + /* Save spin lock */ + spin_lock_irqsave(&log->hb_spinlock, flags); + + discovery = (discovery_t *) hashbin_get_first(log); + while (discovery != NULL) { + /* Mask out the ones we don't want : + * We want to match the discovery mask, and to get only + * the most recent one (unless we want old ones) */ + if ((get_unaligned((__u16 *)discovery->data.hints) & mask) && + ((old_entries) || + ((jiffies - discovery->firststamp) < j_timeout))) { + /* Create buffer as needed. + * As this function get called a lot and most time + * we don't have anything to put in the log (we are + * quite picky), we can save a lot of overhead + * by not calling kmalloc. Jean II */ + if(buffer == NULL) { + /* Create the client specific buffer */ + n = HASHBIN_GET_SIZE(log); + buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC); + if (buffer == NULL) { + spin_unlock_irqrestore(&log->hb_spinlock, flags); + return NULL; + } + + } + + /* Copy discovery information */ + memcpy(&(buffer[i]), &(discovery->data), + sizeof(discinfo_t)); + i++; + } + discovery = (discovery_t *) hashbin_get_next(log); + } + + spin_unlock_irqrestore(&log->hb_spinlock, flags); + + /* Get the actual number of device in the buffer and return */ + *pn = i; + return buffer; +} + +#ifdef CONFIG_PROC_FS +static inline discovery_t *discovery_seq_idx(loff_t pos) + +{ + discovery_t *discovery; + + for (discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog); + discovery != NULL; + discovery = (discovery_t *) hashbin_get_next(irlmp->cachelog)) { + if (pos-- == 0) + break; + } + + return discovery; +} + +static void *discovery_seq_start(struct seq_file *seq, loff_t *pos) +{ + spin_lock_irq(&irlmp->cachelog->hb_spinlock); + return *pos ? discovery_seq_idx(*pos - 1) : SEQ_START_TOKEN; +} + +static void *discovery_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + return (v == SEQ_START_TOKEN) + ? (void *) hashbin_get_first(irlmp->cachelog) + : (void *) hashbin_get_next(irlmp->cachelog); +} + +static void discovery_seq_stop(struct seq_file *seq, void *v) +{ + spin_unlock_irq(&irlmp->cachelog->hb_spinlock); +} + +static int discovery_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_puts(seq, "IrLMP: Discovery log:\n\n"); + else { + const discovery_t *discovery = v; + + seq_printf(seq, "nickname: %s, hint: 0x%02x%02x", + discovery->data.info, + discovery->data.hints[0], + discovery->data.hints[1]); +#if 0 + if ( discovery->data.hints[0] & HINT_PNP) + seq_puts(seq, "PnP Compatible "); + if ( discovery->data.hints[0] & HINT_PDA) + seq_puts(seq, "PDA/Palmtop "); + if ( discovery->data.hints[0] & HINT_COMPUTER) + seq_puts(seq, "Computer "); + if ( discovery->data.hints[0] & HINT_PRINTER) + seq_puts(seq, "Printer "); + if ( discovery->data.hints[0] & HINT_MODEM) + seq_puts(seq, "Modem "); + if ( discovery->data.hints[0] & HINT_FAX) + seq_puts(seq, "Fax "); + if ( discovery->data.hints[0] & HINT_LAN) + seq_puts(seq, "LAN Access "); + + if ( discovery->data.hints[1] & HINT_TELEPHONY) + seq_puts(seq, "Telephony "); + if ( discovery->data.hints[1] & HINT_FILE_SERVER) + seq_puts(seq, "File Server "); + if ( discovery->data.hints[1] & HINT_COMM) + seq_puts(seq, "IrCOMM "); + if ( discovery->data.hints[1] & HINT_OBEX) + seq_puts(seq, "IrOBEX "); +#endif + seq_printf(seq,", saddr: 0x%08x, daddr: 0x%08x\n\n", + discovery->data.saddr, + discovery->data.daddr); + + seq_putc(seq, '\n'); + } + return 0; +} + +static const struct seq_operations discovery_seq_ops = { + .start = discovery_seq_start, + .next = discovery_seq_next, + .stop = discovery_seq_stop, + .show = discovery_seq_show, +}; + +static int discovery_seq_open(struct inode *inode, struct file *file) +{ + IRDA_ASSERT(irlmp != NULL, return -EINVAL;); + + return seq_open(file, &discovery_seq_ops); +} + +const struct file_operations discovery_seq_fops = { + .owner = THIS_MODULE, + .open = discovery_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif diff --git a/drivers/staging/irda/net/ircomm/Kconfig b/drivers/staging/irda/net/ircomm/Kconfig new file mode 100644 index 000000000000..19492c1707b7 --- /dev/null +++ b/drivers/staging/irda/net/ircomm/Kconfig @@ -0,0 +1,12 @@ +config IRCOMM + tristate "IrCOMM protocol" + depends on IRDA && TTY + help + Say Y here if you want to build support for the IrCOMM protocol. + To compile it as modules, choose M here: the modules will be + called ircomm and ircomm_tty. + IrCOMM implements serial port emulation, and makes it possible to + use all existing applications that understands TTY's with an + infrared link. Thus you should be able to use application like PPP, + minicom and others. + diff --git a/drivers/staging/irda/net/ircomm/Makefile b/drivers/staging/irda/net/ircomm/Makefile new file mode 100644 index 000000000000..ab23b5ba7e33 --- /dev/null +++ b/drivers/staging/irda/net/ircomm/Makefile @@ -0,0 +1,8 @@ +# +# Makefile for the Linux IrDA IrCOMM protocol layer. +# + +obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o + +ircomm-y := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o +ircomm-tty-y := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o diff --git a/drivers/staging/irda/net/ircomm/ircomm_core.c b/drivers/staging/irda/net/ircomm/ircomm_core.c new file mode 100644 index 000000000000..3af219545f6d --- /dev/null +++ b/drivers/staging/irda/net/ircomm/ircomm_core.c @@ -0,0 +1,563 @@ +/********************************************************************* + * + * Filename: ircomm_core.c + * Version: 1.0 + * Description: IrCOMM service interface + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sun Jun 6 20:37:34 1999 + * Modified at: Tue Dec 21 13:26:41 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +static int __ircomm_close(struct ircomm_cb *self); +static void ircomm_control_indication(struct ircomm_cb *self, + struct sk_buff *skb, int clen); + +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry *proc_irda; +static int ircomm_seq_open(struct inode *, struct file *); + +static const struct file_operations ircomm_proc_fops = { + .owner = THIS_MODULE, + .open = ircomm_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; +#endif /* CONFIG_PROC_FS */ + +hashbin_t *ircomm = NULL; + +static int __init ircomm_init(void) +{ + ircomm = hashbin_new(HB_LOCK); + if (ircomm == NULL) { + net_err_ratelimited("%s(), can't allocate hashbin!\n", + __func__); + return -ENOMEM; + } + +#ifdef CONFIG_PROC_FS + { struct proc_dir_entry *ent; + ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops); + if (!ent) { + printk(KERN_ERR "ircomm_init: can't create /proc entry!\n"); + return -ENODEV; + } + } +#endif /* CONFIG_PROC_FS */ + + net_info_ratelimited("IrCOMM protocol (Dag Brattli)\n"); + + return 0; +} + +static void __exit ircomm_cleanup(void) +{ + hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close); + +#ifdef CONFIG_PROC_FS + remove_proc_entry("ircomm", proc_irda); +#endif /* CONFIG_PROC_FS */ +} + +/* + * Function ircomm_open (client_notify) + * + * Start a new IrCOMM instance + * + */ +struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line) +{ + struct ircomm_cb *self = NULL; + int ret; + + pr_debug("%s(), service_type=0x%02x\n", __func__ , + service_type); + + IRDA_ASSERT(ircomm != NULL, return NULL;); + + self = kzalloc(sizeof(struct ircomm_cb), GFP_KERNEL); + if (self == NULL) + return NULL; + + self->notify = *notify; + self->magic = IRCOMM_MAGIC; + + /* Check if we should use IrLMP or IrTTP */ + if (service_type & IRCOMM_3_WIRE_RAW) { + self->flow_status = FLOW_START; + ret = ircomm_open_lsap(self); + } else + ret = ircomm_open_tsap(self); + + if (ret < 0) { + kfree(self); + return NULL; + } + + self->service_type = service_type; + self->line = line; + + hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL); + + ircomm_next_state(self, IRCOMM_IDLE); + + return self; +} + +EXPORT_SYMBOL(ircomm_open); + +/* + * Function ircomm_close_instance (self) + * + * Remove IrCOMM instance + * + */ +static int __ircomm_close(struct ircomm_cb *self) +{ + /* Disconnect link if any */ + ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL); + + /* Remove TSAP */ + if (self->tsap) { + irttp_close_tsap(self->tsap); + self->tsap = NULL; + } + + /* Remove LSAP */ + if (self->lsap) { + irlmp_close_lsap(self->lsap); + self->lsap = NULL; + } + self->magic = 0; + + kfree(self); + + return 0; +} + +/* + * Function ircomm_close (self) + * + * Closes and removes the specified IrCOMM instance + * + */ +int ircomm_close(struct ircomm_cb *self) +{ + struct ircomm_cb *entry; + + IRDA_ASSERT(self != NULL, return -EIO;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;); + + entry = hashbin_remove(ircomm, self->line, NULL); + + IRDA_ASSERT(entry == self, return -1;); + + return __ircomm_close(self); +} + +EXPORT_SYMBOL(ircomm_close); + +/* + * Function ircomm_connect_request (self, service_type) + * + * Impl. of this function is differ from one of the reference. This + * function does discovery as well as sending connect request + * + */ +int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel, + __u32 saddr, __u32 daddr, struct sk_buff *skb, + __u8 service_type) +{ + struct ircomm_info info; + int ret; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); + + self->service_type= service_type; + + info.dlsap_sel = dlsap_sel; + info.saddr = saddr; + info.daddr = daddr; + + ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info); + + return ret; +} + +EXPORT_SYMBOL(ircomm_connect_request); + +/* + * Function ircomm_connect_indication (self, qos, skb) + * + * Notify user layer about the incoming connection + * + */ +void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb, + struct ircomm_info *info) +{ + /* + * If there are any data hiding in the control channel, we must + * deliver it first. The side effect is that the control channel + * will be removed from the skb + */ + if (self->notify.connect_indication) + self->notify.connect_indication(self->notify.instance, self, + info->qos, info->max_data_size, + info->max_header_size, skb); + else { + pr_debug("%s(), missing handler\n", __func__); + } +} + +/* + * Function ircomm_connect_response (self, userdata, max_sdu_size) + * + * User accepts connection + * + */ +int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata) +{ + int ret; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); + + ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL); + + return ret; +} + +EXPORT_SYMBOL(ircomm_connect_response); + +/* + * Function connect_confirm (self, skb) + * + * Notify user layer that the link is now connected + * + */ +void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb, + struct ircomm_info *info) +{ + if (self->notify.connect_confirm ) + self->notify.connect_confirm(self->notify.instance, + self, info->qos, + info->max_data_size, + info->max_header_size, skb); + else { + pr_debug("%s(), missing handler\n", __func__); + } +} + +/* + * Function ircomm_data_request (self, userdata) + * + * Send IrCOMM data to peer device + * + */ +int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb) +{ + int ret; + + IRDA_ASSERT(self != NULL, return -EFAULT;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;); + IRDA_ASSERT(skb != NULL, return -EFAULT;); + + ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL); + + return ret; +} + +EXPORT_SYMBOL(ircomm_data_request); + +/* + * Function ircomm_data_indication (self, skb) + * + * Data arrived, so deliver it to user + * + */ +void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb) +{ + IRDA_ASSERT(skb->len > 0, return;); + + if (self->notify.data_indication) + self->notify.data_indication(self->notify.instance, self, skb); + else { + pr_debug("%s(), missing handler\n", __func__); + } +} + +/* + * Function ircomm_process_data (self, skb) + * + * Data arrived which may contain control channel data + * + */ +void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb) +{ + int clen; + + IRDA_ASSERT(skb->len > 0, return;); + + clen = skb->data[0]; + + /* + * Input validation check: a stir4200/mcp2150 combinations sometimes + * results in frames with clen > remaining packet size. These are + * illegal; if we throw away just this frame then it seems to carry on + * fine + */ + if (unlikely(skb->len < (clen + 1))) { + pr_debug("%s() throwing away illegal frame\n", + __func__); + return; + } + + /* + * If there are any data hiding in the control channel, we must + * deliver it first. The side effect is that the control channel + * will be removed from the skb + */ + if (clen > 0) + ircomm_control_indication(self, skb, clen); + + /* Remove control channel from data channel */ + skb_pull(skb, clen+1); + + if (skb->len) + ircomm_data_indication(self, skb); + else { + pr_debug("%s(), data was control info only!\n", + __func__); + } +} + +/* + * Function ircomm_control_request (self, params) + * + * Send control data to peer device + * + */ +int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb) +{ + int ret; + + IRDA_ASSERT(self != NULL, return -EFAULT;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;); + IRDA_ASSERT(skb != NULL, return -EFAULT;); + + ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL); + + return ret; +} + +EXPORT_SYMBOL(ircomm_control_request); + +/* + * Function ircomm_control_indication (self, skb) + * + * Data has arrived on the control channel + * + */ +static void ircomm_control_indication(struct ircomm_cb *self, + struct sk_buff *skb, int clen) +{ + /* Use udata for delivering data on the control channel */ + if (self->notify.udata_indication) { + struct sk_buff *ctrl_skb; + + /* We don't own the skb, so clone it */ + ctrl_skb = skb_clone(skb, GFP_ATOMIC); + if (!ctrl_skb) + return; + + /* Remove data channel from control channel */ + skb_trim(ctrl_skb, clen+1); + + self->notify.udata_indication(self->notify.instance, self, + ctrl_skb); + + /* Drop reference count - + * see ircomm_tty_control_indication(). */ + dev_kfree_skb(ctrl_skb); + } else { + pr_debug("%s(), missing handler\n", __func__); + } +} + +/* + * Function ircomm_disconnect_request (self, userdata, priority) + * + * User layer wants to disconnect the IrCOMM connection + * + */ +int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata) +{ + struct ircomm_info info; + int ret; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); + + ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata, + &info); + return ret; +} + +EXPORT_SYMBOL(ircomm_disconnect_request); + +/* + * Function disconnect_indication (self, skb) + * + * Tell user that the link has been disconnected + * + */ +void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb, + struct ircomm_info *info) +{ + IRDA_ASSERT(info != NULL, return;); + + if (self->notify.disconnect_indication) { + self->notify.disconnect_indication(self->notify.instance, self, + info->reason, skb); + } else { + pr_debug("%s(), missing handler\n", __func__); + } +} + +/* + * Function ircomm_flow_request (self, flow) + * + * + * + */ +void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); + + if (self->service_type == IRCOMM_3_WIRE_RAW) + return; + + irttp_flow_request(self->tsap, flow); +} + +EXPORT_SYMBOL(ircomm_flow_request); + +#ifdef CONFIG_PROC_FS +static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct ircomm_cb *self; + loff_t off = 0; + + spin_lock_irq(&ircomm->hb_spinlock); + + for (self = (struct ircomm_cb *) hashbin_get_first(ircomm); + self != NULL; + self = (struct ircomm_cb *) hashbin_get_next(ircomm)) { + if (off++ == *pos) + break; + + } + return self; +} + +static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + + return (void *) hashbin_get_next(ircomm); +} + +static void ircomm_seq_stop(struct seq_file *seq, void *v) +{ + spin_unlock_irq(&ircomm->hb_spinlock); +} + +static int ircomm_seq_show(struct seq_file *seq, void *v) +{ + const struct ircomm_cb *self = v; + + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; ); + + if(self->line < 0x10) + seq_printf(seq, "ircomm%d", self->line); + else + seq_printf(seq, "irlpt%d", self->line - 0x10); + + seq_printf(seq, + " state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:", + ircomm_state[ self->state], + self->slsap_sel, self->dlsap_sel); + + if(self->service_type & IRCOMM_3_WIRE_RAW) + seq_printf(seq, " 3-wire-raw"); + if(self->service_type & IRCOMM_3_WIRE) + seq_printf(seq, " 3-wire"); + if(self->service_type & IRCOMM_9_WIRE) + seq_printf(seq, " 9-wire"); + if(self->service_type & IRCOMM_CENTRONICS) + seq_printf(seq, " Centronics"); + seq_putc(seq, '\n'); + + return 0; +} + +static const struct seq_operations ircomm_seq_ops = { + .start = ircomm_seq_start, + .next = ircomm_seq_next, + .stop = ircomm_seq_stop, + .show = ircomm_seq_show, +}; + +static int ircomm_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &ircomm_seq_ops); +} +#endif /* CONFIG_PROC_FS */ + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("IrCOMM protocol"); +MODULE_LICENSE("GPL"); + +module_init(ircomm_init); +module_exit(ircomm_cleanup); diff --git a/drivers/staging/irda/net/ircomm/ircomm_event.c b/drivers/staging/irda/net/ircomm/ircomm_event.c new file mode 100644 index 000000000000..b0730ac9f388 --- /dev/null +++ b/drivers/staging/irda/net/ircomm/ircomm_event.c @@ -0,0 +1,246 @@ +/********************************************************************* + * + * Filename: ircomm_event.c + * Version: 1.0 + * Description: IrCOMM layer state machine + * Status: Stable + * Author: Dag Brattli + * Created at: Sun Jun 6 20:33:11 1999 + * Modified at: Sun Dec 12 13:44:32 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info); +static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info); +static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info); +static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info); + +const char *const ircomm_state[] = { + "IRCOMM_IDLE", + "IRCOMM_WAITI", + "IRCOMM_WAITR", + "IRCOMM_CONN", +}; + +static const char *const ircomm_event[] __maybe_unused = { + "IRCOMM_CONNECT_REQUEST", + "IRCOMM_CONNECT_RESPONSE", + "IRCOMM_TTP_CONNECT_INDICATION", + "IRCOMM_LMP_CONNECT_INDICATION", + "IRCOMM_TTP_CONNECT_CONFIRM", + "IRCOMM_LMP_CONNECT_CONFIRM", + + "IRCOMM_LMP_DISCONNECT_INDICATION", + "IRCOMM_TTP_DISCONNECT_INDICATION", + "IRCOMM_DISCONNECT_REQUEST", + + "IRCOMM_TTP_DATA_INDICATION", + "IRCOMM_LMP_DATA_INDICATION", + "IRCOMM_DATA_REQUEST", + "IRCOMM_CONTROL_REQUEST", + "IRCOMM_CONTROL_INDICATION", +}; + +static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info) = +{ + ircomm_state_idle, + ircomm_state_waiti, + ircomm_state_waitr, + ircomm_state_conn, +}; + +/* + * Function ircomm_state_idle (self, event, skb) + * + * IrCOMM is currently idle + * + */ +static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info) +{ + int ret = 0; + + switch (event) { + case IRCOMM_CONNECT_REQUEST: + ircomm_next_state(self, IRCOMM_WAITI); + ret = self->issue.connect_request(self, skb, info); + break; + case IRCOMM_TTP_CONNECT_INDICATION: + case IRCOMM_LMP_CONNECT_INDICATION: + ircomm_next_state(self, IRCOMM_WAITR); + ircomm_connect_indication(self, skb, info); + break; + default: + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_event[event]); + ret = -EINVAL; + } + return ret; +} + +/* + * Function ircomm_state_waiti (self, event, skb) + * + * The IrCOMM user has requested an IrCOMM connection to the remote + * device and is awaiting confirmation + */ +static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info) +{ + int ret = 0; + + switch (event) { + case IRCOMM_TTP_CONNECT_CONFIRM: + case IRCOMM_LMP_CONNECT_CONFIRM: + ircomm_next_state(self, IRCOMM_CONN); + ircomm_connect_confirm(self, skb, info); + break; + case IRCOMM_TTP_DISCONNECT_INDICATION: + case IRCOMM_LMP_DISCONNECT_INDICATION: + ircomm_next_state(self, IRCOMM_IDLE); + ircomm_disconnect_indication(self, skb, info); + break; + default: + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_event[event]); + ret = -EINVAL; + } + return ret; +} + +/* + * Function ircomm_state_waitr (self, event, skb) + * + * IrCOMM has received an incoming connection request and is awaiting + * response from the user + */ +static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info) +{ + int ret = 0; + + switch (event) { + case IRCOMM_CONNECT_RESPONSE: + ircomm_next_state(self, IRCOMM_CONN); + ret = self->issue.connect_response(self, skb); + break; + case IRCOMM_DISCONNECT_REQUEST: + ircomm_next_state(self, IRCOMM_IDLE); + ret = self->issue.disconnect_request(self, skb, info); + break; + case IRCOMM_TTP_DISCONNECT_INDICATION: + case IRCOMM_LMP_DISCONNECT_INDICATION: + ircomm_next_state(self, IRCOMM_IDLE); + ircomm_disconnect_indication(self, skb, info); + break; + default: + pr_debug("%s(), unknown event = %s\n", __func__ , + ircomm_event[event]); + ret = -EINVAL; + } + return ret; +} + +/* + * Function ircomm_state_conn (self, event, skb) + * + * IrCOMM is connected to the peer IrCOMM device + * + */ +static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info) +{ + int ret = 0; + + switch (event) { + case IRCOMM_DATA_REQUEST: + ret = self->issue.data_request(self, skb, 0); + break; + case IRCOMM_TTP_DATA_INDICATION: + ircomm_process_data(self, skb); + break; + case IRCOMM_LMP_DATA_INDICATION: + ircomm_data_indication(self, skb); + break; + case IRCOMM_CONTROL_REQUEST: + /* Just send a separate frame for now */ + ret = self->issue.data_request(self, skb, skb->len); + break; + case IRCOMM_TTP_DISCONNECT_INDICATION: + case IRCOMM_LMP_DISCONNECT_INDICATION: + ircomm_next_state(self, IRCOMM_IDLE); + ircomm_disconnect_indication(self, skb, info); + break; + case IRCOMM_DISCONNECT_REQUEST: + ircomm_next_state(self, IRCOMM_IDLE); + ret = self->issue.disconnect_request(self, skb, info); + break; + default: + pr_debug("%s(), unknown event = %s\n", __func__ , + ircomm_event[event]); + ret = -EINVAL; + } + return ret; +} + +/* + * Function ircomm_do_event (self, event, skb) + * + * Process event + * + */ +int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event, + struct sk_buff *skb, struct ircomm_info *info) +{ + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_state[self->state], ircomm_event[event]); + + return (*state[self->state])(self, event, skb, info); +} + +/* + * Function ircomm_next_state (self, state) + * + * Switch state + * + */ +void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state) +{ + self->state = state; + + pr_debug("%s: next state=%s, service type=%d\n", __func__ , + ircomm_state[self->state], self->service_type); +} diff --git a/drivers/staging/irda/net/ircomm/ircomm_lmp.c b/drivers/staging/irda/net/ircomm/ircomm_lmp.c new file mode 100644 index 000000000000..e4cc847bb933 --- /dev/null +++ b/drivers/staging/irda/net/ircomm/ircomm_lmp.c @@ -0,0 +1,350 @@ +/********************************************************************* + * + * Filename: ircomm_lmp.c + * Version: 1.0 + * Description: Interface between IrCOMM and IrLMP + * Status: Stable + * Author: Dag Brattli + * Created at: Sun Jun 6 20:48:27 1999 + * Modified at: Sun Dec 12 13:44:17 1999 + * Modified by: Dag Brattli + * Sources: Previous IrLPT work by Thomas Davis + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include + +#include +#include +#include +#include /* struct irda_skb_cb */ + +#include +#include + + +/* + * Function ircomm_lmp_connect_request (self, userdata) + * + * + * + */ +static int ircomm_lmp_connect_request(struct ircomm_cb *self, + struct sk_buff *userdata, + struct ircomm_info *info) +{ + int ret = 0; + + /* Don't forget to refcount it - should be NULL anyway */ + if(userdata) + skb_get(userdata); + + ret = irlmp_connect_request(self->lsap, info->dlsap_sel, + info->saddr, info->daddr, NULL, userdata); + return ret; +} + +/* + * Function ircomm_lmp_connect_response (self, skb) + * + * + * + */ +static int ircomm_lmp_connect_response(struct ircomm_cb *self, + struct sk_buff *userdata) +{ + struct sk_buff *tx_skb; + + /* Any userdata supplied? */ + if (userdata == NULL) { + tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); + if (!tx_skb) + return -ENOMEM; + + /* Reserve space for MUX and LAP header */ + skb_reserve(tx_skb, LMP_MAX_HEADER); + } else { + /* + * Check that the client has reserved enough space for + * headers + */ + IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER, + return -1;); + + /* Don't forget to refcount it - should be NULL anyway */ + skb_get(userdata); + tx_skb = userdata; + } + + return irlmp_connect_response(self->lsap, tx_skb); +} + +static int ircomm_lmp_disconnect_request(struct ircomm_cb *self, + struct sk_buff *userdata, + struct ircomm_info *info) +{ + struct sk_buff *tx_skb; + int ret; + + if (!userdata) { + tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); + if (!tx_skb) + return -ENOMEM; + + /* Reserve space for MUX and LAP header */ + skb_reserve(tx_skb, LMP_MAX_HEADER); + userdata = tx_skb; + } else { + /* Don't forget to refcount it - should be NULL anyway */ + skb_get(userdata); + } + + ret = irlmp_disconnect_request(self->lsap, userdata); + + return ret; +} + +/* + * Function ircomm_lmp_flow_control (skb) + * + * This function is called when a data frame we have sent to IrLAP has + * been deallocated. We do this to make sure we don't flood IrLAP with + * frames, since we are not using the IrTTP flow control mechanism + */ +static void ircomm_lmp_flow_control(struct sk_buff *skb) +{ + struct irda_skb_cb *cb; + struct ircomm_cb *self; + int line; + + IRDA_ASSERT(skb != NULL, return;); + + cb = (struct irda_skb_cb *) skb->cb; + + line = cb->line; + + self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL); + if (!self) { + pr_debug("%s(), didn't find myself\n", __func__); + return; + } + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); + + self->pkt_count--; + + if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) { + pr_debug("%s(), asking TTY to start again!\n", __func__); + self->flow_status = FLOW_START; + if (self->notify.flow_indication) + self->notify.flow_indication(self->notify.instance, + self, FLOW_START); + } +} + +/* + * Function ircomm_lmp_data_request (self, userdata) + * + * Send data frame to peer device + * + */ +static int ircomm_lmp_data_request(struct ircomm_cb *self, + struct sk_buff *skb, + int not_used) +{ + struct irda_skb_cb *cb; + int ret; + + IRDA_ASSERT(skb != NULL, return -1;); + + cb = (struct irda_skb_cb *) skb->cb; + + cb->line = self->line; + + pr_debug("%s(), sending frame\n", __func__); + + /* Don't forget to refcount it - see ircomm_tty_do_softint() */ + skb_get(skb); + + skb_orphan(skb); + skb->destructor = ircomm_lmp_flow_control; + + if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) { + pr_debug("%s(), asking TTY to slow down!\n", __func__); + self->flow_status = FLOW_STOP; + if (self->notify.flow_indication) + self->notify.flow_indication(self->notify.instance, + self, FLOW_STOP); + } + ret = irlmp_data_request(self->lsap, skb); + if (ret) { + net_err_ratelimited("%s(), failed\n", __func__); + /* irlmp_data_request already free the packet */ + } + + return ret; +} + +/* + * Function ircomm_lmp_data_indication (instance, sap, skb) + * + * Incoming data which we must deliver to the state machine, to check + * we are still connected. + */ +static int ircomm_lmp_data_indication(void *instance, void *sap, + struct sk_buff *skb) +{ + struct ircomm_cb *self = (struct ircomm_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); + IRDA_ASSERT(skb != NULL, return -1;); + + ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL); + + /* Drop reference count - see ircomm_tty_data_indication(). */ + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size, + * max_header_size, skb) + * + * Connection has been confirmed by peer device + * + */ +static void ircomm_lmp_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_seg_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct ircomm_cb *self = (struct ircomm_cb *) instance; + struct ircomm_info info; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(qos != NULL, return;); + + info.max_data_size = max_seg_size; + info.max_header_size = max_header_size; + info.qos = qos; + + ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info); + + /* Drop reference count - see ircomm_tty_connect_confirm(). */ + dev_kfree_skb(skb); +} + +/* + * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size, + * max_header_size, skb) + * + * Peer device wants to make a connection with us + * + */ +static void ircomm_lmp_connect_indication(void *instance, void *sap, + struct qos_info *qos, + __u32 max_seg_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct ircomm_cb *self = (struct ircomm_cb *)instance; + struct ircomm_info info; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(qos != NULL, return;); + + info.max_data_size = max_seg_size; + info.max_header_size = max_header_size; + info.qos = qos; + + ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info); + + /* Drop reference count - see ircomm_tty_connect_indication(). */ + dev_kfree_skb(skb); +} + +/* + * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb) + * + * Peer device has closed the connection, or the link went down for some + * other reason + */ +static void ircomm_lmp_disconnect_indication(void *instance, void *sap, + LM_REASON reason, + struct sk_buff *skb) +{ + struct ircomm_cb *self = (struct ircomm_cb *) instance; + struct ircomm_info info; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); + + info.reason = reason; + + ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info); + + /* Drop reference count - see ircomm_tty_disconnect_indication(). */ + if(skb) + dev_kfree_skb(skb); +} +/* + * Function ircomm_open_lsap (self) + * + * Open LSAP. This function will only be used when using "raw" services + * + */ +int ircomm_open_lsap(struct ircomm_cb *self) +{ + notify_t notify; + + /* Register callbacks */ + irda_notify_init(¬ify); + notify.data_indication = ircomm_lmp_data_indication; + notify.connect_confirm = ircomm_lmp_connect_confirm; + notify.connect_indication = ircomm_lmp_connect_indication; + notify.disconnect_indication = ircomm_lmp_disconnect_indication; + notify.instance = self; + strlcpy(notify.name, "IrCOMM", sizeof(notify.name)); + + self->lsap = irlmp_open_lsap(LSAP_ANY, ¬ify, 0); + if (!self->lsap) { + pr_debug("%sfailed to allocate tsap\n", __func__); + return -1; + } + self->slsap_sel = self->lsap->slsap_sel; + + /* + * Initialize the call-table for issuing commands + */ + self->issue.data_request = ircomm_lmp_data_request; + self->issue.connect_request = ircomm_lmp_connect_request; + self->issue.connect_response = ircomm_lmp_connect_response; + self->issue.disconnect_request = ircomm_lmp_disconnect_request; + + return 0; +} diff --git a/drivers/staging/irda/net/ircomm/ircomm_param.c b/drivers/staging/irda/net/ircomm/ircomm_param.c new file mode 100644 index 000000000000..5728e76ca6d5 --- /dev/null +++ b/drivers/staging/irda/net/ircomm/ircomm_param.c @@ -0,0 +1,501 @@ +/********************************************************************* + * + * Filename: ircomm_param.c + * Version: 1.0 + * Description: Parameter handling for the IrCOMM protocol + * Status: Experimental. + * Author: Dag Brattli + * Created at: Mon Jun 7 10:25:11 1999 + * Modified at: Sun Jan 30 14:32:03 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +static int ircomm_param_service_type(void *instance, irda_param_t *param, + int get); +static int ircomm_param_port_type(void *instance, irda_param_t *param, + int get); +static int ircomm_param_port_name(void *instance, irda_param_t *param, + int get); +static int ircomm_param_service_type(void *instance, irda_param_t *param, + int get); +static int ircomm_param_data_rate(void *instance, irda_param_t *param, + int get); +static int ircomm_param_data_format(void *instance, irda_param_t *param, + int get); +static int ircomm_param_flow_control(void *instance, irda_param_t *param, + int get); +static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get); +static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get); +static int ircomm_param_line_status(void *instance, irda_param_t *param, + int get); +static int ircomm_param_dte(void *instance, irda_param_t *param, int get); +static int ircomm_param_dce(void *instance, irda_param_t *param, int get); +static int ircomm_param_poll(void *instance, irda_param_t *param, int get); + +static const pi_minor_info_t pi_minor_call_table_common[] = { + { ircomm_param_service_type, PV_INT_8_BITS }, + { ircomm_param_port_type, PV_INT_8_BITS }, + { ircomm_param_port_name, PV_STRING } +}; +static const pi_minor_info_t pi_minor_call_table_non_raw[] = { + { ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN }, + { ircomm_param_data_format, PV_INT_8_BITS }, + { ircomm_param_flow_control, PV_INT_8_BITS }, + { ircomm_param_xon_xoff, PV_INT_16_BITS }, + { ircomm_param_enq_ack, PV_INT_16_BITS }, + { ircomm_param_line_status, PV_INT_8_BITS } +}; +static const pi_minor_info_t pi_minor_call_table_9_wire[] = { + { ircomm_param_dte, PV_INT_8_BITS }, + { ircomm_param_dce, PV_INT_8_BITS }, + { ircomm_param_poll, PV_NO_VALUE }, +}; + +static const pi_major_info_t pi_major_call_table[] = { + { pi_minor_call_table_common, 3 }, + { pi_minor_call_table_non_raw, 6 }, + { pi_minor_call_table_9_wire, 3 } +/* { pi_minor_call_table_centronics } */ +}; + +pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 }; + +/* + * Function ircomm_param_request (self, pi, flush) + * + * Queue a parameter for the control channel + * + */ +int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) +{ + unsigned long flags; + struct sk_buff *skb; + int count; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + /* Make sure we don't send parameters for raw mode */ + if (self->service_type == IRCOMM_3_WIRE_RAW) + return 0; + + spin_lock_irqsave(&self->spinlock, flags); + + skb = self->ctrl_skb; + if (!skb) { + skb = alloc_skb(256, GFP_ATOMIC); + if (!skb) { + spin_unlock_irqrestore(&self->spinlock, flags); + return -ENOMEM; + } + + skb_reserve(skb, self->max_header_size); + self->ctrl_skb = skb; + } + /* + * Inserting is a little bit tricky since we don't know how much + * room we will need. But this should hopefully work OK + */ + count = irda_param_insert(self, pi, skb_tail_pointer(skb), + skb_tailroom(skb), &ircomm_param_info); + if (count < 0) { + net_warn_ratelimited("%s(), no room for parameter!\n", + __func__); + spin_unlock_irqrestore(&self->spinlock, flags); + return -1; + } + skb_put(skb, count); + pr_debug("%s(), skb->len=%d\n", __func__, skb->len); + + spin_unlock_irqrestore(&self->spinlock, flags); + + if (flush) { + /* ircomm_tty_do_softint will take care of the rest */ + schedule_work(&self->tqueue); + } + + return count; +} + +/* + * Function ircomm_param_service_type (self, buf, len) + * + * Handle service type, this function will both be called after the LM-IAS + * query and then the remote device sends its initial parameters + * + */ +static int ircomm_param_service_type(void *instance, irda_param_t *param, + int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + __u8 service_type = (__u8) param->pv.i; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (get) { + param->pv.i = self->settings.service_type; + return 0; + } + + /* Find all common service types */ + service_type &= self->service_type; + if (!service_type) { + pr_debug("%s(), No common service type to use!\n", __func__); + return -1; + } + pr_debug("%s(), services in common=%02x\n", __func__ , + service_type); + + /* + * Now choose a preferred service type of those available + */ + if (service_type & IRCOMM_CENTRONICS) + self->settings.service_type = IRCOMM_CENTRONICS; + else if (service_type & IRCOMM_9_WIRE) + self->settings.service_type = IRCOMM_9_WIRE; + else if (service_type & IRCOMM_3_WIRE) + self->settings.service_type = IRCOMM_3_WIRE; + else if (service_type & IRCOMM_3_WIRE_RAW) + self->settings.service_type = IRCOMM_3_WIRE_RAW; + + pr_debug("%s(), resulting service type=0x%02x\n", __func__ , + self->settings.service_type); + + /* + * Now the line is ready for some communication. Check if we are a + * server, and send over some initial parameters. + * Client do it in ircomm_tty_state_setup(). + * Note : we may get called from ircomm_tty_getvalue_confirm(), + * therefore before we even have open any socket. And self->client + * is initialised to TRUE only later. So, we check if the link is + * really initialised. - Jean II + */ + if ((self->max_header_size != IRCOMM_TTY_HDR_UNINITIALISED) && + (!self->client) && + (self->settings.service_type != IRCOMM_3_WIRE_RAW)) + { + /* Init connection */ + ircomm_tty_send_initial_parameters(self); + ircomm_tty_link_established(self); + } + + return 0; +} + +/* + * Function ircomm_param_port_type (self, param) + * + * The port type parameter tells if the devices are serial or parallel. + * Since we only advertise serial service, this parameter should only + * be equal to IRCOMM_SERIAL. + */ +static int ircomm_param_port_type(void *instance, irda_param_t *param, int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (get) + param->pv.i = IRCOMM_SERIAL; + else { + self->settings.port_type = (__u8) param->pv.i; + + pr_debug("%s(), port type=%d\n", __func__ , + self->settings.port_type); + } + return 0; +} + +/* + * Function ircomm_param_port_name (self, param) + * + * Exchange port name + * + */ +static int ircomm_param_port_name(void *instance, irda_param_t *param, int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (get) { + pr_debug("%s(), not imp!\n", __func__); + } else { + pr_debug("%s(), port-name=%s\n", __func__ , param->pv.c); + strncpy(self->settings.port_name, param->pv.c, 32); + } + + return 0; +} + +/* + * Function ircomm_param_data_rate (self, param) + * + * Exchange data rate to be used in this settings + * + */ +static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (get) + param->pv.i = self->settings.data_rate; + else + self->settings.data_rate = param->pv.i; + + pr_debug("%s(), data rate = %d\n", __func__ , param->pv.i); + + return 0; +} + +/* + * Function ircomm_param_data_format (self, param) + * + * Exchange data format to be used in this settings + * + */ +static int ircomm_param_data_format(void *instance, irda_param_t *param, + int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (get) + param->pv.i = self->settings.data_format; + else + self->settings.data_format = (__u8) param->pv.i; + + return 0; +} + +/* + * Function ircomm_param_flow_control (self, param) + * + * Exchange flow control settings to be used in this settings + * + */ +static int ircomm_param_flow_control(void *instance, irda_param_t *param, + int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (get) + param->pv.i = self->settings.flow_control; + else + self->settings.flow_control = (__u8) param->pv.i; + + pr_debug("%s(), flow control = 0x%02x\n", __func__ , (__u8)param->pv.i); + + return 0; +} + +/* + * Function ircomm_param_xon_xoff (self, param) + * + * Exchange XON/XOFF characters + * + */ +static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (get) { + param->pv.i = self->settings.xonxoff[0]; + param->pv.i |= self->settings.xonxoff[1] << 8; + } else { + self->settings.xonxoff[0] = (__u16) param->pv.i & 0xff; + self->settings.xonxoff[1] = (__u16) param->pv.i >> 8; + } + + pr_debug("%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ , + param->pv.i & 0xff, param->pv.i >> 8); + + return 0; +} + +/* + * Function ircomm_param_enq_ack (self, param) + * + * Exchange ENQ/ACK characters + * + */ +static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (get) { + param->pv.i = self->settings.enqack[0]; + param->pv.i |= self->settings.enqack[1] << 8; + } else { + self->settings.enqack[0] = (__u16) param->pv.i & 0xff; + self->settings.enqack[1] = (__u16) param->pv.i >> 8; + } + + pr_debug("%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ , + param->pv.i & 0xff, param->pv.i >> 8); + + return 0; +} + +/* + * Function ircomm_param_line_status (self, param) + * + * + * + */ +static int ircomm_param_line_status(void *instance, irda_param_t *param, + int get) +{ + pr_debug("%s(), not impl.\n", __func__); + + return 0; +} + +/* + * Function ircomm_param_dte (instance, param) + * + * If we get here, there must be some sort of null-modem connection, and + * we are probably working in server mode as well. + */ +static int ircomm_param_dte(void *instance, irda_param_t *param, int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + __u8 dte; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (get) + param->pv.i = self->settings.dte; + else { + dte = (__u8) param->pv.i; + + self->settings.dce = 0; + + if (dte & IRCOMM_DELTA_DTR) + self->settings.dce |= (IRCOMM_DELTA_DSR| + IRCOMM_DELTA_RI | + IRCOMM_DELTA_CD); + if (dte & IRCOMM_DTR) + self->settings.dce |= (IRCOMM_DSR| + IRCOMM_RI | + IRCOMM_CD); + + if (dte & IRCOMM_DELTA_RTS) + self->settings.dce |= IRCOMM_DELTA_CTS; + if (dte & IRCOMM_RTS) + self->settings.dce |= IRCOMM_CTS; + + /* Take appropriate actions */ + ircomm_tty_check_modem_status(self); + + /* Null modem cable emulator */ + self->settings.null_modem = TRUE; + } + + return 0; +} + +/* + * Function ircomm_param_dce (instance, param) + * + * + * + */ +static int ircomm_param_dce(void *instance, irda_param_t *param, int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + __u8 dce; + + pr_debug("%s(), dce = 0x%02x\n", __func__ , (__u8)param->pv.i); + + dce = (__u8) param->pv.i; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + self->settings.dce = dce; + + /* Check if any of the settings have changed */ + if (dce & 0x0f) { + if (dce & IRCOMM_DELTA_CTS) { + pr_debug("%s(), CTS\n", __func__); + } + } + + ircomm_tty_check_modem_status(self); + + return 0; +} + +/* + * Function ircomm_param_poll (instance, param) + * + * Called when the peer device is polling for the line settings + * + */ +static int ircomm_param_poll(void *instance, irda_param_t *param, int get) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + /* Poll parameters are always of length 0 (just a signal) */ + if (!get) { + /* Respond with DTE line settings */ + ircomm_param_request(self, IRCOMM_DTE, TRUE); + } + return 0; +} + + + + + diff --git a/drivers/staging/irda/net/ircomm/ircomm_ttp.c b/drivers/staging/irda/net/ircomm/ircomm_ttp.c new file mode 100644 index 000000000000..4b81e0934770 --- /dev/null +++ b/drivers/staging/irda/net/ircomm/ircomm_ttp.c @@ -0,0 +1,350 @@ +/********************************************************************* + * + * Filename: ircomm_ttp.c + * Version: 1.0 + * Description: Interface between IrCOMM and IrTTP + * Status: Stable + * Author: Dag Brattli + * Created at: Sun Jun 6 20:48:27 1999 + * Modified at: Mon Dec 13 11:35:13 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include + +#include +#include +#include +#include + +#include +#include + +static int ircomm_ttp_data_indication(void *instance, void *sap, + struct sk_buff *skb); +static void ircomm_ttp_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb); +static void ircomm_ttp_connect_indication(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb); +static void ircomm_ttp_flow_indication(void *instance, void *sap, + LOCAL_FLOW cmd); +static void ircomm_ttp_disconnect_indication(void *instance, void *sap, + LM_REASON reason, + struct sk_buff *skb); +static int ircomm_ttp_data_request(struct ircomm_cb *self, + struct sk_buff *skb, + int clen); +static int ircomm_ttp_connect_request(struct ircomm_cb *self, + struct sk_buff *userdata, + struct ircomm_info *info); +static int ircomm_ttp_connect_response(struct ircomm_cb *self, + struct sk_buff *userdata); +static int ircomm_ttp_disconnect_request(struct ircomm_cb *self, + struct sk_buff *userdata, + struct ircomm_info *info); + +/* + * Function ircomm_open_tsap (self) + * + * + * + */ +int ircomm_open_tsap(struct ircomm_cb *self) +{ + notify_t notify; + + /* Register callbacks */ + irda_notify_init(¬ify); + notify.data_indication = ircomm_ttp_data_indication; + notify.connect_confirm = ircomm_ttp_connect_confirm; + notify.connect_indication = ircomm_ttp_connect_indication; + notify.flow_indication = ircomm_ttp_flow_indication; + notify.disconnect_indication = ircomm_ttp_disconnect_indication; + notify.instance = self; + strlcpy(notify.name, "IrCOMM", sizeof(notify.name)); + + self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, + ¬ify); + if (!self->tsap) { + pr_debug("%sfailed to allocate tsap\n", __func__); + return -1; + } + self->slsap_sel = self->tsap->stsap_sel; + + /* + * Initialize the call-table for issuing commands + */ + self->issue.data_request = ircomm_ttp_data_request; + self->issue.connect_request = ircomm_ttp_connect_request; + self->issue.connect_response = ircomm_ttp_connect_response; + self->issue.disconnect_request = ircomm_ttp_disconnect_request; + + return 0; +} + +/* + * Function ircomm_ttp_connect_request (self, userdata) + * + * + * + */ +static int ircomm_ttp_connect_request(struct ircomm_cb *self, + struct sk_buff *userdata, + struct ircomm_info *info) +{ + int ret = 0; + + /* Don't forget to refcount it - should be NULL anyway */ + if(userdata) + skb_get(userdata); + + ret = irttp_connect_request(self->tsap, info->dlsap_sel, + info->saddr, info->daddr, NULL, + TTP_SAR_DISABLE, userdata); + + return ret; +} + +/* + * Function ircomm_ttp_connect_response (self, skb) + * + * + * + */ +static int ircomm_ttp_connect_response(struct ircomm_cb *self, + struct sk_buff *userdata) +{ + int ret; + + /* Don't forget to refcount it - should be NULL anyway */ + if(userdata) + skb_get(userdata); + + ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata); + + return ret; +} + +/* + * Function ircomm_ttp_data_request (self, userdata) + * + * Send IrCOMM data to IrTTP layer. Currently we do not try to combine + * control data with pure data, so they will be sent as separate frames. + * Should not be a big problem though, since control frames are rare. But + * some of them are sent after connection establishment, so this can + * increase the latency a bit. + */ +static int ircomm_ttp_data_request(struct ircomm_cb *self, + struct sk_buff *skb, + int clen) +{ + int ret; + + IRDA_ASSERT(skb != NULL, return -1;); + + pr_debug("%s(), clen=%d\n", __func__ , clen); + + /* + * Insert clen field, currently we either send data only, or control + * only frames, to make things easier and avoid queueing + */ + IRDA_ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;); + + /* Don't forget to refcount it - see ircomm_tty_do_softint() */ + skb_get(skb); + + skb_push(skb, IRCOMM_HEADER_SIZE); + + skb->data[0] = clen; + + ret = irttp_data_request(self->tsap, skb); + if (ret) { + net_err_ratelimited("%s(), failed\n", __func__); + /* irttp_data_request already free the packet */ + } + + return ret; +} + +/* + * Function ircomm_ttp_data_indication (instance, sap, skb) + * + * Incoming data + * + */ +static int ircomm_ttp_data_indication(void *instance, void *sap, + struct sk_buff *skb) +{ + struct ircomm_cb *self = (struct ircomm_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); + IRDA_ASSERT(skb != NULL, return -1;); + + ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL); + + /* Drop reference count - see ircomm_tty_data_indication(). */ + dev_kfree_skb(skb); + + return 0; +} + +static void ircomm_ttp_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct ircomm_cb *self = (struct ircomm_cb *) instance; + struct ircomm_info info; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(qos != NULL, goto out;); + + if (max_sdu_size != TTP_SAR_DISABLE) { + net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n", + __func__); + goto out; + } + + info.max_data_size = irttp_get_max_seg_size(self->tsap) + - IRCOMM_HEADER_SIZE; + info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE; + info.qos = qos; + + ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info); + +out: + /* Drop reference count - see ircomm_tty_connect_confirm(). */ + dev_kfree_skb(skb); +} + +/* + * Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size, + * max_header_size, skb) + * + * + * + */ +static void ircomm_ttp_connect_indication(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct ircomm_cb *self = (struct ircomm_cb *)instance; + struct ircomm_info info; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(qos != NULL, goto out;); + + if (max_sdu_size != TTP_SAR_DISABLE) { + net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n", + __func__); + goto out; + } + + info.max_data_size = irttp_get_max_seg_size(self->tsap) + - IRCOMM_HEADER_SIZE; + info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE; + info.qos = qos; + + ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info); + +out: + /* Drop reference count - see ircomm_tty_connect_indication(). */ + dev_kfree_skb(skb); +} + +/* + * Function ircomm_ttp_disconnect_request (self, userdata, info) + * + * + * + */ +static int ircomm_ttp_disconnect_request(struct ircomm_cb *self, + struct sk_buff *userdata, + struct ircomm_info *info) +{ + int ret; + + /* Don't forget to refcount it - should be NULL anyway */ + if(userdata) + skb_get(userdata); + + ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL); + + return ret; +} + +/* + * Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb) + * + * + * + */ +static void ircomm_ttp_disconnect_indication(void *instance, void *sap, + LM_REASON reason, + struct sk_buff *skb) +{ + struct ircomm_cb *self = (struct ircomm_cb *) instance; + struct ircomm_info info; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); + + info.reason = reason; + + ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info); + + /* Drop reference count - see ircomm_tty_disconnect_indication(). */ + if(skb) + dev_kfree_skb(skb); +} + +/* + * Function ircomm_ttp_flow_indication (instance, sap, cmd) + * + * Layer below is telling us to start or stop the flow of data + * + */ +static void ircomm_ttp_flow_indication(void *instance, void *sap, + LOCAL_FLOW cmd) +{ + struct ircomm_cb *self = (struct ircomm_cb *) instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); + + if (self->notify.flow_indication) + self->notify.flow_indication(self->notify.instance, self, cmd); +} + + diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty.c b/drivers/staging/irda/net/ircomm/ircomm_tty.c new file mode 100644 index 000000000000..ec157c3419b5 --- /dev/null +++ b/drivers/staging/irda/net/ircomm/ircomm_tty.c @@ -0,0 +1,1329 @@ +/********************************************************************* + * + * Filename: ircomm_tty.c + * Version: 1.0 + * Description: IrCOMM serial TTY driver + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sun Jun 6 21:00:56 1999 + * Modified at: Wed Feb 23 00:09:02 2000 + * Modified by: Dag Brattli + * Sources: serial.c and previous IrCOMM work by Takahide Higuchi + * + * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for MODULE_ALIAS_CHARDEV_MAJOR */ + +#include + +#include +#include + +#include +#include +#include +#include + +static int ircomm_tty_install(struct tty_driver *driver, + struct tty_struct *tty); +static int ircomm_tty_open(struct tty_struct *tty, struct file *filp); +static void ircomm_tty_close(struct tty_struct * tty, struct file *filp); +static int ircomm_tty_write(struct tty_struct * tty, + const unsigned char *buf, int count); +static int ircomm_tty_write_room(struct tty_struct *tty); +static void ircomm_tty_throttle(struct tty_struct *tty); +static void ircomm_tty_unthrottle(struct tty_struct *tty); +static int ircomm_tty_chars_in_buffer(struct tty_struct *tty); +static void ircomm_tty_flush_buffer(struct tty_struct *tty); +static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch); +static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout); +static void ircomm_tty_hangup(struct tty_struct *tty); +static void ircomm_tty_do_softint(struct work_struct *work); +static void ircomm_tty_shutdown(struct ircomm_tty_cb *self); +static void ircomm_tty_stop(struct tty_struct *tty); + +static int ircomm_tty_data_indication(void *instance, void *sap, + struct sk_buff *skb); +static int ircomm_tty_control_indication(void *instance, void *sap, + struct sk_buff *skb); +static void ircomm_tty_flow_indication(void *instance, void *sap, + LOCAL_FLOW cmd); +#ifdef CONFIG_PROC_FS +static const struct file_operations ircomm_tty_proc_fops; +#endif /* CONFIG_PROC_FS */ +static struct tty_driver *driver; + +static hashbin_t *ircomm_tty = NULL; + +static const struct tty_operations ops = { + .install = ircomm_tty_install, + .open = ircomm_tty_open, + .close = ircomm_tty_close, + .write = ircomm_tty_write, + .write_room = ircomm_tty_write_room, + .chars_in_buffer = ircomm_tty_chars_in_buffer, + .flush_buffer = ircomm_tty_flush_buffer, + .ioctl = ircomm_tty_ioctl, /* ircomm_tty_ioctl.c */ + .tiocmget = ircomm_tty_tiocmget, /* ircomm_tty_ioctl.c */ + .tiocmset = ircomm_tty_tiocmset, /* ircomm_tty_ioctl.c */ + .throttle = ircomm_tty_throttle, + .unthrottle = ircomm_tty_unthrottle, + .send_xchar = ircomm_tty_send_xchar, + .set_termios = ircomm_tty_set_termios, + .stop = ircomm_tty_stop, + .start = ircomm_tty_start, + .hangup = ircomm_tty_hangup, + .wait_until_sent = ircomm_tty_wait_until_sent, +#ifdef CONFIG_PROC_FS + .proc_fops = &ircomm_tty_proc_fops, +#endif /* CONFIG_PROC_FS */ +}; + +static void ircomm_port_raise_dtr_rts(struct tty_port *port, int raise) +{ + struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb, + port); + /* + * Here, we use to lock those two guys, but as ircomm_param_request() + * does it itself, I don't see the point (and I see the deadlock). + * Jean II + */ + if (raise) + self->settings.dte |= IRCOMM_RTS | IRCOMM_DTR; + else + self->settings.dte &= ~(IRCOMM_RTS | IRCOMM_DTR); + + ircomm_param_request(self, IRCOMM_DTE, TRUE); +} + +static int ircomm_port_carrier_raised(struct tty_port *port) +{ + struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb, + port); + return self->settings.dce & IRCOMM_CD; +} + +static const struct tty_port_operations ircomm_port_ops = { + .dtr_rts = ircomm_port_raise_dtr_rts, + .carrier_raised = ircomm_port_carrier_raised, +}; + +/* + * Function ircomm_tty_init() + * + * Init IrCOMM TTY layer/driver + * + */ +static int __init ircomm_tty_init(void) +{ + driver = alloc_tty_driver(IRCOMM_TTY_PORTS); + if (!driver) + return -ENOMEM; + ircomm_tty = hashbin_new(HB_LOCK); + if (ircomm_tty == NULL) { + net_err_ratelimited("%s(), can't allocate hashbin!\n", + __func__); + put_tty_driver(driver); + return -ENOMEM; + } + + driver->driver_name = "ircomm"; + driver->name = "ircomm"; + driver->major = IRCOMM_TTY_MAJOR; + driver->minor_start = IRCOMM_TTY_MINOR; + driver->type = TTY_DRIVER_TYPE_SERIAL; + driver->subtype = SERIAL_TYPE_NORMAL; + driver->init_termios = tty_std_termios; + driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + driver->flags = TTY_DRIVER_REAL_RAW; + tty_set_operations(driver, &ops); + if (tty_register_driver(driver)) { + net_err_ratelimited("%s(): Couldn't register serial driver\n", + __func__); + put_tty_driver(driver); + return -1; + } + return 0; +} + +static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + ircomm_tty_shutdown(self); + + self->magic = 0; + tty_port_destroy(&self->port); + kfree(self); +} + +/* + * Function ircomm_tty_cleanup () + * + * Remove IrCOMM TTY layer/driver + * + */ +static void __exit ircomm_tty_cleanup(void) +{ + int ret; + + ret = tty_unregister_driver(driver); + if (ret) { + net_err_ratelimited("%s(), failed to unregister driver\n", + __func__); + return; + } + + hashbin_delete(ircomm_tty, (FREE_FUNC) __ircomm_tty_cleanup); + put_tty_driver(driver); +} + +/* + * Function ircomm_startup (self) + * + * + * + */ +static int ircomm_tty_startup(struct ircomm_tty_cb *self) +{ + notify_t notify; + int ret = -ENODEV; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + /* Check if already open */ + if (tty_port_initialized(&self->port)) { + pr_debug("%s(), already open so break out!\n", __func__); + return 0; + } + tty_port_set_initialized(&self->port, 1); + + /* Register with IrCOMM */ + irda_notify_init(¬ify); + /* These callbacks we must handle ourselves */ + notify.data_indication = ircomm_tty_data_indication; + notify.udata_indication = ircomm_tty_control_indication; + notify.flow_indication = ircomm_tty_flow_indication; + + /* Use the ircomm_tty interface for these ones */ + notify.disconnect_indication = ircomm_tty_disconnect_indication; + notify.connect_confirm = ircomm_tty_connect_confirm; + notify.connect_indication = ircomm_tty_connect_indication; + strlcpy(notify.name, "ircomm_tty", sizeof(notify.name)); + notify.instance = self; + + if (!self->ircomm) { + self->ircomm = ircomm_open(¬ify, self->service_type, + self->line); + } + if (!self->ircomm) + goto err; + + self->slsap_sel = self->ircomm->slsap_sel; + + /* Connect IrCOMM link with remote device */ + ret = ircomm_tty_attach_cable(self); + if (ret < 0) { + net_err_ratelimited("%s(), error attaching cable!\n", __func__); + goto err; + } + + return 0; +err: + tty_port_set_initialized(&self->port, 0); + return ret; +} + +/* + * Function ircomm_block_til_ready (self, filp) + * + * + * + */ +static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, + struct tty_struct *tty, struct file *filp) +{ + struct tty_port *port = &self->port; + DECLARE_WAITQUEUE(wait, current); + int retval; + int do_clocal = 0; + unsigned long flags; + + /* + * If non-blocking mode is set, or the port is not enabled, + * then make the check up front and then exit. + */ + if (tty_io_error(tty)) { + tty_port_set_active(port, 1); + return 0; + } + + if (filp->f_flags & O_NONBLOCK) { + /* nonblock mode is set */ + if (C_BAUD(tty)) + tty_port_raise_dtr_rts(port); + tty_port_set_active(port, 1); + pr_debug("%s(), O_NONBLOCK requested!\n", __func__); + return 0; + } + + if (C_CLOCAL(tty)) { + pr_debug("%s(), doing CLOCAL!\n", __func__); + do_clocal = 1; + } + + /* Wait for carrier detect and the line to become + * free (i.e., not in use by the callout). While we are in + * this loop, port->count is dropped by one, so that + * mgsl_close() knows when to free things. We restore it upon + * exit, either normal or abnormal. + */ + + retval = 0; + add_wait_queue(&port->open_wait, &wait); + + pr_debug("%s(%d):block_til_ready before block on %s open_count=%d\n", + __FILE__, __LINE__, tty->driver->name, port->count); + + spin_lock_irqsave(&port->lock, flags); + port->count--; + port->blocked_open++; + spin_unlock_irqrestore(&port->lock, flags); + + while (1) { + if (C_BAUD(tty) && tty_port_initialized(port)) + tty_port_raise_dtr_rts(port); + + set_current_state(TASK_INTERRUPTIBLE); + + if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { + retval = (port->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS; + break; + } + + /* + * Check if link is ready now. Even if CLOCAL is + * specified, we cannot return before the IrCOMM link is + * ready + */ + if ((do_clocal || tty_port_carrier_raised(port)) && + self->state == IRCOMM_TTY_READY) + { + break; + } + + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + pr_debug("%s(%d):block_til_ready blocking on %s open_count=%d\n", + __FILE__, __LINE__, tty->driver->name, port->count); + + schedule(); + } + + __set_current_state(TASK_RUNNING); + remove_wait_queue(&port->open_wait, &wait); + + spin_lock_irqsave(&port->lock, flags); + if (!tty_hung_up_p(filp)) + port->count++; + port->blocked_open--; + spin_unlock_irqrestore(&port->lock, flags); + + pr_debug("%s(%d):block_til_ready after blocking on %s open_count=%d\n", + __FILE__, __LINE__, tty->driver->name, port->count); + + if (!retval) + tty_port_set_active(port, 1); + + return retval; +} + + +static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) +{ + struct ircomm_tty_cb *self; + unsigned int line = tty->index; + + /* Check if instance already exists */ + self = hashbin_lock_find(ircomm_tty, line, NULL); + if (!self) { + /* No, so make new instance */ + self = kzalloc(sizeof(struct ircomm_tty_cb), GFP_KERNEL); + if (self == NULL) + return -ENOMEM; + + tty_port_init(&self->port); + self->port.ops = &ircomm_port_ops; + self->magic = IRCOMM_TTY_MAGIC; + self->flow = FLOW_STOP; + + self->line = line; + INIT_WORK(&self->tqueue, ircomm_tty_do_softint); + self->max_header_size = IRCOMM_TTY_HDR_UNINITIALISED; + self->max_data_size = IRCOMM_TTY_DATA_UNINITIALISED; + + /* Init some important stuff */ + init_timer(&self->watchdog_timer); + spin_lock_init(&self->spinlock); + + /* + * Force TTY into raw mode by default which is usually what + * we want for IrCOMM and IrLPT. This way applications will + * not have to twiddle with printcap etc. + * + * Note this is completely usafe and doesn't work properly + */ + tty->termios.c_iflag = 0; + tty->termios.c_oflag = 0; + + /* Insert into hash */ + hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL); + } + + tty->driver_data = self; + + return tty_port_install(&self->port, driver, tty); +} + +/* + * Function ircomm_tty_open (tty, filp) + * + * This routine is called when a particular tty device is opened. This + * routine is mandatory; if this routine is not filled in, the attempted + * open will fail with ENODEV. + */ +static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct ircomm_tty_cb *self = tty->driver_data; + unsigned long flags; + int ret; + + /* ++ is not atomic, so this should be protected - Jean II */ + spin_lock_irqsave(&self->port.lock, flags); + self->port.count++; + spin_unlock_irqrestore(&self->port.lock, flags); + tty_port_tty_set(&self->port, tty); + + pr_debug("%s(), %s%d, count = %d\n", __func__ , tty->driver->name, + self->line, self->port.count); + + /* Not really used by us, but lets do it anyway */ + self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; + + /* Check if this is a "normal" ircomm device, or an irlpt device */ + if (self->line < 0x10) { + self->service_type = IRCOMM_3_WIRE | IRCOMM_9_WIRE; + self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */ + /* Jan Kiszka -> add DSR/RI -> Conform to IrCOMM spec */ + self->settings.dce = IRCOMM_CTS | IRCOMM_CD | IRCOMM_DSR | IRCOMM_RI; /* Default line settings */ + pr_debug("%s(), IrCOMM device\n", __func__); + } else { + pr_debug("%s(), IrLPT device\n", __func__); + self->service_type = IRCOMM_3_WIRE_RAW; + self->settings.service_type = IRCOMM_3_WIRE_RAW; /* Default */ + } + + ret = ircomm_tty_startup(self); + if (ret) + return ret; + + ret = ircomm_tty_block_til_ready(self, tty, filp); + if (ret) { + pr_debug("%s(), returning after block_til_ready with %d\n", + __func__, ret); + + return ret; + } + return 0; +} + +/* + * Function ircomm_tty_close (tty, filp) + * + * This routine is called when a particular tty device is closed. + * + */ +static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + struct tty_port *port = &self->port; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + if (tty_port_close_start(port, tty, filp) == 0) + return; + + ircomm_tty_shutdown(self); + + tty_driver_flush_buffer(tty); + + tty_port_close_end(port, tty); + tty_port_tty_set(port, NULL); +} + +/* + * Function ircomm_tty_flush_buffer (tty) + * + * + * + */ +static void ircomm_tty_flush_buffer(struct tty_struct *tty) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + /* + * Let do_softint() do this to avoid race condition with + * do_softint() ;-) + */ + schedule_work(&self->tqueue); +} + +/* + * Function ircomm_tty_do_softint (work) + * + * We use this routine to give the write wakeup to the user at at a + * safe time (as fast as possible after write have completed). This + * can be compared to the Tx interrupt. + */ +static void ircomm_tty_do_softint(struct work_struct *work) +{ + struct ircomm_tty_cb *self = + container_of(work, struct ircomm_tty_cb, tqueue); + struct tty_struct *tty; + unsigned long flags; + struct sk_buff *skb, *ctrl_skb; + + if (!self || self->magic != IRCOMM_TTY_MAGIC) + return; + + tty = tty_port_tty_get(&self->port); + if (!tty) + return; + + /* Unlink control buffer */ + spin_lock_irqsave(&self->spinlock, flags); + + ctrl_skb = self->ctrl_skb; + self->ctrl_skb = NULL; + + spin_unlock_irqrestore(&self->spinlock, flags); + + /* Flush control buffer if any */ + if(ctrl_skb) { + if(self->flow == FLOW_START) + ircomm_control_request(self->ircomm, ctrl_skb); + /* Drop reference count - see ircomm_ttp_data_request(). */ + dev_kfree_skb(ctrl_skb); + } + + if (tty->hw_stopped) + goto put; + + /* Unlink transmit buffer */ + spin_lock_irqsave(&self->spinlock, flags); + + skb = self->tx_skb; + self->tx_skb = NULL; + + spin_unlock_irqrestore(&self->spinlock, flags); + + /* Flush transmit buffer if any */ + if (skb) { + ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL); + /* Drop reference count - see ircomm_ttp_data_request(). */ + dev_kfree_skb(skb); + } + + /* Check if user (still) wants to be waken up */ + tty_wakeup(tty); +put: + tty_kref_put(tty); +} + +/* + * Function ircomm_tty_write (tty, buf, count) + * + * This routine is called by the kernel to write a series of characters + * to the tty device. The characters may come from user space or kernel + * space. This routine will return the number of characters actually + * accepted for writing. This routine is mandatory. + */ +static int ircomm_tty_write(struct tty_struct *tty, + const unsigned char *buf, int count) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + unsigned long flags; + struct sk_buff *skb; + int tailroom = 0; + int len = 0; + int size; + + pr_debug("%s(), count=%d, hw_stopped=%d\n", __func__ , count, + tty->hw_stopped); + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + /* We may receive packets from the TTY even before we have finished + * our setup. Not cool. + * The problem is that we don't know the final header and data size + * to create the proper skb, so any skb we would create would have + * bogus header and data size, so need care. + * We use a bogus header size to safely detect this condition. + * Another problem is that hw_stopped was set to 0 way before it + * should be, so we would drop this skb. It should now be fixed. + * One option is to not accept data until we are properly setup. + * But, I suspect that when it happens, the ppp line discipline + * just "drops" the data, which might screw up connect scripts. + * The second option is to create a "safe skb", with large header + * and small size (see ircomm_tty_open() for values). + * We just need to make sure that when the real values get filled, + * we don't mess up the original "safe skb" (see tx_data_size). + * Jean II */ + if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) { + pr_debug("%s() : not initialised\n", __func__); +#ifdef IRCOMM_NO_TX_BEFORE_INIT + /* We didn't consume anything, TTY will retry */ + return 0; +#endif + } + + if (count < 1) + return 0; + + /* Protect our manipulation of self->tx_skb and related */ + spin_lock_irqsave(&self->spinlock, flags); + + /* Fetch current transmit buffer */ + skb = self->tx_skb; + + /* + * Send out all the data we get, possibly as multiple fragmented + * frames, but this will only happen if the data is larger than the + * max data size. The normal case however is just the opposite, and + * this function may be called multiple times, and will then actually + * defragment the data and send it out as one packet as soon as + * possible, but at a safer point in time + */ + while (count) { + size = count; + + /* Adjust data size to the max data size */ + if (size > self->max_data_size) + size = self->max_data_size; + + /* + * Do we already have a buffer ready for transmit, or do + * we need to allocate a new frame + */ + if (skb) { + /* + * Any room for more data at the end of the current + * transmit buffer? Cannot use skb_tailroom, since + * dev_alloc_skb gives us a larger skb than we + * requested + * Note : use tx_data_size, because max_data_size + * may have changed and we don't want to overwrite + * the skb. - Jean II + */ + if ((tailroom = (self->tx_data_size - skb->len)) > 0) { + /* Adjust data to tailroom */ + if (size > tailroom) + size = tailroom; + } else { + /* + * Current transmit frame is full, so break + * out, so we can send it as soon as possible + */ + break; + } + } else { + /* Prepare a full sized frame */ + skb = alloc_skb(self->max_data_size+ + self->max_header_size, + GFP_ATOMIC); + if (!skb) { + spin_unlock_irqrestore(&self->spinlock, flags); + return -ENOBUFS; + } + skb_reserve(skb, self->max_header_size); + self->tx_skb = skb; + /* Remember skb size because max_data_size may + * change later on - Jean II */ + self->tx_data_size = self->max_data_size; + } + + /* Copy data */ + skb_put_data(skb, buf + len, size); + + count -= size; + len += size; + } + + spin_unlock_irqrestore(&self->spinlock, flags); + + /* + * Schedule a new thread which will transmit the frame as soon + * as possible, but at a safe point in time. We do this so the + * "user" can give us data multiple times, as PPP does (because of + * its 256 byte tx buffer). We will then defragment and send out + * all this data as one single packet. + */ + schedule_work(&self->tqueue); + + return len; +} + +/* + * Function ircomm_tty_write_room (tty) + * + * This routine returns the numbers of characters the tty driver will + * accept for queuing to be written. This number is subject to change as + * output buffers get emptied, or if the output flow control is acted. + */ +static int ircomm_tty_write_room(struct tty_struct *tty) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + unsigned long flags; + int ret; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + +#ifdef IRCOMM_NO_TX_BEFORE_INIT + /* max_header_size tells us if the channel is initialised or not. */ + if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) + /* Don't bother us yet */ + return 0; +#endif + + /* Check if we are allowed to transmit any data. + * hw_stopped is the regular flow control. + * Jean II */ + if (tty->hw_stopped) + ret = 0; + else { + spin_lock_irqsave(&self->spinlock, flags); + if (self->tx_skb) + ret = self->tx_data_size - self->tx_skb->len; + else + ret = self->max_data_size; + spin_unlock_irqrestore(&self->spinlock, flags); + } + pr_debug("%s(), ret=%d\n", __func__ , ret); + + return ret; +} + +/* + * Function ircomm_tty_wait_until_sent (tty, timeout) + * + * This routine waits until the device has written out all of the + * characters in its transmitter FIFO. + */ +static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + unsigned long orig_jiffies, poll_time; + unsigned long flags; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + orig_jiffies = jiffies; + + /* Set poll time to 200 ms */ + poll_time = msecs_to_jiffies(200); + if (timeout) + poll_time = min_t(unsigned long, timeout, poll_time); + + spin_lock_irqsave(&self->spinlock, flags); + while (self->tx_skb && self->tx_skb->len) { + spin_unlock_irqrestore(&self->spinlock, flags); + schedule_timeout_interruptible(poll_time); + spin_lock_irqsave(&self->spinlock, flags); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } + spin_unlock_irqrestore(&self->spinlock, flags); + __set_current_state(TASK_RUNNING); +} + +/* + * Function ircomm_tty_throttle (tty) + * + * This routine notifies the tty driver that input buffers for the line + * discipline are close to full, and it should somehow signal that no + * more characters should be sent to the tty. + */ +static void ircomm_tty_throttle(struct tty_struct *tty) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + /* Software flow control? */ + if (I_IXOFF(tty)) + ircomm_tty_send_xchar(tty, STOP_CHAR(tty)); + + /* Hardware flow control? */ + if (C_CRTSCTS(tty)) { + self->settings.dte &= ~IRCOMM_RTS; + self->settings.dte |= IRCOMM_DELTA_RTS; + + ircomm_param_request(self, IRCOMM_DTE, TRUE); + } + + ircomm_flow_request(self->ircomm, FLOW_STOP); +} + +/* + * Function ircomm_tty_unthrottle (tty) + * + * This routine notifies the tty drivers that it should signals that + * characters can now be sent to the tty without fear of overrunning the + * input buffers of the line disciplines. + */ +static void ircomm_tty_unthrottle(struct tty_struct *tty) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + /* Using software flow control? */ + if (I_IXOFF(tty)) + ircomm_tty_send_xchar(tty, START_CHAR(tty)); + + /* Using hardware flow control? */ + if (C_CRTSCTS(tty)) { + self->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS); + + ircomm_param_request(self, IRCOMM_DTE, TRUE); + pr_debug("%s(), FLOW_START\n", __func__); + } + ircomm_flow_request(self->ircomm, FLOW_START); +} + +/* + * Function ircomm_tty_chars_in_buffer (tty) + * + * Indicates if there are any data in the buffer + * + */ +static int ircomm_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + unsigned long flags; + int len = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + spin_lock_irqsave(&self->spinlock, flags); + + if (self->tx_skb) + len = self->tx_skb->len; + + spin_unlock_irqrestore(&self->spinlock, flags); + + return len; +} + +static void ircomm_tty_shutdown(struct ircomm_tty_cb *self) +{ + unsigned long flags; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + if (!tty_port_initialized(&self->port)) + return; + tty_port_set_initialized(&self->port, 0); + + ircomm_tty_detach_cable(self); + + spin_lock_irqsave(&self->spinlock, flags); + + del_timer(&self->watchdog_timer); + + /* Free parameter buffer */ + if (self->ctrl_skb) { + dev_kfree_skb(self->ctrl_skb); + self->ctrl_skb = NULL; + } + + /* Free transmit buffer */ + if (self->tx_skb) { + dev_kfree_skb(self->tx_skb); + self->tx_skb = NULL; + } + + if (self->ircomm) { + ircomm_close(self->ircomm); + self->ircomm = NULL; + } + + spin_unlock_irqrestore(&self->spinlock, flags); +} + +/* + * Function ircomm_tty_hangup (tty) + * + * This routine notifies the tty driver that it should hangup the tty + * device. + * + */ +static void ircomm_tty_hangup(struct tty_struct *tty) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + struct tty_port *port = &self->port; + unsigned long flags; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + /* ircomm_tty_flush_buffer(tty); */ + ircomm_tty_shutdown(self); + + spin_lock_irqsave(&port->lock, flags); + if (port->tty) { + set_bit(TTY_IO_ERROR, &port->tty->flags); + tty_kref_put(port->tty); + } + port->tty = NULL; + port->count = 0; + spin_unlock_irqrestore(&port->lock, flags); + tty_port_set_active(port, 0); + + wake_up_interruptible(&port->open_wait); +} + +/* + * Function ircomm_tty_send_xchar (tty, ch) + * + * This routine is used to send a high-priority XON/XOFF character to + * the device. + */ +static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch) +{ + pr_debug("%s(), not impl\n", __func__); +} + +/* + * Function ircomm_tty_start (tty) + * + * This routine notifies the tty driver that it resume sending + * characters to the tty device. + */ +void ircomm_tty_start(struct tty_struct *tty) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + + ircomm_flow_request(self->ircomm, FLOW_START); +} + +/* + * Function ircomm_tty_stop (tty) + * + * This routine notifies the tty driver that it should stop outputting + * characters to the tty device. + */ +static void ircomm_tty_stop(struct tty_struct *tty) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + ircomm_flow_request(self->ircomm, FLOW_STOP); +} + +/* + * Function ircomm_check_modem_status (self) + * + * Check for any changes in the DCE's line settings. This function should + * be called whenever the dce parameter settings changes, to update the + * flow control settings and other things + */ +void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) +{ + struct tty_struct *tty; + int status; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + tty = tty_port_tty_get(&self->port); + + status = self->settings.dce; + + if (status & IRCOMM_DCE_DELTA_ANY) { + /*wake_up_interruptible(&self->delta_msr_wait);*/ + } + if (tty_port_check_carrier(&self->port) && (status & IRCOMM_DELTA_CD)) { + pr_debug("%s(), ircomm%d CD now %s...\n", __func__ , self->line, + (status & IRCOMM_CD) ? "on" : "off"); + + if (status & IRCOMM_CD) { + wake_up_interruptible(&self->port.open_wait); + } else { + pr_debug("%s(), Doing serial hangup..\n", __func__); + if (tty) + tty_hangup(tty); + + /* Hangup will remote the tty, so better break out */ + goto put; + } + } + if (tty && tty_port_cts_enabled(&self->port)) { + if (tty->hw_stopped) { + if (status & IRCOMM_CTS) { + pr_debug("%s(), CTS tx start...\n", __func__); + tty->hw_stopped = 0; + + /* Wake up processes blocked on open */ + wake_up_interruptible(&self->port.open_wait); + + schedule_work(&self->tqueue); + goto put; + } + } else { + if (!(status & IRCOMM_CTS)) { + pr_debug("%s(), CTS tx stop...\n", __func__); + tty->hw_stopped = 1; + } + } + } +put: + tty_kref_put(tty); +} + +/* + * Function ircomm_tty_data_indication (instance, sap, skb) + * + * Handle incoming data, and deliver it to the line discipline + * + */ +static int ircomm_tty_data_indication(void *instance, void *sap, + struct sk_buff *skb) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + struct tty_struct *tty; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + IRDA_ASSERT(skb != NULL, return -1;); + + tty = tty_port_tty_get(&self->port); + if (!tty) { + pr_debug("%s(), no tty!\n", __func__); + return 0; + } + + /* + * If we receive data when hardware is stopped then something is wrong. + * We try to poll the peers line settings to check if we are up todate. + * Devices like WinCE can do this, and since they don't send any + * params, we can just as well declare the hardware for running. + */ + if (tty->hw_stopped && (self->flow == FLOW_START)) { + pr_debug("%s(), polling for line settings!\n", __func__); + ircomm_param_request(self, IRCOMM_POLL, TRUE); + + /* We can just as well declare the hardware for running */ + ircomm_tty_send_initial_parameters(self); + ircomm_tty_link_established(self); + } + tty_kref_put(tty); + + /* + * Use flip buffer functions since the code may be called from interrupt + * context + */ + tty_insert_flip_string(&self->port, skb->data, skb->len); + tty_flip_buffer_push(&self->port); + + /* No need to kfree_skb - see ircomm_ttp_data_indication() */ + + return 0; +} + +/* + * Function ircomm_tty_control_indication (instance, sap, skb) + * + * Parse all incoming parameters (easy!) + * + */ +static int ircomm_tty_control_indication(void *instance, void *sap, + struct sk_buff *skb) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + int clen; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + IRDA_ASSERT(skb != NULL, return -1;); + + clen = skb->data[0]; + + irda_param_extract_all(self, skb->data+1, IRDA_MIN(skb->len-1, clen), + &ircomm_param_info); + + /* No need to kfree_skb - see ircomm_control_indication() */ + + return 0; +} + +/* + * Function ircomm_tty_flow_indication (instance, sap, cmd) + * + * This function is called by IrTTP when it wants us to slow down the + * transmission of data. We just mark the hardware as stopped, and wait + * for IrTTP to notify us that things are OK again. + */ +static void ircomm_tty_flow_indication(void *instance, void *sap, + LOCAL_FLOW cmd) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + struct tty_struct *tty; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + tty = tty_port_tty_get(&self->port); + + switch (cmd) { + case FLOW_START: + pr_debug("%s(), hw start!\n", __func__); + if (tty) + tty->hw_stopped = 0; + + /* ircomm_tty_do_softint will take care of the rest */ + schedule_work(&self->tqueue); + break; + default: /* If we get here, something is very wrong, better stop */ + case FLOW_STOP: + pr_debug("%s(), hw stopped!\n", __func__); + if (tty) + tty->hw_stopped = 1; + break; + } + + tty_kref_put(tty); + self->flow = cmd; +} + +#ifdef CONFIG_PROC_FS +static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) +{ + struct tty_struct *tty; + char sep; + + seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]); + + seq_puts(m, "Service type: "); + if (self->service_type & IRCOMM_9_WIRE) + seq_puts(m, "9_WIRE"); + else if (self->service_type & IRCOMM_3_WIRE) + seq_puts(m, "3_WIRE"); + else if (self->service_type & IRCOMM_3_WIRE_RAW) + seq_puts(m, "3_WIRE_RAW"); + else + seq_puts(m, "No common service type!\n"); + seq_putc(m, '\n'); + + seq_printf(m, "Port name: %s\n", self->settings.port_name); + + seq_printf(m, "DTE status:"); + sep = ' '; + if (self->settings.dte & IRCOMM_RTS) { + seq_printf(m, "%cRTS", sep); + sep = '|'; + } + if (self->settings.dte & IRCOMM_DTR) { + seq_printf(m, "%cDTR", sep); + sep = '|'; + } + seq_putc(m, '\n'); + + seq_puts(m, "DCE status:"); + sep = ' '; + if (self->settings.dce & IRCOMM_CTS) { + seq_printf(m, "%cCTS", sep); + sep = '|'; + } + if (self->settings.dce & IRCOMM_DSR) { + seq_printf(m, "%cDSR", sep); + sep = '|'; + } + if (self->settings.dce & IRCOMM_CD) { + seq_printf(m, "%cCD", sep); + sep = '|'; + } + if (self->settings.dce & IRCOMM_RI) { + seq_printf(m, "%cRI", sep); + sep = '|'; + } + seq_putc(m, '\n'); + + seq_puts(m, "Configuration: "); + if (!self->settings.null_modem) + seq_puts(m, "DTE <-> DCE\n"); + else + seq_puts(m, "DTE <-> DTE (null modem emulation)\n"); + + seq_printf(m, "Data rate: %d\n", self->settings.data_rate); + + seq_puts(m, "Flow control:"); + sep = ' '; + if (self->settings.flow_control & IRCOMM_XON_XOFF_IN) { + seq_printf(m, "%cXON_XOFF_IN", sep); + sep = '|'; + } + if (self->settings.flow_control & IRCOMM_XON_XOFF_OUT) { + seq_printf(m, "%cXON_XOFF_OUT", sep); + sep = '|'; + } + if (self->settings.flow_control & IRCOMM_RTS_CTS_IN) { + seq_printf(m, "%cRTS_CTS_IN", sep); + sep = '|'; + } + if (self->settings.flow_control & IRCOMM_RTS_CTS_OUT) { + seq_printf(m, "%cRTS_CTS_OUT", sep); + sep = '|'; + } + if (self->settings.flow_control & IRCOMM_DSR_DTR_IN) { + seq_printf(m, "%cDSR_DTR_IN", sep); + sep = '|'; + } + if (self->settings.flow_control & IRCOMM_DSR_DTR_OUT) { + seq_printf(m, "%cDSR_DTR_OUT", sep); + sep = '|'; + } + if (self->settings.flow_control & IRCOMM_ENQ_ACK_IN) { + seq_printf(m, "%cENQ_ACK_IN", sep); + sep = '|'; + } + if (self->settings.flow_control & IRCOMM_ENQ_ACK_OUT) { + seq_printf(m, "%cENQ_ACK_OUT", sep); + sep = '|'; + } + seq_putc(m, '\n'); + + seq_puts(m, "Flags:"); + sep = ' '; + if (tty_port_cts_enabled(&self->port)) { + seq_printf(m, "%cASYNC_CTS_FLOW", sep); + sep = '|'; + } + if (tty_port_check_carrier(&self->port)) { + seq_printf(m, "%cASYNC_CHECK_CD", sep); + sep = '|'; + } + if (tty_port_initialized(&self->port)) { + seq_printf(m, "%cASYNC_INITIALIZED", sep); + sep = '|'; + } + if (self->port.flags & ASYNC_LOW_LATENCY) { + seq_printf(m, "%cASYNC_LOW_LATENCY", sep); + sep = '|'; + } + if (tty_port_active(&self->port)) { + seq_printf(m, "%cASYNC_NORMAL_ACTIVE", sep); + sep = '|'; + } + seq_putc(m, '\n'); + + seq_printf(m, "Role: %s\n", self->client ? "client" : "server"); + seq_printf(m, "Open count: %d\n", self->port.count); + seq_printf(m, "Max data size: %d\n", self->max_data_size); + seq_printf(m, "Max header size: %d\n", self->max_header_size); + + tty = tty_port_tty_get(&self->port); + if (tty) { + seq_printf(m, "Hardware: %s\n", + tty->hw_stopped ? "Stopped" : "Running"); + tty_kref_put(tty); + } +} + +static int ircomm_tty_proc_show(struct seq_file *m, void *v) +{ + struct ircomm_tty_cb *self; + unsigned long flags; + + spin_lock_irqsave(&ircomm_tty->hb_spinlock, flags); + + self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty); + while (self != NULL) { + if (self->magic != IRCOMM_TTY_MAGIC) + break; + + ircomm_tty_line_info(self, m); + self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty); + } + spin_unlock_irqrestore(&ircomm_tty->hb_spinlock, flags); + return 0; +} + +static int ircomm_tty_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, ircomm_tty_proc_show, NULL); +} + +static const struct file_operations ircomm_tty_proc_fops = { + .owner = THIS_MODULE, + .open = ircomm_tty_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* CONFIG_PROC_FS */ + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("IrCOMM serial TTY driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV_MAJOR(IRCOMM_TTY_MAJOR); + +module_init(ircomm_tty_init); +module_exit(ircomm_tty_cleanup); diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c b/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c new file mode 100644 index 000000000000..0a411019c098 --- /dev/null +++ b/drivers/staging/irda/net/ircomm/ircomm_tty_attach.c @@ -0,0 +1,987 @@ +/********************************************************************* + * + * Filename: ircomm_tty_attach.c + * Version: + * Description: Code for attaching the serial driver to IrCOMM + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sat Jun 5 17:42:00 1999 + * Modified at: Tue Jan 4 14:20:49 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +static void ircomm_tty_ias_register(struct ircomm_tty_cb *self); +static void ircomm_tty_discovery_indication(discinfo_t *discovery, + DISCOVERY_MODE mode, + void *priv); +static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, + struct ias_value *value, void *priv); +static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, + int timeout); +static void ircomm_tty_watchdog_timer_expired(void *data); + +static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info); +static int ircomm_tty_state_search(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info); +static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info); +static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info); +static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info); +static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info); + +const char *const ircomm_tty_state[] = { + "IRCOMM_TTY_IDLE", + "IRCOMM_TTY_SEARCH", + "IRCOMM_TTY_QUERY_PARAMETERS", + "IRCOMM_TTY_QUERY_LSAP_SEL", + "IRCOMM_TTY_SETUP", + "IRCOMM_TTY_READY", + "*** ERROR *** ", +}; + +static const char *const ircomm_tty_event[] __maybe_unused = { + "IRCOMM_TTY_ATTACH_CABLE", + "IRCOMM_TTY_DETACH_CABLE", + "IRCOMM_TTY_DATA_REQUEST", + "IRCOMM_TTY_DATA_INDICATION", + "IRCOMM_TTY_DISCOVERY_REQUEST", + "IRCOMM_TTY_DISCOVERY_INDICATION", + "IRCOMM_TTY_CONNECT_CONFIRM", + "IRCOMM_TTY_CONNECT_INDICATION", + "IRCOMM_TTY_DISCONNECT_REQUEST", + "IRCOMM_TTY_DISCONNECT_INDICATION", + "IRCOMM_TTY_WD_TIMER_EXPIRED", + "IRCOMM_TTY_GOT_PARAMETERS", + "IRCOMM_TTY_GOT_LSAPSEL", + "*** ERROR ****", +}; + +static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, + struct sk_buff *skb, struct ircomm_tty_info *info) = +{ + ircomm_tty_state_idle, + ircomm_tty_state_search, + ircomm_tty_state_query_parameters, + ircomm_tty_state_query_lsap_sel, + ircomm_tty_state_setup, + ircomm_tty_state_ready, +}; + +/* + * Function ircomm_tty_attach_cable (driver) + * + * Try to attach cable (IrCOMM link). This function will only return + * when the link has been connected, or if an error condition occurs. + * If success, the return value is the resulting service type. + */ +int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) +{ + struct tty_struct *tty; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + /* Check if somebody has already connected to us */ + if (ircomm_is_connected(self->ircomm)) { + pr_debug("%s(), already connected!\n", __func__); + return 0; + } + + /* Make sure nobody tries to write before the link is up */ + tty = tty_port_tty_get(&self->port); + if (tty) { + tty->hw_stopped = 1; + tty_kref_put(tty); + } + + ircomm_tty_ias_register(self); + + ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL); + + return 0; +} + +/* + * Function ircomm_detach_cable (driver) + * + * Detach cable, or cable has been detached by peer + * + */ +void ircomm_tty_detach_cable(struct ircomm_tty_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + del_timer(&self->watchdog_timer); + + /* Remove discovery handler */ + if (self->ckey) { + irlmp_unregister_client(self->ckey); + self->ckey = NULL; + } + /* Remove IrCOMM hint bits */ + if (self->skey) { + irlmp_unregister_service(self->skey); + self->skey = NULL; + } + + if (self->iriap) { + iriap_close(self->iriap); + self->iriap = NULL; + } + + /* Remove LM-IAS object */ + if (self->obj) { + irias_delete_object(self->obj); + self->obj = NULL; + } + + ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL); + + /* Reset some values */ + self->daddr = self->saddr = 0; + self->dlsap_sel = self->slsap_sel = 0; + + memset(&self->settings, 0, sizeof(struct ircomm_params)); +} + +/* + * Function ircomm_tty_ias_register (self) + * + * Register with LM-IAS depending on which service type we are + * + */ +static void ircomm_tty_ias_register(struct ircomm_tty_cb *self) +{ + __u8 oct_seq[6]; + __u16 hints; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + /* Compute hint bits based on service */ + hints = irlmp_service_to_hint(S_COMM); + if (self->service_type & IRCOMM_3_WIRE_RAW) + hints |= irlmp_service_to_hint(S_PRINTER); + + /* Advertise IrCOMM hint bit in discovery */ + if (!self->skey) + self->skey = irlmp_register_service(hints); + /* Set up a discovery handler */ + if (!self->ckey) + self->ckey = irlmp_register_client(hints, + ircomm_tty_discovery_indication, + NULL, (void *) self); + + /* If already done, no need to do it again */ + if (self->obj) + return; + + if (self->service_type & IRCOMM_3_WIRE_RAW) { + /* Register IrLPT with LM-IAS */ + self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID); + irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel", + self->slsap_sel, IAS_KERNEL_ATTR); + } else { + /* Register IrCOMM with LM-IAS */ + self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID); + irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel", + self->slsap_sel, IAS_KERNEL_ATTR); + + /* Code the parameters into the buffer */ + irda_param_pack(oct_seq, "bbbbbb", + IRCOMM_SERVICE_TYPE, 1, self->service_type, + IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL); + + /* Register parameters with LM-IAS */ + irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6, + IAS_KERNEL_ATTR); + } + irias_insert_object(self->obj); +} + +/* + * Function ircomm_tty_ias_unregister (self) + * + * Remove our IAS object and client hook while connected. + * + */ +static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self) +{ + /* Remove LM-IAS object now so it is not reused. + * IrCOMM deals very poorly with multiple incoming connections. + * It should looks a lot more like IrNET, and "dup" a server TSAP + * to the application TSAP (based on various rules). + * This is a cheap workaround allowing multiple clients to + * connect to us. It will not always work. + * Each IrCOMM socket has an IAS entry. Incoming connection will + * pick the first one found. So, when we are fully connected, + * we remove our IAS entries so that the next IAS entry is used. + * We do that for *both* client and server, because a server + * can also create client instances. + * Jean II */ + if (self->obj) { + irias_delete_object(self->obj); + self->obj = NULL; + } + +#if 0 + /* Remove discovery handler. + * While we are connected, we no longer need to receive + * discovery events. This would be the case if there is + * multiple IrLAP interfaces. Jean II */ + if (self->ckey) { + irlmp_unregister_client(self->ckey); + self->ckey = NULL; + } +#endif +} + +/* + * Function ircomm_send_initial_parameters (self) + * + * Send initial parameters to the remote IrCOMM device. These parameters + * must be sent before any data. + */ +int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self) +{ + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (self->service_type & IRCOMM_3_WIRE_RAW) + return 0; + + /* + * Set default values, but only if the application for some reason + * haven't set them already + */ + pr_debug("%s(), data-rate = %d\n", __func__ , + self->settings.data_rate); + if (!self->settings.data_rate) + self->settings.data_rate = 9600; + pr_debug("%s(), data-format = %d\n", __func__ , + self->settings.data_format); + if (!self->settings.data_format) + self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */ + + pr_debug("%s(), flow-control = %d\n", __func__ , + self->settings.flow_control); + /*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/ + + /* Do not set delta values for the initial parameters */ + self->settings.dte = IRCOMM_DTR | IRCOMM_RTS; + + /* Only send service type parameter when we are the client */ + if (self->client) + ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE); + ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE); + ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE); + + /* For a 3 wire service, we just flush the last parameter and return */ + if (self->settings.service_type == IRCOMM_3_WIRE) { + ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE); + return 0; + } + + /* Only 9-wire service types continue here */ + ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE); +#if 0 + ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE); + ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE); +#endif + /* Notify peer that we are ready to receive data */ + ircomm_param_request(self, IRCOMM_DTE, TRUE); + + return 0; +} + +/* + * Function ircomm_tty_discovery_indication (discovery) + * + * Remote device is discovered, try query the remote IAS to see which + * device it is, and which services it has. + * + */ +static void ircomm_tty_discovery_indication(discinfo_t *discovery, + DISCOVERY_MODE mode, + void *priv) +{ + struct ircomm_tty_cb *self; + struct ircomm_tty_info info; + + /* Important note : + * We need to drop all passive discoveries. + * The LSAP management of IrComm is deficient and doesn't deal + * with the case of two instance connecting to each other + * simultaneously (it will deadlock in LMP). + * The proper fix would be to use the same technique as in IrNET, + * to have one server socket and separate instances for the + * connecting/connected socket. + * The workaround is to drop passive discovery, which drastically + * reduce the probability of this happening. + * Jean II */ + if(mode == DISCOVERY_PASSIVE) + return; + + info.daddr = discovery->daddr; + info.saddr = discovery->saddr; + + self = priv; + ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION, + NULL, &info); +} + +/* + * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb) + * + * Link disconnected + * + */ +void ircomm_tty_disconnect_indication(void *instance, void *sap, + LM_REASON reason, + struct sk_buff *skb) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + struct tty_struct *tty; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + tty = tty_port_tty_get(&self->port); + if (!tty) + return; + + /* This will stop control data transfers */ + self->flow = FLOW_STOP; + + /* Stop data transfers */ + tty->hw_stopped = 1; + + ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL, + NULL); + tty_kref_put(tty); +} + +/* + * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv) + * + * Got result from the IAS query we make + * + */ +static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, + struct ias_value *value, + void *priv) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + /* We probably don't need to make any more queries */ + iriap_close(self->iriap); + self->iriap = NULL; + + /* Check if request succeeded */ + if (result != IAS_SUCCESS) { + pr_debug("%s(), got NULL value!\n", __func__); + return; + } + + switch (value->type) { + case IAS_OCT_SEQ: + pr_debug("%s(), got octet sequence\n", __func__); + + irda_param_extract_all(self, value->t.oct_seq, value->len, + &ircomm_param_info); + + ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL, + NULL); + break; + case IAS_INTEGER: + /* Got LSAP selector */ + pr_debug("%s(), got lsapsel = %d\n", __func__ , + value->t.integer); + + if (value->t.integer == -1) { + pr_debug("%s(), invalid value!\n", __func__); + } else + self->dlsap_sel = value->t.integer; + + ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL); + break; + case IAS_MISSING: + pr_debug("%s(), got IAS_MISSING\n", __func__); + break; + default: + pr_debug("%s(), got unknown type!\n", __func__); + break; + } + irias_delete_value(value); +} + +/* + * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb) + * + * Connection confirmed + * + */ +void ircomm_tty_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_data_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + self->client = TRUE; + self->max_data_size = max_data_size; + self->max_header_size = max_header_size; + self->flow = FLOW_START; + + ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL); + + /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */ +} + +/* + * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size, + * skb) + * + * we are discovered and being requested to connect by remote device ! + * + */ +void ircomm_tty_connect_indication(void *instance, void *sap, + struct qos_info *qos, + __u32 max_data_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; + int clen; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + self->client = FALSE; + self->max_data_size = max_data_size; + self->max_header_size = max_header_size; + self->flow = FLOW_START; + + clen = skb->data[0]; + if (clen) + irda_param_extract_all(self, skb->data+1, + IRDA_MIN(skb->len, clen), + &ircomm_param_info); + + ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL); + + /* No need to kfree_skb - see ircomm_ttp_connect_indication() */ +} + +/* + * Function ircomm_tty_link_established (self) + * + * Called when the IrCOMM link is established + * + */ +void ircomm_tty_link_established(struct ircomm_tty_cb *self) +{ + struct tty_struct *tty; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + tty = tty_port_tty_get(&self->port); + if (!tty) + return; + + del_timer(&self->watchdog_timer); + + /* + * IrCOMM link is now up, and if we are not using hardware + * flow-control, then declare the hardware as running. Otherwise we + * will have to wait for the peer device (DCE) to raise the CTS + * line. + */ + if (tty_port_cts_enabled(&self->port) && + ((self->settings.dce & IRCOMM_CTS) == 0)) { + pr_debug("%s(), waiting for CTS ...\n", __func__); + goto put; + } else { + pr_debug("%s(), starting hardware!\n", __func__); + + tty->hw_stopped = 0; + + /* Wake up processes blocked on open */ + wake_up_interruptible(&self->port.open_wait); + } + + schedule_work(&self->tqueue); +put: + tty_kref_put(tty); +} + +/* + * Function ircomm_tty_start_watchdog_timer (self, timeout) + * + * Start the watchdog timer. This timer is used to make sure that any + * connection attempt is successful, and if not, we will retry after + * the timeout + */ +static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, + int timeout) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + irda_start_timer(&self->watchdog_timer, timeout, (void *) self, + ircomm_tty_watchdog_timer_expired); +} + +/* + * Function ircomm_tty_watchdog_timer_expired (data) + * + * Called when the connect procedure have taken to much time. + * + */ +static void ircomm_tty_watchdog_timer_expired(void *data) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL); +} + + +/* + * Function ircomm_tty_do_event (self, event, skb) + * + * Process event + * + */ +int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, + struct sk_buff *skb, struct ircomm_tty_info *info) +{ + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); + + return (*state[self->state])(self, event, skb, info); +} + +/* + * Function ircomm_tty_next_state (self, state) + * + * Switch state + * + */ +static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state) +{ + /* + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); + + pr_debug("%s: next state=%s, service type=%d\n", __func__ , + ircomm_tty_state[self->state], self->service_type); + */ + self->state = state; +} + +/* + * Function ircomm_tty_state_idle (self, event, skb, info) + * + * Just hanging around + * + */ +static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info) +{ + int ret = 0; + + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); + switch (event) { + case IRCOMM_TTY_ATTACH_CABLE: + /* Try to discover any remote devices */ + ircomm_tty_start_watchdog_timer(self, 3*HZ); + ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); + + irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); + break; + case IRCOMM_TTY_DISCOVERY_INDICATION: + self->daddr = info->daddr; + self->saddr = info->saddr; + + if (self->iriap) { + net_warn_ratelimited("%s(), busy with a previous query\n", + __func__); + return -EBUSY; + } + + self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, + ircomm_tty_getvalue_confirm); + + iriap_getvaluebyclass_request(self->iriap, + self->saddr, self->daddr, + "IrDA:IrCOMM", "Parameters"); + + ircomm_tty_start_watchdog_timer(self, 3*HZ); + ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS); + break; + case IRCOMM_TTY_CONNECT_INDICATION: + del_timer(&self->watchdog_timer); + + /* Accept connection */ + ircomm_connect_response(self->ircomm, NULL); + ircomm_tty_next_state(self, IRCOMM_TTY_READY); + break; + case IRCOMM_TTY_WD_TIMER_EXPIRED: + /* Just stay idle */ + break; + case IRCOMM_TTY_DETACH_CABLE: + ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); + break; + default: + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); + ret = -EINVAL; + } + return ret; +} + +/* + * Function ircomm_tty_state_search (self, event, skb, info) + * + * Trying to discover an IrCOMM device + * + */ +static int ircomm_tty_state_search(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info) +{ + int ret = 0; + + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); + + switch (event) { + case IRCOMM_TTY_DISCOVERY_INDICATION: + self->daddr = info->daddr; + self->saddr = info->saddr; + + if (self->iriap) { + net_warn_ratelimited("%s(), busy with a previous query\n", + __func__); + return -EBUSY; + } + + self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, + ircomm_tty_getvalue_confirm); + + if (self->service_type == IRCOMM_3_WIRE_RAW) { + iriap_getvaluebyclass_request(self->iriap, self->saddr, + self->daddr, "IrLPT", + "IrDA:IrLMP:LsapSel"); + ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL); + } else { + iriap_getvaluebyclass_request(self->iriap, self->saddr, + self->daddr, + "IrDA:IrCOMM", + "Parameters"); + + ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS); + } + ircomm_tty_start_watchdog_timer(self, 3*HZ); + break; + case IRCOMM_TTY_CONNECT_INDICATION: + del_timer(&self->watchdog_timer); + ircomm_tty_ias_unregister(self); + + /* Accept connection */ + ircomm_connect_response(self->ircomm, NULL); + ircomm_tty_next_state(self, IRCOMM_TTY_READY); + break; + case IRCOMM_TTY_WD_TIMER_EXPIRED: +#if 1 + /* Give up */ +#else + /* Try to discover any remote devices */ + ircomm_tty_start_watchdog_timer(self, 3*HZ); + irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); +#endif + break; + case IRCOMM_TTY_DETACH_CABLE: + ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); + break; + default: + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); + ret = -EINVAL; + } + return ret; +} + +/* + * Function ircomm_tty_state_query (self, event, skb, info) + * + * Querying the remote LM-IAS for IrCOMM parameters + * + */ +static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info) +{ + int ret = 0; + + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); + + switch (event) { + case IRCOMM_TTY_GOT_PARAMETERS: + if (self->iriap) { + net_warn_ratelimited("%s(), busy with a previous query\n", + __func__); + return -EBUSY; + } + + self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, + ircomm_tty_getvalue_confirm); + + iriap_getvaluebyclass_request(self->iriap, self->saddr, + self->daddr, "IrDA:IrCOMM", + "IrDA:TinyTP:LsapSel"); + + ircomm_tty_start_watchdog_timer(self, 3*HZ); + ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL); + break; + case IRCOMM_TTY_WD_TIMER_EXPIRED: + /* Go back to search mode */ + ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); + ircomm_tty_start_watchdog_timer(self, 3*HZ); + break; + case IRCOMM_TTY_CONNECT_INDICATION: + del_timer(&self->watchdog_timer); + ircomm_tty_ias_unregister(self); + + /* Accept connection */ + ircomm_connect_response(self->ircomm, NULL); + ircomm_tty_next_state(self, IRCOMM_TTY_READY); + break; + case IRCOMM_TTY_DETACH_CABLE: + ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); + break; + default: + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); + ret = -EINVAL; + } + return ret; +} + +/* + * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info) + * + * Query remote LM-IAS for the LSAP selector which we can connect to + * + */ +static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info) +{ + int ret = 0; + + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); + + switch (event) { + case IRCOMM_TTY_GOT_LSAPSEL: + /* Connect to remote device */ + ret = ircomm_connect_request(self->ircomm, self->dlsap_sel, + self->saddr, self->daddr, + NULL, self->service_type); + ircomm_tty_start_watchdog_timer(self, 3*HZ); + ircomm_tty_next_state(self, IRCOMM_TTY_SETUP); + break; + case IRCOMM_TTY_WD_TIMER_EXPIRED: + /* Go back to search mode */ + ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); + ircomm_tty_start_watchdog_timer(self, 3*HZ); + break; + case IRCOMM_TTY_CONNECT_INDICATION: + del_timer(&self->watchdog_timer); + ircomm_tty_ias_unregister(self); + + /* Accept connection */ + ircomm_connect_response(self->ircomm, NULL); + ircomm_tty_next_state(self, IRCOMM_TTY_READY); + break; + case IRCOMM_TTY_DETACH_CABLE: + ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); + break; + default: + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); + ret = -EINVAL; + } + return ret; +} + +/* + * Function ircomm_tty_state_setup (self, event, skb, info) + * + * Trying to connect + * + */ +static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info) +{ + int ret = 0; + + pr_debug("%s: state=%s, event=%s\n", __func__ , + ircomm_tty_state[self->state], ircomm_tty_event[event]); + + switch (event) { + case IRCOMM_TTY_CONNECT_CONFIRM: + del_timer(&self->watchdog_timer); + ircomm_tty_ias_unregister(self); + + /* + * Send initial parameters. This will also send out queued + * parameters waiting for the connection to come up + */ + ircomm_tty_send_initial_parameters(self); + ircomm_tty_link_established(self); + ircomm_tty_next_state(self, IRCOMM_TTY_READY); + break; + case IRCOMM_TTY_CONNECT_INDICATION: + del_timer(&self->watchdog_timer); + ircomm_tty_ias_unregister(self); + + /* Accept connection */ + ircomm_connect_response(self->ircomm, NULL); + ircomm_tty_next_state(self, IRCOMM_TTY_READY); + break; + case IRCOMM_TTY_WD_TIMER_EXPIRED: + /* Go back to search mode */ + ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); + ircomm_tty_start_watchdog_timer(self, 3*HZ); + break; + case IRCOMM_TTY_DETACH_CABLE: + /* ircomm_disconnect_request(self->ircomm, NULL); */ + ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); + break; + default: + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); + ret = -EINVAL; + } + return ret; +} + +/* + * Function ircomm_tty_state_ready (self, event, skb, info) + * + * IrCOMM is now connected + * + */ +static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, + IRCOMM_TTY_EVENT event, + struct sk_buff *skb, + struct ircomm_tty_info *info) +{ + int ret = 0; + + switch (event) { + case IRCOMM_TTY_DATA_REQUEST: + ret = ircomm_data_request(self->ircomm, skb); + break; + case IRCOMM_TTY_DETACH_CABLE: + ircomm_disconnect_request(self->ircomm, NULL); + ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); + break; + case IRCOMM_TTY_DISCONNECT_INDICATION: + ircomm_tty_ias_register(self); + ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); + ircomm_tty_start_watchdog_timer(self, 3*HZ); + + if (tty_port_check_carrier(&self->port)) { + /* Drop carrier */ + self->settings.dce = IRCOMM_DELTA_CD; + ircomm_tty_check_modem_status(self); + } else { + pr_debug("%s(), hanging up!\n", __func__); + tty_port_tty_hangup(&self->port, false); + } + break; + default: + pr_debug("%s(), unknown event: %s\n", __func__ , + ircomm_tty_event[event]); + ret = -EINVAL; + } + return ret; +} + diff --git a/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c b/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c new file mode 100644 index 000000000000..171c3dee760e --- /dev/null +++ b/drivers/staging/irda/net/ircomm/ircomm_tty_ioctl.c @@ -0,0 +1,291 @@ +/********************************************************************* + * + * Filename: ircomm_tty_ioctl.c + * Version: + * Description: + * Status: Experimental. + * Author: Dag Brattli + * Created at: Thu Jun 10 14:39:09 1999 + * Modified at: Wed Jan 5 14:45:43 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include +#include +#include + +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + +/* + * Function ircomm_tty_change_speed (driver) + * + * Change speed of the driver. If the remote device is a DCE, then this + * should make it change the speed of its serial port + */ +static void ircomm_tty_change_speed(struct ircomm_tty_cb *self, + struct tty_struct *tty) +{ + unsigned int cflag, cval; + int baud; + + if (!self->ircomm) + return; + + cflag = tty->termios.c_cflag; + + /* byte size and parity */ + switch (cflag & CSIZE) { + case CS5: cval = IRCOMM_WSIZE_5; break; + case CS6: cval = IRCOMM_WSIZE_6; break; + case CS7: cval = IRCOMM_WSIZE_7; break; + case CS8: cval = IRCOMM_WSIZE_8; break; + default: cval = IRCOMM_WSIZE_5; break; + } + if (cflag & CSTOPB) + cval |= IRCOMM_2_STOP_BIT; + + if (cflag & PARENB) + cval |= IRCOMM_PARITY_ENABLE; + if (!(cflag & PARODD)) + cval |= IRCOMM_PARITY_EVEN; + + /* Determine divisor based on baud rate */ + baud = tty_get_baud_rate(tty); + if (!baud) + baud = 9600; /* B0 transition handled in rs_set_termios */ + + self->settings.data_rate = baud; + ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE); + + /* CTS flow control flag and modem status interrupts */ + tty_port_set_cts_flow(&self->port, cflag & CRTSCTS); + if (cflag & CRTSCTS) { + self->settings.flow_control |= IRCOMM_RTS_CTS_IN; + /* This got me. Bummer. Jean II */ + if (self->service_type == IRCOMM_3_WIRE_RAW) + net_warn_ratelimited("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", + __func__); + } else { + self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN; + } + tty_port_set_check_carrier(&self->port, ~cflag & CLOCAL); + + self->settings.data_format = cval; + + ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE); + ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE); +} + +/* + * Function ircomm_tty_set_termios (tty, old_termios) + * + * This routine allows the tty driver to be notified when device's + * termios settings have changed. Note that a well-designed tty driver + * should be prepared to accept the case where old == NULL, and try to + * do something rational. + */ +void ircomm_tty_set_termios(struct tty_struct *tty, + struct ktermios *old_termios) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + unsigned int cflag = tty->termios.c_cflag; + + if ((cflag == old_termios->c_cflag) && + (RELEVANT_IFLAG(tty->termios.c_iflag) == + RELEVANT_IFLAG(old_termios->c_iflag))) + { + return; + } + + ircomm_tty_change_speed(self, tty); + + /* Handle transition to B0 status */ + if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { + self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS); + ircomm_param_request(self, IRCOMM_DTE, TRUE); + } + + /* Handle transition away from B0 status */ + if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { + self->settings.dte |= IRCOMM_DTR; + if (!C_CRTSCTS(tty) || !tty_throttled(tty)) + self->settings.dte |= IRCOMM_RTS; + ircomm_param_request(self, IRCOMM_DTE, TRUE); + } + + /* Handle turning off CRTSCTS */ + if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) + { + tty->hw_stopped = 0; + ircomm_tty_start(tty); + } +} + +/* + * Function ircomm_tty_tiocmget (tty) + * + * + * + */ +int ircomm_tty_tiocmget(struct tty_struct *tty) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + unsigned int result; + + if (tty_io_error(tty)) + return -EIO; + + result = ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0) + | ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0) + | ((self->settings.dce & IRCOMM_CD) ? TIOCM_CAR : 0) + | ((self->settings.dce & IRCOMM_RI) ? TIOCM_RNG : 0) + | ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0) + | ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0); + return result; +} + +/* + * Function ircomm_tty_tiocmset (tty, set, clear) + * + * + * + */ +int ircomm_tty_tiocmset(struct tty_struct *tty, + unsigned int set, unsigned int clear) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + + if (tty_io_error(tty)) + return -EIO; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); + + if (set & TIOCM_RTS) + self->settings.dte |= IRCOMM_RTS; + if (set & TIOCM_DTR) + self->settings.dte |= IRCOMM_DTR; + + if (clear & TIOCM_RTS) + self->settings.dte &= ~IRCOMM_RTS; + if (clear & TIOCM_DTR) + self->settings.dte &= ~IRCOMM_DTR; + + if ((set|clear) & TIOCM_RTS) + self->settings.dte |= IRCOMM_DELTA_RTS; + if ((set|clear) & TIOCM_DTR) + self->settings.dte |= IRCOMM_DELTA_DTR; + + ircomm_param_request(self, IRCOMM_DTE, TRUE); + + return 0; +} + +/* + * Function get_serial_info (driver, retinfo) + * + * + * + */ +static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self, + struct serial_struct __user *retinfo) +{ + struct serial_struct info; + + memset(&info, 0, sizeof(info)); + info.line = self->line; + info.flags = self->port.flags; + info.baud_base = self->settings.data_rate; + info.close_delay = self->port.close_delay; + info.closing_wait = self->port.closing_wait; + + /* For compatibility */ + info.type = PORT_16550A; + + if (copy_to_user(retinfo, &info, sizeof(*retinfo))) + return -EFAULT; + + return 0; +} + +/* + * Function set_serial_info (driver, new_info) + * + * + * + */ +static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self, + struct serial_struct __user *new_info) +{ + return 0; +} + +/* + * Function ircomm_tty_ioctl (tty, cmd, arg) + * + * + * + */ +int ircomm_tty_ioctl(struct tty_struct *tty, + unsigned int cmd, unsigned long arg) +{ + struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; + int ret = 0; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty_io_error(tty)) + return -EIO; + } + + switch (cmd) { + case TIOCGSERIAL: + ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg); + break; + case TIOCSSERIAL: + ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg); + break; + case TIOCMIWAIT: + pr_debug("(), TIOCMIWAIT, not impl!\n"); + break; + + case TIOCGICOUNT: + pr_debug("%s(), TIOCGICOUNT not impl!\n", __func__); + return 0; + default: + ret = -ENOIOCTLCMD; /* ioctls which we must ignore */ + } + return ret; +} + + + diff --git a/drivers/staging/irda/net/irda_device.c b/drivers/staging/irda/net/irda_device.c new file mode 100644 index 000000000000..890b90d055d5 --- /dev/null +++ b/drivers/staging/irda/net/irda_device.c @@ -0,0 +1,316 @@ +/********************************************************************* + * + * Filename: irda_device.c + * Version: 0.9 + * Description: Utility functions used by the device drivers + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sat Oct 9 09:22:27 1999 + * Modified at: Sun Jan 23 17:41:24 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2001 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +static void __irda_task_delete(struct irda_task *task); + +static hashbin_t *dongles = NULL; +static hashbin_t *tasks = NULL; + +static void irda_task_timer_expired(void *data); + +int __init irda_device_init( void) +{ + dongles = hashbin_new(HB_NOLOCK); + if (dongles == NULL) { + net_warn_ratelimited("IrDA: Can't allocate dongles hashbin!\n"); + return -ENOMEM; + } + spin_lock_init(&dongles->hb_spinlock); + + tasks = hashbin_new(HB_LOCK); + if (tasks == NULL) { + net_warn_ratelimited("IrDA: Can't allocate tasks hashbin!\n"); + hashbin_delete(dongles, NULL); + return -ENOMEM; + } + + /* We no longer initialise the driver ourselves here, we let + * the system do it for us... - Jean II */ + + return 0; +} + +static void leftover_dongle(void *arg) +{ + struct dongle_reg *reg = arg; + net_warn_ratelimited("IrDA: Dongle type %x not unregistered\n", + reg->type); +} + +void irda_device_cleanup(void) +{ + hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete); + + hashbin_delete(dongles, leftover_dongle); +} + +/* + * Function irda_device_set_media_busy (self, status) + * + * Called when we have detected that another station is transmitting + * in contention mode. + */ +void irda_device_set_media_busy(struct net_device *dev, int status) +{ + struct irlap_cb *self; + + pr_debug("%s(%s)\n", __func__, status ? "TRUE" : "FALSE"); + + self = (struct irlap_cb *) dev->atalk_ptr; + + /* Some drivers may enable the receive interrupt before calling + * irlap_open(), or they may disable the receive interrupt + * after calling irlap_close(). + * The IrDA stack is protected from this in irlap_driver_rcv(). + * However, the driver calls directly the wrapper, that calls + * us directly. Make sure we protect ourselves. + * Jean II */ + if (!self || self->magic != LAP_MAGIC) + return; + + if (status) { + self->media_busy = TRUE; + if (status == SMALL) + irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT); + else + irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT); + pr_debug("Media busy!\n"); + } else { + self->media_busy = FALSE; + irlap_stop_mbusy_timer(self); + } +} +EXPORT_SYMBOL(irda_device_set_media_busy); + + +/* + * Function irda_device_is_receiving (dev) + * + * Check if the device driver is currently receiving data + * + */ +int irda_device_is_receiving(struct net_device *dev) +{ + struct if_irda_req req; + int ret; + + if (!dev->netdev_ops->ndo_do_ioctl) { + net_err_ratelimited("%s: do_ioctl not impl. by device driver\n", + __func__); + return -1; + } + + ret = (dev->netdev_ops->ndo_do_ioctl)(dev, (struct ifreq *) &req, + SIOCGRECEIVING); + if (ret < 0) + return ret; + + return req.ifr_receiving; +} + +static void __irda_task_delete(struct irda_task *task) +{ + del_timer(&task->timer); + + kfree(task); +} + +static void irda_task_delete(struct irda_task *task) +{ + /* Unregister task */ + hashbin_remove(tasks, (long) task, NULL); + + __irda_task_delete(task); +} + +/* + * Function irda_task_kick (task) + * + * Tries to execute a task possible multiple times until the task is either + * finished, or askes for a timeout. When a task is finished, we do post + * processing, and notify the parent task, that is waiting for this task + * to complete. + */ +static int irda_task_kick(struct irda_task *task) +{ + int finished = TRUE; + int count = 0; + int timeout; + + IRDA_ASSERT(task != NULL, return -1;); + IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;); + + /* Execute task until it's finished, or askes for a timeout */ + do { + timeout = task->function(task); + if (count++ > 100) { + net_err_ratelimited("%s: error in task handler!\n", + __func__); + irda_task_delete(task); + return TRUE; + } + } while ((timeout == 0) && (task->state != IRDA_TASK_DONE)); + + if (timeout < 0) { + net_err_ratelimited("%s: Error executing task!\n", __func__); + irda_task_delete(task); + return TRUE; + } + + /* Check if we are finished */ + if (task->state == IRDA_TASK_DONE) { + del_timer(&task->timer); + + /* Do post processing */ + if (task->finished) + task->finished(task); + + /* Notify parent */ + if (task->parent) { + /* Check if parent is waiting for us to complete */ + if (task->parent->state == IRDA_TASK_CHILD_WAIT) { + task->parent->state = IRDA_TASK_CHILD_DONE; + + /* Stop timer now that we are here */ + del_timer(&task->parent->timer); + + /* Kick parent task */ + irda_task_kick(task->parent); + } + } + irda_task_delete(task); + } else if (timeout > 0) { + irda_start_timer(&task->timer, timeout, (void *) task, + irda_task_timer_expired); + finished = FALSE; + } else { + pr_debug("%s(), not finished, and no timeout!\n", + __func__); + finished = FALSE; + } + + return finished; +} + +/* + * Function irda_task_timer_expired (data) + * + * Task time has expired. We now try to execute task (again), and restart + * the timer if the task has not finished yet + */ +static void irda_task_timer_expired(void *data) +{ + struct irda_task *task; + + task = data; + + irda_task_kick(task); +} + +/* + * Function irda_device_setup (dev) + * + * This function should be used by low level device drivers in a similar way + * as ether_setup() is used by normal network device drivers + */ +static void irda_device_setup(struct net_device *dev) +{ + dev->hard_header_len = 0; + dev->addr_len = LAP_ALEN; + + dev->type = ARPHRD_IRDA; + dev->tx_queue_len = 8; /* Window size + 1 s-frame */ + + memset(dev->broadcast, 0xff, LAP_ALEN); + + dev->mtu = 2048; + dev->flags = IFF_NOARP; +} + +/* + * Funciton alloc_irdadev + * Allocates and sets up an IRDA device in a manner similar to + * alloc_etherdev. + */ +struct net_device *alloc_irdadev(int sizeof_priv) +{ + return alloc_netdev(sizeof_priv, "irda%d", NET_NAME_UNKNOWN, + irda_device_setup); +} +EXPORT_SYMBOL(alloc_irdadev); + +#ifdef CONFIG_ISA_DMA_API +/* + * Function setup_dma (idev, buffer, count, mode) + * + * Setup the DMA channel. Commonly used by LPC FIR drivers + * + */ +void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode) +{ + unsigned long flags; + + flags = claim_dma_lock(); + + disable_dma(channel); + clear_dma_ff(channel); + set_dma_mode(channel, mode); + set_dma_addr(channel, buffer); + set_dma_count(channel, count); + enable_dma(channel); + + release_dma_lock(flags); +} +EXPORT_SYMBOL(irda_setup_dma); +#endif diff --git a/drivers/staging/irda/net/iriap.c b/drivers/staging/irda/net/iriap.c new file mode 100644 index 000000000000..1138eaf5c682 --- /dev/null +++ b/drivers/staging/irda/net/iriap.c @@ -0,0 +1,1085 @@ +/********************************************************************* + * + * Filename: iriap.c + * Version: 0.8 + * Description: Information Access Protocol (IAP) + * Status: Experimental. + * Author: Dag Brattli + * Created at: Thu Aug 21 00:02:07 1997 + * Modified at: Sat Dec 25 16:42:42 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999 Dag Brattli , + * All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +/* FIXME: This one should go in irlmp.c */ +static const char *const ias_charset_types[] __maybe_unused = { + "CS_ASCII", + "CS_ISO_8859_1", + "CS_ISO_8859_2", + "CS_ISO_8859_3", + "CS_ISO_8859_4", + "CS_ISO_8859_5", + "CS_ISO_8859_6", + "CS_ISO_8859_7", + "CS_ISO_8859_8", + "CS_ISO_8859_9", + "CS_UNICODE" +}; + +static hashbin_t *iriap = NULL; +static void *service_handle; + +static void __iriap_close(struct iriap_cb *self); +static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode); +static void iriap_disconnect_indication(void *instance, void *sap, + LM_REASON reason, struct sk_buff *skb); +static void iriap_connect_indication(void *instance, void *sap, + struct qos_info *qos, __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb); +static void iriap_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, __u8 max_header_size, + struct sk_buff *skb); +static int iriap_data_indication(void *instance, void *sap, + struct sk_buff *skb); + +static void iriap_watchdog_timer_expired(void *data); + +static inline void iriap_start_watchdog_timer(struct iriap_cb *self, + int timeout) +{ + irda_start_timer(&self->watchdog_timer, timeout, self, + iriap_watchdog_timer_expired); +} + +static struct lock_class_key irias_objects_key; + +/* + * Function iriap_init (void) + * + * Initializes the IrIAP layer, called by the module initialization code + * in irmod.c + */ +int __init iriap_init(void) +{ + struct ias_object *obj; + struct iriap_cb *server; + __u8 oct_seq[6]; + __u16 hints; + + /* Allocate master array */ + iriap = hashbin_new(HB_LOCK); + if (!iriap) + return -ENOMEM; + + /* Object repository - defined in irias_object.c */ + irias_objects = hashbin_new(HB_LOCK); + if (!irias_objects) { + net_warn_ratelimited("%s: Can't allocate irias_objects hashbin!\n", + __func__); + hashbin_delete(iriap, NULL); + return -ENOMEM; + } + + lockdep_set_class_and_name(&irias_objects->hb_spinlock, &irias_objects_key, + "irias_objects"); + + /* + * Register some default services for IrLMP + */ + hints = irlmp_service_to_hint(S_COMPUTER); + service_handle = irlmp_register_service(hints); + + /* Register the Device object with LM-IAS */ + obj = irias_new_object("Device", IAS_DEVICE_ID); + irias_add_string_attrib(obj, "DeviceName", "Linux", IAS_KERNEL_ATTR); + + oct_seq[0] = 0x01; /* Version 1 */ + oct_seq[1] = 0x00; /* IAS support bits */ + oct_seq[2] = 0x00; /* LM-MUX support bits */ +#ifdef CONFIG_IRDA_ULTRA + oct_seq[2] |= 0x04; /* Connectionless Data support */ +#endif + irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3, + IAS_KERNEL_ATTR); + irias_insert_object(obj); + + /* + * Register server support with IrLMP so we can accept incoming + * connections + */ + server = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL); + if (!server) { + pr_debug("%s(), unable to open server\n", __func__); + return -1; + } + iriap_register_lsap(server, LSAP_IAS, IAS_SERVER); + + return 0; +} + +/* + * Function iriap_cleanup (void) + * + * Initializes the IrIAP layer, called by the module cleanup code in + * irmod.c + */ +void iriap_cleanup(void) +{ + irlmp_unregister_service(service_handle); + + hashbin_delete(iriap, (FREE_FUNC) __iriap_close); + hashbin_delete(irias_objects, (FREE_FUNC) __irias_delete_object); +} + +/* + * Function iriap_open (void) + * + * Opens an instance of the IrIAP layer, and registers with IrLMP + */ +struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv, + CONFIRM_CALLBACK callback) +{ + struct iriap_cb *self; + + self = kzalloc(sizeof(*self), GFP_ATOMIC); + if (!self) + return NULL; + + /* + * Initialize instance + */ + + self->magic = IAS_MAGIC; + self->mode = mode; + if (mode == IAS_CLIENT) { + if (iriap_register_lsap(self, slsap_sel, mode)) { + kfree(self); + return NULL; + } + } + + self->confirm = callback; + self->priv = priv; + + /* iriap_getvaluebyclass_request() will construct packets before + * we connect, so this must have a sane value... Jean II */ + self->max_header_size = LMP_MAX_HEADER; + + init_timer(&self->watchdog_timer); + + hashbin_insert(iriap, (irda_queue_t *) self, (long) self, NULL); + + /* Initialize state machines */ + iriap_next_client_state(self, S_DISCONNECT); + iriap_next_call_state(self, S_MAKE_CALL); + iriap_next_server_state(self, R_DISCONNECT); + iriap_next_r_connect_state(self, R_WAITING); + + return self; +} +EXPORT_SYMBOL(iriap_open); + +/* + * Function __iriap_close (self) + * + * Removes (deallocates) the IrIAP instance + * + */ +static void __iriap_close(struct iriap_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + del_timer(&self->watchdog_timer); + + if (self->request_skb) + dev_kfree_skb(self->request_skb); + + self->magic = 0; + + kfree(self); +} + +/* + * Function iriap_close (void) + * + * Closes IrIAP and deregisters with IrLMP + */ +void iriap_close(struct iriap_cb *self) +{ + struct iriap_cb *entry; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + if (self->lsap) { + irlmp_close_lsap(self->lsap); + self->lsap = NULL; + } + + entry = (struct iriap_cb *) hashbin_remove(iriap, (long) self, NULL); + IRDA_ASSERT(entry == self, return;); + + __iriap_close(self); +} +EXPORT_SYMBOL(iriap_close); + +static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode) +{ + notify_t notify; + + irda_notify_init(¬ify); + notify.connect_confirm = iriap_connect_confirm; + notify.connect_indication = iriap_connect_indication; + notify.disconnect_indication = iriap_disconnect_indication; + notify.data_indication = iriap_data_indication; + notify.instance = self; + if (mode == IAS_CLIENT) + strcpy(notify.name, "IrIAS cli"); + else + strcpy(notify.name, "IrIAS srv"); + + self->lsap = irlmp_open_lsap(slsap_sel, ¬ify, 0); + if (self->lsap == NULL) { + net_err_ratelimited("%s: Unable to allocated LSAP!\n", + __func__); + return -1; + } + self->slsap_sel = self->lsap->slsap_sel; + + return 0; +} + +/* + * Function iriap_disconnect_indication (handle, reason) + * + * Got disconnect, so clean up everything associated with this connection + * + */ +static void iriap_disconnect_indication(void *instance, void *sap, + LM_REASON reason, + struct sk_buff *skb) +{ + struct iriap_cb *self; + + pr_debug("%s(), reason=%s [%d]\n", __func__, + irlmp_reason_str(reason), reason); + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + IRDA_ASSERT(iriap != NULL, return;); + + del_timer(&self->watchdog_timer); + + /* Not needed */ + if (skb) + dev_kfree_skb(skb); + + if (self->mode == IAS_CLIENT) { + pr_debug("%s(), disconnect as client\n", __func__); + + + iriap_do_client_event(self, IAP_LM_DISCONNECT_INDICATION, + NULL); + /* + * Inform service user that the request failed by sending + * it a NULL value. Warning, the client might close us, so + * remember no to use self anymore after calling confirm + */ + if (self->confirm) + self->confirm(IAS_DISCONNECT, 0, NULL, self->priv); + } else { + pr_debug("%s(), disconnect as server\n", __func__); + iriap_do_server_event(self, IAP_LM_DISCONNECT_INDICATION, + NULL); + iriap_close(self); + } +} + +/* + * Function iriap_disconnect_request (handle) + */ +static void iriap_disconnect_request(struct iriap_cb *self) +{ + struct sk_buff *tx_skb; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); + if (tx_skb == NULL) { + pr_debug("%s(), Could not allocate an sk_buff of length %d\n", + __func__, LMP_MAX_HEADER); + return; + } + + /* + * Reserve space for MUX control and LAP header + */ + skb_reserve(tx_skb, LMP_MAX_HEADER); + + irlmp_disconnect_request(self->lsap, tx_skb); +} + +/* + * Function iriap_getvaluebyclass (addr, name, attr) + * + * Retrieve all values from attribute in all objects with given class + * name + */ +int iriap_getvaluebyclass_request(struct iriap_cb *self, + __u32 saddr, __u32 daddr, + char *name, char *attr) +{ + struct sk_buff *tx_skb; + int name_len, attr_len, skb_len; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return -1;); + + /* Client must supply the destination device address */ + if (!daddr) + return -1; + + self->daddr = daddr; + self->saddr = saddr; + + /* + * Save operation, so we know what the later indication is about + */ + self->operation = GET_VALUE_BY_CLASS; + + /* Give ourselves 10 secs to finish this operation */ + iriap_start_watchdog_timer(self, 10*HZ); + + name_len = strlen(name); /* Up to IAS_MAX_CLASSNAME = 60 */ + attr_len = strlen(attr); /* Up to IAS_MAX_ATTRIBNAME = 60 */ + + skb_len = self->max_header_size+2+name_len+1+attr_len+4; + tx_skb = alloc_skb(skb_len, GFP_ATOMIC); + if (!tx_skb) + return -ENOMEM; + + /* Reserve space for MUX and LAP header */ + skb_reserve(tx_skb, self->max_header_size); + skb_put(tx_skb, 3+name_len+attr_len); + frame = tx_skb->data; + + /* Build frame */ + frame[0] = IAP_LST | GET_VALUE_BY_CLASS; + frame[1] = name_len; /* Insert length of name */ + memcpy(frame+2, name, name_len); /* Insert name */ + frame[2+name_len] = attr_len; /* Insert length of attr */ + memcpy(frame+3+name_len, attr, attr_len); /* Insert attr */ + + iriap_do_client_event(self, IAP_CALL_REQUEST_GVBC, tx_skb); + + /* Drop reference count - see state_s_disconnect(). */ + dev_kfree_skb(tx_skb); + + return 0; +} +EXPORT_SYMBOL(iriap_getvaluebyclass_request); + +/* + * Function iriap_getvaluebyclass_confirm (self, skb) + * + * Got result from GetValueByClass command. Parse it and return result + * to service user. + * + */ +static void iriap_getvaluebyclass_confirm(struct iriap_cb *self, + struct sk_buff *skb) +{ + struct ias_value *value; + int charset; + __u32 value_len; + __u32 tmp_cpu32; + __u16 obj_id; + __u16 len; + __u8 type; + __u8 *fp; + int n; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + /* Initialize variables */ + fp = skb->data; + n = 2; + + /* Get length, MSB first */ + len = get_unaligned_be16(fp + n); + n += 2; + + pr_debug("%s(), len=%d\n", __func__, len); + + /* Get object ID, MSB first */ + obj_id = get_unaligned_be16(fp + n); + n += 2; + + type = fp[n++]; + pr_debug("%s(), Value type = %d\n", __func__, type); + + switch (type) { + case IAS_INTEGER: + memcpy(&tmp_cpu32, fp+n, 4); n += 4; + be32_to_cpus(&tmp_cpu32); + value = irias_new_integer_value(tmp_cpu32); + + /* Legal values restricted to 0x01-0x6f, page 15 irttp */ + pr_debug("%s(), lsap=%d\n", __func__, value->t.integer); + break; + case IAS_STRING: + charset = fp[n++]; + + switch (charset) { + case CS_ASCII: + break; +/* case CS_ISO_8859_1: */ +/* case CS_ISO_8859_2: */ +/* case CS_ISO_8859_3: */ +/* case CS_ISO_8859_4: */ +/* case CS_ISO_8859_5: */ +/* case CS_ISO_8859_6: */ +/* case CS_ISO_8859_7: */ +/* case CS_ISO_8859_8: */ +/* case CS_ISO_8859_9: */ +/* case CS_UNICODE: */ + default: + pr_debug("%s(), charset [%d] %s, not supported\n", + __func__, charset, + charset < ARRAY_SIZE(ias_charset_types) ? + ias_charset_types[charset] : + "(unknown)"); + + /* Aborting, close connection! */ + iriap_disconnect_request(self); + return; + /* break; */ + } + value_len = fp[n++]; + pr_debug("%s(), strlen=%d\n", __func__, value_len); + + /* Make sure the string is null-terminated */ + if (n + value_len < skb->len) + fp[n + value_len] = 0x00; + pr_debug("Got string %s\n", fp+n); + + /* Will truncate to IAS_MAX_STRING bytes */ + value = irias_new_string_value(fp+n); + break; + case IAS_OCT_SEQ: + value_len = get_unaligned_be16(fp + n); + n += 2; + + /* Will truncate to IAS_MAX_OCTET_STRING bytes */ + value = irias_new_octseq_value(fp+n, value_len); + break; + default: + value = irias_new_missing_value(); + break; + } + + /* Finished, close connection! */ + iriap_disconnect_request(self); + + /* Warning, the client might close us, so remember no to use self + * anymore after calling confirm + */ + if (self->confirm) + self->confirm(IAS_SUCCESS, obj_id, value, self->priv); + else { + pr_debug("%s(), missing handler!\n", __func__); + irias_delete_value(value); + } +} + +/* + * Function iriap_getvaluebyclass_response () + * + * Send answer back to remote LM-IAS + * + */ +static void iriap_getvaluebyclass_response(struct iriap_cb *self, + __u16 obj_id, + __u8 ret_code, + struct ias_value *value) +{ + struct sk_buff *tx_skb; + int n; + __be32 tmp_be32; + __be16 tmp_be16; + __u8 *fp; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + IRDA_ASSERT(value != NULL, return;); + IRDA_ASSERT(value->len <= 1024, return;); + + /* Initialize variables */ + n = 0; + + /* + * We must adjust the size of the response after the length of the + * value. We add 32 bytes because of the 6 bytes for the frame and + * max 5 bytes for the value coding. + */ + tx_skb = alloc_skb(value->len + self->max_header_size + 32, + GFP_ATOMIC); + if (!tx_skb) + return; + + /* Reserve space for MUX and LAP header */ + skb_reserve(tx_skb, self->max_header_size); + skb_put(tx_skb, 6); + + fp = tx_skb->data; + + /* Build frame */ + fp[n++] = GET_VALUE_BY_CLASS | IAP_LST; + fp[n++] = ret_code; + + /* Insert list length (MSB first) */ + tmp_be16 = htons(0x0001); + memcpy(fp+n, &tmp_be16, 2); n += 2; + + /* Insert object identifier ( MSB first) */ + tmp_be16 = cpu_to_be16(obj_id); + memcpy(fp+n, &tmp_be16, 2); n += 2; + + switch (value->type) { + case IAS_STRING: + skb_put(tx_skb, 3 + value->len); + fp[n++] = value->type; + fp[n++] = 0; /* ASCII */ + fp[n++] = (__u8) value->len; + memcpy(fp+n, value->t.string, value->len); n+=value->len; + break; + case IAS_INTEGER: + skb_put(tx_skb, 5); + fp[n++] = value->type; + + tmp_be32 = cpu_to_be32(value->t.integer); + memcpy(fp+n, &tmp_be32, 4); n += 4; + break; + case IAS_OCT_SEQ: + skb_put(tx_skb, 3 + value->len); + fp[n++] = value->type; + + tmp_be16 = cpu_to_be16(value->len); + memcpy(fp+n, &tmp_be16, 2); n += 2; + memcpy(fp+n, value->t.oct_seq, value->len); n+=value->len; + break; + case IAS_MISSING: + pr_debug("%s: sending IAS_MISSING\n", __func__); + skb_put(tx_skb, 1); + fp[n++] = value->type; + break; + default: + pr_debug("%s(), type not implemented!\n", __func__); + break; + } + iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, tx_skb); + + /* Drop reference count - see state_r_execute(). */ + dev_kfree_skb(tx_skb); +} + +/* + * Function iriap_getvaluebyclass_indication (self, skb) + * + * getvaluebyclass is requested from peer LM-IAS + * + */ +static void iriap_getvaluebyclass_indication(struct iriap_cb *self, + struct sk_buff *skb) +{ + struct ias_object *obj; + struct ias_attrib *attrib; + int name_len; + int attr_len; + char name[IAS_MAX_CLASSNAME + 1]; /* 60 bytes */ + char attr[IAS_MAX_ATTRIBNAME + 1]; /* 60 bytes */ + __u8 *fp; + int n; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + fp = skb->data; + n = 1; + + name_len = fp[n++]; + + IRDA_ASSERT(name_len < IAS_MAX_CLASSNAME + 1, return;); + + memcpy(name, fp+n, name_len); n+=name_len; + name[name_len] = '\0'; + + attr_len = fp[n++]; + + IRDA_ASSERT(attr_len < IAS_MAX_ATTRIBNAME + 1, return;); + + memcpy(attr, fp+n, attr_len); n+=attr_len; + attr[attr_len] = '\0'; + + pr_debug("LM-IAS: Looking up %s: %s\n", name, attr); + obj = irias_find_object(name); + + if (obj == NULL) { + pr_debug("LM-IAS: Object %s not found\n", name); + iriap_getvaluebyclass_response(self, 0x1235, IAS_CLASS_UNKNOWN, + &irias_missing); + return; + } + pr_debug("LM-IAS: found %s, id=%d\n", obj->name, obj->id); + + attrib = irias_find_attrib(obj, attr); + if (attrib == NULL) { + pr_debug("LM-IAS: Attribute %s not found\n", attr); + iriap_getvaluebyclass_response(self, obj->id, + IAS_ATTRIB_UNKNOWN, + &irias_missing); + return; + } + + /* We have a match; send the value. */ + iriap_getvaluebyclass_response(self, obj->id, IAS_SUCCESS, + attrib->value); +} + +/* + * Function iriap_send_ack (void) + * + * Currently not used + * + */ +void iriap_send_ack(struct iriap_cb *self) +{ + struct sk_buff *tx_skb; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + tx_skb = alloc_skb(LMP_MAX_HEADER + 1, GFP_ATOMIC); + if (!tx_skb) + return; + + /* Reserve space for MUX and LAP header */ + skb_reserve(tx_skb, self->max_header_size); + skb_put(tx_skb, 1); + frame = tx_skb->data; + + /* Build frame */ + frame[0] = IAP_LST | IAP_ACK | self->operation; + + irlmp_data_request(self->lsap, tx_skb); +} + +void iriap_connect_request(struct iriap_cb *self) +{ + int ret; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + ret = irlmp_connect_request(self->lsap, LSAP_IAS, + self->saddr, self->daddr, + NULL, NULL); + if (ret < 0) { + pr_debug("%s(), connect failed!\n", __func__); + self->confirm(IAS_DISCONNECT, 0, NULL, self->priv); + } +} + +/* + * Function iriap_connect_confirm (handle, skb) + * + * LSAP connection confirmed! + * + */ +static void iriap_connect_confirm(void *instance, void *sap, + struct qos_info *qos, __u32 max_seg_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct iriap_cb *self; + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + self->max_data_size = max_seg_size; + self->max_header_size = max_header_size; + + del_timer(&self->watchdog_timer); + + iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, skb); + + /* Drop reference count - see state_s_make_call(). */ + dev_kfree_skb(skb); +} + +/* + * Function iriap_connect_indication ( handle, skb) + * + * Remote LM-IAS is requesting connection + * + */ +static void iriap_connect_indication(void *instance, void *sap, + struct qos_info *qos, __u32 max_seg_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct iriap_cb *self, *new; + + self = instance; + + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(self != NULL, goto out;); + IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;); + + /* Start new server */ + new = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL); + if (!new) { + pr_debug("%s(), open failed\n", __func__); + goto out; + } + + /* Now attach up the new "socket" */ + new->lsap = irlmp_dup(self->lsap, new); + if (!new->lsap) { + pr_debug("%s(), dup failed!\n", __func__); + goto out; + } + + new->max_data_size = max_seg_size; + new->max_header_size = max_header_size; + + /* Clean up the original one to keep it in listen state */ + irlmp_listen(self->lsap); + + iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, skb); + +out: + /* Drop reference count - see state_r_disconnect(). */ + dev_kfree_skb(skb); +} + +/* + * Function iriap_data_indication (handle, skb) + * + * Receives data from connection identified by handle from IrLMP + * + */ +static int iriap_data_indication(void *instance, void *sap, + struct sk_buff *skb) +{ + struct iriap_cb *self; + __u8 *frame; + __u8 opcode; + + self = instance; + + IRDA_ASSERT(skb != NULL, return 0;); + IRDA_ASSERT(self != NULL, goto out;); + IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;); + + frame = skb->data; + + if (self->mode == IAS_SERVER) { + /* Call server */ + pr_debug("%s(), Calling server!\n", __func__); + iriap_do_r_connect_event(self, IAP_RECV_F_LST, skb); + goto out; + } + opcode = frame[0]; + if (~opcode & IAP_LST) { + net_warn_ratelimited("%s:, IrIAS multiframe commands or results is not implemented yet!\n", + __func__); + goto out; + } + + /* Check for ack frames since they don't contain any data */ + if (opcode & IAP_ACK) { + pr_debug("%s() Got ack frame!\n", __func__); + goto out; + } + + opcode &= ~IAP_LST; /* Mask away LST bit */ + + switch (opcode) { + case GET_INFO_BASE: + pr_debug("IrLMP GetInfoBaseDetails not implemented!\n"); + break; + case GET_VALUE_BY_CLASS: + iriap_do_call_event(self, IAP_RECV_F_LST, NULL); + + switch (frame[1]) { + case IAS_SUCCESS: + iriap_getvaluebyclass_confirm(self, skb); + break; + case IAS_CLASS_UNKNOWN: + pr_debug("%s(), No such class!\n", __func__); + /* Finished, close connection! */ + iriap_disconnect_request(self); + + /* + * Warning, the client might close us, so remember + * no to use self anymore after calling confirm + */ + if (self->confirm) + self->confirm(IAS_CLASS_UNKNOWN, 0, NULL, + self->priv); + break; + case IAS_ATTRIB_UNKNOWN: + pr_debug("%s(), No such attribute!\n", __func__); + /* Finished, close connection! */ + iriap_disconnect_request(self); + + /* + * Warning, the client might close us, so remember + * no to use self anymore after calling confirm + */ + if (self->confirm) + self->confirm(IAS_ATTRIB_UNKNOWN, 0, NULL, + self->priv); + break; + } + break; + default: + pr_debug("%s(), Unknown op-code: %02x\n", __func__, + opcode); + break; + } + +out: + /* Cleanup - sub-calls will have done skb_get() as needed. */ + dev_kfree_skb(skb); + return 0; +} + +/* + * Function iriap_call_indication (self, skb) + * + * Received call to server from peer LM-IAS + * + */ +void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb) +{ + __u8 *fp; + __u8 opcode; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + fp = skb->data; + + opcode = fp[0]; + if (~opcode & 0x80) { + net_warn_ratelimited("%s: IrIAS multiframe commands or results is not implemented yet!\n", + __func__); + return; + } + opcode &= 0x7f; /* Mask away LST bit */ + + switch (opcode) { + case GET_INFO_BASE: + net_warn_ratelimited("%s: GetInfoBaseDetails not implemented yet!\n", + __func__); + break; + case GET_VALUE_BY_CLASS: + iriap_getvaluebyclass_indication(self, skb); + break; + } + /* skb will be cleaned up in iriap_data_indication */ +} + +/* + * Function iriap_watchdog_timer_expired (data) + * + * Query has taken too long time, so abort + * + */ +static void iriap_watchdog_timer_expired(void *data) +{ + struct iriap_cb *self = (struct iriap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + /* iriap_close(self); */ +} + +#ifdef CONFIG_PROC_FS + +static const char *const ias_value_types[] = { + "IAS_MISSING", + "IAS_INTEGER", + "IAS_OCT_SEQ", + "IAS_STRING" +}; + +static inline struct ias_object *irias_seq_idx(loff_t pos) +{ + struct ias_object *obj; + + for (obj = (struct ias_object *) hashbin_get_first(irias_objects); + obj; obj = (struct ias_object *) hashbin_get_next(irias_objects)) { + if (pos-- == 0) + break; + } + + return obj; +} + +static void *irias_seq_start(struct seq_file *seq, loff_t *pos) +{ + spin_lock_irq(&irias_objects->hb_spinlock); + + return *pos ? irias_seq_idx(*pos - 1) : SEQ_START_TOKEN; +} + +static void *irias_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + ++*pos; + + return (v == SEQ_START_TOKEN) + ? (void *) hashbin_get_first(irias_objects) + : (void *) hashbin_get_next(irias_objects); +} + +static void irias_seq_stop(struct seq_file *seq, void *v) +{ + spin_unlock_irq(&irias_objects->hb_spinlock); +} + +static int irias_seq_show(struct seq_file *seq, void *v) +{ + if (v == SEQ_START_TOKEN) + seq_puts(seq, "LM-IAS Objects:\n"); + else { + struct ias_object *obj = v; + struct ias_attrib *attrib; + + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -EINVAL;); + + seq_printf(seq, "name: %s, id=%d\n", + obj->name, obj->id); + + /* Careful for priority inversions here ! + * All other uses of attrib spinlock are independent of + * the object spinlock, so we are safe. Jean II */ + spin_lock(&obj->attribs->hb_spinlock); + + /* List all attributes for this object */ + for (attrib = (struct ias_attrib *) hashbin_get_first(obj->attribs); + attrib != NULL; + attrib = (struct ias_attrib *) hashbin_get_next(obj->attribs)) { + + IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, + goto outloop; ); + + seq_printf(seq, " - Attribute name: \"%s\", ", + attrib->name); + seq_printf(seq, "value[%s]: ", + ias_value_types[attrib->value->type]); + + switch (attrib->value->type) { + case IAS_INTEGER: + seq_printf(seq, "%d\n", + attrib->value->t.integer); + break; + case IAS_STRING: + seq_printf(seq, "\"%s\"\n", + attrib->value->t.string); + break; + case IAS_OCT_SEQ: + seq_printf(seq, "octet sequence (%d bytes)\n", + attrib->value->len); + break; + case IAS_MISSING: + seq_puts(seq, "missing\n"); + break; + default: + seq_printf(seq, "type %d?\n", + attrib->value->type); + } + seq_putc(seq, '\n'); + + } + IRDA_ASSERT_LABEL(outloop:) + spin_unlock(&obj->attribs->hb_spinlock); + } + + return 0; +} + +static const struct seq_operations irias_seq_ops = { + .start = irias_seq_start, + .next = irias_seq_next, + .stop = irias_seq_stop, + .show = irias_seq_show, +}; + +static int irias_seq_open(struct inode *inode, struct file *file) +{ + IRDA_ASSERT( irias_objects != NULL, return -EINVAL;); + + return seq_open(file, &irias_seq_ops); +} + +const struct file_operations irias_seq_fops = { + .owner = THIS_MODULE, + .open = irias_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +#endif /* PROC_FS */ diff --git a/drivers/staging/irda/net/iriap_event.c b/drivers/staging/irda/net/iriap_event.c new file mode 100644 index 000000000000..e6098b2e048a --- /dev/null +++ b/drivers/staging/irda/net/iriap_event.c @@ -0,0 +1,496 @@ +/********************************************************************* + * + * Filename: iriap_event.c + * Version: 0.1 + * Description: IAP Finite State Machine + * Status: Experimental. + * Author: Dag Brattli + * Created at: Thu Aug 21 00:02:07 1997 + * Modified at: Wed Mar 1 11:28:34 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1997, 1999-2000 Dag Brattli , + * All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include + +#include +#include +#include +#include + +static void state_s_disconnect (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_s_connecting (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_s_call (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); + +static void state_s_make_call (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_s_calling (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_s_outstanding (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_s_replying (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_s_wait_active (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); + +static void state_r_disconnect (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_r_call (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_r_waiting (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_r_wait_active (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_r_receiving (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_r_execute (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); +static void state_r_returning (struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb); + +static void (*iriap_state[])(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) = { + /* Client FSM */ + state_s_disconnect, + state_s_connecting, + state_s_call, + + /* S-Call FSM */ + state_s_make_call, + state_s_calling, + state_s_outstanding, + state_s_replying, + state_s_wait_for_call, + state_s_wait_active, + + /* Server FSM */ + state_r_disconnect, + state_r_call, + + /* R-Connect FSM */ + state_r_waiting, + state_r_wait_active, + state_r_receiving, + state_r_execute, + state_r_returning, +}; + +void iriap_next_client_state(struct iriap_cb *self, IRIAP_STATE state) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + self->client_state = state; +} + +void iriap_next_call_state(struct iriap_cb *self, IRIAP_STATE state) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + self->call_state = state; +} + +void iriap_next_server_state(struct iriap_cb *self, IRIAP_STATE state) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + self->server_state = state; +} + +void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + self->r_connect_state = state; +} + +void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + (*iriap_state[ self->client_state]) (self, event, skb); +} + +void iriap_do_call_event(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + (*iriap_state[ self->call_state]) (self, event, skb); +} + +void iriap_do_server_event(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + (*iriap_state[ self->server_state]) (self, event, skb); +} + +void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + (*iriap_state[ self->r_connect_state]) (self, event, skb); +} + + +/* + * Function state_s_disconnect (event, skb) + * + * S-Disconnect, The device has no LSAP connection to a particular + * remote device. + */ +static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + switch (event) { + case IAP_CALL_REQUEST_GVBC: + iriap_next_client_state(self, S_CONNECTING); + IRDA_ASSERT(self->request_skb == NULL, return;); + /* Don't forget to refcount it - + * see iriap_getvaluebyclass_request(). */ + skb_get(skb); + self->request_skb = skb; + iriap_connect_request(self); + break; + case IAP_LM_DISCONNECT_INDICATION: + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__, event); + break; + } +} + +/* + * Function state_s_connecting (self, event, skb) + * + * S-Connecting + * + */ +static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + switch (event) { + case IAP_LM_CONNECT_CONFIRM: + /* + * Jump to S-Call FSM + */ + iriap_do_call_event(self, IAP_CALL_REQUEST, skb); + /* iriap_call_request(self, 0,0,0); */ + iriap_next_client_state(self, S_CALL); + break; + case IAP_LM_DISCONNECT_INDICATION: + /* Abort calls */ + iriap_next_call_state(self, S_MAKE_CALL); + iriap_next_client_state(self, S_DISCONNECT); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__, event); + break; + } +} + +/* + * Function state_s_call (self, event, skb) + * + * S-Call, The device can process calls to a specific remote + * device. Whenever the LSAP connection is disconnected, this state + * catches that event and clears up + */ +static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + + switch (event) { + case IAP_LM_DISCONNECT_INDICATION: + /* Abort calls */ + iriap_next_call_state(self, S_MAKE_CALL); + iriap_next_client_state(self, S_DISCONNECT); + break; + default: + pr_debug("state_s_call: Unknown event %d\n", event); + break; + } +} + +/* + * Function state_s_make_call (event, skb) + * + * S-Make-Call + * + */ +static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + struct sk_buff *tx_skb; + + IRDA_ASSERT(self != NULL, return;); + + switch (event) { + case IAP_CALL_REQUEST: + /* Already refcounted - see state_s_disconnect() */ + tx_skb = self->request_skb; + self->request_skb = NULL; + + irlmp_data_request(self->lsap, tx_skb); + iriap_next_call_state(self, S_OUTSTANDING); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__, event); + break; + } +} + +/* + * Function state_s_calling (event, skb) + * + * S-Calling + * + */ +static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + pr_debug("%s(), Not implemented\n", __func__); +} + +/* + * Function state_s_outstanding (event, skb) + * + * S-Outstanding, The device is waiting for a response to a command + * + */ +static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + + switch (event) { + case IAP_RECV_F_LST: + /*iriap_send_ack(self);*/ + /*LM_Idle_request(idle); */ + + iriap_next_call_state(self, S_WAIT_FOR_CALL); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__, event); + break; + } +} + +/* + * Function state_s_replying (event, skb) + * + * S-Replying, The device is collecting a multiple part response + */ +static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + pr_debug("%s(), Not implemented\n", __func__); +} + +/* + * Function state_s_wait_for_call (event, skb) + * + * S-Wait-for-Call + * + */ +static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + pr_debug("%s(), Not implemented\n", __func__); +} + + +/* + * Function state_s_wait_active (event, skb) + * + * S-Wait-Active + * + */ +static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + pr_debug("%s(), Not implemented\n", __func__); +} + +/************************************************************************** + * + * Server FSM + * + **************************************************************************/ + +/* + * Function state_r_disconnect (self, event, skb) + * + * LM-IAS server is disconnected (not processing any requests!) + * + */ +static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + struct sk_buff *tx_skb; + + switch (event) { + case IAP_LM_CONNECT_INDICATION: + tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); + if (tx_skb == NULL) + return; + + /* Reserve space for MUX_CONTROL and LAP header */ + skb_reserve(tx_skb, LMP_MAX_HEADER); + + irlmp_connect_response(self->lsap, tx_skb); + /*LM_Idle_request(idle); */ + + iriap_next_server_state(self, R_CALL); + + /* + * Jump to R-Connect FSM, we skip R-Waiting since we do not + * care about LM_Idle_request()! + */ + iriap_next_r_connect_state(self, R_RECEIVING); + break; + default: + pr_debug("%s(), unknown event %d\n", __func__, event); + break; + } +} + +/* + * Function state_r_call (self, event, skb) + */ +static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + switch (event) { + case IAP_LM_DISCONNECT_INDICATION: + /* Abort call */ + iriap_next_server_state(self, R_DISCONNECT); + iriap_next_r_connect_state(self, R_WAITING); + break; + default: + pr_debug("%s(), unknown event!\n", __func__); + break; + } +} + +/* + * R-Connect FSM + */ + +/* + * Function state_r_waiting (self, event, skb) + */ +static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + pr_debug("%s(), Not implemented\n", __func__); +} + +static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + pr_debug("%s(), Not implemented\n", __func__); +} + +/* + * Function state_r_receiving (self, event, skb) + * + * We are receiving a command + * + */ +static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + switch (event) { + case IAP_RECV_F_LST: + iriap_next_r_connect_state(self, R_EXECUTE); + + iriap_call_indication(self, skb); + break; + default: + pr_debug("%s(), unknown event!\n", __func__); + break; + } +} + +/* + * Function state_r_execute (self, event, skb) + * + * The server is processing the request + * + */ +static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IAS_MAGIC, return;); + + switch (event) { + case IAP_CALL_RESPONSE: + /* + * Since we don't implement the Waiting state, we return + * to state Receiving instead, DB. + */ + iriap_next_r_connect_state(self, R_RECEIVING); + + /* Don't forget to refcount it - see + * iriap_getvaluebyclass_response(). */ + skb_get(skb); + + irlmp_data_request(self->lsap, skb); + break; + default: + pr_debug("%s(), unknown event!\n", __func__); + break; + } +} + +static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event, + struct sk_buff *skb) +{ + pr_debug("%s(), event=%d\n", __func__, event); + + switch (event) { + case IAP_RECV_F_LST: + break; + default: + break; + } +} diff --git a/drivers/staging/irda/net/irias_object.c b/drivers/staging/irda/net/irias_object.c new file mode 100644 index 000000000000..53b86d0e1630 --- /dev/null +++ b/drivers/staging/irda/net/irias_object.c @@ -0,0 +1,555 @@ +/********************************************************************* + * + * Filename: irias_object.c + * Version: 0.3 + * Description: IAS object database and functions + * Status: Experimental. + * Author: Dag Brattli + * Created at: Thu Oct 1 22:50:04 1998 + * Modified at: Wed Dec 15 11:23:16 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include + +#include +#include + +hashbin_t *irias_objects; + +/* + * Used when a missing value needs to be returned + */ +struct ias_value irias_missing = { IAS_MISSING, 0, 0, 0, {0}}; + + +/* + * Function ias_new_object (name, id) + * + * Create a new IAS object + * + */ +struct ias_object *irias_new_object( char *name, int id) +{ + struct ias_object *obj; + + obj = kzalloc(sizeof(struct ias_object), GFP_ATOMIC); + if (obj == NULL) { + net_warn_ratelimited("%s(), Unable to allocate object!\n", + __func__); + return NULL; + } + + obj->magic = IAS_OBJECT_MAGIC; + obj->name = kstrndup(name, IAS_MAX_CLASSNAME, GFP_ATOMIC); + if (!obj->name) { + net_warn_ratelimited("%s(), Unable to allocate name!\n", + __func__); + kfree(obj); + return NULL; + } + obj->id = id; + + /* Locking notes : the attrib spinlock has lower precendence + * than the objects spinlock. Never grap the objects spinlock + * while holding any attrib spinlock (risk of deadlock). Jean II */ + obj->attribs = hashbin_new(HB_LOCK); + + if (obj->attribs == NULL) { + net_warn_ratelimited("%s(), Unable to allocate attribs!\n", + __func__); + kfree(obj->name); + kfree(obj); + return NULL; + } + + return obj; +} +EXPORT_SYMBOL(irias_new_object); + +/* + * Function irias_delete_attrib (attrib) + * + * Delete given attribute and deallocate all its memory + * + */ +static void __irias_delete_attrib(struct ias_attrib *attrib) +{ + IRDA_ASSERT(attrib != NULL, return;); + IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;); + + kfree(attrib->name); + + irias_delete_value(attrib->value); + attrib->magic = ~IAS_ATTRIB_MAGIC; + + kfree(attrib); +} + +void __irias_delete_object(struct ias_object *obj) +{ + IRDA_ASSERT(obj != NULL, return;); + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); + + kfree(obj->name); + + hashbin_delete(obj->attribs, (FREE_FUNC) __irias_delete_attrib); + + obj->magic = ~IAS_OBJECT_MAGIC; + + kfree(obj); +} + +/* + * Function irias_delete_object (obj) + * + * Remove object from hashbin and deallocate all attributes associated with + * with this object and the object itself + * + */ +int irias_delete_object(struct ias_object *obj) +{ + struct ias_object *node; + + IRDA_ASSERT(obj != NULL, return -1;); + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;); + + /* Remove from list */ + node = hashbin_remove_this(irias_objects, (irda_queue_t *) obj); + if (!node) + pr_debug("%s(), object already removed!\n", + __func__); + + /* Destroy */ + __irias_delete_object(obj); + + return 0; +} +EXPORT_SYMBOL(irias_delete_object); + +/* + * Function irias_delete_attrib (obj) + * + * Remove attribute from hashbin and, if it was the last attribute of + * the object, remove the object as well. + * + */ +int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib, + int cleanobject) +{ + struct ias_attrib *node; + + IRDA_ASSERT(obj != NULL, return -1;); + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;); + IRDA_ASSERT(attrib != NULL, return -1;); + + /* Remove attribute from object */ + node = hashbin_remove_this(obj->attribs, (irda_queue_t *) attrib); + if (!node) + return 0; /* Already removed or non-existent */ + + /* Deallocate attribute */ + __irias_delete_attrib(node); + + /* Check if object has still some attributes, destroy it if none. + * At first glance, this look dangerous, as the kernel reference + * various IAS objects. However, we only use this function on + * user attributes, not kernel attributes, so there is no risk + * of deleting a kernel object this way. Jean II */ + node = (struct ias_attrib *) hashbin_get_first(obj->attribs); + if (cleanobject && !node) + irias_delete_object(obj); + + return 0; +} + +/* + * Function irias_insert_object (obj) + * + * Insert an object into the LM-IAS database + * + */ +void irias_insert_object(struct ias_object *obj) +{ + IRDA_ASSERT(obj != NULL, return;); + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); + + hashbin_insert(irias_objects, (irda_queue_t *) obj, 0, obj->name); +} +EXPORT_SYMBOL(irias_insert_object); + +/* + * Function irias_find_object (name) + * + * Find object with given name + * + */ +struct ias_object *irias_find_object(char *name) +{ + IRDA_ASSERT(name != NULL, return NULL;); + + /* Unsafe (locking), object might change */ + return hashbin_lock_find(irias_objects, 0, name); +} +EXPORT_SYMBOL(irias_find_object); + +/* + * Function irias_find_attrib (obj, name) + * + * Find named attribute in object + * + */ +struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name) +{ + struct ias_attrib *attrib; + + IRDA_ASSERT(obj != NULL, return NULL;); + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return NULL;); + IRDA_ASSERT(name != NULL, return NULL;); + + attrib = hashbin_lock_find(obj->attribs, 0, name); + if (attrib == NULL) + return NULL; + + /* Unsafe (locking), attrib might change */ + return attrib; +} + +/* + * Function irias_add_attribute (obj, attrib) + * + * Add attribute to object + * + */ +static void irias_add_attrib(struct ias_object *obj, struct ias_attrib *attrib, + int owner) +{ + IRDA_ASSERT(obj != NULL, return;); + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); + + IRDA_ASSERT(attrib != NULL, return;); + IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;); + + /* Set if attrib is owned by kernel or user space */ + attrib->value->owner = owner; + + hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name); +} + +/* + * Function irias_object_change_attribute (obj_name, attrib_name, new_value) + * + * Change the value of an objects attribute. + * + */ +int irias_object_change_attribute(char *obj_name, char *attrib_name, + struct ias_value *new_value) +{ + struct ias_object *obj; + struct ias_attrib *attrib; + unsigned long flags; + + /* Find object */ + obj = hashbin_lock_find(irias_objects, 0, obj_name); + if (obj == NULL) { + net_warn_ratelimited("%s: Unable to find object: %s\n", + __func__, obj_name); + return -1; + } + + /* Slightly unsafe (obj might get removed under us) */ + spin_lock_irqsave(&obj->attribs->hb_spinlock, flags); + + /* Find attribute */ + attrib = hashbin_find(obj->attribs, 0, attrib_name); + if (attrib == NULL) { + net_warn_ratelimited("%s: Unable to find attribute: %s\n", + __func__, attrib_name); + spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); + return -1; + } + + if ( attrib->value->type != new_value->type) { + pr_debug("%s(), changing value type not allowed!\n", + __func__); + spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); + return -1; + } + + /* Delete old value */ + irias_delete_value(attrib->value); + + /* Insert new value */ + attrib->value = new_value; + + /* Success */ + spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); + return 0; +} +EXPORT_SYMBOL(irias_object_change_attribute); + +/* + * Function irias_object_add_integer_attrib (obj, name, value) + * + * Add an integer attribute to an LM-IAS object + * + */ +void irias_add_integer_attrib(struct ias_object *obj, char *name, int value, + int owner) +{ + struct ias_attrib *attrib; + + IRDA_ASSERT(obj != NULL, return;); + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); + IRDA_ASSERT(name != NULL, return;); + + attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC); + if (attrib == NULL) { + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); + return; + } + + attrib->magic = IAS_ATTRIB_MAGIC; + attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); + + /* Insert value */ + attrib->value = irias_new_integer_value(value); + if (!attrib->name || !attrib->value) { + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); + if (attrib->value) + irias_delete_value(attrib->value); + kfree(attrib->name); + kfree(attrib); + return; + } + + irias_add_attrib(obj, attrib, owner); +} +EXPORT_SYMBOL(irias_add_integer_attrib); + + /* + * Function irias_add_octseq_attrib (obj, name, octet_seq, len) + * + * Add a octet sequence attribute to an LM-IAS object + * + */ + +void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, + int len, int owner) +{ + struct ias_attrib *attrib; + + IRDA_ASSERT(obj != NULL, return;); + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); + + IRDA_ASSERT(name != NULL, return;); + IRDA_ASSERT(octets != NULL, return;); + + attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC); + if (attrib == NULL) { + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); + return; + } + + attrib->magic = IAS_ATTRIB_MAGIC; + attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); + + attrib->value = irias_new_octseq_value( octets, len); + if (!attrib->name || !attrib->value) { + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); + if (attrib->value) + irias_delete_value(attrib->value); + kfree(attrib->name); + kfree(attrib); + return; + } + + irias_add_attrib(obj, attrib, owner); +} +EXPORT_SYMBOL(irias_add_octseq_attrib); + +/* + * Function irias_object_add_string_attrib (obj, string) + * + * Add a string attribute to an LM-IAS object + * + */ +void irias_add_string_attrib(struct ias_object *obj, char *name, char *value, + int owner) +{ + struct ias_attrib *attrib; + + IRDA_ASSERT(obj != NULL, return;); + IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); + + IRDA_ASSERT(name != NULL, return;); + IRDA_ASSERT(value != NULL, return;); + + attrib = kzalloc(sizeof( struct ias_attrib), GFP_ATOMIC); + if (attrib == NULL) { + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); + return; + } + + attrib->magic = IAS_ATTRIB_MAGIC; + attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); + + attrib->value = irias_new_string_value(value); + if (!attrib->name || !attrib->value) { + net_warn_ratelimited("%s: Unable to allocate attribute!\n", + __func__); + if (attrib->value) + irias_delete_value(attrib->value); + kfree(attrib->name); + kfree(attrib); + return; + } + + irias_add_attrib(obj, attrib, owner); +} +EXPORT_SYMBOL(irias_add_string_attrib); + +/* + * Function irias_new_integer_value (integer) + * + * Create new IAS integer value + * + */ +struct ias_value *irias_new_integer_value(int integer) +{ + struct ias_value *value; + + value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); + if (value == NULL) + return NULL; + + value->type = IAS_INTEGER; + value->len = 4; + value->t.integer = integer; + + return value; +} +EXPORT_SYMBOL(irias_new_integer_value); + +/* + * Function irias_new_string_value (string) + * + * Create new IAS string value + * + * Per IrLMP 1.1, 4.3.3.2, strings are up to 256 chars - Jean II + */ +struct ias_value *irias_new_string_value(char *string) +{ + struct ias_value *value; + + value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); + if (value == NULL) + return NULL; + + value->type = IAS_STRING; + value->charset = CS_ASCII; + value->t.string = kstrndup(string, IAS_MAX_STRING, GFP_ATOMIC); + if (!value->t.string) { + net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__); + kfree(value); + return NULL; + } + + value->len = strlen(value->t.string); + + return value; +} + +/* + * Function irias_new_octseq_value (octets, len) + * + * Create new IAS octet-sequence value + * + * Per IrLMP 1.1, 4.3.3.2, octet-sequence are up to 1024 bytes - Jean II + */ +struct ias_value *irias_new_octseq_value(__u8 *octseq , int len) +{ + struct ias_value *value; + + value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); + if (value == NULL) + return NULL; + + value->type = IAS_OCT_SEQ; + /* Check length */ + if(len > IAS_MAX_OCTET_STRING) + len = IAS_MAX_OCTET_STRING; + value->len = len; + + value->t.oct_seq = kmemdup(octseq, len, GFP_ATOMIC); + if (value->t.oct_seq == NULL){ + net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__); + kfree(value); + return NULL; + } + return value; +} + +struct ias_value *irias_new_missing_value(void) +{ + struct ias_value *value; + + value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); + if (value == NULL) + return NULL; + + value->type = IAS_MISSING; + + return value; +} + +/* + * Function irias_delete_value (value) + * + * Delete IAS value + * + */ +void irias_delete_value(struct ias_value *value) +{ + IRDA_ASSERT(value != NULL, return;); + + switch (value->type) { + case IAS_INTEGER: /* Fallthrough */ + case IAS_MISSING: + /* No need to deallocate */ + break; + case IAS_STRING: + /* Deallocate string */ + kfree(value->t.string); + break; + case IAS_OCT_SEQ: + /* Deallocate byte stream */ + kfree(value->t.oct_seq); + break; + default: + pr_debug("%s(), Unknown value type!\n", __func__); + break; + } + kfree(value); +} +EXPORT_SYMBOL(irias_delete_value); diff --git a/drivers/staging/irda/net/irlan/Kconfig b/drivers/staging/irda/net/irlan/Kconfig new file mode 100644 index 000000000000..951abc2e3a7f --- /dev/null +++ b/drivers/staging/irda/net/irlan/Kconfig @@ -0,0 +1,14 @@ +config IRLAN + tristate "IrLAN protocol" + depends on IRDA + help + Say Y here if you want to build support for the IrLAN protocol. + To compile it as a module, choose M here: the module will be called + irlan. IrLAN emulates an Ethernet and makes it possible to put up + a wireless LAN using infrared beams. + + The IrLAN protocol can be used to talk with infrared access points + like the HP NetbeamIR, or the ESI JetEye NET. You can also connect + to another Linux machine running the IrLAN protocol for ad-hoc + networking! + diff --git a/drivers/staging/irda/net/irlan/Makefile b/drivers/staging/irda/net/irlan/Makefile new file mode 100644 index 000000000000..94eefbc8e6b9 --- /dev/null +++ b/drivers/staging/irda/net/irlan/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux IrDA IrLAN protocol layer. +# + +obj-$(CONFIG_IRLAN) += irlan.o + +irlan-y := irlan_common.o irlan_eth.o irlan_event.o irlan_client.o irlan_provider.o irlan_filter.o irlan_provider_event.o irlan_client_event.o diff --git a/drivers/staging/irda/net/irlan/irlan_client.c b/drivers/staging/irda/net/irlan/irlan_client.c new file mode 100644 index 000000000000..c5837a40c78e --- /dev/null +++ b/drivers/staging/irda/net/irlan/irlan_client.c @@ -0,0 +1,559 @@ +/********************************************************************* + * + * Filename: irlan_client.c + * Version: 0.9 + * Description: IrDA LAN Access Protocol (IrLAN) Client + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sun Aug 31 20:14:37 1997 + * Modified at: Tue Dec 14 15:47:02 1999 + * Modified by: Dag Brattli + * Sources: skeleton.c by Donald Becker + * slip.c by Laurence Culhane, + * Fred N. van Kempen, + * + * Copyright (c) 1998-1999 Dag Brattli , + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#undef CONFIG_IRLAN_GRATUITOUS_ARP + +static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap, + LM_REASON reason, + struct sk_buff *); +static int irlan_client_ctrl_data_indication(void *instance, void *sap, + struct sk_buff *skb); +static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *); +static void irlan_check_response_param(struct irlan_cb *self, char *param, + char *value, int val_len); +static void irlan_client_open_ctrl_tsap(struct irlan_cb *self); + +static void irlan_client_kick_timer_expired(void *data) +{ + struct irlan_cb *self = (struct irlan_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + /* + * If we are in peer mode, the client may not have got the discovery + * indication it needs to make progress. If the client is still in + * IDLE state, we must kick it to, but only if the provider is not IDLE + */ + if ((self->provider.access_type == ACCESS_PEER) && + (self->client.state == IRLAN_IDLE) && + (self->provider.state != IRLAN_IDLE)) { + irlan_client_wakeup(self, self->saddr, self->daddr); + } +} + +static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout) +{ + irda_start_timer(&self->client.kick_timer, timeout, (void *) self, + irlan_client_kick_timer_expired); +} + +/* + * Function irlan_client_wakeup (self, saddr, daddr) + * + * Wake up client + * + */ +void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + /* + * Check if we are already awake, or if we are a provider in direct + * mode (in that case we must leave the client idle + */ + if ((self->client.state != IRLAN_IDLE) || + (self->provider.access_type == ACCESS_DIRECT)) + { + pr_debug("%s(), already awake!\n", __func__); + return; + } + + /* Addresses may have changed! */ + self->saddr = saddr; + self->daddr = daddr; + + if (self->disconnect_reason == LM_USER_REQUEST) { + pr_debug("%s(), still stopped by user\n", __func__); + return; + } + + /* Open TSAPs */ + irlan_client_open_ctrl_tsap(self); + irlan_open_data_tsap(self); + + irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL); + + /* Start kick timer */ + irlan_client_start_kick_timer(self, 2*HZ); +} + +/* + * Function irlan_discovery_indication (daddr) + * + * Remote device with IrLAN server support discovered + * + */ +void irlan_client_discovery_indication(discinfo_t *discovery, + DISCOVERY_MODE mode, + void *priv) +{ + struct irlan_cb *self; + __u32 saddr, daddr; + + IRDA_ASSERT(discovery != NULL, return;); + + /* + * I didn't check it, but I bet that IrLAN suffer from the same + * deficiency as IrComm and doesn't handle two instances + * simultaneously connecting to each other. + * Same workaround, drop passive discoveries. + * Jean II */ + if(mode == DISCOVERY_PASSIVE) + return; + + saddr = discovery->saddr; + daddr = discovery->daddr; + + /* Find instance */ + rcu_read_lock(); + self = irlan_get_any(); + if (self) { + IRDA_ASSERT(self->magic == IRLAN_MAGIC, goto out;); + + pr_debug("%s(), Found instance (%08x)!\n", __func__ , + daddr); + + irlan_client_wakeup(self, saddr, daddr); + } +IRDA_ASSERT_LABEL(out:) + rcu_read_unlock(); +} + +/* + * Function irlan_client_data_indication (handle, skb) + * + * This function gets the data that is received on the control channel + * + */ +static int irlan_client_ctrl_data_indication(void *instance, void *sap, + struct sk_buff *skb) +{ + struct irlan_cb *self; + + self = instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); + IRDA_ASSERT(skb != NULL, return -1;); + + irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb); + + /* Ready for a new command */ + pr_debug("%s(), clearing tx_busy\n", __func__); + self->client.tx_busy = FALSE; + + /* Check if we have some queued commands waiting to be sent */ + irlan_run_ctrl_tx_queue(self); + + return 0; +} + +static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap, + LM_REASON reason, + struct sk_buff *userdata) +{ + struct irlan_cb *self; + struct tsap_cb *tsap; + struct sk_buff *skb; + + pr_debug("%s(), reason=%d\n", __func__ , reason); + + self = instance; + tsap = sap; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + IRDA_ASSERT(tsap != NULL, return;); + IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); + + IRDA_ASSERT(tsap == self->client.tsap_ctrl, return;); + + /* Remove frames queued on the control channel */ + while ((skb = skb_dequeue(&self->client.txq)) != NULL) { + dev_kfree_skb(skb); + } + self->client.tx_busy = FALSE; + + irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); +} + +/* + * Function irlan_client_open_tsaps (self) + * + * Initialize callbacks and open IrTTP TSAPs + * + */ +static void irlan_client_open_ctrl_tsap(struct irlan_cb *self) +{ + struct tsap_cb *tsap; + notify_t notify; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + /* Check if already open */ + if (self->client.tsap_ctrl) + return; + + irda_notify_init(¬ify); + + /* Set up callbacks */ + notify.data_indication = irlan_client_ctrl_data_indication; + notify.connect_confirm = irlan_client_ctrl_connect_confirm; + notify.disconnect_indication = irlan_client_ctrl_disconnect_indication; + notify.instance = self; + strlcpy(notify.name, "IrLAN ctrl (c)", sizeof(notify.name)); + + tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); + if (!tsap) { + pr_debug("%s(), Got no tsap!\n", __func__); + return; + } + self->client.tsap_ctrl = tsap; +} + +/* + * Function irlan_client_connect_confirm (handle, skb) + * + * Connection to peer IrLAN laye confirmed + * + */ +static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct irlan_cb *self; + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + self->client.max_sdu_size = max_sdu_size; + self->client.max_header_size = max_header_size; + + /* TODO: we could set the MTU depending on the max_sdu_size */ + + irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL); +} + +/* + * Function print_ret_code (code) + * + * Print return code of request to peer IrLAN layer. + * + */ +static void print_ret_code(__u8 code) +{ + switch(code) { + case 0: + printk(KERN_INFO "Success\n"); + break; + case 1: + net_warn_ratelimited("IrLAN: Insufficient resources\n"); + break; + case 2: + net_warn_ratelimited("IrLAN: Invalid command format\n"); + break; + case 3: + net_warn_ratelimited("IrLAN: Command not supported\n"); + break; + case 4: + net_warn_ratelimited("IrLAN: Parameter not supported\n"); + break; + case 5: + net_warn_ratelimited("IrLAN: Value not supported\n"); + break; + case 6: + net_warn_ratelimited("IrLAN: Not open\n"); + break; + case 7: + net_warn_ratelimited("IrLAN: Authentication required\n"); + break; + case 8: + net_warn_ratelimited("IrLAN: Invalid password\n"); + break; + case 9: + net_warn_ratelimited("IrLAN: Protocol error\n"); + break; + case 255: + net_warn_ratelimited("IrLAN: Asynchronous status\n"); + break; + } +} + +/* + * Function irlan_client_parse_response (self, skb) + * + * Extract all parameters from received buffer, then feed them to + * check_params for parsing + */ +void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb) +{ + __u8 *frame; + __u8 *ptr; + int count; + int ret; + __u16 val_len; + int i; + char *name; + char *value; + + IRDA_ASSERT(skb != NULL, return;); + + pr_debug("%s() skb->len=%d\n", __func__ , (int)skb->len); + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + if (!skb) { + net_err_ratelimited("%s(), Got NULL skb!\n", __func__); + return; + } + frame = skb->data; + + /* + * Check return code and print it if not success + */ + if (frame[0]) { + print_ret_code(frame[0]); + return; + } + + name = kmalloc(255, GFP_ATOMIC); + if (!name) + return; + value = kmalloc(1016, GFP_ATOMIC); + if (!value) { + kfree(name); + return; + } + + /* How many parameters? */ + count = frame[1]; + + pr_debug("%s(), got %d parameters\n", __func__ , count); + + ptr = frame+2; + + /* For all parameters */ + for (i=0; imagic == IRLAN_MAGIC, return;); + + /* Media type */ + if (strcmp(param, "MEDIA") == 0) { + if (strcmp(value, "802.3") == 0) + self->media = MEDIA_802_3; + else + self->media = MEDIA_802_5; + return; + } + if (strcmp(param, "FILTER_TYPE") == 0) { + if (strcmp(value, "DIRECTED") == 0) + self->client.filter_type |= IRLAN_DIRECTED; + else if (strcmp(value, "FUNCTIONAL") == 0) + self->client.filter_type |= IRLAN_FUNCTIONAL; + else if (strcmp(value, "GROUP") == 0) + self->client.filter_type |= IRLAN_GROUP; + else if (strcmp(value, "MAC_FRAME") == 0) + self->client.filter_type |= IRLAN_MAC_FRAME; + else if (strcmp(value, "MULTICAST") == 0) + self->client.filter_type |= IRLAN_MULTICAST; + else if (strcmp(value, "BROADCAST") == 0) + self->client.filter_type |= IRLAN_BROADCAST; + else if (strcmp(value, "IPX_SOCKET") == 0) + self->client.filter_type |= IRLAN_IPX_SOCKET; + + } + if (strcmp(param, "ACCESS_TYPE") == 0) { + if (strcmp(value, "DIRECT") == 0) + self->client.access_type = ACCESS_DIRECT; + else if (strcmp(value, "PEER") == 0) + self->client.access_type = ACCESS_PEER; + else if (strcmp(value, "HOSTED") == 0) + self->client.access_type = ACCESS_HOSTED; + else { + pr_debug("%s(), unknown access type!\n", __func__); + } + } + /* IRLAN version */ + if (strcmp(param, "IRLAN_VER") == 0) { + pr_debug("IrLAN version %d.%d\n", (__u8)value[0], + (__u8)value[1]); + + self->version[0] = value[0]; + self->version[1] = value[1]; + return; + } + /* Which remote TSAP to use for data channel */ + if (strcmp(param, "DATA_CHAN") == 0) { + self->dtsap_sel_data = value[0]; + pr_debug("Data TSAP = %02x\n", self->dtsap_sel_data); + return; + } + if (strcmp(param, "CON_ARB") == 0) { + memcpy(&tmp_cpu, value, 2); /* Align value */ + le16_to_cpus(&tmp_cpu); /* Convert to host order */ + self->client.recv_arb_val = tmp_cpu; + pr_debug("%s(), receive arb val=%d\n", __func__ , + self->client.recv_arb_val); + } + if (strcmp(param, "MAX_FRAME") == 0) { + memcpy(&tmp_cpu, value, 2); /* Align value */ + le16_to_cpus(&tmp_cpu); /* Convert to host order */ + self->client.max_frame = tmp_cpu; + pr_debug("%s(), max frame=%d\n", __func__ , + self->client.max_frame); + } + + /* RECONNECT_KEY, in case the link goes down! */ + if (strcmp(param, "RECONNECT_KEY") == 0) { + pr_debug("Got reconnect key: "); + /* for (i = 0; i < val_len; i++) */ +/* printk("%02x", value[i]); */ + memcpy(self->client.reconnect_key, value, val_len); + self->client.key_len = val_len; + pr_debug("\n"); + } + /* FILTER_ENTRY, have we got an ethernet address? */ + if (strcmp(param, "FILTER_ENTRY") == 0) { + bytes = value; + pr_debug("Ethernet address = %pM\n", bytes); + for (i = 0; i < 6; i++) + self->dev->dev_addr[i] = bytes[i]; + } +} + +/* + * Function irlan_client_get_value_confirm (obj_id, value) + * + * Got results from remote LM-IAS + * + */ +void irlan_client_get_value_confirm(int result, __u16 obj_id, + struct ias_value *value, void *priv) +{ + struct irlan_cb *self; + + IRDA_ASSERT(priv != NULL, return;); + + self = priv; + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + /* We probably don't need to make any more queries */ + iriap_close(self->client.iriap); + self->client.iriap = NULL; + + /* Check if request succeeded */ + if (result != IAS_SUCCESS) { + pr_debug("%s(), got NULL value!\n", __func__); + irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, + NULL); + return; + } + + switch (value->type) { + case IAS_INTEGER: + self->dtsap_sel_ctrl = value->t.integer; + + if (value->t.integer != -1) { + irlan_do_client_event(self, IRLAN_IAS_PROVIDER_AVAIL, + NULL); + return; + } + irias_delete_value(value); + break; + default: + pr_debug("%s(), unknown type!\n", __func__); + break; + } + irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL); +} diff --git a/drivers/staging/irda/net/irlan/irlan_client_event.c b/drivers/staging/irda/net/irlan/irlan_client_event.c new file mode 100644 index 000000000000..cc93fabbbb19 --- /dev/null +++ b/drivers/staging/irda/net/irlan/irlan_client_event.c @@ -0,0 +1,511 @@ +/********************************************************************* + * + * Filename: irlan_client_event.c + * Version: 0.9 + * Description: IrLAN client state machine + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sun Aug 31 20:14:37 1997 + * Modified at: Sun Dec 26 21:52:24 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999 Dag Brattli , + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +static int irlan_client_state_idle (struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_conn (struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_info (struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_open (struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_wait (struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_arb (struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_data (struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_client_state_sync (struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); + +static int (*state[])(struct irlan_cb *, IRLAN_EVENT event, struct sk_buff *) = +{ + irlan_client_state_idle, + irlan_client_state_query, + irlan_client_state_conn, + irlan_client_state_info, + irlan_client_state_media, + irlan_client_state_open, + irlan_client_state_wait, + irlan_client_state_arb, + irlan_client_state_data, + irlan_client_state_close, + irlan_client_state_sync +}; + +void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + (*state[ self->client.state]) (self, event, skb); +} + +/* + * Function irlan_client_state_idle (event, skb, info) + * + * IDLE, We are waiting for an indication that there is a provider + * available. + */ +static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); + + switch (event) { + case IRLAN_DISCOVERY_INDICATION: + if (self->client.iriap) { + net_warn_ratelimited("%s(), busy with a previous query\n", + __func__); + return -EBUSY; + } + + self->client.iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, + irlan_client_get_value_confirm); + /* Get some values from peer IAS */ + irlan_next_client_state(self, IRLAN_QUERY); + iriap_getvaluebyclass_request(self->client.iriap, + self->saddr, self->daddr, + "IrLAN", "IrDA:TinyTP:LsapSel"); + break; + case IRLAN_WATCHDOG_TIMEOUT: + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_client_state_query (event, skb, info) + * + * QUERY, We have queryed the remote IAS and is ready to connect + * to provider, just waiting for the confirm. + * + */ +static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); + + switch(event) { + case IRLAN_IAS_PROVIDER_AVAIL: + IRDA_ASSERT(self->dtsap_sel_ctrl != 0, return -1;); + + self->client.open_retries = 0; + + irttp_connect_request(self->client.tsap_ctrl, + self->dtsap_sel_ctrl, + self->saddr, self->daddr, NULL, + IRLAN_MTU, NULL); + irlan_next_client_state(self, IRLAN_CONN); + break; + case IRLAN_IAS_PROVIDER_NOT_AVAIL: + pr_debug("%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__); + irlan_next_client_state(self, IRLAN_IDLE); + + /* Give the client a kick! */ + if ((self->provider.access_type == ACCESS_PEER) && + (self->provider.state != IRLAN_IDLE)) + irlan_client_wakeup(self, self->saddr, self->daddr); + break; + case IRLAN_LMP_DISCONNECT: + case IRLAN_LAP_DISCONNECT: + irlan_next_client_state(self, IRLAN_IDLE); + break; + case IRLAN_WATCHDOG_TIMEOUT: + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_client_state_conn (event, skb, info) + * + * CONN, We have connected to a provider but has not issued any + * commands yet. + * + */ +static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + + switch (event) { + case IRLAN_CONNECT_COMPLETE: + /* Send getinfo cmd */ + irlan_get_provider_info(self); + irlan_next_client_state(self, IRLAN_INFO); + break; + case IRLAN_LMP_DISCONNECT: + case IRLAN_LAP_DISCONNECT: + irlan_next_client_state(self, IRLAN_IDLE); + break; + case IRLAN_WATCHDOG_TIMEOUT: + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_client_state_info (self, event, skb, info) + * + * INFO, We have issued a GetInfo command and is awaiting a reply. + */ +static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + + switch (event) { + case IRLAN_DATA_INDICATION: + IRDA_ASSERT(skb != NULL, return -1;); + + irlan_client_parse_response(self, skb); + + irlan_next_client_state(self, IRLAN_MEDIA); + + irlan_get_media_char(self); + break; + + case IRLAN_LMP_DISCONNECT: + case IRLAN_LAP_DISCONNECT: + irlan_next_client_state(self, IRLAN_IDLE); + break; + case IRLAN_WATCHDOG_TIMEOUT: + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_client_state_media (self, event, skb, info) + * + * MEDIA, The irlan_client has issued a GetMedia command and is awaiting a + * reply. + * + */ +static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + + switch(event) { + case IRLAN_DATA_INDICATION: + irlan_client_parse_response(self, skb); + irlan_open_data_channel(self); + irlan_next_client_state(self, IRLAN_OPEN); + break; + case IRLAN_LMP_DISCONNECT: + case IRLAN_LAP_DISCONNECT: + irlan_next_client_state(self, IRLAN_IDLE); + break; + case IRLAN_WATCHDOG_TIMEOUT: + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_client_state_open (self, event, skb, info) + * + * OPEN, The irlan_client has issued a OpenData command and is awaiting a + * reply + * + */ +static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + struct qos_info qos; + + IRDA_ASSERT(self != NULL, return -1;); + + switch(event) { + case IRLAN_DATA_INDICATION: + irlan_client_parse_response(self, skb); + + /* + * Check if we have got the remote TSAP for data + * communications + */ + IRDA_ASSERT(self->dtsap_sel_data != 0, return -1;); + + /* Check which access type we are dealing with */ + switch (self->client.access_type) { + case ACCESS_PEER: + if (self->provider.state == IRLAN_OPEN) { + + irlan_next_client_state(self, IRLAN_ARB); + irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, + NULL); + } else { + + irlan_next_client_state(self, IRLAN_WAIT); + } + break; + case ACCESS_DIRECT: + case ACCESS_HOSTED: + qos.link_disc_time.bits = 0x01; /* 3 secs */ + + irttp_connect_request(self->tsap_data, + self->dtsap_sel_data, + self->saddr, self->daddr, &qos, + IRLAN_MTU, NULL); + + irlan_next_client_state(self, IRLAN_DATA); + break; + default: + pr_debug("%s(), unknown access type!\n", __func__); + break; + } + break; + case IRLAN_LMP_DISCONNECT: + case IRLAN_LAP_DISCONNECT: + irlan_next_client_state(self, IRLAN_IDLE); + break; + case IRLAN_WATCHDOG_TIMEOUT: + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_client_state_wait (self, event, skb, info) + * + * WAIT, The irlan_client is waiting for the local provider to enter the + * provider OPEN state. + * + */ +static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + + switch(event) { + case IRLAN_PROVIDER_SIGNAL: + irlan_next_client_state(self, IRLAN_ARB); + irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, NULL); + break; + case IRLAN_LMP_DISCONNECT: + case IRLAN_LAP_DISCONNECT: + irlan_next_client_state(self, IRLAN_IDLE); + break; + case IRLAN_WATCHDOG_TIMEOUT: + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + struct qos_info qos; + + IRDA_ASSERT(self != NULL, return -1;); + + switch(event) { + case IRLAN_CHECK_CON_ARB: + if (self->client.recv_arb_val == self->provider.send_arb_val) { + irlan_next_client_state(self, IRLAN_CLOSE); + irlan_close_data_channel(self); + } else if (self->client.recv_arb_val < + self->provider.send_arb_val) + { + qos.link_disc_time.bits = 0x01; /* 3 secs */ + + irlan_next_client_state(self, IRLAN_DATA); + irttp_connect_request(self->tsap_data, + self->dtsap_sel_data, + self->saddr, self->daddr, &qos, + IRLAN_MTU, NULL); + } else if (self->client.recv_arb_val > + self->provider.send_arb_val) + { + pr_debug("%s(), lost the battle :-(\n", __func__); + } + break; + case IRLAN_DATA_CONNECT_INDICATION: + irlan_next_client_state(self, IRLAN_DATA); + break; + case IRLAN_LMP_DISCONNECT: + case IRLAN_LAP_DISCONNECT: + irlan_next_client_state(self, IRLAN_IDLE); + break; + case IRLAN_WATCHDOG_TIMEOUT: + pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_client_state_data (self, event, skb, info) + * + * DATA, The data channel is connected, allowing data transfers between + * the local and remote machines. + * + */ +static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); + + switch(event) { + case IRLAN_DATA_INDICATION: + irlan_client_parse_response(self, skb); + break; + case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ + case IRLAN_LAP_DISCONNECT: + irlan_next_client_state(self, IRLAN_IDLE); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_client_state_close (self, event, skb, info) + * + * + * + */ +static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_client_state_sync (self, event, skb, info) + * + * + * + */ +static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + if (skb) + dev_kfree_skb(skb); + + return 0; +} + + + + + + + + + + + + + diff --git a/drivers/staging/irda/net/irlan/irlan_common.c b/drivers/staging/irda/net/irlan/irlan_common.c new file mode 100644 index 000000000000..481bbc2a4349 --- /dev/null +++ b/drivers/staging/irda/net/irlan/irlan_common.c @@ -0,0 +1,1176 @@ +/********************************************************************* + * + * Filename: irlan_common.c + * Version: 0.9 + * Description: IrDA LAN Access Protocol Implementation + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sun Aug 31 20:14:37 1997 + * Modified at: Sun Dec 26 21:53:10 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1997, 1999 Dag Brattli , + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + + +/* extern char sysctl_devname[]; */ + +/* + * Master structure + */ +static LIST_HEAD(irlans); + +static void *ckey; +static void *skey; + +/* Module parameters */ +static bool eth; /* Use "eth" or "irlan" name for devices */ +static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */ + +#ifdef CONFIG_PROC_FS +static const char *const irlan_access[] = { + "UNKNOWN", + "DIRECT", + "PEER", + "HOSTED" +}; + +static const char *const irlan_media[] = { + "UNKNOWN", + "802.3", + "802.5" +}; + +extern struct proc_dir_entry *proc_irda; + +static int irlan_seq_open(struct inode *inode, struct file *file); + +static const struct file_operations irlan_fops = { + .owner = THIS_MODULE, + .open = irlan_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release, +}; + +extern struct proc_dir_entry *proc_irda; +#endif /* CONFIG_PROC_FS */ + +static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr); +static void __irlan_close(struct irlan_cb *self); +static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, + __u8 value_byte, __u16 value_short, + __u8 *value_array, __u16 value_len); +static void irlan_open_unicast_addr(struct irlan_cb *self); +static void irlan_get_unicast_addr(struct irlan_cb *self); +void irlan_close_tsaps(struct irlan_cb *self); + +/* + * Function irlan_init (void) + * + * Initialize IrLAN layer + * + */ +static int __init irlan_init(void) +{ + struct irlan_cb *new; + __u16 hints; + +#ifdef CONFIG_PROC_FS + { struct proc_dir_entry *proc; + proc = proc_create("irlan", 0, proc_irda, &irlan_fops); + if (!proc) { + printk(KERN_ERR "irlan_init: can't create /proc entry!\n"); + return -ENODEV; + } + } +#endif /* CONFIG_PROC_FS */ + + hints = irlmp_service_to_hint(S_LAN); + + /* Register with IrLMP as a client */ + ckey = irlmp_register_client(hints, &irlan_client_discovery_indication, + NULL, NULL); + if (!ckey) + goto err_ckey; + + /* Register with IrLMP as a service */ + skey = irlmp_register_service(hints); + if (!skey) + goto err_skey; + + /* Start the master IrLAN instance (the only one for now) */ + new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY); + if (!new) + goto err_open; + + /* The master will only open its (listen) control TSAP */ + irlan_provider_open_ctrl_tsap(new); + + /* Do some fast discovery! */ + irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); + + return 0; + +err_open: + irlmp_unregister_service(skey); +err_skey: + irlmp_unregister_client(ckey); +err_ckey: +#ifdef CONFIG_PROC_FS + remove_proc_entry("irlan", proc_irda); +#endif /* CONFIG_PROC_FS */ + + return -ENOMEM; +} + +static void __exit irlan_cleanup(void) +{ + struct irlan_cb *self, *next; + + irlmp_unregister_client(ckey); + irlmp_unregister_service(skey); + +#ifdef CONFIG_PROC_FS + remove_proc_entry("irlan", proc_irda); +#endif /* CONFIG_PROC_FS */ + + /* Cleanup any leftover network devices */ + rtnl_lock(); + list_for_each_entry_safe(self, next, &irlans, dev_list) { + __irlan_close(self); + } + rtnl_unlock(); +} + +/* + * Function irlan_open (void) + * + * Open new instance of a client/provider, we should only register the + * network device if this instance is ment for a particular client/provider + */ +static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr) +{ + struct net_device *dev; + struct irlan_cb *self; + + /* Create network device with irlan */ + dev = alloc_irlandev(eth ? "eth%d" : "irlan%d"); + if (!dev) + return NULL; + + self = netdev_priv(dev); + self->dev = dev; + + /* + * Initialize local device structure + */ + self->magic = IRLAN_MAGIC; + self->saddr = saddr; + self->daddr = daddr; + + /* Provider access can only be PEER, DIRECT, or HOSTED */ + self->provider.access_type = access; + if (access == ACCESS_DIRECT) { + /* + * Since we are emulating an IrLAN sever we will have to + * give ourself an ethernet address! + */ + dev->dev_addr[0] = 0x40; + dev->dev_addr[1] = 0x00; + dev->dev_addr[2] = 0x00; + dev->dev_addr[3] = 0x00; + get_random_bytes(dev->dev_addr+4, 1); + get_random_bytes(dev->dev_addr+5, 1); + } + + self->media = MEDIA_802_3; + self->disconnect_reason = LM_USER_REQUEST; + init_timer(&self->watchdog_timer); + init_timer(&self->client.kick_timer); + init_waitqueue_head(&self->open_wait); + + skb_queue_head_init(&self->client.txq); + + irlan_next_client_state(self, IRLAN_IDLE); + irlan_next_provider_state(self, IRLAN_IDLE); + + if (register_netdev(dev)) { + pr_debug("%s(), register_netdev() failed!\n", + __func__); + self = NULL; + free_netdev(dev); + } else { + rtnl_lock(); + list_add_rcu(&self->dev_list, &irlans); + rtnl_unlock(); + } + + return self; +} +/* + * Function __irlan_close (self) + * + * This function closes and deallocates the IrLAN client instances. Be + * aware that other functions which calls client_close() must + * remove self from irlans list first. + */ +static void __irlan_close(struct irlan_cb *self) +{ + ASSERT_RTNL(); + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + del_timer_sync(&self->watchdog_timer); + del_timer_sync(&self->client.kick_timer); + + /* Close all open connections and remove TSAPs */ + irlan_close_tsaps(self); + + if (self->client.iriap) + iriap_close(self->client.iriap); + + /* Remove frames queued on the control channel */ + skb_queue_purge(&self->client.txq); + + /* Unregister and free self via destructor */ + unregister_netdevice(self->dev); +} + +/* Find any instance of irlan, used for client discovery wakeup */ +struct irlan_cb *irlan_get_any(void) +{ + struct irlan_cb *self; + + list_for_each_entry_rcu(self, &irlans, dev_list) { + return self; + } + return NULL; +} + +/* + * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb) + * + * Here we receive the connect indication for the data channel + * + */ +static void irlan_connect_indication(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct irlan_cb *self; + struct tsap_cb *tsap; + + self = instance; + tsap = sap; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + IRDA_ASSERT(tsap == self->tsap_data,return;); + + self->max_sdu_size = max_sdu_size; + self->max_header_size = max_header_size; + + pr_debug("%s: We are now connected!\n", __func__); + + del_timer(&self->watchdog_timer); + + /* If you want to pass the skb to *both* state machines, you will + * need to skb_clone() it, so that you don't free it twice. + * As the state machines don't need it, git rid of it here... + * Jean II */ + if (skb) + dev_kfree_skb(skb); + + irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); + irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); + + if (self->provider.access_type == ACCESS_PEER) { + /* + * Data channel is open, so we are now allowed to + * configure the remote filter + */ + irlan_get_unicast_addr(self); + irlan_open_unicast_addr(self); + } + /* Ready to transfer Ethernet frames (at last) */ + netif_start_queue(self->dev); /* Clear reason */ +} + +static void irlan_connect_confirm(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct irlan_cb *self; + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + self->max_sdu_size = max_sdu_size; + self->max_header_size = max_header_size; + + /* TODO: we could set the MTU depending on the max_sdu_size */ + + pr_debug("%s: We are now connected!\n", __func__); + del_timer(&self->watchdog_timer); + + /* + * Data channel is open, so we are now allowed to configure the remote + * filter + */ + irlan_get_unicast_addr(self); + irlan_open_unicast_addr(self); + + /* Open broadcast and multicast filter by default */ + irlan_set_broadcast_filter(self, TRUE); + irlan_set_multicast_filter(self, TRUE); + + /* Ready to transfer Ethernet frames */ + netif_start_queue(self->dev); + self->disconnect_reason = 0; /* Clear reason */ + wake_up_interruptible(&self->open_wait); +} + +/* + * Function irlan_client_disconnect_indication (handle) + * + * Callback function for the IrTTP layer. Indicates a disconnection of + * the specified connection (handle) + */ +static void irlan_disconnect_indication(void *instance, + void *sap, LM_REASON reason, + struct sk_buff *userdata) +{ + struct irlan_cb *self; + struct tsap_cb *tsap; + + pr_debug("%s(), reason=%d\n", __func__ , reason); + + self = instance; + tsap = sap; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + IRDA_ASSERT(tsap != NULL, return;); + IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); + + IRDA_ASSERT(tsap == self->tsap_data, return;); + + pr_debug("IrLAN, data channel disconnected by peer!\n"); + + /* Save reason so we know if we should try to reconnect or not */ + self->disconnect_reason = reason; + + switch (reason) { + case LM_USER_REQUEST: /* User request */ + pr_debug("%s(), User requested\n", __func__); + break; + case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */ + pr_debug("%s(), Unexpected IrLAP disconnect\n", __func__); + break; + case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */ + pr_debug("%s(), IrLAP connect failed\n", __func__); + break; + case LM_LAP_RESET: /* IrLAP reset */ + pr_debug("%s(), IrLAP reset\n", __func__); + break; + case LM_INIT_DISCONNECT: + pr_debug("%s(), IrLMP connect failed\n", __func__); + break; + default: + net_err_ratelimited("%s(), Unknown disconnect reason\n", + __func__); + break; + } + + /* If you want to pass the skb to *both* state machines, you will + * need to skb_clone() it, so that you don't free it twice. + * As the state machines don't need it, git rid of it here... + * Jean II */ + if (userdata) + dev_kfree_skb(userdata); + + irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); + irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); + + wake_up_interruptible(&self->open_wait); +} + +void irlan_open_data_tsap(struct irlan_cb *self) +{ + struct tsap_cb *tsap; + notify_t notify; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + /* Check if already open */ + if (self->tsap_data) + return; + + irda_notify_init(¬ify); + + notify.data_indication = irlan_eth_receive; + notify.udata_indication = irlan_eth_receive; + notify.connect_indication = irlan_connect_indication; + notify.connect_confirm = irlan_connect_confirm; + notify.flow_indication = irlan_eth_flow_indication; + notify.disconnect_indication = irlan_disconnect_indication; + notify.instance = self; + strlcpy(notify.name, "IrLAN data", sizeof(notify.name)); + + tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); + if (!tsap) { + pr_debug("%s(), Got no tsap!\n", __func__); + return; + } + self->tsap_data = tsap; + + /* + * This is the data TSAP selector which we will pass to the client + * when the client ask for it. + */ + self->stsap_sel_data = self->tsap_data->stsap_sel; +} + +void irlan_close_tsaps(struct irlan_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + /* Disconnect and close all open TSAP connections */ + if (self->tsap_data) { + irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL); + irttp_close_tsap(self->tsap_data); + self->tsap_data = NULL; + } + if (self->client.tsap_ctrl) { + irttp_disconnect_request(self->client.tsap_ctrl, NULL, + P_NORMAL); + irttp_close_tsap(self->client.tsap_ctrl); + self->client.tsap_ctrl = NULL; + } + if (self->provider.tsap_ctrl) { + irttp_disconnect_request(self->provider.tsap_ctrl, NULL, + P_NORMAL); + irttp_close_tsap(self->provider.tsap_ctrl); + self->provider.tsap_ctrl = NULL; + } + self->disconnect_reason = LM_USER_REQUEST; +} + +/* + * Function irlan_ias_register (self, tsap_sel) + * + * Register with LM-IAS + * + */ +void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel) +{ + struct ias_object *obj; + struct ias_value *new_value; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + /* + * Check if object has already been registered by a previous provider. + * If that is the case, we just change the value of the attribute + */ + if (!irias_find_object("IrLAN")) { + obj = irias_new_object("IrLAN", IAS_IRLAN_ID); + irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel, + IAS_KERNEL_ATTR); + irias_insert_object(obj); + } else { + new_value = irias_new_integer_value(tsap_sel); + irias_object_change_attribute("IrLAN", "IrDA:TinyTP:LsapSel", + new_value); + } + + /* Register PnP object only if not registered before */ + if (!irias_find_object("PnP")) { + obj = irias_new_object("PnP", IAS_PNP_ID); +#if 0 + irias_add_string_attrib(obj, "Name", sysctl_devname, + IAS_KERNEL_ATTR); +#else + irias_add_string_attrib(obj, "Name", "Linux", IAS_KERNEL_ATTR); +#endif + irias_add_string_attrib(obj, "DeviceID", "HWP19F0", + IAS_KERNEL_ATTR); + irias_add_integer_attrib(obj, "CompCnt", 1, IAS_KERNEL_ATTR); + if (self->provider.access_type == ACCESS_PEER) + irias_add_string_attrib(obj, "Comp#01", "PNP8389", + IAS_KERNEL_ATTR); + else + irias_add_string_attrib(obj, "Comp#01", "PNP8294", + IAS_KERNEL_ATTR); + + irias_add_string_attrib(obj, "Manufacturer", + "Linux-IrDA Project", IAS_KERNEL_ATTR); + irias_insert_object(obj); + } +} + +/* + * Function irlan_run_ctrl_tx_queue (self) + * + * Try to send the next command in the control transmit queue + * + */ +int irlan_run_ctrl_tx_queue(struct irlan_cb *self) +{ + struct sk_buff *skb; + + if (irda_lock(&self->client.tx_busy) == FALSE) + return -EBUSY; + + skb = skb_dequeue(&self->client.txq); + if (!skb) { + self->client.tx_busy = FALSE; + return 0; + } + + /* Check that it's really possible to send commands */ + if ((self->client.tsap_ctrl == NULL) || + (self->client.state == IRLAN_IDLE)) + { + self->client.tx_busy = FALSE; + dev_kfree_skb(skb); + return -1; + } + pr_debug("%s(), sending ...\n", __func__); + + return irttp_data_request(self->client.tsap_ctrl, skb); +} + +/* + * Function irlan_ctrl_data_request (self, skb) + * + * This function makes sure that commands on the control channel is being + * sent in a command/response fashion + */ +static void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb) +{ + /* Queue command */ + skb_queue_tail(&self->client.txq, skb); + + /* Try to send command */ + irlan_run_ctrl_tx_queue(self); +} + +/* + * Function irlan_get_provider_info (self) + * + * Send Get Provider Information command to peer IrLAN layer + * + */ +void irlan_get_provider_info(struct irlan_cb *self) +{ + struct sk_buff *skb; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER, + GFP_ATOMIC); + if (!skb) + return; + + /* Reserve space for TTP, LMP, and LAP header */ + skb_reserve(skb, self->client.max_header_size); + skb_put(skb, 2); + + frame = skb->data; + + frame[0] = CMD_GET_PROVIDER_INFO; + frame[1] = 0x00; /* Zero parameters */ + + irlan_ctrl_data_request(self, skb); +} + +/* + * Function irlan_open_data_channel (self) + * + * Send an Open Data Command to provider + * + */ +void irlan_open_data_channel(struct irlan_cb *self) +{ + struct sk_buff *skb; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + + IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3") + + IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "DIRECT"), + GFP_ATOMIC); + if (!skb) + return; + + skb_reserve(skb, self->client.max_header_size); + skb_put(skb, 2); + + frame = skb->data; + + /* Build frame */ + frame[0] = CMD_OPEN_DATA_CHANNEL; + frame[1] = 0x02; /* Two parameters */ + + irlan_insert_string_param(skb, "MEDIA", "802.3"); + irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT"); + /* irlan_insert_string_param(skb, "MODE", "UNRELIABLE"); */ + +/* self->use_udata = TRUE; */ + + irlan_ctrl_data_request(self, skb); +} + +void irlan_close_data_channel(struct irlan_cb *self) +{ + struct sk_buff *skb; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + /* Check if the TSAP is still there */ + if (self->client.tsap_ctrl == NULL) + return; + + skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + + IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN"), + GFP_ATOMIC); + if (!skb) + return; + + skb_reserve(skb, self->client.max_header_size); + skb_put(skb, 2); + + frame = skb->data; + + /* Build frame */ + frame[0] = CMD_CLOSE_DATA_CHAN; + frame[1] = 0x01; /* One parameter */ + + irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); + + irlan_ctrl_data_request(self, skb); +} + +/* + * Function irlan_open_unicast_addr (self) + * + * Make IrLAN provider accept ethernet frames addressed to the unicast + * address. + * + */ +static void irlan_open_unicast_addr(struct irlan_cb *self) +{ + struct sk_buff *skb; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + + IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + + IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + + IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"), + GFP_ATOMIC); + if (!skb) + return; + + /* Reserve space for TTP, LMP, and LAP header */ + skb_reserve(skb, self->max_header_size); + skb_put(skb, 2); + + frame = skb->data; + + frame[0] = CMD_FILTER_OPERATION; + frame[1] = 0x03; /* Three parameters */ + irlan_insert_byte_param(skb, "DATA_CHAN" , self->dtsap_sel_data); + irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); + irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); + + irlan_ctrl_data_request(self, skb); +} + +/* + * Function irlan_set_broadcast_filter (self, status) + * + * Make IrLAN provider accept ethernet frames addressed to the broadcast + * address. Be careful with the use of this one, since there may be a lot + * of broadcast traffic out there. We can still function without this + * one but then _we_ have to initiate all communication with other + * hosts, since ARP request for this host will not be answered. + */ +void irlan_set_broadcast_filter(struct irlan_cb *self, int status) +{ + struct sk_buff *skb; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + + IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + + IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") + + /* We may waste one byte here...*/ + IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"), + GFP_ATOMIC); + if (!skb) + return; + + /* Reserve space for TTP, LMP, and LAP header */ + skb_reserve(skb, self->client.max_header_size); + skb_put(skb, 2); + + frame = skb->data; + + frame[0] = CMD_FILTER_OPERATION; + frame[1] = 0x03; /* Three parameters */ + irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); + irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST"); + if (status) + irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); + else + irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); + + irlan_ctrl_data_request(self, skb); +} + +/* + * Function irlan_set_multicast_filter (self, status) + * + * Make IrLAN provider accept ethernet frames addressed to the multicast + * address. + * + */ +void irlan_set_multicast_filter(struct irlan_cb *self, int status) +{ + struct sk_buff *skb; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + + IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + + IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") + + /* We may waste one byte here...*/ + IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "NONE"), + GFP_ATOMIC); + if (!skb) + return; + + /* Reserve space for TTP, LMP, and LAP header */ + skb_reserve(skb, self->client.max_header_size); + skb_put(skb, 2); + + frame = skb->data; + + frame[0] = CMD_FILTER_OPERATION; + frame[1] = 0x03; /* Three parameters */ + irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); + irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST"); + if (status) + irlan_insert_string_param(skb, "FILTER_MODE", "ALL"); + else + irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); + + irlan_ctrl_data_request(self, skb); +} + +/* + * Function irlan_get_unicast_addr (self) + * + * Retrieves the unicast address from the IrLAN provider. This address + * will be inserted into the devices structure, so the ethernet layer + * can construct its packets. + * + */ +static void irlan_get_unicast_addr(struct irlan_cb *self) +{ + struct sk_buff *skb; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + + IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + + IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + + IRLAN_STRING_PARAMETER_LEN("FILTER_OPERATION", + "DYNAMIC"), + GFP_ATOMIC); + if (!skb) + return; + + /* Reserve space for TTP, LMP, and LAP header */ + skb_reserve(skb, self->client.max_header_size); + skb_put(skb, 2); + + frame = skb->data; + + frame[0] = CMD_FILTER_OPERATION; + frame[1] = 0x03; /* Three parameters */ + irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); + irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); + irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC"); + + irlan_ctrl_data_request(self, skb); +} + +/* + * Function irlan_get_media_char (self) + * + * + * + */ +void irlan_get_media_char(struct irlan_cb *self) +{ + struct sk_buff *skb; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + + IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3"), + GFP_ATOMIC); + + if (!skb) + return; + + /* Reserve space for TTP, LMP, and LAP header */ + skb_reserve(skb, self->client.max_header_size); + skb_put(skb, 2); + + frame = skb->data; + + /* Build frame */ + frame[0] = CMD_GET_MEDIA_CHAR; + frame[1] = 0x01; /* One parameter */ + + irlan_insert_string_param(skb, "MEDIA", "802.3"); + irlan_ctrl_data_request(self, skb); +} + +/* + * Function insert_byte_param (skb, param, value) + * + * Insert byte parameter into frame + * + */ +int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value) +{ + return __irlan_insert_param(skb, param, IRLAN_BYTE, value, 0, NULL, 0); +} + +int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value) +{ + return __irlan_insert_param(skb, param, IRLAN_SHORT, 0, value, NULL, 0); +} + +/* + * Function insert_string (skb, param, value) + * + * Insert string parameter into frame + * + */ +int irlan_insert_string_param(struct sk_buff *skb, char *param, char *string) +{ + int string_len = strlen(string); + + return __irlan_insert_param(skb, param, IRLAN_ARRAY, 0, 0, string, + string_len); +} + +/* + * Function insert_array_param(skb, param, value, len_value) + * + * Insert array parameter into frame + * + */ +int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *array, + __u16 array_len) +{ + return __irlan_insert_param(skb, name, IRLAN_ARRAY, 0, 0, array, + array_len); +} + +/* + * Function insert_param (skb, param, value, byte) + * + * Insert parameter at end of buffer, structure of a parameter is: + * + * ----------------------------------------------------------------------- + * | Name Length[1] | Param Name[1..255] | Val Length[2] | Value[0..1016]| + * ----------------------------------------------------------------------- + */ +static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, + __u8 value_byte, __u16 value_short, + __u8 *value_array, __u16 value_len) +{ + __u8 *frame; + __u8 param_len; + __le16 tmp_le; /* Temporary value in little endian format */ + int n=0; + + if (skb == NULL) { + pr_debug("%s(), Got NULL skb\n", __func__); + return 0; + } + + param_len = strlen(param); + switch (type) { + case IRLAN_BYTE: + value_len = 1; + break; + case IRLAN_SHORT: + value_len = 2; + break; + case IRLAN_ARRAY: + IRDA_ASSERT(value_array != NULL, return 0;); + IRDA_ASSERT(value_len > 0, return 0;); + break; + default: + pr_debug("%s(), Unknown parameter type!\n", __func__); + return 0; + } + + /* Insert at end of sk-buffer */ + frame = skb_tail_pointer(skb); + + /* Make space for data */ + if (skb_tailroom(skb) < (param_len+value_len+3)) { + pr_debug("%s(), No more space at end of skb\n", __func__); + return 0; + } + skb_put(skb, param_len+value_len+3); + + /* Insert parameter length */ + frame[n++] = param_len; + + /* Insert parameter */ + memcpy(frame+n, param, param_len); n += param_len; + + /* Insert value length (2 byte little endian format, LSB first) */ + tmp_le = cpu_to_le16(value_len); + memcpy(frame+n, &tmp_le, 2); n += 2; /* To avoid alignment problems */ + + /* Insert value */ + switch (type) { + case IRLAN_BYTE: + frame[n++] = value_byte; + break; + case IRLAN_SHORT: + tmp_le = cpu_to_le16(value_short); + memcpy(frame+n, &tmp_le, 2); n += 2; + break; + case IRLAN_ARRAY: + memcpy(frame+n, value_array, value_len); n+=value_len; + break; + default: + break; + } + IRDA_ASSERT(n == (param_len+value_len+3), return 0;); + + return param_len+value_len+3; +} + +/* + * Function irlan_extract_param (buf, name, value, len) + * + * Extracts a single parameter name/value pair from buffer and updates + * the buffer pointer to point to the next name/value pair. + */ +int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len) +{ + __u8 name_len; + __u16 val_len; + int n=0; + + /* get length of parameter name (1 byte) */ + name_len = buf[n++]; + + if (name_len > 254) { + pr_debug("%s(), name_len > 254\n", __func__); + return -RSP_INVALID_COMMAND_FORMAT; + } + + /* get parameter name */ + memcpy(name, buf+n, name_len); + name[name_len] = '\0'; + n+=name_len; + + /* + * Get length of parameter value (2 bytes in little endian + * format) + */ + memcpy(&val_len, buf+n, 2); /* To avoid alignment problems */ + le16_to_cpus(&val_len); n+=2; + + if (val_len >= 1016) { + pr_debug("%s(), parameter length to long\n", __func__); + return -RSP_INVALID_COMMAND_FORMAT; + } + *len = val_len; + + /* get parameter value */ + memcpy(value, buf+n, val_len); + value[val_len] = '\0'; + n+=val_len; + + pr_debug("Parameter: %s ", name); + pr_debug("Value: %s\n", value); + + return n; +} + +#ifdef CONFIG_PROC_FS + +/* + * Start of reading /proc entries. + * Return entry at pos, + * or start_token to indicate print header line + * or NULL if end of file + */ +static void *irlan_seq_start(struct seq_file *seq, loff_t *pos) +{ + rcu_read_lock(); + return seq_list_start_head(&irlans, *pos); +} + +/* Return entry after v, and increment pos */ +static void *irlan_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + return seq_list_next(v, &irlans, pos); +} + +/* End of reading /proc file */ +static void irlan_seq_stop(struct seq_file *seq, void *v) +{ + rcu_read_unlock(); +} + + +/* + * Show one entry in /proc file. + */ +static int irlan_seq_show(struct seq_file *seq, void *v) +{ + if (v == &irlans) + seq_puts(seq, "IrLAN instances:\n"); + else { + struct irlan_cb *self = list_entry(v, struct irlan_cb, dev_list); + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); + + seq_printf(seq,"ifname: %s,\n", + self->dev->name); + seq_printf(seq,"client state: %s, ", + irlan_state[ self->client.state]); + seq_printf(seq,"provider state: %s,\n", + irlan_state[ self->provider.state]); + seq_printf(seq,"saddr: %#08x, ", + self->saddr); + seq_printf(seq,"daddr: %#08x\n", + self->daddr); + seq_printf(seq,"version: %d.%d,\n", + self->version[1], self->version[0]); + seq_printf(seq,"access type: %s\n", + irlan_access[self->client.access_type]); + seq_printf(seq,"media: %s\n", + irlan_media[self->media]); + + seq_printf(seq,"local filter:\n"); + seq_printf(seq,"remote filter: "); + irlan_print_filter(seq, self->client.filter_type); + seq_printf(seq,"tx busy: %s\n", + netif_queue_stopped(self->dev) ? "TRUE" : "FALSE"); + + seq_putc(seq,'\n'); + } + return 0; +} + +static const struct seq_operations irlan_seq_ops = { + .start = irlan_seq_start, + .next = irlan_seq_next, + .stop = irlan_seq_stop, + .show = irlan_seq_show, +}; + +static int irlan_seq_open(struct inode *inode, struct file *file) +{ + return seq_open(file, &irlan_seq_ops); +} +#endif + +MODULE_AUTHOR("Dag Brattli "); +MODULE_DESCRIPTION("The Linux IrDA LAN protocol"); +MODULE_LICENSE("GPL"); + +module_param(eth, bool, 0); +MODULE_PARM_DESC(eth, "Name devices ethX (0) or irlanX (1)"); +module_param(access, int, 0); +MODULE_PARM_DESC(access, "Access type DIRECT=1, PEER=2, HOSTED=3"); + +module_init(irlan_init); +module_exit(irlan_cleanup); + diff --git a/drivers/staging/irda/net/irlan/irlan_eth.c b/drivers/staging/irda/net/irlan/irlan_eth.c new file mode 100644 index 000000000000..3be852808a9d --- /dev/null +++ b/drivers/staging/irda/net/irlan/irlan_eth.c @@ -0,0 +1,340 @@ +/********************************************************************* + * + * Filename: irlan_eth.c + * Version: + * Description: + * Status: Experimental. + * Author: Dag Brattli + * Created at: Thu Oct 15 08:37:58 1998 + * Modified at: Tue Mar 21 09:06:41 2000 + * Modified by: Dag Brattli + * Sources: skeleton.c by Donald Becker + * slip.c by Laurence Culhane, + * Fred N. van Kempen, + * + * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static int irlan_eth_open(struct net_device *dev); +static int irlan_eth_close(struct net_device *dev); +static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb, + struct net_device *dev); +static void irlan_eth_set_multicast_list(struct net_device *dev); + +static const struct net_device_ops irlan_eth_netdev_ops = { + .ndo_open = irlan_eth_open, + .ndo_stop = irlan_eth_close, + .ndo_start_xmit = irlan_eth_xmit, + .ndo_set_rx_mode = irlan_eth_set_multicast_list, + .ndo_validate_addr = eth_validate_addr, +}; + +/* + * Function irlan_eth_setup (dev) + * + * The network device initialization function. + * + */ +static void irlan_eth_setup(struct net_device *dev) +{ + ether_setup(dev); + + dev->netdev_ops = &irlan_eth_netdev_ops; + dev->needs_free_netdev = true; + dev->min_mtu = 0; + dev->max_mtu = ETH_MAX_MTU; + + /* + * Lets do all queueing in IrTTP instead of this device driver. + * Queueing here as well can introduce some strange latency + * problems, which we will avoid by setting the queue size to 0. + */ + /* + * The bugs in IrTTP and IrLAN that created this latency issue + * have now been fixed, and we can propagate flow control properly + * to the network layer. However, this requires a minimal queue of + * packets for the device. + * Without flow control, the Tx Queue is 14 (ttp) + 0 (dev) = 14 + * With flow control, the Tx Queue is 7 (ttp) + 4 (dev) = 11 + * See irlan_eth_flow_indication()... + * Note : this number was randomly selected and would need to + * be adjusted. + * Jean II */ + dev->tx_queue_len = 4; +} + +/* + * Function alloc_irlandev + * + * Allocate network device and control block + * + */ +struct net_device *alloc_irlandev(const char *name) +{ + return alloc_netdev(sizeof(struct irlan_cb), name, NET_NAME_UNKNOWN, + irlan_eth_setup); +} + +/* + * Function irlan_eth_open (dev) + * + * Network device has been opened by user + * + */ +static int irlan_eth_open(struct net_device *dev) +{ + struct irlan_cb *self = netdev_priv(dev); + + /* Ready to play! */ + netif_stop_queue(dev); /* Wait until data link is ready */ + + /* We are now open, so time to do some work */ + self->disconnect_reason = 0; + irlan_client_wakeup(self, self->saddr, self->daddr); + + /* Make sure we have a hardware address before we return, + so DHCP clients gets happy */ + return wait_event_interruptible(self->open_wait, + !self->tsap_data->connected); +} + +/* + * Function irlan_eth_close (dev) + * + * Stop the ether network device, his function will usually be called by + * ifconfig down. We should now disconnect the link, We start the + * close timer, so that the instance will be removed if we are unable + * to discover the remote device after the disconnect. + */ +static int irlan_eth_close(struct net_device *dev) +{ + struct irlan_cb *self = netdev_priv(dev); + + /* Stop device */ + netif_stop_queue(dev); + + irlan_close_data_channel(self); + irlan_close_tsaps(self); + + irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); + irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); + + /* Remove frames queued on the control channel */ + skb_queue_purge(&self->client.txq); + + self->client.tx_busy = 0; + + return 0; +} + +/* + * Function irlan_eth_tx (skb) + * + * Transmits ethernet frames over IrDA link. + * + */ +static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb, + struct net_device *dev) +{ + struct irlan_cb *self = netdev_priv(dev); + int ret; + unsigned int len; + + /* skb headroom large enough to contain all IrDA-headers? */ + if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) { + struct sk_buff *new_skb = + skb_realloc_headroom(skb, self->max_header_size); + + /* We have to free the original skb anyway */ + dev_kfree_skb(skb); + + /* Did the realloc succeed? */ + if (new_skb == NULL) + return NETDEV_TX_OK; + + /* Use the new skb instead */ + skb = new_skb; + } + + netif_trans_update(dev); + + len = skb->len; + /* Now queue the packet in the transport layer */ + if (self->use_udata) + ret = irttp_udata_request(self->tsap_data, skb); + else + ret = irttp_data_request(self->tsap_data, skb); + + if (ret < 0) { + /* + * IrTTPs tx queue is full, so we just have to + * drop the frame! You might think that we should + * just return -1 and don't deallocate the frame, + * but that is dangerous since it's possible that + * we have replaced the original skb with a new + * one with larger headroom, and that would really + * confuse do_dev_queue_xmit() in dev.c! I have + * tried :-) DB + */ + /* irttp_data_request already free the packet */ + dev->stats.tx_dropped++; + } else { + dev->stats.tx_packets++; + dev->stats.tx_bytes += len; + } + + return NETDEV_TX_OK; +} + +/* + * Function irlan_eth_receive (handle, skb) + * + * This function gets the data that is received on the data channel + * + */ +int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb) +{ + struct irlan_cb *self = instance; + struct net_device *dev = self->dev; + + if (skb == NULL) { + dev->stats.rx_dropped++; + return 0; + } + if (skb->len < ETH_HLEN) { + pr_debug("%s() : IrLAN frame too short (%d)\n", + __func__, skb->len); + dev->stats.rx_dropped++; + dev_kfree_skb(skb); + return 0; + } + + /* + * Adopt this frame! Important to set all these fields since they + * might have been previously set by the low level IrDA network + * device driver + */ + skb->protocol = eth_type_trans(skb, dev); /* Remove eth header */ + + dev->stats.rx_packets++; + dev->stats.rx_bytes += skb->len; + + netif_rx(skb); /* Eat it! */ + + return 0; +} + +/* + * Function irlan_eth_flow (status) + * + * Do flow control between IP/Ethernet and IrLAN/IrTTP. This is done by + * controlling the queue stop/start. + * + * The IrDA link layer has the advantage to have flow control, and + * IrTTP now properly handles that. Flow controlling the higher layers + * prevent us to drop Tx packets in here (up to 15% for a TCP socket, + * more for UDP socket). + * Also, this allow us to reduce the overall transmit queue, which means + * less latency in case of mixed traffic. + * Jean II + */ +void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) +{ + struct irlan_cb *self; + struct net_device *dev; + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + dev = self->dev; + + IRDA_ASSERT(dev != NULL, return;); + + pr_debug("%s() : flow %s ; running %d\n", __func__, + flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START", + netif_running(dev)); + + switch (flow) { + case FLOW_STOP: + /* IrTTP is full, stop higher layers */ + netif_stop_queue(dev); + break; + case FLOW_START: + default: + /* Tell upper layers that its time to transmit frames again */ + /* Schedule network layer */ + netif_wake_queue(dev); + break; + } +} + +/* + * Function set_multicast_list (dev) + * + * Configure the filtering of the device + * + */ +#define HW_MAX_ADDRS 4 /* Must query to get it! */ +static void irlan_eth_set_multicast_list(struct net_device *dev) +{ + struct irlan_cb *self = netdev_priv(dev); + + /* Check if data channel has been connected yet */ + if (self->client.state != IRLAN_DATA) { + pr_debug("%s(), delaying!\n", __func__); + return; + } + + if (dev->flags & IFF_PROMISC) { + /* Enable promiscuous mode */ + net_warn_ratelimited("Promiscuous mode not implemented by IrLAN!\n"); + } else if ((dev->flags & IFF_ALLMULTI) || + netdev_mc_count(dev) > HW_MAX_ADDRS) { + /* Disable promiscuous mode, use normal mode. */ + pr_debug("%s(), Setting multicast filter\n", __func__); + /* hardware_set_filter(NULL); */ + + irlan_set_multicast_filter(self, TRUE); + } else if (!netdev_mc_empty(dev)) { + pr_debug("%s(), Setting multicast filter\n", __func__); + /* Walk the address list, and load the filter */ + /* hardware_set_filter(dev->mc_list); */ + + irlan_set_multicast_filter(self, TRUE); + } else { + pr_debug("%s(), Clearing multicast filter\n", __func__); + irlan_set_multicast_filter(self, FALSE); + } + + if (dev->flags & IFF_BROADCAST) + irlan_set_broadcast_filter(self, TRUE); + else + irlan_set_broadcast_filter(self, FALSE); +} diff --git a/drivers/staging/irda/net/irlan/irlan_event.c b/drivers/staging/irda/net/irlan/irlan_event.c new file mode 100644 index 000000000000..9a1cc11c16f6 --- /dev/null +++ b/drivers/staging/irda/net/irlan/irlan_event.c @@ -0,0 +1,60 @@ +/********************************************************************* + * + * Filename: irlan_event.c + * Version: + * Description: + * Status: Experimental. + * Author: Dag Brattli + * Created at: Tue Oct 20 09:10:16 1998 + * Modified at: Sat Oct 30 12:59:01 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include + +const char * const irlan_state[] = { + "IRLAN_IDLE", + "IRLAN_QUERY", + "IRLAN_CONN", + "IRLAN_INFO", + "IRLAN_MEDIA", + "IRLAN_OPEN", + "IRLAN_WAIT", + "IRLAN_ARB", + "IRLAN_DATA", + "IRLAN_CLOSE", + "IRLAN_SYNC", +}; + +void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state) +{ + pr_debug("%s(), %s\n", __func__ , irlan_state[state]); + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + self->client.state = state; +} + +void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state) +{ + pr_debug("%s(), %s\n", __func__ , irlan_state[state]); + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + self->provider.state = state; +} + diff --git a/drivers/staging/irda/net/irlan/irlan_filter.c b/drivers/staging/irda/net/irlan/irlan_filter.c new file mode 100644 index 000000000000..e755e90b2f26 --- /dev/null +++ b/drivers/staging/irda/net/irlan/irlan_filter.c @@ -0,0 +1,240 @@ +/********************************************************************* + * + * Filename: irlan_filter.c + * Version: + * Description: + * Status: Experimental. + * Author: Dag Brattli + * Created at: Fri Jan 29 11:16:38 1999 + * Modified at: Sat Oct 30 12:58:45 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include + +#include +#include + +/* + * Function irlan_filter_request (self, skb) + * + * Handle filter request from client peer device + * + */ +void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + if ((self->provider.filter_type == IRLAN_DIRECTED) && + (self->provider.filter_operation == DYNAMIC)) + { + pr_debug("Giving peer a dynamic Ethernet address\n"); + self->provider.mac_address[0] = 0x40; + self->provider.mac_address[1] = 0x00; + self->provider.mac_address[2] = 0x00; + self->provider.mac_address[3] = 0x00; + + /* Use arbitration value to generate MAC address */ + if (self->provider.access_type == ACCESS_PEER) { + self->provider.mac_address[4] = + self->provider.send_arb_val & 0xff; + self->provider.mac_address[5] = + (self->provider.send_arb_val >> 8) & 0xff; + } else { + /* Just generate something for now */ + get_random_bytes(self->provider.mac_address+4, 1); + get_random_bytes(self->provider.mac_address+5, 1); + } + + skb->data[0] = 0x00; /* Success */ + skb->data[1] = 0x03; + irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); + irlan_insert_short_param(skb, "MAX_ENTRY", 0x0001); + irlan_insert_array_param(skb, "FILTER_ENTRY", + self->provider.mac_address, 6); + return; + } + + if ((self->provider.filter_type == IRLAN_DIRECTED) && + (self->provider.filter_mode == FILTER)) + { + pr_debug("Directed filter on\n"); + skb->data[0] = 0x00; /* Success */ + skb->data[1] = 0x00; + return; + } + if ((self->provider.filter_type == IRLAN_DIRECTED) && + (self->provider.filter_mode == NONE)) + { + pr_debug("Directed filter off\n"); + skb->data[0] = 0x00; /* Success */ + skb->data[1] = 0x00; + return; + } + + if ((self->provider.filter_type == IRLAN_BROADCAST) && + (self->provider.filter_mode == FILTER)) + { + pr_debug("Broadcast filter on\n"); + skb->data[0] = 0x00; /* Success */ + skb->data[1] = 0x00; + return; + } + if ((self->provider.filter_type == IRLAN_BROADCAST) && + (self->provider.filter_mode == NONE)) + { + pr_debug("Broadcast filter off\n"); + skb->data[0] = 0x00; /* Success */ + skb->data[1] = 0x00; + return; + } + if ((self->provider.filter_type == IRLAN_MULTICAST) && + (self->provider.filter_mode == FILTER)) + { + pr_debug("Multicast filter on\n"); + skb->data[0] = 0x00; /* Success */ + skb->data[1] = 0x00; + return; + } + if ((self->provider.filter_type == IRLAN_MULTICAST) && + (self->provider.filter_mode == NONE)) + { + pr_debug("Multicast filter off\n"); + skb->data[0] = 0x00; /* Success */ + skb->data[1] = 0x00; + return; + } + if ((self->provider.filter_type == IRLAN_MULTICAST) && + (self->provider.filter_operation == GET)) + { + pr_debug("Multicast filter get\n"); + skb->data[0] = 0x00; /* Success? */ + skb->data[1] = 0x02; + irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); + irlan_insert_short_param(skb, "MAX_ENTRY", 16); + return; + } + skb->data[0] = 0x00; /* Command not supported */ + skb->data[1] = 0x00; + + pr_debug("Not implemented!\n"); +} + +/* + * Function check_request_param (self, param, value) + * + * Check parameters in request from peer device + * + */ +void irlan_check_command_param(struct irlan_cb *self, char *param, char *value) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + pr_debug("%s, %s\n", param, value); + + /* + * This is experimental!! DB. + */ + if (strcmp(param, "MODE") == 0) { + self->use_udata = TRUE; + return; + } + + /* + * FILTER_TYPE + */ + if (strcmp(param, "FILTER_TYPE") == 0) { + if (strcmp(value, "DIRECTED") == 0) { + self->provider.filter_type = IRLAN_DIRECTED; + return; + } + if (strcmp(value, "MULTICAST") == 0) { + self->provider.filter_type = IRLAN_MULTICAST; + return; + } + if (strcmp(value, "BROADCAST") == 0) { + self->provider.filter_type = IRLAN_BROADCAST; + return; + } + } + /* + * FILTER_MODE + */ + if (strcmp(param, "FILTER_MODE") == 0) { + if (strcmp(value, "ALL") == 0) { + self->provider.filter_mode = ALL; + return; + } + if (strcmp(value, "FILTER") == 0) { + self->provider.filter_mode = FILTER; + return; + } + if (strcmp(value, "NONE") == 0) { + self->provider.filter_mode = FILTER; + return; + } + } + /* + * FILTER_OPERATION + */ + if (strcmp(param, "FILTER_OPERATION") == 0) { + if (strcmp(value, "DYNAMIC") == 0) { + self->provider.filter_operation = DYNAMIC; + return; + } + if (strcmp(value, "GET") == 0) { + self->provider.filter_operation = GET; + return; + } + } +} + +/* + * Function irlan_print_filter (filter_type, buf) + * + * Print status of filter. Used by /proc file system + * + */ +#ifdef CONFIG_PROC_FS +#define MASK2STR(m,s) { .mask = m, .str = s } + +void irlan_print_filter(struct seq_file *seq, int filter_type) +{ + static struct { + int mask; + const char *str; + } filter_mask2str[] = { + MASK2STR(IRLAN_DIRECTED, "DIRECTED"), + MASK2STR(IRLAN_FUNCTIONAL, "FUNCTIONAL"), + MASK2STR(IRLAN_GROUP, "GROUP"), + MASK2STR(IRLAN_MAC_FRAME, "MAC_FRAME"), + MASK2STR(IRLAN_MULTICAST, "MULTICAST"), + MASK2STR(IRLAN_BROADCAST, "BROADCAST"), + MASK2STR(IRLAN_IPX_SOCKET, "IPX_SOCKET"), + MASK2STR(0, NULL) + }, *p; + + for (p = filter_mask2str; p->str; p++) { + if (filter_type & p->mask) + seq_printf(seq, "%s ", p->str); + } + seq_putc(seq, '\n'); +} +#undef MASK2STR +#endif diff --git a/drivers/staging/irda/net/irlan/irlan_provider.c b/drivers/staging/irda/net/irlan/irlan_provider.c new file mode 100644 index 000000000000..15c292cf2644 --- /dev/null +++ b/drivers/staging/irda/net/irlan/irlan_provider.c @@ -0,0 +1,408 @@ +/********************************************************************* + * + * Filename: irlan_provider.c + * Version: 0.9 + * Description: IrDA LAN Access Protocol Implementation + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sun Aug 31 20:14:37 1997 + * Modified at: Sat Oct 30 12:52:10 1999 + * Modified by: Dag Brattli + * Sources: skeleton.c by Donald Becker + * slip.c by Laurence Culhane, + * Fred N. van Kempen, + * + * Copyright (c) 1998-1999 Dag Brattli , + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +static void irlan_provider_connect_indication(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb); + +/* + * Function irlan_provider_control_data_indication (handle, skb) + * + * This function gets the data that is received on the control channel + * + */ +static int irlan_provider_data_indication(void *instance, void *sap, + struct sk_buff *skb) +{ + struct irlan_cb *self; + __u8 code; + + self = instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); + + IRDA_ASSERT(skb != NULL, return -1;); + + code = skb->data[0]; + switch(code) { + case CMD_GET_PROVIDER_INFO: + pr_debug("Got GET_PROVIDER_INFO command!\n"); + irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb); + break; + + case CMD_GET_MEDIA_CHAR: + pr_debug("Got GET_MEDIA_CHAR command!\n"); + irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb); + break; + case CMD_OPEN_DATA_CHANNEL: + pr_debug("Got OPEN_DATA_CHANNEL command!\n"); + irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb); + break; + case CMD_FILTER_OPERATION: + pr_debug("Got FILTER_OPERATION command!\n"); + irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb); + break; + case CMD_RECONNECT_DATA_CHAN: + pr_debug("%s(), Got RECONNECT_DATA_CHAN command\n", __func__); + pr_debug("%s(), NOT IMPLEMENTED\n", __func__); + break; + case CMD_CLOSE_DATA_CHAN: + pr_debug("Got CLOSE_DATA_CHAN command!\n"); + pr_debug("%s(), NOT IMPLEMENTED\n", __func__); + break; + default: + pr_debug("%s(), Unknown command!\n", __func__); + break; + } + return 0; +} + +/* + * Function irlan_provider_connect_indication (handle, skb, priv) + * + * Got connection from peer IrLAN client + * + */ +static void irlan_provider_connect_indication(void *instance, void *sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + struct irlan_cb *self; + struct tsap_cb *tsap; + + self = instance; + tsap = sap; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + IRDA_ASSERT(tsap == self->provider.tsap_ctrl,return;); + IRDA_ASSERT(self->provider.state == IRLAN_IDLE, return;); + + self->provider.max_sdu_size = max_sdu_size; + self->provider.max_header_size = max_header_size; + + irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL); + + /* + * If we are in peer mode, the client may not have got the discovery + * indication it needs to make progress. If the client is still in + * IDLE state, we must kick it. + */ + if ((self->provider.access_type == ACCESS_PEER) && + (self->client.state == IRLAN_IDLE)) + { + irlan_client_wakeup(self, self->saddr, self->daddr); + } +} + +/* + * Function irlan_provider_connect_response (handle) + * + * Accept incoming connection + * + */ +void irlan_provider_connect_response(struct irlan_cb *self, + struct tsap_cb *tsap) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + + /* Just accept */ + irttp_connect_response(tsap, IRLAN_MTU, NULL); +} + +static void irlan_provider_disconnect_indication(void *instance, void *sap, + LM_REASON reason, + struct sk_buff *userdata) +{ + struct irlan_cb *self; + struct tsap_cb *tsap; + + pr_debug("%s(), reason=%d\n", __func__ , reason); + + self = instance; + tsap = sap; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); + IRDA_ASSERT(tsap != NULL, return;); + IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); + + IRDA_ASSERT(tsap == self->provider.tsap_ctrl, return;); + + irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); +} + +/* + * Function irlan_parse_open_data_cmd (self, skb) + * + * + * + */ +int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb) +{ + int ret; + + ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb); + + /* Open data channel */ + irlan_open_data_tsap(self); + + return ret; +} + +/* + * Function parse_command (skb) + * + * Extract all parameters from received buffer, then feed them to + * check_params for parsing + * + */ +int irlan_provider_parse_command(struct irlan_cb *self, int cmd, + struct sk_buff *skb) +{ + __u8 *frame; + __u8 *ptr; + int count; + __u16 val_len; + int i; + char *name; + char *value; + int ret = RSP_SUCCESS; + + IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;); + + pr_debug("%s(), skb->len=%d\n", __func__ , (int)skb->len); + + IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;); + + if (!skb) + return -RSP_PROTOCOL_ERROR; + + frame = skb->data; + + name = kmalloc(255, GFP_ATOMIC); + if (!name) + return -RSP_INSUFFICIENT_RESOURCES; + value = kmalloc(1016, GFP_ATOMIC); + if (!value) { + kfree(name); + return -RSP_INSUFFICIENT_RESOURCES; + } + + /* How many parameters? */ + count = frame[1]; + + pr_debug("Got %d parameters\n", count); + + ptr = frame+2; + + /* For all parameters */ + for (i=0; imagic == IRLAN_MAGIC, return;); + + skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + + /* Bigger param length comes from CMD_GET_MEDIA_CHAR */ + IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + + IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") + + IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") + + IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "HOSTED"), + GFP_ATOMIC); + + if (!skb) + return; + + /* Reserve space for TTP, LMP, and LAP header */ + skb_reserve(skb, self->provider.max_header_size); + skb_put(skb, 2); + + switch (command) { + case CMD_GET_PROVIDER_INFO: + skb->data[0] = 0x00; /* Success */ + skb->data[1] = 0x02; /* 2 parameters */ + switch (self->media) { + case MEDIA_802_3: + irlan_insert_string_param(skb, "MEDIA", "802.3"); + break; + case MEDIA_802_5: + irlan_insert_string_param(skb, "MEDIA", "802.5"); + break; + default: + pr_debug("%s(), unknown media type!\n", __func__); + break; + } + irlan_insert_short_param(skb, "IRLAN_VER", 0x0101); + break; + + case CMD_GET_MEDIA_CHAR: + skb->data[0] = 0x00; /* Success */ + skb->data[1] = 0x05; /* 5 parameters */ + irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); + irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST"); + irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST"); + + switch (self->provider.access_type) { + case ACCESS_DIRECT: + irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT"); + break; + case ACCESS_PEER: + irlan_insert_string_param(skb, "ACCESS_TYPE", "PEER"); + break; + case ACCESS_HOSTED: + irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED"); + break; + default: + pr_debug("%s(), Unknown access type\n", __func__); + break; + } + irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee); + break; + case CMD_OPEN_DATA_CHANNEL: + skb->data[0] = 0x00; /* Success */ + if (self->provider.send_arb_val) { + skb->data[1] = 0x03; /* 3 parameters */ + irlan_insert_short_param(skb, "CON_ARB", + self->provider.send_arb_val); + } else + skb->data[1] = 0x02; /* 2 parameters */ + irlan_insert_byte_param(skb, "DATA_CHAN", self->stsap_sel_data); + irlan_insert_string_param(skb, "RECONNECT_KEY", "LINUX RULES!"); + break; + case CMD_FILTER_OPERATION: + irlan_filter_request(self, skb); + break; + default: + pr_debug("%s(), Unknown command!\n", __func__); + break; + } + + irttp_data_request(self->provider.tsap_ctrl, skb); +} + +/* + * Function irlan_provider_register(void) + * + * Register provider support so we can accept incoming connections. + * + */ +int irlan_provider_open_ctrl_tsap(struct irlan_cb *self) +{ + struct tsap_cb *tsap; + notify_t notify; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); + + /* Check if already open */ + if (self->provider.tsap_ctrl) + return -1; + + /* + * First register well known control TSAP + */ + irda_notify_init(¬ify); + notify.data_indication = irlan_provider_data_indication; + notify.connect_indication = irlan_provider_connect_indication; + notify.disconnect_indication = irlan_provider_disconnect_indication; + notify.instance = self; + strlcpy(notify.name, "IrLAN ctrl (p)", sizeof(notify.name)); + + tsap = irttp_open_tsap(LSAP_ANY, 1, ¬ify); + if (!tsap) { + pr_debug("%s(), Got no tsap!\n", __func__); + return -1; + } + self->provider.tsap_ctrl = tsap; + + /* Register with LM-IAS */ + irlan_ias_register(self, tsap->stsap_sel); + + return 0; +} + diff --git a/drivers/staging/irda/net/irlan/irlan_provider_event.c b/drivers/staging/irda/net/irlan/irlan_provider_event.c new file mode 100644 index 000000000000..9c4f7f51d6b5 --- /dev/null +++ b/drivers/staging/irda/net/irlan/irlan_provider_event.c @@ -0,0 +1,233 @@ +/********************************************************************* + * + * Filename: irlan_provider_event.c + * Version: 0.9 + * Description: IrLAN provider state machine) + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sun Aug 31 20:14:37 1997 + * Modified at: Sat Oct 30 12:52:41 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999 Dag Brattli , All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include + +#include +#include + +static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); +static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb); + +static int (*state[])(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) = +{ + irlan_provider_state_idle, + NULL, /* Query */ + NULL, /* Info */ + irlan_provider_state_info, + NULL, /* Media */ + irlan_provider_state_open, + NULL, /* Wait */ + NULL, /* Arb */ + irlan_provider_state_data, + NULL, /* Close */ + NULL, /* Sync */ +}; + +void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(*state[ self->provider.state] != NULL, return;); + + (*state[self->provider.state]) (self, event, skb); +} + +/* + * Function irlan_provider_state_idle (event, skb, info) + * + * IDLE, We are waiting for an indication that there is a provider + * available. + */ +static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + + switch(event) { + case IRLAN_CONNECT_INDICATION: + irlan_provider_connect_response( self, self->provider.tsap_ctrl); + irlan_next_provider_state( self, IRLAN_INFO); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_provider_state_info (self, event, skb, info) + * + * INFO, We have issued a GetInfo command and is awaiting a reply. + */ +static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + int ret; + + IRDA_ASSERT(self != NULL, return -1;); + + switch(event) { + case IRLAN_GET_INFO_CMD: + /* Be sure to use 802.3 in case of peer mode */ + if (self->provider.access_type == ACCESS_PEER) { + self->media = MEDIA_802_3; + + /* Check if client has started yet */ + if (self->client.state == IRLAN_IDLE) { + /* This should get the client going */ + irlmp_discovery_request(8); + } + } + + irlan_provider_send_reply(self, CMD_GET_PROVIDER_INFO, + RSP_SUCCESS); + /* Keep state */ + break; + case IRLAN_GET_MEDIA_CMD: + irlan_provider_send_reply(self, CMD_GET_MEDIA_CHAR, + RSP_SUCCESS); + /* Keep state */ + break; + case IRLAN_OPEN_DATA_CMD: + ret = irlan_parse_open_data_cmd(self, skb); + if (self->provider.access_type == ACCESS_PEER) { + /* FIXME: make use of random functions! */ + self->provider.send_arb_val = (jiffies & 0xffff); + } + irlan_provider_send_reply(self, CMD_OPEN_DATA_CHANNEL, ret); + + if (ret == RSP_SUCCESS) { + irlan_next_provider_state(self, IRLAN_OPEN); + + /* Signal client that we are now open */ + irlan_do_client_event(self, IRLAN_PROVIDER_SIGNAL, NULL); + } + break; + case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ + case IRLAN_LAP_DISCONNECT: + irlan_next_provider_state(self, IRLAN_IDLE); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_provider_state_open (self, event, skb, info) + * + * OPEN, The client has issued a OpenData command and is awaiting a + * reply + * + */ +static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + + switch(event) { + case IRLAN_FILTER_CONFIG_CMD: + irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb); + irlan_provider_send_reply(self, CMD_FILTER_OPERATION, + RSP_SUCCESS); + /* Keep state */ + break; + case IRLAN_DATA_CONNECT_INDICATION: + irlan_next_provider_state(self, IRLAN_DATA); + irlan_provider_connect_response(self, self->tsap_data); + break; + case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ + case IRLAN_LAP_DISCONNECT: + irlan_next_provider_state(self, IRLAN_IDLE); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irlan_provider_state_data (self, event, skb, info) + * + * DATA, The data channel is connected, allowing data transfers between + * the local and remote machines. + * + */ +static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); + + switch(event) { + case IRLAN_FILTER_CONFIG_CMD: + irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb); + irlan_provider_send_reply(self, CMD_FILTER_OPERATION, + RSP_SUCCESS); + break; + case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ + case IRLAN_LAP_DISCONNECT: + irlan_next_provider_state(self, IRLAN_IDLE); + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__ , event); + break; + } + if (skb) + dev_kfree_skb(skb); + + return 0; +} + + + + + + + + + + diff --git a/drivers/staging/irda/net/irlap.c b/drivers/staging/irda/net/irlap.c new file mode 100644 index 000000000000..1cde711bcab5 --- /dev/null +++ b/drivers/staging/irda/net/irlap.c @@ -0,0 +1,1207 @@ +/********************************************************************* + * + * Filename: irlap.c + * Version: 1.0 + * Description: IrLAP implementation for Linux + * Status: Stable + * Author: Dag Brattli + * Created at: Mon Aug 4 20:40:53 1997 + * Modified at: Tue Dec 14 09:26:44 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static hashbin_t *irlap = NULL; +int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ; + +/* This is the delay of missed pf period before generating an event + * to the application. The spec mandate 3 seconds, but in some cases + * it's way too long. - Jean II */ +int sysctl_warn_noreply_time = 3; + +extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb); +static void __irlap_close(struct irlap_cb *self); +static void irlap_init_qos_capabilities(struct irlap_cb *self, + struct qos_info *qos_user); + +static const char *const lap_reasons[] __maybe_unused = { + "ERROR, NOT USED", + "LAP_DISC_INDICATION", + "LAP_NO_RESPONSE", + "LAP_RESET_INDICATION", + "LAP_FOUND_NONE", + "LAP_MEDIA_BUSY", + "LAP_PRIMARY_CONFLICT", + "ERROR, NOT USED", +}; + +int __init irlap_init(void) +{ + /* Check if the compiler did its job properly. + * May happen on some ARM configuration, check with Russell King. */ + IRDA_ASSERT(sizeof(struct xid_frame) == 14, ;); + IRDA_ASSERT(sizeof(struct test_frame) == 10, ;); + IRDA_ASSERT(sizeof(struct ua_frame) == 10, ;); + IRDA_ASSERT(sizeof(struct snrm_frame) == 11, ;); + + /* Allocate master array */ + irlap = hashbin_new(HB_LOCK); + if (irlap == NULL) { + net_err_ratelimited("%s: can't allocate irlap hashbin!\n", + __func__); + return -ENOMEM; + } + + return 0; +} + +void irlap_cleanup(void) +{ + IRDA_ASSERT(irlap != NULL, return;); + + hashbin_delete(irlap, (FREE_FUNC) __irlap_close); +} + +/* + * Function irlap_open (driver) + * + * Initialize IrLAP layer + * + */ +struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, + const char *hw_name) +{ + struct irlap_cb *self; + + /* Initialize the irlap structure. */ + self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL); + if (self == NULL) + return NULL; + + self->magic = LAP_MAGIC; + + /* Make a binding between the layers */ + self->netdev = dev; + self->qos_dev = qos; + /* Copy hardware name */ + if(hw_name != NULL) { + strlcpy(self->hw_name, hw_name, sizeof(self->hw_name)); + } else { + self->hw_name[0] = '\0'; + } + + /* FIXME: should we get our own field? */ + dev->atalk_ptr = self; + + self->state = LAP_OFFLINE; + + /* Initialize transmit queue */ + skb_queue_head_init(&self->txq); + skb_queue_head_init(&self->txq_ultra); + skb_queue_head_init(&self->wx_list); + + /* My unique IrLAP device address! */ + /* We don't want the broadcast address, neither the NULL address + * (most often used to signify "invalid"), and we don't want an + * address already in use (otherwise connect won't be able + * to select the proper link). - Jean II */ + do { + get_random_bytes(&self->saddr, sizeof(self->saddr)); + } while ((self->saddr == 0x0) || (self->saddr == BROADCAST) || + (hashbin_lock_find(irlap, self->saddr, NULL)) ); + /* Copy to the driver */ + memcpy(dev->dev_addr, &self->saddr, 4); + + init_timer(&self->slot_timer); + init_timer(&self->query_timer); + init_timer(&self->discovery_timer); + init_timer(&self->final_timer); + init_timer(&self->poll_timer); + init_timer(&self->wd_timer); + init_timer(&self->backoff_timer); + init_timer(&self->media_busy_timer); + + irlap_apply_default_connection_parameters(self); + + self->N3 = 3; /* # connections attempts to try before giving up */ + + self->state = LAP_NDM; + + hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL); + + irlmp_register_link(self, self->saddr, &self->notify); + + return self; +} +EXPORT_SYMBOL(irlap_open); + +/* + * Function __irlap_close (self) + * + * Remove IrLAP and all allocated memory. Stop any pending timers. + * + */ +static void __irlap_close(struct irlap_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Stop timers */ + del_timer(&self->slot_timer); + del_timer(&self->query_timer); + del_timer(&self->discovery_timer); + del_timer(&self->final_timer); + del_timer(&self->poll_timer); + del_timer(&self->wd_timer); + del_timer(&self->backoff_timer); + del_timer(&self->media_busy_timer); + + irlap_flush_all_queues(self); + + self->magic = 0; + + kfree(self); +} + +/* + * Function irlap_close (self) + * + * Remove IrLAP instance + * + */ +void irlap_close(struct irlap_cb *self) +{ + struct irlap_cb *lap; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* We used to send a LAP_DISC_INDICATION here, but this was + * racy. This has been move within irlmp_unregister_link() + * itself. Jean II */ + + /* Kill the LAP and all LSAPs on top of it */ + irlmp_unregister_link(self->saddr); + self->notify.instance = NULL; + + /* Be sure that we manage to remove ourself from the hash */ + lap = hashbin_remove(irlap, self->saddr, NULL); + if (!lap) { + pr_debug("%s(), Didn't find myself!\n", __func__); + return; + } + __irlap_close(lap); +} +EXPORT_SYMBOL(irlap_close); + +/* + * Function irlap_connect_indication (self, skb) + * + * Another device is attempting to make a connection + * + */ +void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + irlap_init_qos_capabilities(self, NULL); /* No user QoS! */ + + irlmp_link_connect_indication(self->notify.instance, self->saddr, + self->daddr, &self->qos_tx, skb); +} + +/* + * Function irlap_connect_response (self, skb) + * + * Service user has accepted incoming connection + * + */ +void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata) +{ + irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL); +} + +/* + * Function irlap_connect_request (self, daddr, qos_user, sniff) + * + * Request connection with another device, sniffing is not implemented + * yet. + * + */ +void irlap_connect_request(struct irlap_cb *self, __u32 daddr, + struct qos_info *qos_user, int sniff) +{ + pr_debug("%s(), daddr=0x%08x\n", __func__, daddr); + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + self->daddr = daddr; + + /* + * If the service user specifies QoS values for this connection, + * then use them + */ + irlap_init_qos_capabilities(self, qos_user); + + if ((self->state == LAP_NDM) && !self->media_busy) + irlap_do_event(self, CONNECT_REQUEST, NULL, NULL); + else + self->connect_pending = TRUE; +} + +/* + * Function irlap_connect_confirm (self, skb) + * + * Connection request has been accepted + * + */ +void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb); +} + +/* + * Function irlap_data_indication (self, skb) + * + * Received data frames from IR-port, so we just pass them up to + * IrLMP for further processing + * + */ +void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb, + int unreliable) +{ + /* Hide LAP header from IrLMP layer */ + skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); + + irlmp_link_data_indication(self->notify.instance, skb, unreliable); +} + + +/* + * Function irlap_data_request (self, skb) + * + * Queue data for transmission, must wait until XMIT state + * + */ +void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb, + int unreliable) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), + return;); + skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); + + /* + * Must set frame format now so that the rest of the code knows + * if its dealing with an I or an UI frame + */ + if (unreliable) + skb->data[1] = UI_FRAME; + else + skb->data[1] = I_FRAME; + + /* Don't forget to refcount it - see irlmp_connect_request(). */ + skb_get(skb); + + /* Add at the end of the queue (keep ordering) - Jean II */ + skb_queue_tail(&self->txq, skb); + + /* + * Send event if this frame only if we are in the right state + * FIXME: udata should be sent first! (skb_queue_head?) + */ + if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) { + /* If we are not already processing the Tx queue, trigger + * transmission immediately - Jean II */ + if((skb_queue_len(&self->txq) <= 1) && (!self->local_busy)) + irlap_do_event(self, DATA_REQUEST, skb, NULL); + /* Otherwise, the packets will be sent normally at the + * next pf-poll - Jean II */ + } +} + +/* + * Function irlap_unitdata_request (self, skb) + * + * Send Ultra data. This is data that must be sent outside any connection + * + */ +#ifdef CONFIG_IRDA_ULTRA +void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), + return;); + skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); + + skb->data[0] = CBROADCAST; + skb->data[1] = UI_FRAME; + + /* Don't need to refcount, see irlmp_connless_data_request() */ + + skb_queue_tail(&self->txq_ultra, skb); + + irlap_do_event(self, SEND_UI_FRAME, NULL, NULL); +} +#endif /*CONFIG_IRDA_ULTRA */ + +/* + * Function irlap_udata_indication (self, skb) + * + * Receive Ultra data. This is data that is received outside any connection + * + */ +#ifdef CONFIG_IRDA_ULTRA +void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + /* Hide LAP header from IrLMP layer */ + skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); + + irlmp_link_unitdata_indication(self->notify.instance, skb); +} +#endif /* CONFIG_IRDA_ULTRA */ + +/* + * Function irlap_disconnect_request (void) + * + * Request to disconnect connection by service user + */ +void irlap_disconnect_request(struct irlap_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Don't disconnect until all data frames are successfully sent */ + if (!skb_queue_empty(&self->txq)) { + self->disconnect_pending = TRUE; + return; + } + + /* Check if we are in the right state for disconnecting */ + switch (self->state) { + case LAP_XMIT_P: /* FALLTHROUGH */ + case LAP_XMIT_S: /* FALLTHROUGH */ + case LAP_CONN: /* FALLTHROUGH */ + case LAP_RESET_WAIT: /* FALLTHROUGH */ + case LAP_RESET_CHECK: + irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL); + break; + default: + pr_debug("%s(), disconnect pending!\n", __func__); + self->disconnect_pending = TRUE; + break; + } +} + +/* + * Function irlap_disconnect_indication (void) + * + * Disconnect request from other device + * + */ +void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason) +{ + pr_debug("%s(), reason=%s\n", __func__, lap_reasons[reason]); + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Flush queues */ + irlap_flush_all_queues(self); + + switch (reason) { + case LAP_RESET_INDICATION: + pr_debug("%s(), Sending reset request!\n", __func__); + irlap_do_event(self, RESET_REQUEST, NULL, NULL); + break; + case LAP_NO_RESPONSE: /* FALLTHROUGH */ + case LAP_DISC_INDICATION: /* FALLTHROUGH */ + case LAP_FOUND_NONE: /* FALLTHROUGH */ + case LAP_MEDIA_BUSY: + irlmp_link_disconnect_indication(self->notify.instance, self, + reason, NULL); + break; + default: + net_err_ratelimited("%s: Unknown reason %d\n", + __func__, reason); + } +} + +/* + * Function irlap_discovery_request (gen_addr_bit) + * + * Start one single discovery operation. + * + */ +void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) +{ + struct irlap_info info; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + IRDA_ASSERT(discovery != NULL, return;); + + pr_debug("%s(), nslots = %d\n", __func__, discovery->nslots); + + IRDA_ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) || + (discovery->nslots == 8) || (discovery->nslots == 16), + return;); + + /* Discovery is only possible in NDM mode */ + if (self->state != LAP_NDM) { + pr_debug("%s(), discovery only possible in NDM mode\n", + __func__); + irlap_discovery_confirm(self, NULL); + /* Note : in theory, if we are not in NDM, we could postpone + * the discovery like we do for connection request. + * In practice, it's not worth it. If the media was busy, + * it's likely next time around it won't be busy. If we are + * in REPLY state, we will get passive discovery info & event. + * Jean II */ + return; + } + + /* Check if last discovery request finished in time, or if + * it was aborted due to the media busy flag. */ + if (self->discovery_log != NULL) { + hashbin_delete(self->discovery_log, (FREE_FUNC) kfree); + self->discovery_log = NULL; + } + + /* All operations will occur at predictable time, no need to lock */ + self->discovery_log = hashbin_new(HB_NOLOCK); + + if (self->discovery_log == NULL) { + net_warn_ratelimited("%s(), Unable to allocate discovery log!\n", + __func__); + return; + } + + info.S = discovery->nslots; /* Number of slots */ + info.s = 0; /* Current slot */ + + self->discovery_cmd = discovery; + info.discovery = discovery; + + /* sysctl_slot_timeout bounds are checked in irsysctl.c - Jean II */ + self->slot_timeout = msecs_to_jiffies(sysctl_slot_timeout); + + irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info); +} + +/* + * Function irlap_discovery_confirm (log) + * + * A device has been discovered in front of this station, we + * report directly to LMP. + */ +void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + IRDA_ASSERT(self->notify.instance != NULL, return;); + + /* + * Check for successful discovery, since we are then allowed to clear + * the media busy condition (IrLAP 6.13.4 - p.94). This should allow + * us to make connection attempts much faster and easier (i.e. no + * collisions). + * Setting media busy to false will also generate an event allowing + * to process pending events in NDM state machine. + * Note : the spec doesn't define what's a successful discovery is. + * If we want Ultra to work, it's successful even if there is + * nobody discovered - Jean II + */ + if (discovery_log) + irda_device_set_media_busy(self->netdev, FALSE); + + /* Inform IrLMP */ + irlmp_link_discovery_confirm(self->notify.instance, discovery_log); +} + +/* + * Function irlap_discovery_indication (log) + * + * Somebody is trying to discover us! + * + */ +void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + IRDA_ASSERT(discovery != NULL, return;); + + IRDA_ASSERT(self->notify.instance != NULL, return;); + + /* A device is very likely to connect immediately after it performs + * a successful discovery. This means that in our case, we are much + * more likely to receive a connection request over the medium. + * So, we backoff to avoid collisions. + * IrLAP spec 6.13.4 suggest 100ms... + * Note : this little trick actually make a *BIG* difference. If I set + * my Linux box with discovery enabled and one Ultra frame sent every + * second, my Palm has no trouble connecting to it every time ! + * Jean II */ + irda_device_set_media_busy(self->netdev, SMALL); + + irlmp_link_discovery_indication(self->notify.instance, discovery); +} + +/* + * Function irlap_status_indication (quality_of_link) + */ +void irlap_status_indication(struct irlap_cb *self, int quality_of_link) +{ + switch (quality_of_link) { + case STATUS_NO_ACTIVITY: + net_info_ratelimited("IrLAP, no activity on link!\n"); + break; + case STATUS_NOISY: + net_info_ratelimited("IrLAP, noisy link!\n"); + break; + default: + break; + } + irlmp_status_indication(self->notify.instance, + quality_of_link, LOCK_NO_CHANGE); +} + +/* + * Function irlap_reset_indication (void) + */ +void irlap_reset_indication(struct irlap_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + if (self->state == LAP_RESET_WAIT) + irlap_do_event(self, RESET_REQUEST, NULL, NULL); + else + irlap_do_event(self, RESET_RESPONSE, NULL, NULL); +} + +/* + * Function irlap_reset_confirm (void) + */ +void irlap_reset_confirm(void) +{ +} + +/* + * Function irlap_generate_rand_time_slot (S, s) + * + * Generate a random time slot between s and S-1 where + * S = Number of slots (0 -> S-1) + * s = Current slot + */ +int irlap_generate_rand_time_slot(int S, int s) +{ + static int rand; + int slot; + + IRDA_ASSERT((S - s) > 0, return 0;); + + rand += jiffies; + rand ^= (rand << 12); + rand ^= (rand >> 20); + + slot = s + rand % (S-s); + + IRDA_ASSERT((slot >= s) || (slot < S), return 0;); + + return slot; +} + +/* + * Function irlap_update_nr_received (nr) + * + * Remove all acknowledged frames in current window queue. This code is + * not intuitive and you should not try to change it. If you think it + * contains bugs, please mail a patch to the author instead. + */ +void irlap_update_nr_received(struct irlap_cb *self, int nr) +{ + struct sk_buff *skb = NULL; + int count = 0; + + /* + * Remove all the ack-ed frames from the window queue. + */ + + /* + * Optimize for the common case. It is most likely that the receiver + * will acknowledge all the frames we have sent! So in that case we + * delete all frames stored in window. + */ + if (nr == self->vs) { + while ((skb = skb_dequeue(&self->wx_list)) != NULL) { + dev_kfree_skb(skb); + } + /* The last acked frame is the next to send minus one */ + self->va = nr - 1; + } else { + /* Remove all acknowledged frames in current window */ + while ((skb_peek(&self->wx_list) != NULL) && + (((self->va+1) % 8) != nr)) + { + skb = skb_dequeue(&self->wx_list); + dev_kfree_skb(skb); + + self->va = (self->va + 1) % 8; + count++; + } + } + + /* Advance window */ + self->window = self->window_size - skb_queue_len(&self->wx_list); +} + +/* + * Function irlap_validate_ns_received (ns) + * + * Validate the next to send (ns) field from received frame. + */ +int irlap_validate_ns_received(struct irlap_cb *self, int ns) +{ + /* ns as expected? */ + if (ns == self->vr) + return NS_EXPECTED; + /* + * Stations are allowed to treat invalid NS as unexpected NS + * IrLAP, Recv ... with-invalid-Ns. p. 84 + */ + return NS_UNEXPECTED; + + /* return NR_INVALID; */ +} +/* + * Function irlap_validate_nr_received (nr) + * + * Validate the next to receive (nr) field from received frame. + * + */ +int irlap_validate_nr_received(struct irlap_cb *self, int nr) +{ + /* nr as expected? */ + if (nr == self->vs) { + pr_debug("%s(), expected!\n", __func__); + return NR_EXPECTED; + } + + /* + * unexpected nr? (but within current window), first we check if the + * ns numbers of the frames in the current window wrap. + */ + if (self->va < self->vs) { + if ((nr >= self->va) && (nr <= self->vs)) + return NR_UNEXPECTED; + } else { + if ((nr >= self->va) || (nr <= self->vs)) + return NR_UNEXPECTED; + } + + /* Invalid nr! */ + return NR_INVALID; +} + +/* + * Function irlap_initiate_connection_state () + * + * Initialize the connection state parameters + * + */ +void irlap_initiate_connection_state(struct irlap_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Next to send and next to receive */ + self->vs = self->vr = 0; + + /* Last frame which got acked (0 - 1) % 8 */ + self->va = 7; + + self->window = 1; + + self->remote_busy = FALSE; + self->retry_count = 0; +} + +/* + * Function irlap_wait_min_turn_around (self, qos) + * + * Wait negotiated minimum turn around time, this function actually sets + * the number of BOS's that must be sent before the next transmitted + * frame in order to delay for the specified amount of time. This is + * done to avoid using timers, and the forbidden udelay! + */ +void irlap_wait_min_turn_around(struct irlap_cb *self, struct qos_info *qos) +{ + __u32 min_turn_time; + __u32 speed; + + /* Get QoS values. */ + speed = qos->baud_rate.value; + min_turn_time = qos->min_turn_time.value; + + /* No need to calculate XBOFs for speeds over 115200 bps */ + if (speed > 115200) { + self->mtt_required = min_turn_time; + return; + } + + /* + * Send additional BOF's for the next frame for the requested + * min turn time, so now we must calculate how many chars (XBOF's) we + * must send for the requested time period (min turn time) + */ + self->xbofs_delay = irlap_min_turn_time_in_bytes(speed, min_turn_time); +} + +/* + * Function irlap_flush_all_queues (void) + * + * Flush all queues + * + */ +void irlap_flush_all_queues(struct irlap_cb *self) +{ + struct sk_buff* skb; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Free transmission queue */ + while ((skb = skb_dequeue(&self->txq)) != NULL) + dev_kfree_skb(skb); + + while ((skb = skb_dequeue(&self->txq_ultra)) != NULL) + dev_kfree_skb(skb); + + /* Free sliding window buffered packets */ + while ((skb = skb_dequeue(&self->wx_list)) != NULL) + dev_kfree_skb(skb); +} + +/* + * Function irlap_setspeed (self, speed) + * + * Change the speed of the IrDA port + * + */ +static void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now) +{ + struct sk_buff *skb; + + pr_debug("%s(), setting speed to %d\n", __func__, speed); + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + self->speed = speed; + + /* Change speed now, or just piggyback speed on frames */ + if (now) { + /* Send down empty frame to trigger speed change */ + skb = alloc_skb(0, GFP_ATOMIC); + if (skb) + irlap_queue_xmit(self, skb); + } +} + +/* + * Function irlap_init_qos_capabilities (self, qos) + * + * Initialize QoS for this IrLAP session, What we do is to compute the + * intersection of the QoS capabilities for the user, driver and for + * IrLAP itself. Normally, IrLAP will not specify any values, but it can + * be used to restrict certain values. + */ +static void irlap_init_qos_capabilities(struct irlap_cb *self, + struct qos_info *qos_user) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + IRDA_ASSERT(self->netdev != NULL, return;); + + /* Start out with the maximum QoS support possible */ + irda_init_max_qos_capabilies(&self->qos_rx); + + /* Apply drivers QoS capabilities */ + irda_qos_compute_intersection(&self->qos_rx, self->qos_dev); + + /* + * Check for user supplied QoS parameters. The service user is only + * allowed to supply these values. We check each parameter since the + * user may not have set all of them. + */ + if (qos_user) { + pr_debug("%s(), Found user specified QoS!\n", __func__); + + if (qos_user->baud_rate.bits) + self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits; + + if (qos_user->max_turn_time.bits) + self->qos_rx.max_turn_time.bits &= qos_user->max_turn_time.bits; + if (qos_user->data_size.bits) + self->qos_rx.data_size.bits &= qos_user->data_size.bits; + + if (qos_user->link_disc_time.bits) + self->qos_rx.link_disc_time.bits &= qos_user->link_disc_time.bits; + } + + /* Use 500ms in IrLAP for now */ + self->qos_rx.max_turn_time.bits &= 0x01; + + /* Set data size */ + /*self->qos_rx.data_size.bits &= 0x03;*/ + + irda_qos_bits_to_value(&self->qos_rx); +} + +/* + * Function irlap_apply_default_connection_parameters (void, now) + * + * Use the default connection and transmission parameters + */ +void irlap_apply_default_connection_parameters(struct irlap_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* xbofs : Default value in NDM */ + self->next_bofs = 12; + self->bofs_count = 12; + + /* NDM Speed is 9600 */ + irlap_change_speed(self, 9600, TRUE); + + /* Set mbusy when going to NDM state */ + irda_device_set_media_busy(self->netdev, TRUE); + + /* + * Generate random connection address for this session, which must + * be 7 bits wide and different from 0x00 and 0xfe + */ + while ((self->caddr == 0x00) || (self->caddr == 0xfe)) { + get_random_bytes(&self->caddr, sizeof(self->caddr)); + self->caddr &= 0xfe; + } + + /* Use default values until connection has been negitiated */ + self->slot_timeout = sysctl_slot_timeout; + self->final_timeout = FINAL_TIMEOUT; + self->poll_timeout = POLL_TIMEOUT; + self->wd_timeout = WD_TIMEOUT; + + /* Set some default values */ + self->qos_tx.baud_rate.value = 9600; + self->qos_rx.baud_rate.value = 9600; + self->qos_tx.max_turn_time.value = 0; + self->qos_rx.max_turn_time.value = 0; + self->qos_tx.min_turn_time.value = 0; + self->qos_rx.min_turn_time.value = 0; + self->qos_tx.data_size.value = 64; + self->qos_rx.data_size.value = 64; + self->qos_tx.window_size.value = 1; + self->qos_rx.window_size.value = 1; + self->qos_tx.additional_bofs.value = 12; + self->qos_rx.additional_bofs.value = 12; + self->qos_tx.link_disc_time.value = 0; + self->qos_rx.link_disc_time.value = 0; + + irlap_flush_all_queues(self); + + self->disconnect_pending = FALSE; + self->connect_pending = FALSE; +} + +/* + * Function irlap_apply_connection_parameters (qos, now) + * + * Initialize IrLAP with the negotiated QoS values + * + * If 'now' is false, the speed and xbofs will be changed after the next + * frame is sent. + * If 'now' is true, the speed and xbofs is changed immediately + */ +void irlap_apply_connection_parameters(struct irlap_cb *self, int now) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Set the negotiated xbofs value */ + self->next_bofs = self->qos_tx.additional_bofs.value; + if (now) + self->bofs_count = self->next_bofs; + + /* Set the negotiated link speed (may need the new xbofs value) */ + irlap_change_speed(self, self->qos_tx.baud_rate.value, now); + + self->window_size = self->qos_tx.window_size.value; + self->window = self->qos_tx.window_size.value; + +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + /* + * Calculate how many bytes it is possible to transmit before the + * link must be turned around + */ + self->line_capacity = + irlap_max_line_capacity(self->qos_tx.baud_rate.value, + self->qos_tx.max_turn_time.value); + self->bytes_left = self->line_capacity; +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + + + /* + * Initialize timeout values, some of the rules are listed on + * page 92 in IrLAP. + */ + IRDA_ASSERT(self->qos_tx.max_turn_time.value != 0, return;); + IRDA_ASSERT(self->qos_rx.max_turn_time.value != 0, return;); + /* The poll timeout applies only to the primary station. + * It defines the maximum time the primary stay in XMIT mode + * before timeout and turning the link around (sending a RR). + * Or, this is how much we can keep the pf bit in primary mode. + * Therefore, it must be lower or equal than our *OWN* max turn around. + * Jean II */ + self->poll_timeout = msecs_to_jiffies( + self->qos_tx.max_turn_time.value); + /* The Final timeout applies only to the primary station. + * It defines the maximum time the primary wait (mostly in RECV mode) + * for an answer from the secondary station before polling it again. + * Therefore, it must be greater or equal than our *PARTNER* + * max turn around time - Jean II */ + self->final_timeout = msecs_to_jiffies( + self->qos_rx.max_turn_time.value); + /* The Watchdog Bit timeout applies only to the secondary station. + * It defines the maximum time the secondary wait (mostly in RECV mode) + * for poll from the primary station before getting annoyed. + * Therefore, it must be greater or equal than our *PARTNER* + * max turn around time - Jean II */ + self->wd_timeout = self->final_timeout * 2; + + /* + * N1 and N2 are maximum retry count for *both* the final timer + * and the wd timer (with a factor 2) as defined above. + * After N1 retry of a timer, we give a warning to the user. + * After N2 retry, we consider the link dead and disconnect it. + * Jean II + */ + + /* + * Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to + * 3 seconds otherwise. See page 71 in IrLAP for more details. + * Actually, it's not always 3 seconds, as we allow to set + * it via sysctl... Max maxtt is 500ms, and N1 need to be multiple + * of 2, so 1 second is minimum we can allow. - Jean II + */ + if (self->qos_tx.link_disc_time.value == sysctl_warn_noreply_time) + /* + * If we set N1 to 0, it will trigger immediately, which is + * not what we want. What we really want is to disable it, + * Jean II + */ + self->N1 = -2; /* Disable - Need to be multiple of 2*/ + else + self->N1 = sysctl_warn_noreply_time * 1000 / + self->qos_rx.max_turn_time.value; + + pr_debug("Setting N1 = %d\n", self->N1); + + /* Set N2 to match our own disconnect time */ + self->N2 = self->qos_tx.link_disc_time.value * 1000 / + self->qos_rx.max_turn_time.value; + pr_debug("Setting N2 = %d\n", self->N2); +} + +#ifdef CONFIG_PROC_FS +struct irlap_iter_state { + int id; +}; + +static void *irlap_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct irlap_iter_state *iter = seq->private; + struct irlap_cb *self; + + /* Protect our access to the tsap list */ + spin_lock_irq(&irlap->hb_spinlock); + iter->id = 0; + + for (self = (struct irlap_cb *) hashbin_get_first(irlap); + self; self = (struct irlap_cb *) hashbin_get_next(irlap)) { + if (iter->id == *pos) + break; + ++iter->id; + } + + return self; +} + +static void *irlap_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct irlap_iter_state *iter = seq->private; + + ++*pos; + ++iter->id; + return (void *) hashbin_get_next(irlap); +} + +static void irlap_seq_stop(struct seq_file *seq, void *v) +{ + spin_unlock_irq(&irlap->hb_spinlock); +} + +static int irlap_seq_show(struct seq_file *seq, void *v) +{ + const struct irlap_iter_state *iter = seq->private; + const struct irlap_cb *self = v; + + IRDA_ASSERT(self->magic == LAP_MAGIC, return -EINVAL;); + + seq_printf(seq, "irlap%d ", iter->id); + seq_printf(seq, "state: %s\n", + irlap_state[self->state]); + + seq_printf(seq, " device name: %s, ", + (self->netdev) ? self->netdev->name : "bug"); + seq_printf(seq, "hardware name: %s\n", self->hw_name); + + seq_printf(seq, " caddr: %#02x, ", self->caddr); + seq_printf(seq, "saddr: %#08x, ", self->saddr); + seq_printf(seq, "daddr: %#08x\n", self->daddr); + + seq_printf(seq, " win size: %d, ", + self->window_size); + seq_printf(seq, "win: %d, ", self->window); +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + seq_printf(seq, "line capacity: %d, ", + self->line_capacity); + seq_printf(seq, "bytes left: %d\n", self->bytes_left); +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + seq_printf(seq, " tx queue len: %d ", + skb_queue_len(&self->txq)); + seq_printf(seq, "win queue len: %d ", + skb_queue_len(&self->wx_list)); + seq_printf(seq, "rbusy: %s", self->remote_busy ? + "TRUE" : "FALSE"); + seq_printf(seq, " mbusy: %s\n", self->media_busy ? + "TRUE" : "FALSE"); + + seq_printf(seq, " retrans: %d ", self->retry_count); + seq_printf(seq, "vs: %d ", self->vs); + seq_printf(seq, "vr: %d ", self->vr); + seq_printf(seq, "va: %d\n", self->va); + + seq_printf(seq, " qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n"); + + seq_printf(seq, " tx\t%d\t", + self->qos_tx.baud_rate.value); + seq_printf(seq, "%d\t", + self->qos_tx.max_turn_time.value); + seq_printf(seq, "%d\t", + self->qos_tx.data_size.value); + seq_printf(seq, "%d\t", + self->qos_tx.window_size.value); + seq_printf(seq, "%d\t", + self->qos_tx.additional_bofs.value); + seq_printf(seq, "%d\t", + self->qos_tx.min_turn_time.value); + seq_printf(seq, "%d\t", + self->qos_tx.link_disc_time.value); + seq_printf(seq, "\n"); + + seq_printf(seq, " rx\t%d\t", + self->qos_rx.baud_rate.value); + seq_printf(seq, "%d\t", + self->qos_rx.max_turn_time.value); + seq_printf(seq, "%d\t", + self->qos_rx.data_size.value); + seq_printf(seq, "%d\t", + self->qos_rx.window_size.value); + seq_printf(seq, "%d\t", + self->qos_rx.additional_bofs.value); + seq_printf(seq, "%d\t", + self->qos_rx.min_turn_time.value); + seq_printf(seq, "%d\n", + self->qos_rx.link_disc_time.value); + + return 0; +} + +static const struct seq_operations irlap_seq_ops = { + .start = irlap_seq_start, + .next = irlap_seq_next, + .stop = irlap_seq_stop, + .show = irlap_seq_show, +}; + +static int irlap_seq_open(struct inode *inode, struct file *file) +{ + if (irlap == NULL) + return -EINVAL; + + return seq_open_private(file, &irlap_seq_ops, + sizeof(struct irlap_iter_state)); +} + +const struct file_operations irlap_seq_fops = { + .owner = THIS_MODULE, + .open = irlap_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +#endif /* CONFIG_PROC_FS */ diff --git a/drivers/staging/irda/net/irlap_event.c b/drivers/staging/irda/net/irlap_event.c new file mode 100644 index 000000000000..0e1b4d79f745 --- /dev/null +++ b/drivers/staging/irda/net/irlap_event.c @@ -0,0 +1,2316 @@ +/********************************************************************* + * + * Filename: irlap_event.c + * Version: 0.9 + * Description: IrLAP state machine implementation + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sat Aug 16 00:59:29 1997 + * Modified at: Sat Dec 25 21:07:57 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-2000 Dag Brattli , + * Copyright (c) 1998 Thomas Davis + * All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include /* irlmp_flow_indication(), ... */ + +#include + +#ifdef CONFIG_IRDA_FAST_RR +int sysctl_fast_poll_increase = 50; +#endif + +static int irlap_state_ndm (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_query (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_reply (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_conn (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_setup (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_xmit_p (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_pclose (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_nrm_p (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_reset (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_nrm_s (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_xmit_s (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info); +static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event, + struct sk_buff *, struct irlap_info *); + +static const char *const irlap_event[] __maybe_unused = { + "DISCOVERY_REQUEST", + "CONNECT_REQUEST", + "CONNECT_RESPONSE", + "DISCONNECT_REQUEST", + "DATA_REQUEST", + "RESET_REQUEST", + "RESET_RESPONSE", + "SEND_I_CMD", + "SEND_UI_FRAME", + "RECV_DISCOVERY_XID_CMD", + "RECV_DISCOVERY_XID_RSP", + "RECV_SNRM_CMD", + "RECV_TEST_CMD", + "RECV_TEST_RSP", + "RECV_UA_RSP", + "RECV_DM_RSP", + "RECV_RD_RSP", + "RECV_I_CMD", + "RECV_I_RSP", + "RECV_UI_FRAME", + "RECV_FRMR_RSP", + "RECV_RR_CMD", + "RECV_RR_RSP", + "RECV_RNR_CMD", + "RECV_RNR_RSP", + "RECV_REJ_CMD", + "RECV_REJ_RSP", + "RECV_SREJ_CMD", + "RECV_SREJ_RSP", + "RECV_DISC_CMD", + "SLOT_TIMER_EXPIRED", + "QUERY_TIMER_EXPIRED", + "FINAL_TIMER_EXPIRED", + "POLL_TIMER_EXPIRED", + "DISCOVERY_TIMER_EXPIRED", + "WD_TIMER_EXPIRED", + "BACKOFF_TIMER_EXPIRED", + "MEDIA_BUSY_TIMER_EXPIRED", +}; + +const char *const irlap_state[] = { + "LAP_NDM", + "LAP_QUERY", + "LAP_REPLY", + "LAP_CONN", + "LAP_SETUP", + "LAP_OFFLINE", + "LAP_XMIT_P", + "LAP_PCLOSE", + "LAP_NRM_P", + "LAP_RESET_WAIT", + "LAP_RESET", + "LAP_NRM_S", + "LAP_XMIT_S", + "LAP_SCLOSE", + "LAP_RESET_CHECK", +}; + +static int (*state[])(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) = +{ + irlap_state_ndm, + irlap_state_query, + irlap_state_reply, + irlap_state_conn, + irlap_state_setup, + irlap_state_offline, + irlap_state_xmit_p, + irlap_state_pclose, + irlap_state_nrm_p, + irlap_state_reset_wait, + irlap_state_reset, + irlap_state_nrm_s, + irlap_state_xmit_s, + irlap_state_sclose, + irlap_state_reset_check, +}; + +/* + * Function irda_poll_timer_expired (data) + * + * Poll timer has expired. Normally we must now send a RR frame to the + * remote device + */ +static void irlap_poll_timer_expired(void *data) +{ + struct irlap_cb *self = (struct irlap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL); +} + +/* + * Calculate and set time before we will have to send back the pf bit + * to the peer. Use in primary. + * Make sure that state is XMIT_P/XMIT_S when calling this function + * (and that nobody messed up with the state). - Jean II + */ +static void irlap_start_poll_timer(struct irlap_cb *self, int timeout) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + +#ifdef CONFIG_IRDA_FAST_RR + /* + * Send out the RR frames faster if our own transmit queue is empty, or + * if the peer is busy. The effect is a much faster conversation + */ + if (skb_queue_empty(&self->txq) || self->remote_busy) { + if (self->fast_RR == TRUE) { + /* + * Assert that the fast poll timer has not reached the + * normal poll timer yet + */ + if (self->fast_RR_timeout < timeout) { + /* + * FIXME: this should be a more configurable + * function + */ + self->fast_RR_timeout += + (sysctl_fast_poll_increase * HZ/1000); + + /* Use this fast(er) timeout instead */ + timeout = self->fast_RR_timeout; + } + } else { + self->fast_RR = TRUE; + + /* Start with just 0 ms */ + self->fast_RR_timeout = 0; + timeout = 0; + } + } else + self->fast_RR = FALSE; + + pr_debug("%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies); +#endif /* CONFIG_IRDA_FAST_RR */ + + if (timeout == 0) + irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL); + else + irda_start_timer(&self->poll_timer, timeout, self, + irlap_poll_timer_expired); +} + +/* + * Function irlap_do_event (event, skb, info) + * + * Rushes through the state machine without any delay. If state == XMIT + * then send queued data frames. + */ +void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret; + + if (!self || self->magic != LAP_MAGIC) + return; + + pr_debug("%s(), event = %s, state = %s\n", __func__, + irlap_event[event], irlap_state[self->state]); + + ret = (*state[self->state])(self, event, skb, info); + + /* + * Check if there are any pending events that needs to be executed + */ + switch (self->state) { + case LAP_XMIT_P: /* FALLTHROUGH */ + case LAP_XMIT_S: + /* + * We just received the pf bit and are at the beginning + * of a new LAP transmit window. + * Check if there are any queued data frames, and do not + * try to disconnect link if we send any data frames, since + * that will change the state away form XMIT + */ + pr_debug("%s() : queue len = %d\n", __func__, + skb_queue_len(&self->txq)); + + if (!skb_queue_empty(&self->txq)) { + /* Prevent race conditions with irlap_data_request() */ + self->local_busy = TRUE; + + /* Theory of operation. + * We send frames up to when we fill the window or + * reach line capacity. Those frames will queue up + * in the device queue, and the driver will slowly + * send them. + * After each frame that we send, we poll the higher + * layer for more data. It's the right time to do + * that because the link layer need to perform the mtt + * and then send the first frame, so we can afford + * to send a bit of time in kernel space. + * The explicit flow indication allow to minimise + * buffers (== lower latency), to avoid higher layer + * polling via timers (== less context switches) and + * to implement a crude scheduler - Jean II */ + + /* Try to send away all queued data frames */ + while ((skb = skb_dequeue(&self->txq)) != NULL) { + /* Send one frame */ + ret = (*state[self->state])(self, SEND_I_CMD, + skb, NULL); + /* Drop reference count. + * It will be increase as needed in + * irlap_send_data_xxx() */ + kfree_skb(skb); + + /* Poll the higher layers for one more frame */ + irlmp_flow_indication(self->notify.instance, + FLOW_START); + + if (ret == -EPROTO) + break; /* Try again later! */ + } + /* Finished transmitting */ + self->local_busy = FALSE; + } else if (self->disconnect_pending) { + self->disconnect_pending = FALSE; + + ret = (*state[self->state])(self, DISCONNECT_REQUEST, + NULL, NULL); + } + break; +/* case LAP_NDM: */ +/* case LAP_CONN: */ +/* case LAP_RESET_WAIT: */ +/* case LAP_RESET_CHECK: */ + default: + break; + } +} + +/* + * Function irlap_state_ndm (event, skb, frame) + * + * NDM (Normal Disconnected Mode) state + * + */ +static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + discovery_t *discovery_rsp; + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + switch (event) { + case CONNECT_REQUEST: + IRDA_ASSERT(self->netdev != NULL, return -1;); + + if (self->media_busy) { + /* Note : this will never happen, because we test + * media busy in irlap_connect_request() and + * postpone the event... - Jean II */ + pr_debug("%s(), CONNECT_REQUEST: media busy!\n", + __func__); + + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + irlap_disconnect_indication(self, LAP_MEDIA_BUSY); + } else { + irlap_send_snrm_frame(self, &self->qos_rx); + + /* Start Final-bit timer */ + irlap_start_final_timer(self, self->final_timeout); + + self->retry_count = 0; + irlap_next_state(self, LAP_SETUP); + } + break; + case RECV_SNRM_CMD: + /* Check if the frame contains and I field */ + if (info) { + self->daddr = info->daddr; + self->caddr = info->caddr; + + irlap_next_state(self, LAP_CONN); + + irlap_connect_indication(self, skb); + } else { + pr_debug("%s(), SNRM frame does not contain an I field!\n", + __func__); + } + break; + case DISCOVERY_REQUEST: + IRDA_ASSERT(info != NULL, return -1;); + + if (self->media_busy) { + pr_debug("%s(), DISCOVERY_REQUEST: media busy!\n", + __func__); + /* irlap->log.condition = MEDIA_BUSY; */ + + /* This will make IrLMP try again */ + irlap_discovery_confirm(self, NULL); + /* Note : the discovery log is not cleaned up here, + * it will be done in irlap_discovery_request() + * Jean II */ + return 0; + } + + self->S = info->S; + self->s = info->s; + irlap_send_discovery_xid_frame(self, info->S, info->s, TRUE, + info->discovery); + self->frame_sent = FALSE; + self->s++; + + irlap_start_slot_timer(self, self->slot_timeout); + irlap_next_state(self, LAP_QUERY); + break; + case RECV_DISCOVERY_XID_CMD: + IRDA_ASSERT(info != NULL, return -1;); + + /* Assert that this is not the final slot */ + if (info->s <= info->S) { + self->slot = irlap_generate_rand_time_slot(info->S, + info->s); + if (self->slot == info->s) { + discovery_rsp = irlmp_get_discovery_response(); + discovery_rsp->data.daddr = info->daddr; + + irlap_send_discovery_xid_frame(self, info->S, + self->slot, + FALSE, + discovery_rsp); + self->frame_sent = TRUE; + } else + self->frame_sent = FALSE; + + /* + * Go to reply state until end of discovery to + * inhibit our own transmissions. Set the timer + * to not stay forever there... Jean II + */ + irlap_start_query_timer(self, info->S, info->s); + irlap_next_state(self, LAP_REPLY); + } else { + /* This is the final slot. How is it possible ? + * This would happen is both discoveries are just slightly + * offset (if they are in sync, all packets are lost). + * Most often, all the discovery requests will be received + * in QUERY state (see my comment there), except for the + * last frame that will come here. + * The big trouble when it happen is that active discovery + * doesn't happen, because nobody answer the discoveries + * frame of the other guy, so the log shows up empty. + * What should we do ? + * Not much. It's too late to answer those discovery frames, + * so we just pass the info to IrLMP who will put it in the + * log (and post an event). + * Another cause would be devices that do discovery much + * slower than us, however the latest fixes should minimise + * those cases... + * Jean II + */ + pr_debug("%s(), Receiving final discovery request, missed the discovery slots :-(\n", + __func__); + + /* Last discovery request -> in the log */ + irlap_discovery_indication(self, info->discovery); + } + break; + case MEDIA_BUSY_TIMER_EXPIRED: + /* A bunch of events may be postponed because the media is + * busy (usually immediately after we close a connection), + * or while we are doing discovery (state query/reply). + * In all those cases, the media busy flag will be cleared + * when it's OK for us to process those postponed events. + * This event is not mentioned in the state machines in the + * IrLAP spec. It's because they didn't consider Ultra and + * postponing connection request is optional. + * Jean II */ +#ifdef CONFIG_IRDA_ULTRA + /* Send any pending Ultra frames if any */ + if (!skb_queue_empty(&self->txq_ultra)) { + /* We don't send the frame, just post an event. + * Also, previously this code was in timer.c... + * Jean II */ + ret = (*state[self->state])(self, SEND_UI_FRAME, + NULL, NULL); + } +#endif /* CONFIG_IRDA_ULTRA */ + /* Check if we should try to connect. + * This code was previously in irlap_do_event() */ + if (self->connect_pending) { + self->connect_pending = FALSE; + + /* This one *should* not pend in this state, except + * if a socket try to connect and immediately + * disconnect. - clear - Jean II */ + if (self->disconnect_pending) + irlap_disconnect_indication(self, LAP_DISC_INDICATION); + else + ret = (*state[self->state])(self, + CONNECT_REQUEST, + NULL, NULL); + self->disconnect_pending = FALSE; + } + /* Note : one way to test if this code works well (including + * media busy and small busy) is to create a user space + * application generating an Ultra packet every 3.05 sec (or + * 2.95 sec) and to see how it interact with discovery. + * It's fairly easy to check that no packet is lost, that the + * packets are postponed during discovery and that after + * discovery indication you have a 100ms "gap". + * As connection request and Ultra are now processed the same + * way, this avoid the tedious job of trying IrLAP connection + * in all those cases... + * Jean II */ + break; +#ifdef CONFIG_IRDA_ULTRA + case SEND_UI_FRAME: + { + int i; + /* Only allowed to repeat an operation twice */ + for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) { + skb = skb_dequeue(&self->txq_ultra); + if (skb) + irlap_send_ui_frame(self, skb, CBROADCAST, + CMD_FRAME); + else + break; + /* irlap_send_ui_frame() won't increase skb reference + * count, so no dev_kfree_skb() - Jean II */ + } + if (i == 2) { + /* Force us to listen 500 ms again */ + irda_device_set_media_busy(self->netdev, TRUE); + } + break; + } + case RECV_UI_FRAME: + /* Only accept broadcast frames in NDM mode */ + if (info->caddr != CBROADCAST) { + pr_debug("%s(), not a broadcast frame!\n", + __func__); + } else + irlap_unitdata_indication(self, skb); + break; +#endif /* CONFIG_IRDA_ULTRA */ + case RECV_TEST_CMD: + /* Remove test frame header */ + skb_pull(skb, sizeof(struct test_frame)); + + /* + * Send response. This skb will not be sent out again, and + * will only be used to send out the same info as the cmd + */ + irlap_send_test_frame(self, CBROADCAST, info->daddr, skb); + break; + case RECV_TEST_RSP: + pr_debug("%s() not implemented!\n", __func__); + break; + default: + pr_debug("%s(), Unknown event %s\n", __func__, + irlap_event[event]); + + ret = -1; + break; + } + return ret; +} + +/* + * Function irlap_state_query (event, skb, info) + * + * QUERY state + * + */ +static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + switch (event) { + case RECV_DISCOVERY_XID_RSP: + IRDA_ASSERT(info != NULL, return -1;); + IRDA_ASSERT(info->discovery != NULL, return -1;); + + pr_debug("%s(), daddr=%08x\n", __func__, + info->discovery->data.daddr); + + if (!self->discovery_log) { + net_warn_ratelimited("%s: discovery log is gone! maybe the discovery timeout has been set too short?\n", + __func__); + break; + } + hashbin_insert(self->discovery_log, + (irda_queue_t *) info->discovery, + info->discovery->data.daddr, NULL); + + /* Keep state */ + /* irlap_next_state(self, LAP_QUERY); */ + + break; + case RECV_DISCOVERY_XID_CMD: + /* Yes, it is possible to receive those frames in this mode. + * Note that most often the last discovery request won't + * occur here but in NDM state (see my comment there). + * What should we do ? + * Not much. We are currently performing our own discovery, + * therefore we can't answer those frames. We don't want + * to change state either. We just pass the info to + * IrLMP who will put it in the log (and post an event). + * Jean II + */ + + IRDA_ASSERT(info != NULL, return -1;); + + pr_debug("%s(), Receiving discovery request (s = %d) while performing discovery :-(\n", + __func__, info->s); + + /* Last discovery request ? */ + if (info->s == 0xff) + irlap_discovery_indication(self, info->discovery); + break; + case SLOT_TIMER_EXPIRED: + /* + * Wait a little longer if we detect an incoming frame. This + * is not mentioned in the spec, but is a good thing to do, + * since we want to work even with devices that violate the + * timing requirements. + */ + if (irda_device_is_receiving(self->netdev) && !self->add_wait) { + pr_debug("%s(), device is slow to answer, waiting some more!\n", + __func__); + irlap_start_slot_timer(self, msecs_to_jiffies(10)); + self->add_wait = TRUE; + return ret; + } + self->add_wait = FALSE; + + if (self->s < self->S) { + irlap_send_discovery_xid_frame(self, self->S, + self->s, TRUE, + self->discovery_cmd); + self->s++; + irlap_start_slot_timer(self, self->slot_timeout); + + /* Keep state */ + irlap_next_state(self, LAP_QUERY); + } else { + /* This is the final slot! */ + irlap_send_discovery_xid_frame(self, self->S, 0xff, + TRUE, + self->discovery_cmd); + + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + /* + * We are now finished with the discovery procedure, + * so now we must return the results + */ + irlap_discovery_confirm(self, self->discovery_log); + + /* IrLMP should now have taken care of the log */ + self->discovery_log = NULL; + } + break; + default: + pr_debug("%s(), Unknown event %s\n", __func__, + irlap_event[event]); + + ret = -1; + break; + } + return ret; +} + +/* + * Function irlap_state_reply (self, event, skb, info) + * + * REPLY, we have received a XID discovery frame from a device and we + * are waiting for the right time slot to send a response XID frame + * + */ +static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + discovery_t *discovery_rsp; + int ret=0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + switch (event) { + case QUERY_TIMER_EXPIRED: + pr_debug("%s(), QUERY_TIMER_EXPIRED <%ld>\n", + __func__, jiffies); + irlap_next_state(self, LAP_NDM); + break; + case RECV_DISCOVERY_XID_CMD: + IRDA_ASSERT(info != NULL, return -1;); + /* Last frame? */ + if (info->s == 0xff) { + del_timer(&self->query_timer); + + /* info->log.condition = REMOTE; */ + + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + irlap_discovery_indication(self, info->discovery); + } else { + /* If it's our slot, send our reply */ + if ((info->s >= self->slot) && (!self->frame_sent)) { + discovery_rsp = irlmp_get_discovery_response(); + discovery_rsp->data.daddr = info->daddr; + + irlap_send_discovery_xid_frame(self, info->S, + self->slot, + FALSE, + discovery_rsp); + + self->frame_sent = TRUE; + } + /* Readjust our timer to accommodate devices + * doing faster or slower discovery than us... + * Jean II */ + irlap_start_query_timer(self, info->S, info->s); + + /* Keep state */ + //irlap_next_state(self, LAP_REPLY); + } + break; + default: + pr_debug("%s(), Unknown event %d, %s\n", __func__, + event, irlap_event[event]); + + ret = -1; + break; + } + return ret; +} + +/* + * Function irlap_state_conn (event, skb, info) + * + * CONN, we have received a SNRM command and is waiting for the upper + * layer to accept or refuse connection + * + */ +static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret = 0; + + pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + switch (event) { + case CONNECT_RESPONSE: + skb_pull(skb, sizeof(struct snrm_frame)); + + IRDA_ASSERT(self->netdev != NULL, return -1;); + + irlap_qos_negotiate(self, skb); + + irlap_initiate_connection_state(self); + + /* + * Applying the parameters now will make sure we change speed + * *after* we have sent the next frame + */ + irlap_apply_connection_parameters(self, FALSE); + + /* + * Sending this frame will force a speed change after it has + * been sent (i.e. the frame will be sent at 9600). + */ + irlap_send_ua_response_frame(self, &self->qos_rx); + +#if 0 + /* + * We are allowed to send two frames, but this may increase + * the connect latency, so lets not do it for now. + */ + /* This is full of good intentions, but doesn't work in + * practice. + * After sending the first UA response, we switch the + * dongle to the negotiated speed, which is usually + * different than 9600 kb/s. + * From there, there is two solutions : + * 1) The other end has received the first UA response : + * it will set up the connection, move to state LAP_NRM_P, + * and will ignore and drop the second UA response. + * Actually, it's even worse : the other side will almost + * immediately send a RR that will likely collide with the + * UA response (depending on negotiated turnaround). + * 2) The other end has not received the first UA response, + * will stay at 9600 and will never see the second UA response. + * Jean II */ + irlap_send_ua_response_frame(self, &self->qos_rx); +#endif + + /* + * The WD-timer could be set to the duration of the P-timer + * for this case, but it is recommended to use twice the + * value (note 3 IrLAP p. 60). + */ + irlap_start_wd_timer(self, self->wd_timeout); + irlap_next_state(self, LAP_NRM_S); + + break; + case RECV_DISCOVERY_XID_CMD: + pr_debug("%s(), event RECV_DISCOVER_XID_CMD!\n", + __func__); + irlap_next_state(self, LAP_NDM); + + break; + case DISCONNECT_REQUEST: + pr_debug("%s(), Disconnect request!\n", __func__); + irlap_send_dm_frame(self); + irlap_next_state( self, LAP_NDM); + irlap_disconnect_indication(self, LAP_DISC_INDICATION); + break; + default: + pr_debug("%s(), Unknown event %d, %s\n", __func__, + event, irlap_event[event]); + + ret = -1; + break; + } + + return ret; +} + +/* + * Function irlap_state_setup (event, skb, frame) + * + * SETUP state, The local layer has transmitted a SNRM command frame to + * a remote peer layer and is awaiting a reply . + * + */ +static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + switch (event) { + case FINAL_TIMER_EXPIRED: + if (self->retry_count < self->N3) { +/* + * Perform random backoff, Wait a random number of time units, minimum + * duration half the time taken to transmitt a SNRM frame, maximum duration + * 1.5 times the time taken to transmit a SNRM frame. So this time should + * between 15 msecs and 45 msecs. + */ + irlap_start_backoff_timer(self, msecs_to_jiffies(20 + + (jiffies % 30))); + } else { + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + irlap_disconnect_indication(self, LAP_FOUND_NONE); + } + break; + case BACKOFF_TIMER_EXPIRED: + irlap_send_snrm_frame(self, &self->qos_rx); + irlap_start_final_timer(self, self->final_timeout); + self->retry_count++; + break; + case RECV_SNRM_CMD: + pr_debug("%s(), SNRM battle!\n", __func__); + + IRDA_ASSERT(skb != NULL, return 0;); + IRDA_ASSERT(info != NULL, return 0;); + + /* + * The device with the largest device address wins the battle + * (both have sent a SNRM command!) + */ + if (info &&(info->daddr > self->saddr)) { + del_timer(&self->final_timer); + irlap_initiate_connection_state(self); + + IRDA_ASSERT(self->netdev != NULL, return -1;); + + skb_pull(skb, sizeof(struct snrm_frame)); + + irlap_qos_negotiate(self, skb); + + /* Send UA frame and then change link settings */ + irlap_apply_connection_parameters(self, FALSE); + irlap_send_ua_response_frame(self, &self->qos_rx); + + irlap_next_state(self, LAP_NRM_S); + irlap_connect_confirm(self, skb); + + /* + * The WD-timer could be set to the duration of the + * P-timer for this case, but it is recommended + * to use twice the value (note 3 IrLAP p. 60). + */ + irlap_start_wd_timer(self, self->wd_timeout); + } else { + /* We just ignore the other device! */ + irlap_next_state(self, LAP_SETUP); + } + break; + case RECV_UA_RSP: + /* Stop F-timer */ + del_timer(&self->final_timer); + + /* Initiate connection state */ + irlap_initiate_connection_state(self); + + /* Negotiate connection parameters */ + IRDA_ASSERT(skb->len > 10, return -1;); + + skb_pull(skb, sizeof(struct ua_frame)); + + IRDA_ASSERT(self->netdev != NULL, return -1;); + + irlap_qos_negotiate(self, skb); + + /* Set the new link setting *now* (before the rr frame) */ + irlap_apply_connection_parameters(self, TRUE); + self->retry_count = 0; + + /* Wait for turnaround time to give a chance to the other + * device to be ready to receive us. + * Note : the time to switch speed is typically larger + * than the turnaround time, but as we don't have the other + * side speed switch time, that's our best guess... + * Jean II */ + irlap_wait_min_turn_around(self, &self->qos_tx); + + /* This frame will actually be sent at the new speed */ + irlap_send_rr_frame(self, CMD_FRAME); + + /* The timer is set to half the normal timer to quickly + * detect a failure to negotiate the new connection + * parameters. IrLAP 6.11.3.2, note 3. + * Note that currently we don't process this failure + * properly, as we should do a quick disconnect. + * Jean II */ + irlap_start_final_timer(self, self->final_timeout/2); + irlap_next_state(self, LAP_NRM_P); + + irlap_connect_confirm(self, skb); + break; + case RECV_DM_RSP: /* FALLTHROUGH */ + case RECV_DISC_CMD: + del_timer(&self->final_timer); + irlap_next_state(self, LAP_NDM); + + irlap_disconnect_indication(self, LAP_DISC_INDICATION); + break; + default: + pr_debug("%s(), Unknown event %d, %s\n", __func__, + event, irlap_event[event]); + + ret = -1; + break; + } + return ret; +} + +/* + * Function irlap_state_offline (self, event, skb, info) + * + * OFFLINE state, not used for now! + * + */ +static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + pr_debug("%s(), Unknown event\n", __func__); + + return -1; +} + +/* + * Function irlap_state_xmit_p (self, event, skb, info) + * + * XMIT, Only the primary station has right to transmit, and we + * therefore do not expect to receive any transmissions from other + * stations. + * + */ +static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret = 0; + + switch (event) { + case SEND_I_CMD: + /* + * Only send frame if send-window > 0. + */ + if ((self->window > 0) && (!self->remote_busy)) { + int nextfit; +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + struct sk_buff *skb_next; + + /* With DYNAMIC_WINDOW, we keep the window size + * maximum, and adapt on the packets we are sending. + * At 115k, we can send only 2 packets of 2048 bytes + * in a 500 ms turnaround. Without this option, we + * would always limit the window to 2. With this + * option, if we send smaller packets, we can send + * up to 7 of them (always depending on QoS). + * Jean II */ + + /* Look at the next skb. This is safe, as we are + * the only consumer of the Tx queue (if we are not, + * we have other problems) - Jean II */ + skb_next = skb_peek(&self->txq); + + /* Check if a subsequent skb exist and would fit in + * the current window (with respect to turnaround + * time). + * This allow us to properly mark the current packet + * with the pf bit, to avoid falling back on the + * second test below, and avoid waiting the + * end of the window and sending a extra RR. + * Note : (skb_next != NULL) <=> (skb_queue_len() > 0) + * Jean II */ + nextfit = ((skb_next != NULL) && + ((skb_next->len + skb->len) <= + self->bytes_left)); + + /* + * The current packet may not fit ! Because of test + * above, this should not happen any more !!! + * Test if we have transmitted more bytes over the + * link than its possible to do with the current + * speed and turn-around-time. + */ + if((!nextfit) && (skb->len > self->bytes_left)) { + pr_debug("%s(), Not allowed to transmit more bytes!\n", + __func__); + /* Requeue the skb */ + skb_queue_head(&self->txq, skb_get(skb)); + /* + * We should switch state to LAP_NRM_P, but + * that is not possible since we must be sure + * that we poll the other side. Since we have + * used up our time, the poll timer should + * trigger anyway now, so we just wait for it + * DB + */ + /* + * Sorry, but that's not totally true. If + * we send 2000B packets, we may wait another + * 1000B until our turnaround expire. That's + * why we need to be proactive in avoiding + * coming here. - Jean II + */ + return -EPROTO; + } + + /* Subtract space used by this skb */ + self->bytes_left -= skb->len; +#else /* CONFIG_IRDA_DYNAMIC_WINDOW */ + /* Window has been adjusted for the max packet + * size, so much simpler... - Jean II */ + nextfit = !skb_queue_empty(&self->txq); +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + /* + * Send data with poll bit cleared only if window > 1 + * and there is more frames after this one to be sent + */ + if ((self->window > 1) && (nextfit)) { + /* More packet to send in current window */ + irlap_send_data_primary(self, skb); + irlap_next_state(self, LAP_XMIT_P); + } else { + /* Final packet of window */ + irlap_send_data_primary_poll(self, skb); + + /* + * Make sure state machine does not try to send + * any more frames + */ + ret = -EPROTO; + } +#ifdef CONFIG_IRDA_FAST_RR + /* Peer may want to reply immediately */ + self->fast_RR = FALSE; +#endif /* CONFIG_IRDA_FAST_RR */ + } else { + pr_debug("%s(), Unable to send! remote busy?\n", + __func__); + skb_queue_head(&self->txq, skb_get(skb)); + + /* + * The next ret is important, because it tells + * irlap_next_state _not_ to deliver more frames + */ + ret = -EPROTO; + } + break; + case POLL_TIMER_EXPIRED: + pr_debug("%s(), POLL_TIMER_EXPIRED <%ld>\n", + __func__, jiffies); + irlap_send_rr_frame(self, CMD_FRAME); + /* Return to NRM properly - Jean II */ + self->window = self->window_size; +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + /* Allowed to transmit a maximum number of bytes again. */ + self->bytes_left = self->line_capacity; +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + irlap_start_final_timer(self, self->final_timeout); + irlap_next_state(self, LAP_NRM_P); + break; + case DISCONNECT_REQUEST: + del_timer(&self->poll_timer); + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_disc_frame(self); + irlap_flush_all_queues(self); + irlap_start_final_timer(self, self->final_timeout); + self->retry_count = 0; + irlap_next_state(self, LAP_PCLOSE); + break; + case DATA_REQUEST: + /* Nothing to do, irlap_do_event() will send the packet + * when we return... - Jean II */ + break; + default: + pr_debug("%s(), Unknown event %s\n", + __func__, irlap_event[event]); + + ret = -EINVAL; + break; + } + return ret; +} + +/* + * Function irlap_state_pclose (event, skb, info) + * + * PCLOSE state + */ +static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + switch (event) { + case RECV_UA_RSP: /* FALLTHROUGH */ + case RECV_DM_RSP: + del_timer(&self->final_timer); + + /* Set new link parameters */ + irlap_apply_default_connection_parameters(self); + + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + irlap_disconnect_indication(self, LAP_DISC_INDICATION); + break; + case FINAL_TIMER_EXPIRED: + if (self->retry_count < self->N3) { + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_disc_frame(self); + irlap_start_final_timer(self, self->final_timeout); + self->retry_count++; + /* Keep state */ + } else { + irlap_apply_default_connection_parameters(self); + + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + irlap_disconnect_indication(self, LAP_NO_RESPONSE); + } + break; + default: + pr_debug("%s(), Unknown event %d\n", __func__, event); + + ret = -1; + break; + } + return ret; +} + +/* + * Function irlap_state_nrm_p (self, event, skb, info) + * + * NRM_P (Normal Response Mode as Primary), The primary station has given + * permissions to a secondary station to transmit IrLAP resonse frames + * (by sending a frame with the P bit set). The primary station will not + * transmit any frames and is expecting to receive frames only from the + * secondary to which transmission permissions has been given. + */ +static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret = 0; + int ns_status; + int nr_status; + + switch (event) { + case RECV_I_RSP: /* Optimize for the common case */ + if (unlikely(skb->len <= LAP_ADDR_HEADER + LAP_CTRL_HEADER)) { + /* + * Input validation check: a stir4200/mcp2150 + * combination sometimes results in an empty i:rsp. + * This makes no sense; we can just ignore the frame + * and send an rr:cmd immediately. This happens before + * changing nr or ns so triggers a retransmit + */ + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rr_frame(self, CMD_FRAME); + /* Keep state */ + break; + } + /* FIXME: must check for remote_busy below */ +#ifdef CONFIG_IRDA_FAST_RR + /* + * Reset the fast_RR so we can use the fast RR code with + * full speed the next time since peer may have more frames + * to transmitt + */ + self->fast_RR = FALSE; +#endif /* CONFIG_IRDA_FAST_RR */ + IRDA_ASSERT( info != NULL, return -1;); + + ns_status = irlap_validate_ns_received(self, info->ns); + nr_status = irlap_validate_nr_received(self, info->nr); + + /* + * Check for expected I(nformation) frame + */ + if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) { + + /* Update Vr (next frame for us to receive) */ + self->vr = (self->vr + 1) % 8; + + /* Update Nr received, cleanup our retry queue */ + irlap_update_nr_received(self, info->nr); + + /* + * Got expected NR, so reset the + * retry_count. This is not done by IrLAP spec, + * which is strange! + */ + self->retry_count = 0; + self->ack_required = TRUE; + + /* poll bit cleared? */ + if (!info->pf) { + /* Keep state, do not move this line */ + irlap_next_state(self, LAP_NRM_P); + + irlap_data_indication(self, skb, FALSE); + } else { + /* No longer waiting for pf */ + del_timer(&self->final_timer); + + irlap_wait_min_turn_around(self, &self->qos_tx); + + /* Call higher layer *before* changing state + * to give them a chance to send data in the + * next LAP frame. + * Jean II */ + irlap_data_indication(self, skb, FALSE); + + /* XMIT states are the most dangerous state + * to be in, because user requests are + * processed directly and may change state. + * On the other hand, in NDM_P, those + * requests are queued and we will process + * them when we return to irlap_do_event(). + * Jean II + */ + irlap_next_state(self, LAP_XMIT_P); + + /* This is the last frame. + * Make sure it's always called in XMIT state. + * - Jean II */ + irlap_start_poll_timer(self, self->poll_timeout); + } + break; + + } + /* Unexpected next to send (Ns) */ + if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED)) + { + if (!info->pf) { + irlap_update_nr_received(self, info->nr); + + /* + * Wait until the last frame before doing + * anything + */ + + /* Keep state */ + irlap_next_state(self, LAP_NRM_P); + } else { + pr_debug("%s(), missing or duplicate frame!\n", + __func__); + + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rr_frame(self, CMD_FRAME); + + self->ack_required = FALSE; + + irlap_start_final_timer(self, self->final_timeout); + irlap_next_state(self, LAP_NRM_P); + } + break; + } + /* + * Unexpected next to receive (Nr) + */ + if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED)) + { + if (info->pf) { + self->vr = (self->vr + 1) % 8; + + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + + /* Resend rejected frames */ + irlap_resend_rejected_frames(self, CMD_FRAME); + + self->ack_required = FALSE; + + /* Make sure we account for the time + * to transmit our frames. See comemnts + * in irlap_send_data_primary_poll(). + * Jean II */ + irlap_start_final_timer(self, 2 * self->final_timeout); + + /* Keep state, do not move this line */ + irlap_next_state(self, LAP_NRM_P); + + irlap_data_indication(self, skb, FALSE); + } else { + /* + * Do not resend frames until the last + * frame has arrived from the other + * device. This is not documented in + * IrLAP!! + */ + self->vr = (self->vr + 1) % 8; + + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + + self->ack_required = FALSE; + + /* Keep state, do not move this line!*/ + irlap_next_state(self, LAP_NRM_P); + + irlap_data_indication(self, skb, FALSE); + } + break; + } + /* + * Unexpected next to send (Ns) and next to receive (Nr) + * Not documented by IrLAP! + */ + if ((ns_status == NS_UNEXPECTED) && + (nr_status == NR_UNEXPECTED)) + { + pr_debug("%s(), unexpected nr and ns!\n", + __func__); + if (info->pf) { + /* Resend rejected frames */ + irlap_resend_rejected_frames(self, CMD_FRAME); + + /* Give peer some time to retransmit! + * But account for our own Tx. */ + irlap_start_final_timer(self, 2 * self->final_timeout); + + /* Keep state, do not move this line */ + irlap_next_state(self, LAP_NRM_P); + } else { + /* Update Nr received */ + /* irlap_update_nr_received( info->nr); */ + + self->ack_required = FALSE; + } + break; + } + + /* + * Invalid NR or NS + */ + if ((nr_status == NR_INVALID) || (ns_status == NS_INVALID)) { + if (info->pf) { + del_timer(&self->final_timer); + + irlap_next_state(self, LAP_RESET_WAIT); + + irlap_disconnect_indication(self, LAP_RESET_INDICATION); + self->xmitflag = TRUE; + } else { + del_timer(&self->final_timer); + + irlap_disconnect_indication(self, LAP_RESET_INDICATION); + + self->xmitflag = FALSE; + } + break; + } + pr_debug("%s(), Not implemented!\n", __func__); + pr_debug("%s(), event=%s, ns_status=%d, nr_status=%d\n", + __func__, irlap_event[event], ns_status, nr_status); + break; + case RECV_UI_FRAME: + /* Poll bit cleared? */ + if (!info->pf) { + irlap_data_indication(self, skb, TRUE); + irlap_next_state(self, LAP_NRM_P); + } else { + del_timer(&self->final_timer); + irlap_data_indication(self, skb, TRUE); + irlap_next_state(self, LAP_XMIT_P); + pr_debug("%s: RECV_UI_FRAME: next state %s\n", + __func__, irlap_state[self->state]); + irlap_start_poll_timer(self, self->poll_timeout); + } + break; + case RECV_RR_RSP: + /* + * If you get a RR, the remote isn't busy anymore, + * no matter what the NR + */ + self->remote_busy = FALSE; + + /* Stop final timer */ + del_timer(&self->final_timer); + + /* + * Nr as expected? + */ + ret = irlap_validate_nr_received(self, info->nr); + if (ret == NR_EXPECTED) { + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + + /* + * Got expected NR, so reset the retry_count. This + * is not done by the IrLAP standard , which is + * strange! DB. + */ + self->retry_count = 0; + irlap_wait_min_turn_around(self, &self->qos_tx); + + irlap_next_state(self, LAP_XMIT_P); + + /* Start poll timer */ + irlap_start_poll_timer(self, self->poll_timeout); + } else if (ret == NR_UNEXPECTED) { + IRDA_ASSERT(info != NULL, return -1;); + /* + * Unexpected nr! + */ + + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + + pr_debug("RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n", + self->retry_count, info->nr, self->va, + self->vs, self->vr); + + /* Resend rejected frames */ + irlap_resend_rejected_frames(self, CMD_FRAME); + irlap_start_final_timer(self, self->final_timeout * 2); + + irlap_next_state(self, LAP_NRM_P); + } else if (ret == NR_INVALID) { + pr_debug("%s(), Received RR with invalid nr !\n", + __func__); + + irlap_next_state(self, LAP_RESET_WAIT); + + irlap_disconnect_indication(self, LAP_RESET_INDICATION); + self->xmitflag = TRUE; + } + break; + case RECV_RNR_RSP: + IRDA_ASSERT(info != NULL, return -1;); + + /* Stop final timer */ + del_timer(&self->final_timer); + self->remote_busy = TRUE; + + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + irlap_next_state(self, LAP_XMIT_P); + + /* Start poll timer */ + irlap_start_poll_timer(self, self->poll_timeout); + break; + case RECV_FRMR_RSP: + del_timer(&self->final_timer); + self->xmitflag = TRUE; + irlap_next_state(self, LAP_RESET_WAIT); + irlap_reset_indication(self); + break; + case FINAL_TIMER_EXPIRED: + /* + * We are allowed to wait for additional 300 ms if + * final timer expires when we are in the middle + * of receiving a frame (page 45, IrLAP). Check that + * we only do this once for each frame. + */ + if (irda_device_is_receiving(self->netdev) && !self->add_wait) { + pr_debug("FINAL_TIMER_EXPIRED when receiving a frame! Waiting a little bit more!\n"); + irlap_start_final_timer(self, msecs_to_jiffies(300)); + + /* + * Don't allow this to happen one more time in a row, + * or else we can get a pretty tight loop here if + * if we only receive half a frame. DB. + */ + self->add_wait = TRUE; + break; + } + self->add_wait = FALSE; + + /* N2 is the disconnect timer. Until we reach it, we retry */ + if (self->retry_count < self->N2) { + if (skb_peek(&self->wx_list) == NULL) { + /* Retry sending the pf bit to the secondary */ + pr_debug("nrm_p: resending rr"); + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rr_frame(self, CMD_FRAME); + } else { + pr_debug("nrm_p: resend frames"); + irlap_resend_rejected_frames(self, CMD_FRAME); + } + + irlap_start_final_timer(self, self->final_timeout); + self->retry_count++; + pr_debug("irlap_state_nrm_p: FINAL_TIMER_EXPIRED: retry_count=%d\n", + self->retry_count); + + /* Early warning event. I'm using a pretty liberal + * interpretation of the spec and generate an event + * every time the timer is multiple of N1 (and not + * only the first time). This allow application + * to know precisely if connectivity restart... + * Jean II */ + if((self->retry_count % self->N1) == 0) + irlap_status_indication(self, + STATUS_NO_ACTIVITY); + + /* Keep state */ + } else { + irlap_apply_default_connection_parameters(self); + + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + irlap_disconnect_indication(self, LAP_NO_RESPONSE); + } + break; + case RECV_REJ_RSP: + irlap_update_nr_received(self, info->nr); + if (self->remote_busy) { + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rr_frame(self, CMD_FRAME); + } else + irlap_resend_rejected_frames(self, CMD_FRAME); + irlap_start_final_timer(self, 2 * self->final_timeout); + break; + case RECV_SREJ_RSP: + irlap_update_nr_received(self, info->nr); + if (self->remote_busy) { + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rr_frame(self, CMD_FRAME); + } else + irlap_resend_rejected_frame(self, CMD_FRAME); + irlap_start_final_timer(self, 2 * self->final_timeout); + break; + case RECV_RD_RSP: + pr_debug("%s(), RECV_RD_RSP\n", __func__); + + irlap_flush_all_queues(self); + irlap_next_state(self, LAP_XMIT_P); + /* Call back the LAP state machine to do a proper disconnect */ + irlap_disconnect_request(self); + break; + default: + pr_debug("%s(), Unknown event %s\n", + __func__, irlap_event[event]); + + ret = -1; + break; + } + return ret; +} + +/* + * Function irlap_state_reset_wait (event, skb, info) + * + * We have informed the service user of a reset condition, and is + * awaiting reset of disconnect request. + * + */ +static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret = 0; + + pr_debug("%s(), event = %s\n", __func__, irlap_event[event]); + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + switch (event) { + case RESET_REQUEST: + if (self->xmitflag) { + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_snrm_frame(self, NULL); + irlap_start_final_timer(self, self->final_timeout); + irlap_next_state(self, LAP_RESET); + } else { + irlap_start_final_timer(self, self->final_timeout); + irlap_next_state(self, LAP_RESET); + } + break; + case DISCONNECT_REQUEST: + irlap_wait_min_turn_around( self, &self->qos_tx); + irlap_send_disc_frame( self); + irlap_flush_all_queues( self); + irlap_start_final_timer( self, self->final_timeout); + self->retry_count = 0; + irlap_next_state( self, LAP_PCLOSE); + break; + default: + pr_debug("%s(), Unknown event %s\n", __func__, + irlap_event[event]); + + ret = -1; + break; + } + return ret; +} + +/* + * Function irlap_state_reset (self, event, skb, info) + * + * We have sent a SNRM reset command to the peer layer, and is awaiting + * reply. + * + */ +static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret = 0; + + pr_debug("%s(), event = %s\n", __func__, irlap_event[event]); + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + switch (event) { + case RECV_DISC_CMD: + del_timer(&self->final_timer); + + irlap_apply_default_connection_parameters(self); + + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + irlap_disconnect_indication(self, LAP_NO_RESPONSE); + + break; + case RECV_UA_RSP: + del_timer(&self->final_timer); + + /* Initiate connection state */ + irlap_initiate_connection_state(self); + + irlap_reset_confirm(); + + self->remote_busy = FALSE; + + irlap_next_state(self, LAP_XMIT_P); + + irlap_start_poll_timer(self, self->poll_timeout); + + break; + case FINAL_TIMER_EXPIRED: + if (self->retry_count < 3) { + irlap_wait_min_turn_around(self, &self->qos_tx); + + IRDA_ASSERT(self->netdev != NULL, return -1;); + irlap_send_snrm_frame(self, self->qos_dev); + + self->retry_count++; /* Experimental!! */ + + irlap_start_final_timer(self, self->final_timeout); + irlap_next_state(self, LAP_RESET); + } else if (self->retry_count >= self->N3) { + irlap_apply_default_connection_parameters(self); + + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + irlap_disconnect_indication(self, LAP_NO_RESPONSE); + } + break; + case RECV_SNRM_CMD: + /* + * SNRM frame is not allowed to contain an I-field in this + * state + */ + if (!info) { + pr_debug("%s(), RECV_SNRM_CMD\n", __func__); + irlap_initiate_connection_state(self); + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_ua_response_frame(self, &self->qos_rx); + irlap_reset_confirm(); + irlap_start_wd_timer(self, self->wd_timeout); + irlap_next_state(self, LAP_NDM); + } else { + pr_debug("%s(), SNRM frame contained an I field!\n", + __func__); + } + break; + default: + pr_debug("%s(), Unknown event %s\n", + __func__, irlap_event[event]); + + ret = -1; + break; + } + return ret; +} + +/* + * Function irlap_state_xmit_s (event, skb, info) + * + * XMIT_S, The secondary station has been given the right to transmit, + * and we therefore do not expect to receive any transmissions from other + * stations. + */ +static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ret = 0; + + pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); + + IRDA_ASSERT(self != NULL, return -ENODEV;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); + + switch (event) { + case SEND_I_CMD: + /* + * Send frame only if send window > 0 + */ + if ((self->window > 0) && (!self->remote_busy)) { + int nextfit; +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + struct sk_buff *skb_next; + + /* + * Same deal as in irlap_state_xmit_p(), so see + * the comments at that point. + * We are the secondary, so there are only subtle + * differences. - Jean II + */ + + /* Check if a subsequent skb exist and would fit in + * the current window (with respect to turnaround + * time). - Jean II */ + skb_next = skb_peek(&self->txq); + nextfit = ((skb_next != NULL) && + ((skb_next->len + skb->len) <= + self->bytes_left)); + + /* + * Test if we have transmitted more bytes over the + * link than its possible to do with the current + * speed and turn-around-time. + */ + if((!nextfit) && (skb->len > self->bytes_left)) { + pr_debug("%s(), Not allowed to transmit more bytes!\n", + __func__); + /* Requeue the skb */ + skb_queue_head(&self->txq, skb_get(skb)); + + /* + * Switch to NRM_S, this is only possible + * when we are in secondary mode, since we + * must be sure that we don't miss any RR + * frames + */ + self->window = self->window_size; + self->bytes_left = self->line_capacity; + irlap_start_wd_timer(self, self->wd_timeout); + + irlap_next_state(self, LAP_NRM_S); + /* Slight difference with primary : + * here we would wait for the other side to + * expire the turnaround. - Jean II */ + + return -EPROTO; /* Try again later */ + } + /* Subtract space used by this skb */ + self->bytes_left -= skb->len; +#else /* CONFIG_IRDA_DYNAMIC_WINDOW */ + /* Window has been adjusted for the max packet + * size, so much simpler... - Jean II */ + nextfit = !skb_queue_empty(&self->txq); +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + /* + * Send data with final bit cleared only if window > 1 + * and there is more frames to be sent + */ + if ((self->window > 1) && (nextfit)) { + irlap_send_data_secondary(self, skb); + irlap_next_state(self, LAP_XMIT_S); + } else { + irlap_send_data_secondary_final(self, skb); + irlap_next_state(self, LAP_NRM_S); + + /* + * Make sure state machine does not try to send + * any more frames + */ + ret = -EPROTO; + } + } else { + pr_debug("%s(), Unable to send!\n", __func__); + skb_queue_head(&self->txq, skb_get(skb)); + ret = -EPROTO; + } + break; + case DISCONNECT_REQUEST: + irlap_send_rd_frame(self); + irlap_flush_all_queues(self); + irlap_start_wd_timer(self, self->wd_timeout); + irlap_next_state(self, LAP_SCLOSE); + break; + case DATA_REQUEST: + /* Nothing to do, irlap_do_event() will send the packet + * when we return... - Jean II */ + break; + default: + pr_debug("%s(), Unknown event %s\n", __func__, + irlap_event[event]); + + ret = -EINVAL; + break; + } + return ret; +} + +/* + * Function irlap_state_nrm_s (event, skb, info) + * + * NRM_S (Normal Response Mode as Secondary) state, in this state we are + * expecting to receive frames from the primary station + * + */ +static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + int ns_status; + int nr_status; + int ret = 0; + + pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + switch (event) { + case RECV_I_CMD: /* Optimize for the common case */ + /* FIXME: must check for remote_busy below */ + pr_debug("%s(), event=%s nr=%d, vs=%d, ns=%d, vr=%d, pf=%d\n", + __func__, irlap_event[event], info->nr, + self->vs, info->ns, self->vr, info->pf); + + self->retry_count = 0; + + ns_status = irlap_validate_ns_received(self, info->ns); + nr_status = irlap_validate_nr_received(self, info->nr); + /* + * Check for expected I(nformation) frame + */ + if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) { + + /* Update Vr (next frame for us to receive) */ + self->vr = (self->vr + 1) % 8; + + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + + /* + * poll bit cleared? + */ + if (!info->pf) { + + self->ack_required = TRUE; + + /* + * Starting WD-timer here is optional, but + * not recommended. Note 6 IrLAP p. 83 + */ +#if 0 + irda_start_timer(WD_TIMER, self->wd_timeout); +#endif + /* Keep state, do not move this line */ + irlap_next_state(self, LAP_NRM_S); + + irlap_data_indication(self, skb, FALSE); + break; + } else { + /* + * We should wait before sending RR, and + * also before changing to XMIT_S + * state. (note 1, IrLAP p. 82) + */ + irlap_wait_min_turn_around(self, &self->qos_tx); + + /* + * Give higher layers a chance to + * immediately reply with some data before + * we decide if we should send a RR frame + * or not + */ + irlap_data_indication(self, skb, FALSE); + + /* Any pending data requests? */ + if (!skb_queue_empty(&self->txq) && + (self->window > 0)) + { + self->ack_required = TRUE; + + del_timer(&self->wd_timer); + + irlap_next_state(self, LAP_XMIT_S); + } else { + irlap_send_rr_frame(self, RSP_FRAME); + irlap_start_wd_timer(self, + self->wd_timeout); + + /* Keep the state */ + irlap_next_state(self, LAP_NRM_S); + } + break; + } + } + /* + * Check for Unexpected next to send (Ns) + */ + if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED)) + { + /* Unexpected next to send, with final bit cleared */ + if (!info->pf) { + irlap_update_nr_received(self, info->nr); + + irlap_start_wd_timer(self, self->wd_timeout); + } else { + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rr_frame(self, RSP_FRAME); + + irlap_start_wd_timer(self, self->wd_timeout); + } + break; + } + + /* + * Unexpected Next to Receive(NR) ? + */ + if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED)) + { + if (info->pf) { + pr_debug("RECV_I_RSP: frame(s) lost\n"); + + self->vr = (self->vr + 1) % 8; + + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + + /* Resend rejected frames */ + irlap_resend_rejected_frames(self, RSP_FRAME); + + /* Keep state, do not move this line */ + irlap_next_state(self, LAP_NRM_S); + + irlap_data_indication(self, skb, FALSE); + irlap_start_wd_timer(self, self->wd_timeout); + break; + } + /* + * This is not documented in IrLAP!! Unexpected NR + * with poll bit cleared + */ + if (!info->pf) { + self->vr = (self->vr + 1) % 8; + + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + + /* Keep state, do not move this line */ + irlap_next_state(self, LAP_NRM_S); + + irlap_data_indication(self, skb, FALSE); + irlap_start_wd_timer(self, self->wd_timeout); + } + break; + } + + if (ret == NR_INVALID) { + pr_debug("NRM_S, NR_INVALID not implemented!\n"); + } + if (ret == NS_INVALID) { + pr_debug("NRM_S, NS_INVALID not implemented!\n"); + } + break; + case RECV_UI_FRAME: + /* + * poll bit cleared? + */ + if (!info->pf) { + irlap_data_indication(self, skb, TRUE); + irlap_next_state(self, LAP_NRM_S); /* Keep state */ + } else { + /* + * Any pending data requests? + */ + if (!skb_queue_empty(&self->txq) && + (self->window > 0) && !self->remote_busy) + { + irlap_data_indication(self, skb, TRUE); + + del_timer(&self->wd_timer); + + irlap_next_state(self, LAP_XMIT_S); + } else { + irlap_data_indication(self, skb, TRUE); + + irlap_wait_min_turn_around(self, &self->qos_tx); + + irlap_send_rr_frame(self, RSP_FRAME); + self->ack_required = FALSE; + + irlap_start_wd_timer(self, self->wd_timeout); + + /* Keep the state */ + irlap_next_state(self, LAP_NRM_S); + } + } + break; + case RECV_RR_CMD: + self->retry_count = 0; + + /* + * Nr as expected? + */ + nr_status = irlap_validate_nr_received(self, info->nr); + if (nr_status == NR_EXPECTED) { + if (!skb_queue_empty(&self->txq) && + (self->window > 0)) { + self->remote_busy = FALSE; + + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + del_timer(&self->wd_timer); + + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_next_state(self, LAP_XMIT_S); + } else { + self->remote_busy = FALSE; + /* Update Nr received */ + irlap_update_nr_received(self, info->nr); + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_start_wd_timer(self, self->wd_timeout); + + /* Note : if the link is idle (this case), + * we never go in XMIT_S, so we never get a + * chance to process any DISCONNECT_REQUEST. + * Do it now ! - Jean II */ + if (self->disconnect_pending) { + /* Disconnect */ + irlap_send_rd_frame(self); + irlap_flush_all_queues(self); + + irlap_next_state(self, LAP_SCLOSE); + } else { + /* Just send back pf bit */ + irlap_send_rr_frame(self, RSP_FRAME); + + irlap_next_state(self, LAP_NRM_S); + } + } + } else if (nr_status == NR_UNEXPECTED) { + self->remote_busy = FALSE; + irlap_update_nr_received(self, info->nr); + irlap_resend_rejected_frames(self, RSP_FRAME); + + irlap_start_wd_timer(self, self->wd_timeout); + + /* Keep state */ + irlap_next_state(self, LAP_NRM_S); + } else { + pr_debug("%s(), invalid nr not implemented!\n", + __func__); + } + break; + case RECV_SNRM_CMD: + /* SNRM frame is not allowed to contain an I-field */ + if (!info) { + del_timer(&self->wd_timer); + pr_debug("%s(), received SNRM cmd\n", __func__); + irlap_next_state(self, LAP_RESET_CHECK); + + irlap_reset_indication(self); + } else { + pr_debug("%s(), SNRM frame contained an I-field!\n", + __func__); + + } + break; + case RECV_REJ_CMD: + irlap_update_nr_received(self, info->nr); + if (self->remote_busy) { + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rr_frame(self, RSP_FRAME); + } else + irlap_resend_rejected_frames(self, RSP_FRAME); + irlap_start_wd_timer(self, self->wd_timeout); + break; + case RECV_SREJ_CMD: + irlap_update_nr_received(self, info->nr); + if (self->remote_busy) { + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rr_frame(self, RSP_FRAME); + } else + irlap_resend_rejected_frame(self, RSP_FRAME); + irlap_start_wd_timer(self, self->wd_timeout); + break; + case WD_TIMER_EXPIRED: + /* + * Wait until retry_count * n matches negotiated threshold/ + * disconnect time (note 2 in IrLAP p. 82) + * + * Similar to irlap_state_nrm_p() -> FINAL_TIMER_EXPIRED + * Note : self->wd_timeout = (self->final_timeout * 2), + * which explain why we use (self->N2 / 2) here !!! + * Jean II + */ + pr_debug("%s(), retry_count = %d\n", __func__, + self->retry_count); + + if (self->retry_count < (self->N2 / 2)) { + /* No retry, just wait for primary */ + irlap_start_wd_timer(self, self->wd_timeout); + self->retry_count++; + + if((self->retry_count % (self->N1 / 2)) == 0) + irlap_status_indication(self, + STATUS_NO_ACTIVITY); + } else { + irlap_apply_default_connection_parameters(self); + + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + irlap_disconnect_indication(self, LAP_NO_RESPONSE); + } + break; + case RECV_DISC_CMD: + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + /* Send disconnect response */ + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_ua_response_frame(self, NULL); + + del_timer(&self->wd_timer); + irlap_flush_all_queues(self); + /* Set default link parameters */ + irlap_apply_default_connection_parameters(self); + + irlap_disconnect_indication(self, LAP_DISC_INDICATION); + break; + case RECV_DISCOVERY_XID_CMD: + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rr_frame(self, RSP_FRAME); + self->ack_required = TRUE; + irlap_start_wd_timer(self, self->wd_timeout); + irlap_next_state(self, LAP_NRM_S); + + break; + case RECV_TEST_CMD: + /* Remove test frame header (only LAP header in NRM) */ + skb_pull(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER); + + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_start_wd_timer(self, self->wd_timeout); + + /* Send response (info will be copied) */ + irlap_send_test_frame(self, self->caddr, info->daddr, skb); + break; + default: + pr_debug("%s(), Unknown event %d, (%s)\n", __func__, + event, irlap_event[event]); + + ret = -EINVAL; + break; + } + return ret; +} + +/* + * Function irlap_state_sclose (self, event, skb, info) + */ +static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, struct irlap_info *info) +{ + IRDA_ASSERT(self != NULL, return -ENODEV;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); + + switch (event) { + case RECV_DISC_CMD: + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + /* Send disconnect response */ + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_ua_response_frame(self, NULL); + + del_timer(&self->wd_timer); + /* Set default link parameters */ + irlap_apply_default_connection_parameters(self); + + irlap_disconnect_indication(self, LAP_DISC_INDICATION); + break; + case RECV_DM_RSP: + /* IrLAP-1.1 p.82: in SCLOSE, S and I type RSP frames + * shall take us down into default NDM state, like DM_RSP + */ + case RECV_RR_RSP: + case RECV_RNR_RSP: + case RECV_REJ_RSP: + case RECV_SREJ_RSP: + case RECV_I_RSP: + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + del_timer(&self->wd_timer); + irlap_apply_default_connection_parameters(self); + + irlap_disconnect_indication(self, LAP_DISC_INDICATION); + break; + case WD_TIMER_EXPIRED: + /* Always switch state before calling upper layers */ + irlap_next_state(self, LAP_NDM); + + irlap_apply_default_connection_parameters(self); + + irlap_disconnect_indication(self, LAP_DISC_INDICATION); + break; + default: + /* IrLAP-1.1 p.82: in SCLOSE, basically any received frame + * with pf=1 shall restart the wd-timer and resend the rd:rsp + */ + if (info != NULL && info->pf) { + del_timer(&self->wd_timer); + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rd_frame(self); + irlap_start_wd_timer(self, self->wd_timeout); + break; /* stay in SCLOSE */ + } + + pr_debug("%s(), Unknown event %d, (%s)\n", __func__, + event, irlap_event[event]); + + break; + } + + return -1; +} + +static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event, + struct sk_buff *skb, + struct irlap_info *info) +{ + int ret = 0; + + pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); + + IRDA_ASSERT(self != NULL, return -ENODEV;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); + + switch (event) { + case RESET_RESPONSE: + irlap_send_ua_response_frame(self, &self->qos_rx); + irlap_initiate_connection_state(self); + irlap_start_wd_timer(self, WD_TIMEOUT); + irlap_flush_all_queues(self); + + irlap_next_state(self, LAP_NRM_S); + break; + case DISCONNECT_REQUEST: + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_send_rd_frame(self); + irlap_start_wd_timer(self, WD_TIMEOUT); + irlap_next_state(self, LAP_SCLOSE); + break; + default: + pr_debug("%s(), Unknown event %d, (%s)\n", __func__, + event, irlap_event[event]); + + ret = -EINVAL; + break; + } + return ret; +} diff --git a/drivers/staging/irda/net/irlap_frame.c b/drivers/staging/irda/net/irlap_frame.c new file mode 100644 index 000000000000..debda3de4726 --- /dev/null +++ b/drivers/staging/irda/net/irlap_frame.c @@ -0,0 +1,1407 @@ +/********************************************************************* + * + * Filename: irlap_frame.c + * Version: 1.0 + * Description: Build and transmit IrLAP frames + * Status: Stable + * Author: Dag Brattli + * Created at: Tue Aug 19 10:27:26 1997 + * Modified at: Wed Jan 5 08:59:04 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-2000 Dag Brattli , + * All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb, + int command); + +/* + * Function irlap_insert_info (self, skb) + * + * Insert minimum turnaround time and speed information into the skb. We + * need to do this since it's per packet relevant information. Safe to + * have this function inlined since it's only called from one place + */ +static inline void irlap_insert_info(struct irlap_cb *self, + struct sk_buff *skb) +{ + struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; + + /* + * Insert MTT (min. turn time) and speed into skb, so that the + * device driver knows which settings to use + */ + cb->magic = LAP_MAGIC; + cb->mtt = self->mtt_required; + cb->next_speed = self->speed; + + /* Reset */ + self->mtt_required = 0; + + /* + * Delay equals negotiated BOFs count, plus the number of BOFs to + * force the negotiated minimum turnaround time + */ + cb->xbofs = self->bofs_count; + cb->next_xbofs = self->next_bofs; + cb->xbofs_delay = self->xbofs_delay; + + /* Reset XBOF's delay (used only for getting min turn time) */ + self->xbofs_delay = 0; + /* Put the correct xbofs value for the next packet */ + self->bofs_count = self->next_bofs; +} + +/* + * Function irlap_queue_xmit (self, skb) + * + * A little wrapper for dev_queue_xmit, so we can insert some common + * code into it. + */ +void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb) +{ + /* Some common init stuff */ + skb->dev = self->netdev; + skb_reset_mac_header(skb); + skb_reset_network_header(skb); + skb_reset_transport_header(skb); + skb->protocol = htons(ETH_P_IRDA); + skb->priority = TC_PRIO_BESTEFFORT; + + irlap_insert_info(self, skb); + + if (unlikely(self->mode & IRDA_MODE_MONITOR)) { + pr_debug("%s(): %s is in monitor mode\n", __func__, + self->netdev->name); + dev_kfree_skb(skb); + return; + } + + dev_queue_xmit(skb); +} + +/* + * Function irlap_send_snrm_cmd (void) + * + * Transmits a connect SNRM command frame + */ +void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos) +{ + struct sk_buff *tx_skb; + struct snrm_frame *frame; + int ret; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Allocate frame */ + tx_skb = alloc_skb(sizeof(struct snrm_frame) + + IRLAP_NEGOCIATION_PARAMS_LEN, + GFP_ATOMIC); + if (!tx_skb) + return; + + frame = skb_put(tx_skb, 2); + + /* Insert connection address field */ + if (qos) + frame->caddr = CMD_FRAME | CBROADCAST; + else + frame->caddr = CMD_FRAME | self->caddr; + + /* Insert control field */ + frame->control = SNRM_CMD | PF_BIT; + + /* + * If we are establishing a connection then insert QoS parameters + */ + if (qos) { + skb_put(tx_skb, 9); /* 25 left */ + frame->saddr = cpu_to_le32(self->saddr); + frame->daddr = cpu_to_le32(self->daddr); + + frame->ncaddr = self->caddr; + + ret = irlap_insert_qos_negotiation_params(self, tx_skb); + if (ret < 0) { + dev_kfree_skb(tx_skb); + return; + } + } + irlap_queue_xmit(self, tx_skb); +} + +/* + * Function irlap_recv_snrm_cmd (skb, info) + * + * Received SNRM (Set Normal Response Mode) command frame + * + */ +static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb, + struct irlap_info *info) +{ + struct snrm_frame *frame; + + if (pskb_may_pull(skb,sizeof(struct snrm_frame))) { + frame = (struct snrm_frame *) skb->data; + + /* Copy the new connection address ignoring the C/R bit */ + info->caddr = frame->ncaddr & 0xFE; + + /* Check if the new connection address is valid */ + if ((info->caddr == 0x00) || (info->caddr == 0xfe)) { + pr_debug("%s(), invalid connection address!\n", + __func__); + return; + } + + /* Copy peer device address */ + info->daddr = le32_to_cpu(frame->saddr); + info->saddr = le32_to_cpu(frame->daddr); + + /* Only accept if addressed directly to us */ + if (info->saddr != self->saddr) { + pr_debug("%s(), not addressed to us!\n", + __func__); + return; + } + irlap_do_event(self, RECV_SNRM_CMD, skb, info); + } else { + /* Signal that this SNRM frame does not contain and I-field */ + irlap_do_event(self, RECV_SNRM_CMD, skb, NULL); + } +} + +/* + * Function irlap_send_ua_response_frame (qos) + * + * Send UA (Unnumbered Acknowledgement) frame + * + */ +void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos) +{ + struct sk_buff *tx_skb; + struct ua_frame *frame; + int ret; + + pr_debug("%s() <%ld>\n", __func__, jiffies); + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Allocate frame */ + tx_skb = alloc_skb(sizeof(struct ua_frame) + + IRLAP_NEGOCIATION_PARAMS_LEN, + GFP_ATOMIC); + if (!tx_skb) + return; + + frame = skb_put(tx_skb, 10); + + /* Build UA response */ + frame->caddr = self->caddr; + frame->control = UA_RSP | PF_BIT; + + frame->saddr = cpu_to_le32(self->saddr); + frame->daddr = cpu_to_le32(self->daddr); + + /* Should we send QoS negotiation parameters? */ + if (qos) { + ret = irlap_insert_qos_negotiation_params(self, tx_skb); + if (ret < 0) { + dev_kfree_skb(tx_skb); + return; + } + } + + irlap_queue_xmit(self, tx_skb); +} + + +/* + * Function irlap_send_dm_frame (void) + * + * Send disconnected mode (DM) frame + * + */ +void irlap_send_dm_frame( struct irlap_cb *self) +{ + struct sk_buff *tx_skb = NULL; + struct dm_frame *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + tx_skb = alloc_skb(sizeof(struct dm_frame), GFP_ATOMIC); + if (!tx_skb) + return; + + frame = skb_put(tx_skb, 2); + + if (self->state == LAP_NDM) + frame->caddr = CBROADCAST; + else + frame->caddr = self->caddr; + + frame->control = DM_RSP | PF_BIT; + + irlap_queue_xmit(self, tx_skb); +} + +/* + * Function irlap_send_disc_frame (void) + * + * Send disconnect (DISC) frame + * + */ +void irlap_send_disc_frame(struct irlap_cb *self) +{ + struct sk_buff *tx_skb = NULL; + struct disc_frame *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + tx_skb = alloc_skb(sizeof(struct disc_frame), GFP_ATOMIC); + if (!tx_skb) + return; + + frame = skb_put(tx_skb, 2); + + frame->caddr = self->caddr | CMD_FRAME; + frame->control = DISC_CMD | PF_BIT; + + irlap_queue_xmit(self, tx_skb); +} + +/* + * Function irlap_send_discovery_xid_frame (S, s, command) + * + * Build and transmit a XID (eXchange station IDentifier) discovery + * frame. + */ +void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s, + __u8 command, discovery_t *discovery) +{ + struct sk_buff *tx_skb = NULL; + struct xid_frame *frame; + __u32 bcast = BROADCAST; + __u8 *info; + + pr_debug("%s(), s=%d, S=%d, command=%d\n", __func__, + s, S, command); + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + IRDA_ASSERT(discovery != NULL, return;); + + tx_skb = alloc_skb(sizeof(struct xid_frame) + IRLAP_DISCOVERY_INFO_LEN, + GFP_ATOMIC); + if (!tx_skb) + return; + + skb_put(tx_skb, 14); + frame = (struct xid_frame *) tx_skb->data; + + if (command) { + frame->caddr = CBROADCAST | CMD_FRAME; + frame->control = XID_CMD | PF_BIT; + } else { + frame->caddr = CBROADCAST; + frame->control = XID_RSP | PF_BIT; + } + frame->ident = XID_FORMAT; + + frame->saddr = cpu_to_le32(self->saddr); + + if (command) + frame->daddr = cpu_to_le32(bcast); + else + frame->daddr = cpu_to_le32(discovery->data.daddr); + + switch (S) { + case 1: + frame->flags = 0x00; + break; + case 6: + frame->flags = 0x01; + break; + case 8: + frame->flags = 0x02; + break; + case 16: + frame->flags = 0x03; + break; + default: + frame->flags = 0x02; + break; + } + + frame->slotnr = s; + frame->version = 0x00; + + /* + * Provide info for final slot only in commands, and for all + * responses. Send the second byte of the hint only if the + * EXTENSION bit is set in the first byte. + */ + if (!command || (frame->slotnr == 0xff)) { + int len; + + if (discovery->data.hints[0] & HINT_EXTENSION) { + info = skb_put(tx_skb, 2); + info[0] = discovery->data.hints[0]; + info[1] = discovery->data.hints[1]; + } else { + info = skb_put(tx_skb, 1); + info[0] = discovery->data.hints[0]; + } + info = skb_put(tx_skb, 1); + info[0] = discovery->data.charset; + + len = IRDA_MIN(discovery->name_len, skb_tailroom(tx_skb)); + skb_put_data(tx_skb, discovery->data.info, len); + } + irlap_queue_xmit(self, tx_skb); +} + +/* + * Function irlap_recv_discovery_xid_rsp (skb, info) + * + * Received a XID discovery response + * + */ +static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self, + struct sk_buff *skb, + struct irlap_info *info) +{ + struct xid_frame *xid; + discovery_t *discovery = NULL; + __u8 *discovery_info; + char *text; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { + net_err_ratelimited("%s: frame too short!\n", __func__); + return; + } + + xid = (struct xid_frame *) skb->data; + + info->daddr = le32_to_cpu(xid->saddr); + info->saddr = le32_to_cpu(xid->daddr); + + /* Make sure frame is addressed to us */ + if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { + pr_debug("%s(), frame is not addressed to us!\n", + __func__); + return; + } + + if ((discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) { + net_warn_ratelimited("%s: kmalloc failed!\n", __func__); + return; + } + + discovery->data.daddr = info->daddr; + discovery->data.saddr = self->saddr; + discovery->timestamp = jiffies; + + pr_debug("%s(), daddr=%08x\n", __func__, + discovery->data.daddr); + + discovery_info = skb_pull(skb, sizeof(struct xid_frame)); + + /* Get info returned from peer */ + discovery->data.hints[0] = discovery_info[0]; + if (discovery_info[0] & HINT_EXTENSION) { + pr_debug("EXTENSION\n"); + discovery->data.hints[1] = discovery_info[1]; + discovery->data.charset = discovery_info[2]; + text = (char *) &discovery_info[3]; + } else { + discovery->data.hints[1] = 0; + discovery->data.charset = discovery_info[1]; + text = (char *) &discovery_info[2]; + } + /* + * Terminate info string, should be safe since this is where the + * FCS bytes resides. + */ + skb->data[skb->len] = '\0'; + strncpy(discovery->data.info, text, NICKNAME_MAX_LEN); + discovery->name_len = strlen(discovery->data.info); + + info->discovery = discovery; + + irlap_do_event(self, RECV_DISCOVERY_XID_RSP, skb, info); +} + +/* + * Function irlap_recv_discovery_xid_cmd (skb, info) + * + * Received a XID discovery command + * + */ +static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, + struct sk_buff *skb, + struct irlap_info *info) +{ + struct xid_frame *xid; + discovery_t *discovery = NULL; + __u8 *discovery_info; + char *text; + + if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { + net_err_ratelimited("%s: frame too short!\n", __func__); + return; + } + + xid = (struct xid_frame *) skb->data; + + info->daddr = le32_to_cpu(xid->saddr); + info->saddr = le32_to_cpu(xid->daddr); + + /* Make sure frame is addressed to us */ + if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { + pr_debug("%s(), frame is not addressed to us!\n", + __func__); + return; + } + + switch (xid->flags & 0x03) { + case 0x00: + info->S = 1; + break; + case 0x01: + info->S = 6; + break; + case 0x02: + info->S = 8; + break; + case 0x03: + info->S = 16; + break; + default: + /* Error!! */ + return; + } + info->s = xid->slotnr; + + discovery_info = skb_pull(skb, sizeof(struct xid_frame)); + + /* + * Check if last frame + */ + if (info->s == 0xff) { + /* Check if things are sane at this point... */ + if((discovery_info == NULL) || + !pskb_may_pull(skb, 3)) { + net_err_ratelimited("%s: discovery frame too short!\n", + __func__); + return; + } + + /* + * We now have some discovery info to deliver! + */ + discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC); + if (!discovery) + return; + + discovery->data.daddr = info->daddr; + discovery->data.saddr = self->saddr; + discovery->timestamp = jiffies; + + discovery->data.hints[0] = discovery_info[0]; + if (discovery_info[0] & HINT_EXTENSION) { + discovery->data.hints[1] = discovery_info[1]; + discovery->data.charset = discovery_info[2]; + text = (char *) &discovery_info[3]; + } else { + discovery->data.hints[1] = 0; + discovery->data.charset = discovery_info[1]; + text = (char *) &discovery_info[2]; + } + /* + * Terminate string, should be safe since this is where the + * FCS bytes resides. + */ + skb->data[skb->len] = '\0'; + strncpy(discovery->data.info, text, NICKNAME_MAX_LEN); + discovery->name_len = strlen(discovery->data.info); + + info->discovery = discovery; + } else + info->discovery = NULL; + + irlap_do_event(self, RECV_DISCOVERY_XID_CMD, skb, info); +} + +/* + * Function irlap_send_rr_frame (self, command) + * + * Build and transmit RR (Receive Ready) frame. Notice that it is currently + * only possible to send RR frames with the poll bit set. + */ +void irlap_send_rr_frame(struct irlap_cb *self, int command) +{ + struct sk_buff *tx_skb; + struct rr_frame *frame; + + tx_skb = alloc_skb(sizeof(struct rr_frame), GFP_ATOMIC); + if (!tx_skb) + return; + + frame = skb_put(tx_skb, 2); + + frame->caddr = self->caddr; + frame->caddr |= (command) ? CMD_FRAME : 0; + + frame->control = RR | PF_BIT | (self->vr << 5); + + irlap_queue_xmit(self, tx_skb); +} + +/* + * Function irlap_send_rd_frame (self) + * + * Request disconnect. Used by a secondary station to request the + * disconnection of the link. + */ +void irlap_send_rd_frame(struct irlap_cb *self) +{ + struct sk_buff *tx_skb; + struct rd_frame *frame; + + tx_skb = alloc_skb(sizeof(struct rd_frame), GFP_ATOMIC); + if (!tx_skb) + return; + + frame = skb_put(tx_skb, 2); + + frame->caddr = self->caddr; + frame->control = RD_RSP | PF_BIT; + + irlap_queue_xmit(self, tx_skb); +} + +/* + * Function irlap_recv_rr_frame (skb, info) + * + * Received RR (Receive Ready) frame from peer station, no harm in + * making it inline since its called only from one single place + * (irlap_driver_rcv). + */ +static inline void irlap_recv_rr_frame(struct irlap_cb *self, + struct sk_buff *skb, + struct irlap_info *info, int command) +{ + info->nr = skb->data[1] >> 5; + + /* Check if this is a command or a response frame */ + if (command) + irlap_do_event(self, RECV_RR_CMD, skb, info); + else + irlap_do_event(self, RECV_RR_RSP, skb, info); +} + +/* + * Function irlap_recv_rnr_frame (self, skb, info) + * + * Received RNR (Receive Not Ready) frame from peer station + * + */ +static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb, + struct irlap_info *info, int command) +{ + info->nr = skb->data[1] >> 5; + + pr_debug("%s(), nr=%d, %ld\n", __func__, info->nr, jiffies); + + if (command) + irlap_do_event(self, RECV_RNR_CMD, skb, info); + else + irlap_do_event(self, RECV_RNR_RSP, skb, info); +} + +static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb, + struct irlap_info *info, int command) +{ + info->nr = skb->data[1] >> 5; + + /* Check if this is a command or a response frame */ + if (command) + irlap_do_event(self, RECV_REJ_CMD, skb, info); + else + irlap_do_event(self, RECV_REJ_RSP, skb, info); +} + +static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb, + struct irlap_info *info, int command) +{ + info->nr = skb->data[1] >> 5; + + /* Check if this is a command or a response frame */ + if (command) + irlap_do_event(self, RECV_SREJ_CMD, skb, info); + else + irlap_do_event(self, RECV_SREJ_RSP, skb, info); +} + +static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb, + struct irlap_info *info, int command) +{ + /* Check if this is a command or a response frame */ + if (command) + irlap_do_event(self, RECV_DISC_CMD, skb, info); + else + irlap_do_event(self, RECV_RD_RSP, skb, info); +} + +/* + * Function irlap_recv_ua_frame (skb, frame) + * + * Received UA (Unnumbered Acknowledgement) frame + * + */ +static inline void irlap_recv_ua_frame(struct irlap_cb *self, + struct sk_buff *skb, + struct irlap_info *info) +{ + irlap_do_event(self, RECV_UA_RSP, skb, info); +} + +/* + * Function irlap_send_data_primary(self, skb) + * + * Send I-frames as the primary station but without the poll bit set + * + */ +void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb) +{ + struct sk_buff *tx_skb; + + if (skb->data[1] == I_FRAME) { + + /* + * Insert frame sequence number (Vs) in control field before + * inserting into transmit window queue. + */ + skb->data[1] = I_FRAME | (self->vs << 1); + + /* + * Insert frame in store, in case of retransmissions + * Increase skb reference count, see irlap_do_event() + */ + skb_get(skb); + skb_queue_tail(&self->wx_list, skb); + + /* Copy buffer */ + tx_skb = skb_clone(skb, GFP_ATOMIC); + if (tx_skb == NULL) { + return; + } + + self->vs = (self->vs + 1) % 8; + self->ack_required = FALSE; + self->window -= 1; + + irlap_send_i_frame( self, tx_skb, CMD_FRAME); + } else { + pr_debug("%s(), sending unreliable frame\n", __func__); + irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); + self->window -= 1; + } +} +/* + * Function irlap_send_data_primary_poll (self, skb) + * + * Send I(nformation) frame as primary with poll bit set + */ +void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb) +{ + struct sk_buff *tx_skb; + int transmission_time; + + /* Stop P timer */ + del_timer(&self->poll_timer); + + /* Is this reliable or unreliable data? */ + if (skb->data[1] == I_FRAME) { + + /* + * Insert frame sequence number (Vs) in control field before + * inserting into transmit window queue. + */ + skb->data[1] = I_FRAME | (self->vs << 1); + + /* + * Insert frame in store, in case of retransmissions + * Increase skb reference count, see irlap_do_event() + */ + skb_get(skb); + skb_queue_tail(&self->wx_list, skb); + + /* Copy buffer */ + tx_skb = skb_clone(skb, GFP_ATOMIC); + if (tx_skb == NULL) { + return; + } + + /* + * Set poll bit if necessary. We do this to the copied + * skb, since retransmitted need to set or clear the poll + * bit depending on when they are sent. + */ + tx_skb->data[1] |= PF_BIT; + + self->vs = (self->vs + 1) % 8; + self->ack_required = FALSE; + + irlap_next_state(self, LAP_NRM_P); + irlap_send_i_frame(self, tx_skb, CMD_FRAME); + } else { + pr_debug("%s(), sending unreliable frame\n", __func__); + + if (self->ack_required) { + irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); + irlap_next_state(self, LAP_NRM_P); + irlap_send_rr_frame(self, CMD_FRAME); + self->ack_required = FALSE; + } else { + skb->data[1] |= PF_BIT; + irlap_next_state(self, LAP_NRM_P); + irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); + } + } + + /* How much time we took for transmission of all frames. + * We don't know, so let assume we used the full window. Jean II */ + transmission_time = self->final_timeout; + + /* Reset parameter so that we can fill next window */ + self->window = self->window_size; + +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + /* Remove what we have not used. Just do a prorata of the + * bytes left in window to window capacity. + * See max_line_capacities[][] in qos.c for details. Jean II */ + transmission_time -= (self->final_timeout * self->bytes_left + / self->line_capacity); + pr_debug("%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n", + __func__, self->final_timeout, self->bytes_left, + self->line_capacity, transmission_time); + + /* We are allowed to transmit a maximum number of bytes again. */ + self->bytes_left = self->line_capacity; +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + + /* + * The network layer has a intermediate buffer between IrLAP + * and the IrDA driver which can contain 8 frames. So, even + * though IrLAP is currently sending the *last* frame of the + * tx-window, the driver most likely has only just started + * sending the *first* frame of the same tx-window. + * I.e. we are always at the very beginning of or Tx window. + * Now, we are supposed to set the final timer from the end + * of our tx-window to let the other peer reply. So, we need + * to add extra time to compensate for the fact that we + * are really at the start of tx-window, otherwise the final timer + * might expire before he can answer... + * Jean II + */ + irlap_start_final_timer(self, self->final_timeout + transmission_time); + + /* + * The clever amongst you might ask why we do this adjustement + * only here, and not in all the other cases in irlap_event.c. + * In all those other case, we only send a very short management + * frame (few bytes), so the adjustement would be lost in the + * noise... + * The exception of course is irlap_resend_rejected_frame(). + * Jean II */ +} + +/* + * Function irlap_send_data_secondary_final (self, skb) + * + * Send I(nformation) frame as secondary with final bit set + * + */ +void irlap_send_data_secondary_final(struct irlap_cb *self, + struct sk_buff *skb) +{ + struct sk_buff *tx_skb = NULL; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + /* Is this reliable or unreliable data? */ + if (skb->data[1] == I_FRAME) { + + /* + * Insert frame sequence number (Vs) in control field before + * inserting into transmit window queue. + */ + skb->data[1] = I_FRAME | (self->vs << 1); + + /* + * Insert frame in store, in case of retransmissions + * Increase skb reference count, see irlap_do_event() + */ + skb_get(skb); + skb_queue_tail(&self->wx_list, skb); + + tx_skb = skb_clone(skb, GFP_ATOMIC); + if (tx_skb == NULL) { + return; + } + + tx_skb->data[1] |= PF_BIT; + + self->vs = (self->vs + 1) % 8; + self->ack_required = FALSE; + + irlap_send_i_frame(self, tx_skb, RSP_FRAME); + } else { + if (self->ack_required) { + irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); + irlap_send_rr_frame(self, RSP_FRAME); + self->ack_required = FALSE; + } else { + skb->data[1] |= PF_BIT; + irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); + } + } + + self->window = self->window_size; +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + /* We are allowed to transmit a maximum number of bytes again. */ + self->bytes_left = self->line_capacity; +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + + irlap_start_wd_timer(self, self->wd_timeout); +} + +/* + * Function irlap_send_data_secondary (self, skb) + * + * Send I(nformation) frame as secondary without final bit set + * + */ +void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb) +{ + struct sk_buff *tx_skb = NULL; + + /* Is this reliable or unreliable data? */ + if (skb->data[1] == I_FRAME) { + + /* + * Insert frame sequence number (Vs) in control field before + * inserting into transmit window queue. + */ + skb->data[1] = I_FRAME | (self->vs << 1); + + /* + * Insert frame in store, in case of retransmissions + * Increase skb reference count, see irlap_do_event() + */ + skb_get(skb); + skb_queue_tail(&self->wx_list, skb); + + tx_skb = skb_clone(skb, GFP_ATOMIC); + if (tx_skb == NULL) { + return; + } + + self->vs = (self->vs + 1) % 8; + self->ack_required = FALSE; + self->window -= 1; + + irlap_send_i_frame(self, tx_skb, RSP_FRAME); + } else { + irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); + self->window -= 1; + } +} + +/* + * Function irlap_resend_rejected_frames (nr) + * + * Resend frames which has not been acknowledged. Should be safe to + * traverse the list without locking it since this function will only be + * called from interrupt context (BH) + */ +void irlap_resend_rejected_frames(struct irlap_cb *self, int command) +{ + struct sk_buff *tx_skb; + struct sk_buff *skb; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Resend unacknowledged frame(s) */ + skb_queue_walk(&self->wx_list, skb) { + irlap_wait_min_turn_around(self, &self->qos_tx); + + /* We copy the skb to be retransmitted since we will have to + * modify it. Cloning will confuse packet sniffers + */ + /* tx_skb = skb_clone( skb, GFP_ATOMIC); */ + tx_skb = skb_copy(skb, GFP_ATOMIC); + if (!tx_skb) { + pr_debug("%s(), unable to copy\n", __func__); + return; + } + + /* Clear old Nr field + poll bit */ + tx_skb->data[1] &= 0x0f; + + /* + * Set poll bit on the last frame retransmitted + */ + if (skb_queue_is_last(&self->wx_list, skb)) + tx_skb->data[1] |= PF_BIT; /* Set p/f bit */ + else + tx_skb->data[1] &= ~PF_BIT; /* Clear p/f bit */ + + irlap_send_i_frame(self, tx_skb, command); + } +#if 0 /* Not yet */ + /* + * We can now fill the window with additional data frames + */ + while (!skb_queue_empty(&self->txq)) { + + pr_debug("%s(), sending additional frames!\n", __func__); + if (self->window > 0) { + skb = skb_dequeue( &self->txq); + IRDA_ASSERT(skb != NULL, return;); + + /* + * If send window > 1 then send frame with pf + * bit cleared + */ + if ((self->window > 1) && + !skb_queue_empty(&self->txq)) { + irlap_send_data_primary(self, skb); + } else { + irlap_send_data_primary_poll(self, skb); + } + kfree_skb(skb); + } + } +#endif +} + +void irlap_resend_rejected_frame(struct irlap_cb *self, int command) +{ + struct sk_buff *tx_skb; + struct sk_buff *skb; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + /* Resend unacknowledged frame(s) */ + skb = skb_peek(&self->wx_list); + if (skb != NULL) { + irlap_wait_min_turn_around(self, &self->qos_tx); + + /* We copy the skb to be retransmitted since we will have to + * modify it. Cloning will confuse packet sniffers + */ + /* tx_skb = skb_clone( skb, GFP_ATOMIC); */ + tx_skb = skb_copy(skb, GFP_ATOMIC); + if (!tx_skb) { + pr_debug("%s(), unable to copy\n", __func__); + return; + } + + /* Clear old Nr field + poll bit */ + tx_skb->data[1] &= 0x0f; + + /* Set poll/final bit */ + tx_skb->data[1] |= PF_BIT; /* Set p/f bit */ + + irlap_send_i_frame(self, tx_skb, command); + } +} + +/* + * Function irlap_send_ui_frame (self, skb, command) + * + * Contruct and transmit an Unnumbered Information (UI) frame + * + */ +void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb, + __u8 caddr, int command) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + /* Insert connection address */ + skb->data[0] = caddr | ((command) ? CMD_FRAME : 0); + + irlap_queue_xmit(self, skb); +} + +/* + * Function irlap_send_i_frame (skb) + * + * Contruct and transmit Information (I) frame + */ +static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb, + int command) +{ + /* Insert connection address */ + skb->data[0] = self->caddr; + skb->data[0] |= (command) ? CMD_FRAME : 0; + + /* Insert next to receive (Vr) */ + skb->data[1] |= (self->vr << 5); /* insert nr */ + + irlap_queue_xmit(self, skb); +} + +/* + * Function irlap_recv_i_frame (skb, frame) + * + * Receive and parse an I (Information) frame, no harm in making it inline + * since it's called only from one single place (irlap_driver_rcv). + */ +static inline void irlap_recv_i_frame(struct irlap_cb *self, + struct sk_buff *skb, + struct irlap_info *info, int command) +{ + info->nr = skb->data[1] >> 5; /* Next to receive */ + info->pf = skb->data[1] & PF_BIT; /* Final bit */ + info->ns = (skb->data[1] >> 1) & 0x07; /* Next to send */ + + /* Check if this is a command or a response frame */ + if (command) + irlap_do_event(self, RECV_I_CMD, skb, info); + else + irlap_do_event(self, RECV_I_RSP, skb, info); +} + +/* + * Function irlap_recv_ui_frame (self, skb, info) + * + * Receive and parse an Unnumbered Information (UI) frame + * + */ +static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb, + struct irlap_info *info) +{ + info->pf = skb->data[1] & PF_BIT; /* Final bit */ + + irlap_do_event(self, RECV_UI_FRAME, skb, info); +} + +/* + * Function irlap_recv_frmr_frame (skb, frame) + * + * Received Frame Reject response. + * + */ +static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb, + struct irlap_info *info) +{ + __u8 *frame; + int w, x, y, z; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(info != NULL, return;); + + if (!pskb_may_pull(skb, 4)) { + net_err_ratelimited("%s: frame too short!\n", __func__); + return; + } + + frame = skb->data; + + info->nr = frame[2] >> 5; /* Next to receive */ + info->pf = frame[2] & PF_BIT; /* Final bit */ + info->ns = (frame[2] >> 1) & 0x07; /* Next to send */ + + w = frame[3] & 0x01; + x = frame[3] & 0x02; + y = frame[3] & 0x04; + z = frame[3] & 0x08; + + if (w) { + pr_debug("Rejected control field is undefined or not implemented\n"); + } + if (x) { + pr_debug("Rejected control field was invalid because it contained a non permitted I field\n"); + } + if (y) { + pr_debug("Received I field exceeded the maximum negotiated for the existing connection or exceeded the maximum this station supports if no connection exists\n"); + } + if (z) { + pr_debug("Rejected control field control field contained an invalid Nr count\n"); + } + irlap_do_event(self, RECV_FRMR_RSP, skb, info); +} + +/* + * Function irlap_send_test_frame (self, daddr) + * + * Send a test frame response + * + */ +void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr, + struct sk_buff *cmd) +{ + struct sk_buff *tx_skb; + struct test_frame *frame; + + tx_skb = alloc_skb(cmd->len + sizeof(struct test_frame), GFP_ATOMIC); + if (!tx_skb) + return; + + /* Broadcast frames must include saddr and daddr fields */ + if (caddr == CBROADCAST) { + frame = skb_put(tx_skb, sizeof(struct test_frame)); + + /* Insert the swapped addresses */ + frame->saddr = cpu_to_le32(self->saddr); + frame->daddr = cpu_to_le32(daddr); + } else + frame = skb_put(tx_skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER); + + frame->caddr = caddr; + frame->control = TEST_RSP | PF_BIT; + + /* Copy info */ + skb_put_data(tx_skb, cmd->data, cmd->len); + + /* Return to sender */ + irlap_wait_min_turn_around(self, &self->qos_tx); + irlap_queue_xmit(self, tx_skb); +} + +/* + * Function irlap_recv_test_frame (self, skb) + * + * Receive a test frame + * + */ +static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb, + struct irlap_info *info, int command) +{ + struct test_frame *frame; + + if (!pskb_may_pull(skb, sizeof(*frame))) { + net_err_ratelimited("%s: frame too short!\n", __func__); + return; + } + frame = (struct test_frame *) skb->data; + + /* Broadcast frames must carry saddr and daddr fields */ + if (info->caddr == CBROADCAST) { + if (skb->len < sizeof(struct test_frame)) { + pr_debug("%s() test frame too short!\n", + __func__); + return; + } + + /* Read and swap addresses */ + info->daddr = le32_to_cpu(frame->saddr); + info->saddr = le32_to_cpu(frame->daddr); + + /* Make sure frame is addressed to us */ + if ((info->saddr != self->saddr) && + (info->saddr != BROADCAST)) { + return; + } + } + + if (command) + irlap_do_event(self, RECV_TEST_CMD, skb, info); + else + irlap_do_event(self, RECV_TEST_RSP, skb, info); +} + +/* + * Function irlap_driver_rcv (skb, netdev, ptype) + * + * Called when a frame is received. Dispatches the right receive function + * for processing of the frame. + * + * Note on skb management : + * After calling the higher layers of the IrDA stack, we always + * kfree() the skb, which drop the reference count (and potentially + * destroy it). + * If a higher layer of the stack want to keep the skb around (to put + * in a queue or pass it to the higher layer), it will need to use + * skb_get() to keep a reference on it. This is usually done at the + * LMP level in irlmp.c. + * Jean II + */ +int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, + struct packet_type *ptype, struct net_device *orig_dev) +{ + struct irlap_info info; + struct irlap_cb *self; + int command; + __u8 control; + int ret = -1; + + if (!net_eq(dev_net(dev), &init_net)) + goto out; + + /* FIXME: should we get our own field? */ + self = (struct irlap_cb *) dev->atalk_ptr; + + /* If the net device is down, then IrLAP is gone! */ + if (!self || self->magic != LAP_MAGIC) + goto err; + + /* We are no longer an "old" protocol, so we need to handle + * share and non linear skbs. This should never happen, so + * we don't need to be clever about it. Jean II */ + if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { + net_err_ratelimited("%s: can't clone shared skb!\n", __func__); + goto err; + } + + /* Check if frame is large enough for parsing */ + if (!pskb_may_pull(skb, 2)) { + net_err_ratelimited("%s: frame too short!\n", __func__); + goto err; + } + + command = skb->data[0] & CMD_FRAME; + info.caddr = skb->data[0] & CBROADCAST; + + info.pf = skb->data[1] & PF_BIT; + info.control = skb->data[1] & ~PF_BIT; /* Mask away poll/final bit */ + + control = info.control; + + /* First we check if this frame has a valid connection address */ + if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) { + pr_debug("%s(), wrong connection address!\n", + __func__); + goto out; + } + /* + * Optimize for the common case and check if the frame is an + * I(nformation) frame. Only I-frames have bit 0 set to 0 + */ + if (~control & 0x01) { + irlap_recv_i_frame(self, skb, &info, command); + goto out; + } + /* + * We now check is the frame is an S(upervisory) frame. Only + * S-frames have bit 0 set to 1 and bit 1 set to 0 + */ + if (~control & 0x02) { + /* + * Received S(upervisory) frame, check which frame type it is + * only the first nibble is of interest + */ + switch (control & 0x0f) { + case RR: + irlap_recv_rr_frame(self, skb, &info, command); + break; + case RNR: + irlap_recv_rnr_frame(self, skb, &info, command); + break; + case REJ: + irlap_recv_rej_frame(self, skb, &info, command); + break; + case SREJ: + irlap_recv_srej_frame(self, skb, &info, command); + break; + default: + net_warn_ratelimited("%s: Unknown S-frame %02x received!\n", + __func__, info.control); + break; + } + goto out; + } + /* + * This must be a C(ontrol) frame + */ + switch (control) { + case XID_RSP: + irlap_recv_discovery_xid_rsp(self, skb, &info); + break; + case XID_CMD: + irlap_recv_discovery_xid_cmd(self, skb, &info); + break; + case SNRM_CMD: + irlap_recv_snrm_cmd(self, skb, &info); + break; + case DM_RSP: + irlap_do_event(self, RECV_DM_RSP, skb, &info); + break; + case DISC_CMD: /* And RD_RSP since they have the same value */ + irlap_recv_disc_frame(self, skb, &info, command); + break; + case TEST_CMD: + irlap_recv_test_frame(self, skb, &info, command); + break; + case UA_RSP: + irlap_recv_ua_frame(self, skb, &info); + break; + case FRMR_RSP: + irlap_recv_frmr_frame(self, skb, &info); + break; + case UI_FRAME: + irlap_recv_ui_frame(self, skb, &info); + break; + default: + net_warn_ratelimited("%s: Unknown frame %02x received!\n", + __func__, info.control); + break; + } +out: + ret = 0; +err: + /* Always drop our reference on the skb */ + dev_kfree_skb(skb); + return ret; +} diff --git a/drivers/staging/irda/net/irlmp.c b/drivers/staging/irda/net/irlmp.c new file mode 100644 index 000000000000..43964594aa12 --- /dev/null +++ b/drivers/staging/irda/net/irlmp.c @@ -0,0 +1,1996 @@ +/********************************************************************* + * + * Filename: irlmp.c + * Version: 1.0 + * Description: IrDA Link Management Protocol (LMP) layer + * Status: Stable. + * Author: Dag Brattli + * Created at: Sun Aug 17 20:54:32 1997 + * Modified at: Wed Jan 5 11:26:03 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-2000 Dag Brattli , + * All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +static __u8 irlmp_find_free_slsap(void); +static int irlmp_slsap_inuse(__u8 slsap_sel); + +/* Master structure */ +struct irlmp_cb *irlmp = NULL; + +/* These can be altered by the sysctl interface */ +int sysctl_discovery = 0; +int sysctl_discovery_timeout = 3; /* 3 seconds by default */ +int sysctl_discovery_slots = 6; /* 6 slots by default */ +int sysctl_lap_keepalive_time = LM_IDLE_TIMEOUT * 1000 / HZ; +char sysctl_devname[65]; + +static const char *irlmp_reasons[] = { + "ERROR, NOT USED", + "LM_USER_REQUEST", + "LM_LAP_DISCONNECT", + "LM_CONNECT_FAILURE", + "LM_LAP_RESET", + "LM_INIT_DISCONNECT", + "ERROR, NOT USED", + "UNKNOWN", +}; + +const char *irlmp_reason_str(LM_REASON reason) +{ + reason = min_t(size_t, reason, ARRAY_SIZE(irlmp_reasons) - 1); + return irlmp_reasons[reason]; +} + +/* + * Function irlmp_init (void) + * + * Create (allocate) the main IrLMP structure + * + */ +int __init irlmp_init(void) +{ + /* Initialize the irlmp structure. */ + irlmp = kzalloc( sizeof(struct irlmp_cb), GFP_KERNEL); + if (irlmp == NULL) + return -ENOMEM; + + irlmp->magic = LMP_MAGIC; + + irlmp->clients = hashbin_new(HB_LOCK); + irlmp->services = hashbin_new(HB_LOCK); + irlmp->links = hashbin_new(HB_LOCK); + irlmp->unconnected_lsaps = hashbin_new(HB_LOCK); + irlmp->cachelog = hashbin_new(HB_NOLOCK); + + if ((irlmp->clients == NULL) || + (irlmp->services == NULL) || + (irlmp->links == NULL) || + (irlmp->unconnected_lsaps == NULL) || + (irlmp->cachelog == NULL)) { + return -ENOMEM; + } + + spin_lock_init(&irlmp->cachelog->hb_spinlock); + + irlmp->last_lsap_sel = 0x0f; /* Reserved 0x00-0x0f */ + strcpy(sysctl_devname, "Linux"); + + init_timer(&irlmp->discovery_timer); + + /* Do discovery every 3 seconds, conditionally */ + if (sysctl_discovery) + irlmp_start_discovery_timer(irlmp, + sysctl_discovery_timeout*HZ); + + return 0; +} + +/* + * Function irlmp_cleanup (void) + * + * Remove IrLMP layer + * + */ +void irlmp_cleanup(void) +{ + /* Check for main structure */ + IRDA_ASSERT(irlmp != NULL, return;); + IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;); + + del_timer(&irlmp->discovery_timer); + + hashbin_delete(irlmp->links, (FREE_FUNC) kfree); + hashbin_delete(irlmp->unconnected_lsaps, (FREE_FUNC) kfree); + hashbin_delete(irlmp->clients, (FREE_FUNC) kfree); + hashbin_delete(irlmp->services, (FREE_FUNC) kfree); + hashbin_delete(irlmp->cachelog, (FREE_FUNC) kfree); + + /* De-allocate main structure */ + kfree(irlmp); + irlmp = NULL; +} + +/* + * Function irlmp_open_lsap (slsap, notify) + * + * Register with IrLMP and create a local LSAP, + * returns handle to LSAP. + */ +struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid) +{ + struct lsap_cb *self; + + IRDA_ASSERT(notify != NULL, return NULL;); + IRDA_ASSERT(irlmp != NULL, return NULL;); + IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return NULL;); + IRDA_ASSERT(notify->instance != NULL, return NULL;); + + /* Does the client care which Source LSAP selector it gets? */ + if (slsap_sel == LSAP_ANY) { + slsap_sel = irlmp_find_free_slsap(); + if (!slsap_sel) + return NULL; + } else if (irlmp_slsap_inuse(slsap_sel)) + return NULL; + + /* Allocate new instance of a LSAP connection */ + self = kzalloc(sizeof(struct lsap_cb), GFP_ATOMIC); + if (self == NULL) + return NULL; + + self->magic = LMP_LSAP_MAGIC; + self->slsap_sel = slsap_sel; + + /* Fix connectionless LSAP's */ + if (slsap_sel == LSAP_CONNLESS) { +#ifdef CONFIG_IRDA_ULTRA + self->dlsap_sel = LSAP_CONNLESS; + self->pid = pid; +#endif /* CONFIG_IRDA_ULTRA */ + } else + self->dlsap_sel = LSAP_ANY; + /* self->connected = FALSE; -> already NULL via memset() */ + + init_timer(&self->watchdog_timer); + + self->notify = *notify; + + self->lsap_state = LSAP_DISCONNECTED; + + /* Insert into queue of unconnected LSAPs */ + hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, + (long) self, NULL); + + return self; +} +EXPORT_SYMBOL(irlmp_open_lsap); + +/* + * Function __irlmp_close_lsap (self) + * + * Remove an instance of LSAP + */ +static void __irlmp_close_lsap(struct lsap_cb *self) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); + + /* + * Set some of the variables to preset values + */ + self->magic = 0; + del_timer(&self->watchdog_timer); /* Important! */ + + if (self->conn_skb) + dev_kfree_skb(self->conn_skb); + + kfree(self); +} + +/* + * Function irlmp_close_lsap (self) + * + * Close and remove LSAP + * + */ +void irlmp_close_lsap(struct lsap_cb *self) +{ + struct lap_cb *lap; + struct lsap_cb *lsap = NULL; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); + + /* + * Find out if we should remove this LSAP from a link or from the + * list of unconnected lsaps (not associated with a link) + */ + lap = self->lap; + if (lap) { + IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); + /* We might close a LSAP before it has completed the + * connection setup. In those case, higher layers won't + * send a proper disconnect request. Harmless, except + * that we will forget to close LAP... - Jean II */ + if(self->lsap_state != LSAP_DISCONNECTED) { + self->lsap_state = LSAP_DISCONNECTED; + irlmp_do_lap_event(self->lap, + LM_LAP_DISCONNECT_REQUEST, NULL); + } + /* Now, remove from the link */ + lsap = hashbin_remove(lap->lsaps, (long) self, NULL); +#ifdef CONFIG_IRDA_CACHE_LAST_LSAP + lap->cache.valid = FALSE; +#endif + } + self->lap = NULL; + /* Check if we found the LSAP! If not then try the unconnected lsaps */ + if (!lsap) { + lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, + NULL); + } + if (!lsap) { + pr_debug("%s(), Looks like somebody has removed me already!\n", + __func__); + return; + } + __irlmp_close_lsap(self); +} +EXPORT_SYMBOL(irlmp_close_lsap); + +/* + * Function irlmp_register_irlap (saddr, notify) + * + * Register IrLAP layer with IrLMP. There is possible to have multiple + * instances of the IrLAP layer, each connected to different IrDA ports + * + */ +void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify) +{ + struct lap_cb *lap; + + IRDA_ASSERT(irlmp != NULL, return;); + IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;); + IRDA_ASSERT(notify != NULL, return;); + + /* + * Allocate new instance of a LSAP connection + */ + lap = kzalloc(sizeof(struct lap_cb), GFP_KERNEL); + if (lap == NULL) + return; + + lap->irlap = irlap; + lap->magic = LMP_LAP_MAGIC; + lap->saddr = saddr; + lap->daddr = DEV_ADDR_ANY; +#ifdef CONFIG_IRDA_CACHE_LAST_LSAP + lap->cache.valid = FALSE; +#endif + lap->lsaps = hashbin_new(HB_LOCK); + if (lap->lsaps == NULL) { + net_warn_ratelimited("%s(), unable to kmalloc lsaps\n", + __func__); + kfree(lap); + return; + } + + lap->lap_state = LAP_STANDBY; + + init_timer(&lap->idle_timer); + + /* + * Insert into queue of LMP links + */ + hashbin_insert(irlmp->links, (irda_queue_t *) lap, lap->saddr, NULL); + + /* + * We set only this variable so IrLAP can tell us on which link the + * different events happened on + */ + irda_notify_init(notify); + notify->instance = lap; +} + +/* + * Function irlmp_unregister_irlap (saddr) + * + * IrLAP layer has been removed! + * + */ +void irlmp_unregister_link(__u32 saddr) +{ + struct lap_cb *link; + + /* We must remove ourselves from the hashbin *first*. This ensure + * that no more LSAPs will be open on this link and no discovery + * will be triggered anymore. Jean II */ + link = hashbin_remove(irlmp->links, saddr, NULL); + if (link) { + IRDA_ASSERT(link->magic == LMP_LAP_MAGIC, return;); + + /* Kill all the LSAPs on this link. Jean II */ + link->reason = LAP_DISC_INDICATION; + link->daddr = DEV_ADDR_ANY; + irlmp_do_lap_event(link, LM_LAP_DISCONNECT_INDICATION, NULL); + + /* Remove all discoveries discovered at this link */ + irlmp_expire_discoveries(irlmp->cachelog, link->saddr, TRUE); + + /* Final cleanup */ + del_timer(&link->idle_timer); + link->magic = 0; + hashbin_delete(link->lsaps, (FREE_FUNC) __irlmp_close_lsap); + kfree(link); + } +} + +/* + * Function irlmp_connect_request (handle, dlsap, userdata) + * + * Connect with a peer LSAP + * + */ +int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, + __u32 saddr, __u32 daddr, + struct qos_info *qos, struct sk_buff *userdata) +{ + struct sk_buff *tx_skb = userdata; + struct lap_cb *lap; + struct lsap_cb *lsap; + int ret; + + IRDA_ASSERT(self != NULL, return -EBADR;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;); + + pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n", + __func__, self->slsap_sel, dlsap_sel, saddr, daddr); + + if (test_bit(0, &self->connected)) { + ret = -EISCONN; + goto err; + } + + /* Client must supply destination device address */ + if (!daddr) { + ret = -EINVAL; + goto err; + } + + /* Any userdata? */ + if (tx_skb == NULL) { + tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); + if (!tx_skb) + return -ENOMEM; + + skb_reserve(tx_skb, LMP_MAX_HEADER); + } + + /* Make room for MUX control header (3 bytes) */ + IRDA_ASSERT(skb_headroom(tx_skb) >= LMP_CONTROL_HEADER, return -1;); + skb_push(tx_skb, LMP_CONTROL_HEADER); + + self->dlsap_sel = dlsap_sel; + + /* + * Find the link to where we should try to connect since there may + * be more than one IrDA port on this machine. If the client has + * passed us the saddr (and already knows which link to use), then + * we use that to find the link, if not then we have to look in the + * discovery log and check if any of the links has discovered a + * device with the given daddr + */ + if ((!saddr) || (saddr == DEV_ADDR_ANY)) { + discovery_t *discovery; + unsigned long flags; + + spin_lock_irqsave(&irlmp->cachelog->hb_spinlock, flags); + if (daddr != DEV_ADDR_ANY) + discovery = hashbin_find(irlmp->cachelog, daddr, NULL); + else { + pr_debug("%s(), no daddr\n", __func__); + discovery = (discovery_t *) + hashbin_get_first(irlmp->cachelog); + } + + if (discovery) { + saddr = discovery->data.saddr; + daddr = discovery->data.daddr; + } + spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags); + } + lap = hashbin_lock_find(irlmp->links, saddr, NULL); + if (lap == NULL) { + pr_debug("%s(), Unable to find a usable link!\n", __func__); + ret = -EHOSTUNREACH; + goto err; + } + + /* Check if LAP is disconnected or already connected */ + if (lap->daddr == DEV_ADDR_ANY) + lap->daddr = daddr; + else if (lap->daddr != daddr) { + /* Check if some LSAPs are active on this LAP */ + if (HASHBIN_GET_SIZE(lap->lsaps) == 0) { + /* No active connection, but LAP hasn't been + * disconnected yet (waiting for timeout in LAP). + * Maybe we could give LAP a bit of help in this case. + */ + pr_debug("%s(), sorry, but I'm waiting for LAP to timeout!\n", + __func__); + ret = -EAGAIN; + goto err; + } + + /* LAP is already connected to a different node, and LAP + * can only talk to one node at a time */ + pr_debug("%s(), sorry, but link is busy!\n", __func__); + ret = -EBUSY; + goto err; + } + + self->lap = lap; + + /* + * Remove LSAP from list of unconnected LSAPs and insert it into the + * list of connected LSAPs for the particular link + */ + lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, NULL); + + IRDA_ASSERT(lsap != NULL, return -1;); + IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;); + IRDA_ASSERT(lsap->lap != NULL, return -1;); + IRDA_ASSERT(lsap->lap->magic == LMP_LAP_MAGIC, return -1;); + + hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (long) self, + NULL); + + set_bit(0, &self->connected); /* TRUE */ + + /* + * User supplied qos specifications? + */ + if (qos) + self->qos = *qos; + + irlmp_do_lsap_event(self, LM_CONNECT_REQUEST, tx_skb); + + /* Drop reference count - see irlap_data_request(). */ + dev_kfree_skb(tx_skb); + + return 0; + +err: + /* Cleanup */ + if(tx_skb) + dev_kfree_skb(tx_skb); + return ret; +} +EXPORT_SYMBOL(irlmp_connect_request); + +/* + * Function irlmp_connect_indication (self) + * + * Incoming connection + * + */ +void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb) +{ + int max_seg_size; + int lap_header_size; + int max_header_size; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(self->lap != NULL, return;); + + pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", + __func__, self->slsap_sel, self->dlsap_sel); + + /* Note : self->lap is set in irlmp_link_data_indication(), + * (case CONNECT_CMD:) because we have no way to set it here. + * Similarly, self->dlsap_sel is usually set in irlmp_find_lsap(). + * Jean II */ + + self->qos = *self->lap->qos; + + max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; + lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap); + max_header_size = LMP_HEADER + lap_header_size; + + /* Hide LMP_CONTROL_HEADER header from layer above */ + skb_pull(skb, LMP_CONTROL_HEADER); + + if (self->notify.connect_indication) { + /* Don't forget to refcount it - see irlap_driver_rcv(). */ + skb_get(skb); + self->notify.connect_indication(self->notify.instance, self, + &self->qos, max_seg_size, + max_header_size, skb); + } +} + +/* + * Function irlmp_connect_response (handle, userdata) + * + * Service user is accepting connection + * + */ +int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata) +{ + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); + IRDA_ASSERT(userdata != NULL, return -1;); + + /* We set the connected bit and move the lsap to the connected list + * in the state machine itself. Jean II */ + + pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", + __func__, self->slsap_sel, self->dlsap_sel); + + /* Make room for MUX control header (3 bytes) */ + IRDA_ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return -1;); + skb_push(userdata, LMP_CONTROL_HEADER); + + irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata); + + /* Drop reference count - see irlap_data_request(). */ + dev_kfree_skb(userdata); + + return 0; +} +EXPORT_SYMBOL(irlmp_connect_response); + +/* + * Function irlmp_connect_confirm (handle, skb) + * + * LSAP connection confirmed peer device! + */ +void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb) +{ + int max_header_size; + int lap_header_size; + int max_seg_size; + + IRDA_ASSERT(skb != NULL, return;); + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); + IRDA_ASSERT(self->lap != NULL, return;); + + self->qos = *self->lap->qos; + + max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; + lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap); + max_header_size = LMP_HEADER + lap_header_size; + + pr_debug("%s(), max_header_size=%d\n", + __func__, max_header_size); + + /* Hide LMP_CONTROL_HEADER header from layer above */ + skb_pull(skb, LMP_CONTROL_HEADER); + + if (self->notify.connect_confirm) { + /* Don't forget to refcount it - see irlap_driver_rcv() */ + skb_get(skb); + self->notify.connect_confirm(self->notify.instance, self, + &self->qos, max_seg_size, + max_header_size, skb); + } +} + +/* + * Function irlmp_dup (orig, instance) + * + * Duplicate LSAP, can be used by servers to confirm a connection on a + * new LSAP so it can keep listening on the old one. + * + */ +struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance) +{ + struct lsap_cb *new; + unsigned long flags; + + spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); + + /* Only allowed to duplicate unconnected LSAP's, and only LSAPs + * that have received a connect indication. Jean II */ + if ((!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) || + (orig->lap == NULL)) { + pr_debug("%s(), invalid LSAP (wrong state)\n", + __func__); + spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, + flags); + return NULL; + } + + /* Allocate a new instance */ + new = kmemdup(orig, sizeof(*new), GFP_ATOMIC); + if (!new) { + pr_debug("%s(), unable to kmalloc\n", __func__); + spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, + flags); + return NULL; + } + /* new->lap = orig->lap; => done in the memcpy() */ + /* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */ + new->conn_skb = NULL; + + spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); + + /* Not everything is the same */ + new->notify.instance = instance; + + init_timer(&new->watchdog_timer); + + hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new, + (long) new, NULL); + +#ifdef CONFIG_IRDA_CACHE_LAST_LSAP + /* Make sure that we invalidate the LSAP cache */ + new->lap->cache.valid = FALSE; +#endif /* CONFIG_IRDA_CACHE_LAST_LSAP */ + + return new; +} + +/* + * Function irlmp_disconnect_request (handle, userdata) + * + * The service user is requesting disconnection, this will not remove the + * LSAP, but only mark it as disconnected + */ +int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata) +{ + struct lsap_cb *lsap; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); + IRDA_ASSERT(userdata != NULL, return -1;); + + /* Already disconnected ? + * There is a race condition between irlmp_disconnect_indication() + * and us that might mess up the hashbins below. This fixes it. + * Jean II */ + if (! test_and_clear_bit(0, &self->connected)) { + pr_debug("%s(), already disconnected!\n", __func__); + dev_kfree_skb(userdata); + return -1; + } + + skb_push(userdata, LMP_CONTROL_HEADER); + + /* + * Do the event before the other stuff since we must know + * which lap layer that the frame should be transmitted on + */ + irlmp_do_lsap_event(self, LM_DISCONNECT_REQUEST, userdata); + + /* Drop reference count - see irlap_data_request(). */ + dev_kfree_skb(userdata); + + /* + * Remove LSAP from list of connected LSAPs for the particular link + * and insert it into the list of unconnected LSAPs + */ + IRDA_ASSERT(self->lap != NULL, return -1;); + IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); + IRDA_ASSERT(self->lap->lsaps != NULL, return -1;); + + lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL); +#ifdef CONFIG_IRDA_CACHE_LAST_LSAP + self->lap->cache.valid = FALSE; +#endif + + IRDA_ASSERT(lsap != NULL, return -1;); + IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;); + IRDA_ASSERT(lsap == self, return -1;); + + hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, + (long) self, NULL); + + /* Reset some values */ + self->dlsap_sel = LSAP_ANY; + self->lap = NULL; + + return 0; +} +EXPORT_SYMBOL(irlmp_disconnect_request); + +/* + * Function irlmp_disconnect_indication (reason, userdata) + * + * LSAP is being closed! + */ +void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, + struct sk_buff *skb) +{ + struct lsap_cb *lsap; + + pr_debug("%s(), reason=%s [%d]\n", __func__, + irlmp_reason_str(reason), reason); + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); + + pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", + __func__, self->slsap_sel, self->dlsap_sel); + + /* Already disconnected ? + * There is a race condition between irlmp_disconnect_request() + * and us that might mess up the hashbins below. This fixes it. + * Jean II */ + if (! test_and_clear_bit(0, &self->connected)) { + pr_debug("%s(), already disconnected!\n", __func__); + return; + } + + /* + * Remove association between this LSAP and the link it used + */ + IRDA_ASSERT(self->lap != NULL, return;); + IRDA_ASSERT(self->lap->lsaps != NULL, return;); + + lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL); +#ifdef CONFIG_IRDA_CACHE_LAST_LSAP + self->lap->cache.valid = FALSE; +#endif + + IRDA_ASSERT(lsap != NULL, return;); + IRDA_ASSERT(lsap == self, return;); + hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap, + (long) lsap, NULL); + + self->dlsap_sel = LSAP_ANY; + self->lap = NULL; + + /* + * Inform service user + */ + if (self->notify.disconnect_indication) { + /* Don't forget to refcount it - see irlap_driver_rcv(). */ + if(skb) + skb_get(skb); + self->notify.disconnect_indication(self->notify.instance, + self, reason, skb); + } else { + pr_debug("%s(), no handler\n", __func__); + } +} + +/* + * Function irlmp_do_expiry (void) + * + * Do a cleanup of the discovery log (remove old entries) + * + * Note : separate from irlmp_do_discovery() so that we can handle + * passive discovery properly. + */ +void irlmp_do_expiry(void) +{ + struct lap_cb *lap; + + /* + * Expire discovery on all links which are *not* connected. + * On links which are connected, we can't do discovery + * anymore and can't refresh the log, so we freeze the + * discovery log to keep info about the device we are + * connected to. + * This info is mandatory if we want irlmp_connect_request() + * to work properly. - Jean II + */ + lap = (struct lap_cb *) hashbin_get_first(irlmp->links); + while (lap != NULL) { + IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); + + if (lap->lap_state == LAP_STANDBY) { + /* Expire discoveries discovered on this link */ + irlmp_expire_discoveries(irlmp->cachelog, lap->saddr, + FALSE); + } + lap = (struct lap_cb *) hashbin_get_next(irlmp->links); + } +} + +/* + * Function irlmp_do_discovery (nslots) + * + * Do some discovery on all links + * + * Note : log expiry is done above. + */ +void irlmp_do_discovery(int nslots) +{ + struct lap_cb *lap; + __u16 *data_hintsp; + + /* Make sure the value is sane */ + if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){ + net_warn_ratelimited("%s: invalid value for number of slots!\n", + __func__); + nslots = sysctl_discovery_slots = 8; + } + + /* Construct new discovery info to be used by IrLAP, */ + data_hintsp = (__u16 *) irlmp->discovery_cmd.data.hints; + put_unaligned(irlmp->hints.word, data_hintsp); + + /* + * Set character set for device name (we use ASCII), and + * copy device name. Remember to make room for a \0 at the + * end + */ + irlmp->discovery_cmd.data.charset = CS_ASCII; + strncpy(irlmp->discovery_cmd.data.info, sysctl_devname, + NICKNAME_MAX_LEN); + irlmp->discovery_cmd.name_len = strlen(irlmp->discovery_cmd.data.info); + irlmp->discovery_cmd.nslots = nslots; + + /* + * Try to send discovery packets on all links + */ + lap = (struct lap_cb *) hashbin_get_first(irlmp->links); + while (lap != NULL) { + IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); + + if (lap->lap_state == LAP_STANDBY) { + /* Try to discover */ + irlmp_do_lap_event(lap, LM_LAP_DISCOVERY_REQUEST, + NULL); + } + lap = (struct lap_cb *) hashbin_get_next(irlmp->links); + } +} + +/* + * Function irlmp_discovery_request (nslots) + * + * Do a discovery of devices in front of the computer + * + * If the caller has registered a client discovery callback, this + * allow him to receive the full content of the discovery log through + * this callback (as normally he will receive only new discoveries). + */ +void irlmp_discovery_request(int nslots) +{ + /* Return current cached discovery log (in full) */ + irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_LOG); + + /* + * Start a single discovery operation if discovery is not already + * running + */ + if (!sysctl_discovery) { + /* Check if user wants to override the default */ + if (nslots == DISCOVERY_DEFAULT_SLOTS) + nslots = sysctl_discovery_slots; + + irlmp_do_discovery(nslots); + /* Note : we never do expiry here. Expiry will run on the + * discovery timer regardless of the state of sysctl_discovery + * Jean II */ + } +} +EXPORT_SYMBOL(irlmp_discovery_request); + +/* + * Function irlmp_get_discoveries (pn, mask, slots) + * + * Return the current discovery log + * + * If discovery is not enabled, you should call this function again + * after 1 or 2 seconds (i.e. after discovery has been done). + */ +struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots) +{ + /* If discovery is not enabled, it's likely that the discovery log + * will be empty. So, we trigger a single discovery, so that next + * time the user call us there might be some results in the log. + * Jean II + */ + if (!sysctl_discovery) { + /* Check if user wants to override the default */ + if (nslots == DISCOVERY_DEFAULT_SLOTS) + nslots = sysctl_discovery_slots; + + /* Start discovery - will complete sometime later */ + irlmp_do_discovery(nslots); + /* Note : we never do expiry here. Expiry will run on the + * discovery timer regardless of the state of sysctl_discovery + * Jean II */ + } + + /* Return current cached discovery log */ + return irlmp_copy_discoveries(irlmp->cachelog, pn, mask, TRUE); +} +EXPORT_SYMBOL(irlmp_get_discoveries); + +/* + * Function irlmp_notify_client (log) + * + * Notify all about discovered devices + * + * Clients registered with IrLMP are : + * o IrComm + * o IrLAN + * o Any socket (in any state - ouch, that may be a lot !) + * The client may have defined a callback to be notified in case of + * partial/selective discovery based on the hints that it passed to IrLMP. + */ +static inline void +irlmp_notify_client(irlmp_client_t *client, + hashbin_t *log, DISCOVERY_MODE mode) +{ + discinfo_t *discoveries; /* Copy of the discovery log */ + int number; /* Number of nodes in the log */ + int i; + + /* Check if client wants or not partial/selective log (optimisation) */ + if (!client->disco_callback) + return; + + /* + * Locking notes : + * the old code was manipulating the log directly, which was + * very racy. Now, we use copy_discoveries, that protects + * itself while dumping the log for us. + * The overhead of the copy is compensated by the fact that + * we only pass new discoveries in normal mode and don't + * pass the same old entry every 3s to the caller as we used + * to do (virtual function calling is expensive). + * Jean II + */ + + /* + * Now, check all discovered devices (if any), and notify client + * only about the services that the client is interested in + * We also notify only about the new devices unless the caller + * explicitly request a dump of the log. Jean II + */ + discoveries = irlmp_copy_discoveries(log, &number, + client->hint_mask.word, + (mode == DISCOVERY_LOG)); + /* Check if the we got some results */ + if (discoveries == NULL) + return; /* No nodes discovered */ + + /* Pass all entries to the listener */ + for(i = 0; i < number; i++) + client->disco_callback(&(discoveries[i]), mode, client->priv); + + /* Free up our buffer */ + kfree(discoveries); +} + +/* + * Function irlmp_discovery_confirm ( self, log) + * + * Some device(s) answered to our discovery request! Check to see which + * device it is, and give indication to the client(s) + * + */ +void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode) +{ + irlmp_client_t *client; + irlmp_client_t *client_next; + + IRDA_ASSERT(log != NULL, return;); + + if (!(HASHBIN_GET_SIZE(log))) + return; + + /* For each client - notify callback may touch client list */ + client = (irlmp_client_t *) hashbin_get_first(irlmp->clients); + while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL, + (void *) &client_next) ) { + /* Check if we should notify client */ + irlmp_notify_client(client, log, mode); + + client = client_next; + } +} + +/* + * Function irlmp_discovery_expiry (expiry) + * + * This device is no longer been discovered, and therefore it is being + * purged from the discovery log. Inform all clients who have + * registered for this event... + * + * Note : called exclusively from discovery.c + * Note : this is no longer called under discovery spinlock, so the + * client can do whatever he wants in the callback. + */ +void irlmp_discovery_expiry(discinfo_t *expiries, int number) +{ + irlmp_client_t *client; + irlmp_client_t *client_next; + int i; + + IRDA_ASSERT(expiries != NULL, return;); + + /* For each client - notify callback may touch client list */ + client = (irlmp_client_t *) hashbin_get_first(irlmp->clients); + while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL, + (void *) &client_next) ) { + + /* Pass all entries to the listener */ + for(i = 0; i < number; i++) { + /* Check if we should notify client */ + if ((client->expir_callback) && + (client->hint_mask.word & + get_unaligned((__u16 *)expiries[i].hints) + & 0x7f7f) ) + client->expir_callback(&(expiries[i]), + EXPIRY_TIMEOUT, + client->priv); + } + + /* Next client */ + client = client_next; + } +} + +/* + * Function irlmp_get_discovery_response () + * + * Used by IrLAP to get the discovery info it needs when answering + * discovery requests by other devices. + */ +discovery_t *irlmp_get_discovery_response(void) +{ + IRDA_ASSERT(irlmp != NULL, return NULL;); + + put_unaligned(irlmp->hints.word, (__u16 *)irlmp->discovery_rsp.data.hints); + + /* + * Set character set for device name (we use ASCII), and + * copy device name. Remember to make room for a \0 at the + * end + */ + irlmp->discovery_rsp.data.charset = CS_ASCII; + + strncpy(irlmp->discovery_rsp.data.info, sysctl_devname, + NICKNAME_MAX_LEN); + irlmp->discovery_rsp.name_len = strlen(irlmp->discovery_rsp.data.info); + + return &irlmp->discovery_rsp; +} + +/* + * Function irlmp_data_request (self, skb) + * + * Send some data to peer device + * + * Note on skb management : + * After calling the lower layers of the IrDA stack, we always + * kfree() the skb, which drop the reference count (and potentially + * destroy it). + * IrLMP and IrLAP may queue the packet, and in those cases will need + * to use skb_get() to keep it around. + * Jean II + */ +int irlmp_data_request(struct lsap_cb *self, struct sk_buff *userdata) +{ + int ret; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); + + /* Make room for MUX header */ + IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;); + skb_push(userdata, LMP_HEADER); + + ret = irlmp_do_lsap_event(self, LM_DATA_REQUEST, userdata); + + /* Drop reference count - see irlap_data_request(). */ + dev_kfree_skb(userdata); + + return ret; +} +EXPORT_SYMBOL(irlmp_data_request); + +/* + * Function irlmp_data_indication (handle, skb) + * + * Got data from LAP layer so pass it up to upper layer + * + */ +void irlmp_data_indication(struct lsap_cb *self, struct sk_buff *skb) +{ + /* Hide LMP header from layer above */ + skb_pull(skb, LMP_HEADER); + + if (self->notify.data_indication) { + /* Don't forget to refcount it - see irlap_driver_rcv(). */ + skb_get(skb); + self->notify.data_indication(self->notify.instance, self, skb); + } +} + +/* + * Function irlmp_udata_request (self, skb) + */ +int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata) +{ + int ret; + + IRDA_ASSERT(userdata != NULL, return -1;); + + /* Make room for MUX header */ + IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;); + skb_push(userdata, LMP_HEADER); + + ret = irlmp_do_lsap_event(self, LM_UDATA_REQUEST, userdata); + + /* Drop reference count - see irlap_data_request(). */ + dev_kfree_skb(userdata); + + return ret; +} + +/* + * Function irlmp_udata_indication (self, skb) + * + * Send unreliable data (but still within the connection) + * + */ +void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + /* Hide LMP header from layer above */ + skb_pull(skb, LMP_HEADER); + + if (self->notify.udata_indication) { + /* Don't forget to refcount it - see irlap_driver_rcv(). */ + skb_get(skb); + self->notify.udata_indication(self->notify.instance, self, + skb); + } +} + +/* + * Function irlmp_connless_data_request (self, skb) + */ +#ifdef CONFIG_IRDA_ULTRA +int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata, + __u8 pid) +{ + struct sk_buff *clone_skb; + struct lap_cb *lap; + + IRDA_ASSERT(userdata != NULL, return -1;); + + /* Make room for MUX and PID header */ + IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER+LMP_PID_HEADER, + return -1;); + + /* Insert protocol identifier */ + skb_push(userdata, LMP_PID_HEADER); + if(self != NULL) + userdata->data[0] = self->pid; + else + userdata->data[0] = pid; + + /* Connectionless sockets must use 0x70 */ + skb_push(userdata, LMP_HEADER); + userdata->data[0] = userdata->data[1] = LSAP_CONNLESS; + + /* Try to send Connectionless packets out on all links */ + lap = (struct lap_cb *) hashbin_get_first(irlmp->links); + while (lap != NULL) { + IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return -1;); + + clone_skb = skb_clone(userdata, GFP_ATOMIC); + if (!clone_skb) { + dev_kfree_skb(userdata); + return -ENOMEM; + } + + irlap_unitdata_request(lap->irlap, clone_skb); + /* irlap_unitdata_request() don't increase refcount, + * so no dev_kfree_skb() - Jean II */ + + lap = (struct lap_cb *) hashbin_get_next(irlmp->links); + } + dev_kfree_skb(userdata); + + return 0; +} +#endif /* CONFIG_IRDA_ULTRA */ + +/* + * Function irlmp_connless_data_indication (self, skb) + * + * Receive unreliable data outside any connection. Mostly used by Ultra + * + */ +#ifdef CONFIG_IRDA_ULTRA +void irlmp_connless_data_indication(struct lsap_cb *self, struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + /* Hide LMP and PID header from layer above */ + skb_pull(skb, LMP_HEADER+LMP_PID_HEADER); + + if (self->notify.udata_indication) { + /* Don't forget to refcount it - see irlap_driver_rcv(). */ + skb_get(skb); + self->notify.udata_indication(self->notify.instance, self, + skb); + } +} +#endif /* CONFIG_IRDA_ULTRA */ + +/* + * Propagate status indication from LAP to LSAPs (via LMP) + * This don't trigger any change of state in lap_cb, lmp_cb or lsap_cb, + * and the event is stateless, therefore we can bypass both state machines + * and send the event direct to the LSAP user. + * Jean II + */ +void irlmp_status_indication(struct lap_cb *self, + LINK_STATUS link, LOCK_STATUS lock) +{ + struct lsap_cb *next; + struct lsap_cb *curr; + + /* Send status_indication to all LSAPs using this link */ + curr = (struct lsap_cb *) hashbin_get_first( self->lsaps); + while (NULL != hashbin_find_next(self->lsaps, (long) curr, NULL, + (void *) &next) ) { + IRDA_ASSERT(curr->magic == LMP_LSAP_MAGIC, return;); + /* + * Inform service user if he has requested it + */ + if (curr->notify.status_indication != NULL) + curr->notify.status_indication(curr->notify.instance, + link, lock); + else + pr_debug("%s(), no handler\n", __func__); + + curr = next; + } +} + +/* + * Receive flow control indication from LAP. + * LAP want us to send it one more frame. We implement a simple round + * robin scheduler between the active sockets so that we get a bit of + * fairness. Note that the round robin is far from perfect, but it's + * better than nothing. + * We then poll the selected socket so that we can do synchronous + * refilling of IrLAP (which allow to minimise the number of buffers). + * Jean II + */ +void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow) +{ + struct lsap_cb *next; + struct lsap_cb *curr; + int lsap_todo; + + IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); + IRDA_ASSERT(flow == FLOW_START, return;); + + /* Get the number of lsap. That's the only safe way to know + * that we have looped around... - Jean II */ + lsap_todo = HASHBIN_GET_SIZE(self->lsaps); + pr_debug("%s() : %d lsaps to scan\n", __func__, lsap_todo); + + /* Poll lsap in order until the queue is full or until we + * tried them all. + * Most often, the current LSAP will have something to send, + * so we will go through this loop only once. - Jean II */ + while((lsap_todo--) && + (IRLAP_GET_TX_QUEUE_LEN(self->irlap) < LAP_HIGH_THRESHOLD)) { + /* Try to find the next lsap we should poll. */ + next = self->flow_next; + /* If we have no lsap, restart from first one */ + if(next == NULL) + next = (struct lsap_cb *) hashbin_get_first(self->lsaps); + /* Verify current one and find the next one */ + curr = hashbin_find_next(self->lsaps, (long) next, NULL, + (void *) &self->flow_next); + /* Uh-oh... Paranoia */ + if(curr == NULL) + break; + pr_debug("%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n", + __func__, curr, next, self->flow_next, lsap_todo, + IRLAP_GET_TX_QUEUE_LEN(self->irlap)); + + /* Inform lsap user that it can send one more packet. */ + if (curr->notify.flow_indication != NULL) + curr->notify.flow_indication(curr->notify.instance, + curr, flow); + else + pr_debug("%s(), no handler\n", __func__); + } +} + +#if 0 +/* + * Function irlmp_hint_to_service (hint) + * + * Returns a list of all servics contained in the given hint bits. This + * function assumes that the hint bits have the size of two bytes only + */ +__u8 *irlmp_hint_to_service(__u8 *hint) +{ + __u8 *service; + int i = 0; + + /* + * Allocate array to store services in. 16 entries should be safe + * since we currently only support 2 hint bytes + */ + service = kmalloc(16, GFP_ATOMIC); + if (!service) + return NULL; + + if (!hint[0]) { + pr_debug("\n"); + kfree(service); + return NULL; + } + if (hint[0] & HINT_PNP) + pr_debug("PnP Compatible "); + if (hint[0] & HINT_PDA) + pr_debug("PDA/Palmtop "); + if (hint[0] & HINT_COMPUTER) + pr_debug("Computer "); + if (hint[0] & HINT_PRINTER) { + pr_debug("Printer "); + service[i++] = S_PRINTER; + } + if (hint[0] & HINT_MODEM) + pr_debug("Modem "); + if (hint[0] & HINT_FAX) + pr_debug("Fax "); + if (hint[0] & HINT_LAN) { + pr_debug("LAN Access "); + service[i++] = S_LAN; + } + /* + * Test if extension byte exists. This byte will usually be + * there, but this is not really required by the standard. + * (IrLMP p. 29) + */ + if (hint[0] & HINT_EXTENSION) { + if (hint[1] & HINT_TELEPHONY) { + pr_debug("Telephony "); + service[i++] = S_TELEPHONY; + } + if (hint[1] & HINT_FILE_SERVER) + pr_debug("File Server "); + + if (hint[1] & HINT_COMM) { + pr_debug("IrCOMM "); + service[i++] = S_COMM; + } + if (hint[1] & HINT_OBEX) { + pr_debug("IrOBEX "); + service[i++] = S_OBEX; + } + } + pr_debug("\n"); + + /* So that client can be notified about any discovery */ + service[i++] = S_ANY; + + service[i] = S_END; + + return service; +} +#endif + +static const __u16 service_hint_mapping[S_END][2] = { + { HINT_PNP, 0 }, /* S_PNP */ + { HINT_PDA, 0 }, /* S_PDA */ + { HINT_COMPUTER, 0 }, /* S_COMPUTER */ + { HINT_PRINTER, 0 }, /* S_PRINTER */ + { HINT_MODEM, 0 }, /* S_MODEM */ + { HINT_FAX, 0 }, /* S_FAX */ + { HINT_LAN, 0 }, /* S_LAN */ + { HINT_EXTENSION, HINT_TELEPHONY }, /* S_TELEPHONY */ + { HINT_EXTENSION, HINT_COMM }, /* S_COMM */ + { HINT_EXTENSION, HINT_OBEX }, /* S_OBEX */ + { 0xFF, 0xFF }, /* S_ANY */ +}; + +/* + * Function irlmp_service_to_hint (service) + * + * Converts a service type, to a hint bit + * + * Returns: a 16 bit hint value, with the service bit set + */ +__u16 irlmp_service_to_hint(int service) +{ + __u16_host_order hint; + + hint.byte[0] = service_hint_mapping[service][0]; + hint.byte[1] = service_hint_mapping[service][1]; + + return hint.word; +} +EXPORT_SYMBOL(irlmp_service_to_hint); + +/* + * Function irlmp_register_service (service) + * + * Register local service with IrLMP + * + */ +void *irlmp_register_service(__u16 hints) +{ + irlmp_service_t *service; + + pr_debug("%s(), hints = %04x\n", __func__, hints); + + /* Make a new registration */ + service = kmalloc(sizeof(irlmp_service_t), GFP_ATOMIC); + if (!service) + return NULL; + + service->hints.word = hints; + hashbin_insert(irlmp->services, (irda_queue_t *) service, + (long) service, NULL); + + irlmp->hints.word |= hints; + + return (void *)service; +} +EXPORT_SYMBOL(irlmp_register_service); + +/* + * Function irlmp_unregister_service (handle) + * + * Unregister service with IrLMP. + * + * Returns: 0 on success, -1 on error + */ +int irlmp_unregister_service(void *handle) +{ + irlmp_service_t *service; + unsigned long flags; + + if (!handle) + return -1; + + /* Caller may call with invalid handle (it's legal) - Jean II */ + service = hashbin_lock_find(irlmp->services, (long) handle, NULL); + if (!service) { + pr_debug("%s(), Unknown service!\n", __func__); + return -1; + } + + hashbin_remove_this(irlmp->services, (irda_queue_t *) service); + kfree(service); + + /* Remove old hint bits */ + irlmp->hints.word = 0; + + /* Refresh current hint bits */ + spin_lock_irqsave(&irlmp->services->hb_spinlock, flags); + service = (irlmp_service_t *) hashbin_get_first(irlmp->services); + while (service) { + irlmp->hints.word |= service->hints.word; + + service = (irlmp_service_t *)hashbin_get_next(irlmp->services); + } + spin_unlock_irqrestore(&irlmp->services->hb_spinlock, flags); + return 0; +} +EXPORT_SYMBOL(irlmp_unregister_service); + +/* + * Function irlmp_register_client (hint_mask, callback1, callback2) + * + * Register a local client with IrLMP + * First callback is selective discovery (based on hints) + * Second callback is for selective discovery expiries + * + * Returns: handle > 0 on success, 0 on error + */ +void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb, + DISCOVERY_CALLBACK2 expir_clb, void *priv) +{ + irlmp_client_t *client; + + IRDA_ASSERT(irlmp != NULL, return NULL;); + + /* Make a new registration */ + client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC); + if (!client) + return NULL; + + /* Register the details */ + client->hint_mask.word = hint_mask; + client->disco_callback = disco_clb; + client->expir_callback = expir_clb; + client->priv = priv; + + hashbin_insert(irlmp->clients, (irda_queue_t *) client, + (long) client, NULL); + + return (void *) client; +} +EXPORT_SYMBOL(irlmp_register_client); + +/* + * Function irlmp_update_client (handle, hint_mask, callback1, callback2) + * + * Updates specified client (handle) with possibly new hint_mask and + * callback + * + * Returns: 0 on success, -1 on error + */ +int irlmp_update_client(void *handle, __u16 hint_mask, + DISCOVERY_CALLBACK1 disco_clb, + DISCOVERY_CALLBACK2 expir_clb, void *priv) +{ + irlmp_client_t *client; + + if (!handle) + return -1; + + client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); + if (!client) { + pr_debug("%s(), Unknown client!\n", __func__); + return -1; + } + + client->hint_mask.word = hint_mask; + client->disco_callback = disco_clb; + client->expir_callback = expir_clb; + client->priv = priv; + + return 0; +} +EXPORT_SYMBOL(irlmp_update_client); + +/* + * Function irlmp_unregister_client (handle) + * + * Returns: 0 on success, -1 on error + * + */ +int irlmp_unregister_client(void *handle) +{ + struct irlmp_client *client; + + if (!handle) + return -1; + + /* Caller may call with invalid handle (it's legal) - Jean II */ + client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); + if (!client) { + pr_debug("%s(), Unknown client!\n", __func__); + return -1; + } + + pr_debug("%s(), removing client!\n", __func__); + hashbin_remove_this(irlmp->clients, (irda_queue_t *) client); + kfree(client); + + return 0; +} +EXPORT_SYMBOL(irlmp_unregister_client); + +/* + * Function irlmp_slsap_inuse (slsap) + * + * Check if the given source LSAP selector is in use + * + * This function is clearly not very efficient. On the mitigating side, the + * stack make sure that in 99% of the cases, we are called only once + * for each socket allocation. We could probably keep a bitmap + * of the allocated LSAP, but I'm not sure the complexity is worth it. + * Jean II + */ +static int irlmp_slsap_inuse(__u8 slsap_sel) +{ + struct lsap_cb *self; + struct lap_cb *lap; + unsigned long flags; + + IRDA_ASSERT(irlmp != NULL, return TRUE;); + IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;); + IRDA_ASSERT(slsap_sel != LSAP_ANY, return TRUE;); + +#ifdef CONFIG_IRDA_ULTRA + /* Accept all bindings to the connectionless LSAP */ + if (slsap_sel == LSAP_CONNLESS) + return FALSE; +#endif /* CONFIG_IRDA_ULTRA */ + + /* Valid values are between 0 and 127 (0x0-0x6F) */ + if (slsap_sel > LSAP_MAX) + return TRUE; + + /* + * Check if slsap is already in use. To do this we have to loop over + * every IrLAP connection and check every LSAP associated with each + * the connection. + */ + spin_lock_irqsave_nested(&irlmp->links->hb_spinlock, flags, + SINGLE_DEPTH_NESTING); + lap = (struct lap_cb *) hashbin_get_first(irlmp->links); + while (lap != NULL) { + IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, goto errlap;); + + /* Careful for priority inversions here ! + * irlmp->links is never taken while another IrDA + * spinlock is held, so we are safe. Jean II */ + spin_lock(&lap->lsaps->hb_spinlock); + + /* For this IrLAP, check all the LSAPs */ + self = (struct lsap_cb *) hashbin_get_first(lap->lsaps); + while (self != NULL) { + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, + goto errlsap;); + + if ((self->slsap_sel == slsap_sel)) { + pr_debug("Source LSAP selector=%02x in use\n", + self->slsap_sel); + goto errlsap; + } + self = (struct lsap_cb*) hashbin_get_next(lap->lsaps); + } + spin_unlock(&lap->lsaps->hb_spinlock); + + /* Next LAP */ + lap = (struct lap_cb *) hashbin_get_next(irlmp->links); + } + spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags); + + /* + * Server sockets are typically waiting for connections and + * therefore reside in the unconnected list. We don't want + * to give out their LSAPs for obvious reasons... + * Jean II + */ + spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); + + self = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps); + while (self != NULL) { + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto erruncon;); + if ((self->slsap_sel == slsap_sel)) { + pr_debug("Source LSAP selector=%02x in use (unconnected)\n", + self->slsap_sel); + goto erruncon; + } + self = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps); + } + spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); + + return FALSE; + + /* Error exit from within one of the two nested loops. + * Make sure we release the right spinlock in the righ order. + * Jean II */ +errlsap: + spin_unlock(&lap->lsaps->hb_spinlock); +IRDA_ASSERT_LABEL(errlap:) + spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags); + return TRUE; + + /* Error exit from within the unconnected loop. + * Just one spinlock to release... Jean II */ +erruncon: + spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); + return TRUE; +} + +/* + * Function irlmp_find_free_slsap () + * + * Find a free source LSAP to use. This function is called if the service + * user has requested a source LSAP equal to LM_ANY + */ +static __u8 irlmp_find_free_slsap(void) +{ + __u8 lsap_sel; + int wrapped = 0; + + IRDA_ASSERT(irlmp != NULL, return -1;); + IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return -1;); + + /* Most users don't really care which LSAPs they are given, + * and therefore we automatically give them a free LSAP. + * This function try to find a suitable LSAP, i.e. which is + * not in use and is within the acceptable range. Jean II */ + + do { + /* Always increment to LSAP number before using it. + * In theory, we could reuse the last LSAP number, as long + * as it is no longer in use. Some IrDA stack do that. + * However, the previous socket may be half closed, i.e. + * we closed it, we think it's no longer in use, but the + * other side did not receive our close and think it's + * active and still send data on it. + * This is similar to what is done with PIDs and TCP ports. + * Also, this reduce the number of calls to irlmp_slsap_inuse() + * which is an expensive function to call. + * Jean II */ + irlmp->last_lsap_sel++; + + /* Check if we need to wraparound (0x70-0x7f are reserved) */ + if (irlmp->last_lsap_sel > LSAP_MAX) { + /* 0x00-0x10 are also reserved for well know ports */ + irlmp->last_lsap_sel = 0x10; + + /* Make sure we terminate the loop */ + if (wrapped++) { + net_err_ratelimited("%s: no more free LSAPs !\n", + __func__); + return 0; + } + } + + /* If the LSAP is in use, try the next one. + * Despite the autoincrement, we need to check if the lsap + * is really in use or not, first because LSAP may be + * directly allocated in irlmp_open_lsap(), and also because + * we may wraparound on old sockets. Jean II */ + } while (irlmp_slsap_inuse(irlmp->last_lsap_sel)); + + /* Got it ! */ + lsap_sel = irlmp->last_lsap_sel; + pr_debug("%s(), found free lsap_sel=%02x\n", + __func__, lsap_sel); + + return lsap_sel; +} + +/* + * Function irlmp_convert_lap_reason (lap_reason) + * + * Converts IrLAP disconnect reason codes to IrLMP disconnect reason + * codes + * + */ +LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason) +{ + int reason = LM_LAP_DISCONNECT; + + switch (lap_reason) { + case LAP_DISC_INDICATION: /* Received a disconnect request from peer */ + pr_debug("%s(), LAP_DISC_INDICATION\n", __func__); + reason = LM_USER_REQUEST; + break; + case LAP_NO_RESPONSE: /* To many retransmits without response */ + pr_debug("%s(), LAP_NO_RESPONSE\n", __func__); + reason = LM_LAP_DISCONNECT; + break; + case LAP_RESET_INDICATION: + pr_debug("%s(), LAP_RESET_INDICATION\n", __func__); + reason = LM_LAP_RESET; + break; + case LAP_FOUND_NONE: + case LAP_MEDIA_BUSY: + case LAP_PRIMARY_CONFLICT: + pr_debug("%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n", + __func__); + reason = LM_CONNECT_FAILURE; + break; + default: + pr_debug("%s(), Unknown IrLAP disconnect reason %d!\n", + __func__, lap_reason); + reason = LM_LAP_DISCONNECT; + break; + } + + return reason; +} + +#ifdef CONFIG_PROC_FS + +struct irlmp_iter_state { + hashbin_t *hashbin; +}; + +#define LSAP_START_TOKEN ((void *)1) +#define LINK_START_TOKEN ((void *)2) + +static void *irlmp_seq_hb_idx(struct irlmp_iter_state *iter, loff_t *off) +{ + void *element; + + spin_lock_irq(&iter->hashbin->hb_spinlock); + for (element = hashbin_get_first(iter->hashbin); + element != NULL; + element = hashbin_get_next(iter->hashbin)) { + if (!off || (*off)-- == 0) { + /* NB: hashbin left locked */ + return element; + } + } + spin_unlock_irq(&iter->hashbin->hb_spinlock); + iter->hashbin = NULL; + return NULL; +} + + +static void *irlmp_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct irlmp_iter_state *iter = seq->private; + void *v; + loff_t off = *pos; + + iter->hashbin = NULL; + if (off-- == 0) + return LSAP_START_TOKEN; + + iter->hashbin = irlmp->unconnected_lsaps; + v = irlmp_seq_hb_idx(iter, &off); + if (v) + return v; + + if (off-- == 0) + return LINK_START_TOKEN; + + iter->hashbin = irlmp->links; + return irlmp_seq_hb_idx(iter, &off); +} + +static void *irlmp_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct irlmp_iter_state *iter = seq->private; + + ++*pos; + + if (v == LSAP_START_TOKEN) { /* start of list of lsaps */ + iter->hashbin = irlmp->unconnected_lsaps; + v = irlmp_seq_hb_idx(iter, NULL); + return v ? v : LINK_START_TOKEN; + } + + if (v == LINK_START_TOKEN) { /* start of list of links */ + iter->hashbin = irlmp->links; + return irlmp_seq_hb_idx(iter, NULL); + } + + v = hashbin_get_next(iter->hashbin); + + if (v == NULL) { /* no more in this hash bin */ + spin_unlock_irq(&iter->hashbin->hb_spinlock); + + if (iter->hashbin == irlmp->unconnected_lsaps) + v = LINK_START_TOKEN; + + iter->hashbin = NULL; + } + return v; +} + +static void irlmp_seq_stop(struct seq_file *seq, void *v) +{ + struct irlmp_iter_state *iter = seq->private; + + if (iter->hashbin) + spin_unlock_irq(&iter->hashbin->hb_spinlock); +} + +static int irlmp_seq_show(struct seq_file *seq, void *v) +{ + const struct irlmp_iter_state *iter = seq->private; + struct lsap_cb *self = v; + + if (v == LSAP_START_TOKEN) + seq_puts(seq, "Unconnected LSAPs:\n"); + else if (v == LINK_START_TOKEN) + seq_puts(seq, "\nRegistered Link Layers:\n"); + else if (iter->hashbin == irlmp->unconnected_lsaps) { + self = v; + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EINVAL; ); + seq_printf(seq, "lsap state: %s, ", + irlsap_state[ self->lsap_state]); + seq_printf(seq, + "slsap_sel: %#02x, dlsap_sel: %#02x, ", + self->slsap_sel, self->dlsap_sel); + seq_printf(seq, "(%s)", self->notify.name); + seq_printf(seq, "\n"); + } else if (iter->hashbin == irlmp->links) { + struct lap_cb *lap = v; + + seq_printf(seq, "lap state: %s, ", + irlmp_state[lap->lap_state]); + + seq_printf(seq, "saddr: %#08x, daddr: %#08x, ", + lap->saddr, lap->daddr); + seq_printf(seq, "num lsaps: %d", + HASHBIN_GET_SIZE(lap->lsaps)); + seq_printf(seq, "\n"); + + /* Careful for priority inversions here ! + * All other uses of attrib spinlock are independent of + * the object spinlock, so we are safe. Jean II */ + spin_lock(&lap->lsaps->hb_spinlock); + + seq_printf(seq, "\n Connected LSAPs:\n"); + for (self = (struct lsap_cb *) hashbin_get_first(lap->lsaps); + self != NULL; + self = (struct lsap_cb *)hashbin_get_next(lap->lsaps)) { + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, + goto outloop;); + seq_printf(seq, " lsap state: %s, ", + irlsap_state[ self->lsap_state]); + seq_printf(seq, + "slsap_sel: %#02x, dlsap_sel: %#02x, ", + self->slsap_sel, self->dlsap_sel); + seq_printf(seq, "(%s)", self->notify.name); + seq_putc(seq, '\n'); + + } + IRDA_ASSERT_LABEL(outloop:) + spin_unlock(&lap->lsaps->hb_spinlock); + seq_putc(seq, '\n'); + } else + return -EINVAL; + + return 0; +} + +static const struct seq_operations irlmp_seq_ops = { + .start = irlmp_seq_start, + .next = irlmp_seq_next, + .stop = irlmp_seq_stop, + .show = irlmp_seq_show, +}; + +static int irlmp_seq_open(struct inode *inode, struct file *file) +{ + IRDA_ASSERT(irlmp != NULL, return -EINVAL;); + + return seq_open_private(file, &irlmp_seq_ops, + sizeof(struct irlmp_iter_state)); +} + +const struct file_operations irlmp_seq_fops = { + .owner = THIS_MODULE, + .open = irlmp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +#endif /* PROC_FS */ diff --git a/drivers/staging/irda/net/irlmp_event.c b/drivers/staging/irda/net/irlmp_event.c new file mode 100644 index 000000000000..e306cf2c1e04 --- /dev/null +++ b/drivers/staging/irda/net/irlmp_event.c @@ -0,0 +1,886 @@ +/********************************************************************* + * + * Filename: irlmp_event.c + * Version: 0.8 + * Description: An IrDA LMP event driver for Linux + * Status: Experimental. + * Author: Dag Brattli + * Created at: Mon Aug 4 20:40:53 1997 + * Modified at: Tue Dec 14 23:04:16 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999 Dag Brattli , + * All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +const char *const irlmp_state[] = { + "LAP_STANDBY", + "LAP_U_CONNECT", + "LAP_ACTIVE", +}; + +const char *const irlsap_state[] = { + "LSAP_DISCONNECTED", + "LSAP_CONNECT", + "LSAP_CONNECT_PEND", + "LSAP_DATA_TRANSFER_READY", + "LSAP_SETUP", + "LSAP_SETUP_PEND", +}; + +static const char *const irlmp_event[] __maybe_unused = { + "LM_CONNECT_REQUEST", + "LM_CONNECT_CONFIRM", + "LM_CONNECT_RESPONSE", + "LM_CONNECT_INDICATION", + + "LM_DISCONNECT_INDICATION", + "LM_DISCONNECT_REQUEST", + + "LM_DATA_REQUEST", + "LM_UDATA_REQUEST", + "LM_DATA_INDICATION", + "LM_UDATA_INDICATION", + + "LM_WATCHDOG_TIMEOUT", + + /* IrLAP events */ + "LM_LAP_CONNECT_REQUEST", + "LM_LAP_CONNECT_INDICATION", + "LM_LAP_CONNECT_CONFIRM", + "LM_LAP_DISCONNECT_INDICATION", + "LM_LAP_DISCONNECT_REQUEST", + "LM_LAP_DISCOVERY_REQUEST", + "LM_LAP_DISCOVERY_CONFIRM", + "LM_LAP_IDLE_TIMEOUT", +}; + +/* LAP Connection control proto declarations */ +static void irlmp_state_standby (struct lap_cb *, IRLMP_EVENT, + struct sk_buff *); +static void irlmp_state_u_connect(struct lap_cb *, IRLMP_EVENT, + struct sk_buff *); +static void irlmp_state_active (struct lap_cb *, IRLMP_EVENT, + struct sk_buff *); + +/* LSAP Connection control proto declarations */ +static int irlmp_state_disconnected(struct lsap_cb *, IRLMP_EVENT, + struct sk_buff *); +static int irlmp_state_connect (struct lsap_cb *, IRLMP_EVENT, + struct sk_buff *); +static int irlmp_state_connect_pend(struct lsap_cb *, IRLMP_EVENT, + struct sk_buff *); +static int irlmp_state_dtr (struct lsap_cb *, IRLMP_EVENT, + struct sk_buff *); +static int irlmp_state_setup (struct lsap_cb *, IRLMP_EVENT, + struct sk_buff *); +static int irlmp_state_setup_pend (struct lsap_cb *, IRLMP_EVENT, + struct sk_buff *); + +static void (*lap_state[]) (struct lap_cb *, IRLMP_EVENT, struct sk_buff *) = +{ + irlmp_state_standby, + irlmp_state_u_connect, + irlmp_state_active, +}; + +static int (*lsap_state[])( struct lsap_cb *, IRLMP_EVENT, struct sk_buff *) = +{ + irlmp_state_disconnected, + irlmp_state_connect, + irlmp_state_connect_pend, + irlmp_state_dtr, + irlmp_state_setup, + irlmp_state_setup_pend +}; + +static inline void irlmp_next_lap_state(struct lap_cb *self, + IRLMP_STATE state) +{ + /* + pr_debug("%s(), LMP LAP = %s\n", __func__, irlmp_state[state]); + */ + self->lap_state = state; +} + +static inline void irlmp_next_lsap_state(struct lsap_cb *self, + LSAP_STATE state) +{ + /* + IRDA_ASSERT(self != NULL, return;); + pr_debug("%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]); + */ + self->lsap_state = state; +} + +/* Do connection control events */ +int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); + + pr_debug("%s(), EVENT = %s, STATE = %s\n", + __func__, irlmp_event[event], irlsap_state[self->lsap_state]); + + return (*lsap_state[self->lsap_state]) (self, event, skb); +} + +/* + * Function do_lap_event (event, skb, info) + * + * Do IrLAP control events + * + */ +void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); + + pr_debug("%s(), EVENT = %s, STATE = %s\n", __func__, + irlmp_event[event], + irlmp_state[self->lap_state]); + + (*lap_state[self->lap_state]) (self, event, skb); +} + +void irlmp_discovery_timer_expired(void *data) +{ + /* We always cleanup the log (active & passive discovery) */ + irlmp_do_expiry(); + + irlmp_do_discovery(sysctl_discovery_slots); + + /* Restart timer */ + irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout * HZ); +} + +void irlmp_watchdog_timer_expired(void *data) +{ + struct lsap_cb *self = (struct lsap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); + + irlmp_do_lsap_event(self, LM_WATCHDOG_TIMEOUT, NULL); +} + +void irlmp_idle_timer_expired(void *data) +{ + struct lap_cb *self = (struct lap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); + + irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL); +} + +/* + * Send an event on all LSAPs attached to this LAP. + */ +static inline void +irlmp_do_all_lsap_event(hashbin_t * lsap_hashbin, + IRLMP_EVENT event) +{ + struct lsap_cb *lsap; + struct lsap_cb *lsap_next; + + /* Note : this function use the new hashbin_find_next() + * function, instead of the old hashbin_get_next(). + * This make sure that we are always pointing one lsap + * ahead, so that if the current lsap is removed as the + * result of sending the event, we don't care. + * Also, as we store the context ourselves, if an enumeration + * of the same lsap hashbin happens as the result of sending the + * event, we don't care. + * The only problem is if the next lsap is removed. In that case, + * hashbin_find_next() will return NULL and we will abort the + * enumeration. - Jean II */ + + /* Also : we don't accept any skb in input. We can *NOT* pass + * the same skb to multiple clients safely, we would need to + * skb_clone() it. - Jean II */ + + lsap = (struct lsap_cb *) hashbin_get_first(lsap_hashbin); + + while (NULL != hashbin_find_next(lsap_hashbin, + (long) lsap, + NULL, + (void *) &lsap_next) ) { + irlmp_do_lsap_event(lsap, event, NULL); + lsap = lsap_next; + } +} + +/********************************************************************* + * + * LAP connection control states + * + ********************************************************************/ + +/* + * Function irlmp_state_standby (event, skb, info) + * + * STANDBY, The IrLAP connection does not exist. + * + */ +static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + IRDA_ASSERT(self->irlap != NULL, return;); + + switch (event) { + case LM_LAP_DISCOVERY_REQUEST: + /* irlmp_next_station_state( LMP_DISCOVER); */ + + irlap_discovery_request(self->irlap, &irlmp->discovery_cmd); + break; + case LM_LAP_CONNECT_INDICATION: + /* It's important to switch state first, to avoid IrLMP to + * think that the link is free since IrLMP may then start + * discovery before the connection is properly set up. DB. + */ + irlmp_next_lap_state(self, LAP_ACTIVE); + + /* Just accept connection TODO, this should be fixed */ + irlap_connect_response(self->irlap, skb); + break; + case LM_LAP_CONNECT_REQUEST: + pr_debug("%s() LS_CONNECT_REQUEST\n", __func__); + + irlmp_next_lap_state(self, LAP_U_CONNECT); + + /* FIXME: need to set users requested QoS */ + irlap_connect_request(self->irlap, self->daddr, NULL, 0); + break; + case LM_LAP_DISCONNECT_INDICATION: + pr_debug("%s(), Error LM_LAP_DISCONNECT_INDICATION\n", + __func__); + + irlmp_next_lap_state(self, LAP_STANDBY); + break; + default: + pr_debug("%s(), Unknown event %s\n", + __func__, irlmp_event[event]); + break; + } +} + +/* + * Function irlmp_state_u_connect (event, skb, info) + * + * U_CONNECT, The layer above has tried to open an LSAP connection but + * since the IrLAP connection does not exist, we must first start an + * IrLAP connection. We are now waiting response from IrLAP. + * */ +static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + pr_debug("%s(), event=%s\n", __func__, irlmp_event[event]); + + switch (event) { + case LM_LAP_CONNECT_INDICATION: + /* It's important to switch state first, to avoid IrLMP to + * think that the link is free since IrLMP may then start + * discovery before the connection is properly set up. DB. + */ + irlmp_next_lap_state(self, LAP_ACTIVE); + + /* Just accept connection TODO, this should be fixed */ + irlap_connect_response(self->irlap, skb); + + /* Tell LSAPs that they can start sending data */ + irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); + + /* Note : by the time we get there (LAP retries and co), + * the lsaps may already have gone. This avoid getting stuck + * forever in LAP_ACTIVE state - Jean II */ + if (HASHBIN_GET_SIZE(self->lsaps) == 0) { + pr_debug("%s() NO LSAPs !\n", __func__); + irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); + } + break; + case LM_LAP_CONNECT_REQUEST: + /* Already trying to connect */ + break; + case LM_LAP_CONNECT_CONFIRM: + /* For all lsap_ce E Associated do LS_Connect_confirm */ + irlmp_next_lap_state(self, LAP_ACTIVE); + + /* Tell LSAPs that they can start sending data */ + irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); + + /* Note : by the time we get there (LAP retries and co), + * the lsaps may already have gone. This avoid getting stuck + * forever in LAP_ACTIVE state - Jean II */ + if (HASHBIN_GET_SIZE(self->lsaps) == 0) { + pr_debug("%s() NO LSAPs !\n", __func__); + irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); + } + break; + case LM_LAP_DISCONNECT_INDICATION: + pr_debug("%s(), LM_LAP_DISCONNECT_INDICATION\n", __func__); + irlmp_next_lap_state(self, LAP_STANDBY); + + /* Send disconnect event to all LSAPs using this link */ + irlmp_do_all_lsap_event(self->lsaps, + LM_LAP_DISCONNECT_INDICATION); + break; + case LM_LAP_DISCONNECT_REQUEST: + pr_debug("%s(), LM_LAP_DISCONNECT_REQUEST\n", __func__); + + /* One of the LSAP did timeout or was closed, if it was + * the last one, try to get out of here - Jean II */ + if (HASHBIN_GET_SIZE(self->lsaps) <= 1) { + irlap_disconnect_request(self->irlap); + } + break; + default: + pr_debug("%s(), Unknown event %s\n", + __func__, irlmp_event[event]); + break; + } +} + +/* + * Function irlmp_state_active (event, skb, info) + * + * ACTIVE, IrLAP connection is active + * + */ +static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + switch (event) { + case LM_LAP_CONNECT_REQUEST: + pr_debug("%s(), LS_CONNECT_REQUEST\n", __func__); + + /* + * IrLAP may have a pending disconnect. We tried to close + * IrLAP, but it was postponed because the link was + * busy or we were still sending packets. As we now + * need it, make sure it stays on. Jean II + */ + irlap_clear_disconnect(self->irlap); + + /* + * LAP connection already active, just bounce back! Since we + * don't know which LSAP that tried to do this, we have to + * notify all LSAPs using this LAP, but that should be safe to + * do anyway. + */ + irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); + + /* Needed by connect indication */ + irlmp_do_all_lsap_event(irlmp->unconnected_lsaps, + LM_LAP_CONNECT_CONFIRM); + /* Keep state */ + break; + case LM_LAP_DISCONNECT_REQUEST: + /* + * Need to find out if we should close IrLAP or not. If there + * is only one LSAP connection left on this link, that LSAP + * must be the one that tries to close IrLAP. It will be + * removed later and moved to the list of unconnected LSAPs + */ + if (HASHBIN_GET_SIZE(self->lsaps) > 0) { + /* Timer value is checked in irsysctl - Jean II */ + irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000); + } else { + /* No more connections, so close IrLAP */ + + /* We don't want to change state just yet, because + * we want to reflect accurately the real state of + * the LAP, not the state we wish it was in, + * so that we don't lose LM_LAP_CONNECT_REQUEST. + * In some cases, IrLAP won't close the LAP + * immediately. For example, it might still be + * retrying packets or waiting for the pf bit. + * As the LAP always send a DISCONNECT_INDICATION + * in PCLOSE or SCLOSE, just change state on that. + * Jean II */ + irlap_disconnect_request(self->irlap); + } + break; + case LM_LAP_IDLE_TIMEOUT: + if (HASHBIN_GET_SIZE(self->lsaps) == 0) { + /* Same reasoning as above - keep state */ + irlap_disconnect_request(self->irlap); + } + break; + case LM_LAP_DISCONNECT_INDICATION: + irlmp_next_lap_state(self, LAP_STANDBY); + + /* In some case, at this point our side has already closed + * all lsaps, and we are waiting for the idle_timer to + * expire. If another device reconnect immediately, the + * idle timer will expire in the midle of the connection + * initialisation, screwing up things a lot... + * Therefore, we must stop the timer... */ + irlmp_stop_idle_timer(self); + + /* + * Inform all connected LSAP's using this link + */ + irlmp_do_all_lsap_event(self->lsaps, + LM_LAP_DISCONNECT_INDICATION); + + /* Force an expiry of the discovery log. + * Now that the LAP is free, the system may attempt to + * connect to another device. Unfortunately, our entries + * are stale. There is a small window (<3s) before the + * normal discovery will run and where irlmp_connect_request() + * can get the wrong info, so make sure things get + * cleaned *NOW* ;-) - Jean II */ + irlmp_do_expiry(); + break; + default: + pr_debug("%s(), Unknown event %s\n", + __func__, irlmp_event[event]); + break; + } +} + +/********************************************************************* + * + * LSAP connection control states + * + ********************************************************************/ + +/* + * Function irlmp_state_disconnected (event, skb, info) + * + * DISCONNECTED + * + */ +static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); + + switch (event) { +#ifdef CONFIG_IRDA_ULTRA + case LM_UDATA_INDICATION: + /* This is most bizarre. Those packets are aka unreliable + * connected, aka IrLPT or SOCK_DGRAM/IRDAPROTO_UNITDATA. + * Why do we pass them as Ultra ??? Jean II */ + irlmp_connless_data_indication(self, skb); + break; +#endif /* CONFIG_IRDA_ULTRA */ + case LM_CONNECT_REQUEST: + pr_debug("%s(), LM_CONNECT_REQUEST\n", __func__); + + if (self->conn_skb) { + net_warn_ratelimited("%s: busy with another request!\n", + __func__); + return -EBUSY; + } + /* Don't forget to refcount it (see irlmp_connect_request()) */ + skb_get(skb); + self->conn_skb = skb; + + irlmp_next_lsap_state(self, LSAP_SETUP_PEND); + + /* Start watchdog timer (5 secs for now) */ + irlmp_start_watchdog_timer(self, 5*HZ); + + irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL); + break; + case LM_CONNECT_INDICATION: + if (self->conn_skb) { + net_warn_ratelimited("%s: busy with another request!\n", + __func__); + return -EBUSY; + } + /* Don't forget to refcount it (see irlap_driver_rcv()) */ + skb_get(skb); + self->conn_skb = skb; + + irlmp_next_lsap_state(self, LSAP_CONNECT_PEND); + + /* Start watchdog timer + * This is not mentionned in the spec, but there is a rare + * race condition that can get the socket stuck. + * If we receive this event while our LAP is closing down, + * the LM_LAP_CONNECT_REQUEST get lost and we get stuck in + * CONNECT_PEND state forever. + * The other cause of getting stuck down there is if the + * higher layer never reply to the CONNECT_INDICATION. + * Anyway, it make sense to make sure that we always have + * a backup plan. 1 second is plenty (should be immediate). + * Jean II */ + irlmp_start_watchdog_timer(self, 1*HZ); + + irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL); + break; + default: + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); + break; + } + return ret; +} + +/* + * Function irlmp_state_connect (self, event, skb) + * + * CONNECT + * + */ +static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + struct lsap_cb *lsap; + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); + + switch (event) { + case LM_CONNECT_RESPONSE: + /* + * Bind this LSAP to the IrLAP link where the connect was + * received + */ + lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, + NULL); + + IRDA_ASSERT(lsap == self, return -1;); + IRDA_ASSERT(self->lap != NULL, return -1;); + IRDA_ASSERT(self->lap->lsaps != NULL, return -1;); + + hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, + (long) self, NULL); + + set_bit(0, &self->connected); /* TRUE */ + + irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, + self->slsap_sel, CONNECT_CNF, skb); + + del_timer(&self->watchdog_timer); + + irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY); + break; + case LM_WATCHDOG_TIMEOUT: + /* May happen, who knows... + * Jean II */ + pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); + + /* Disconnect, get out... - Jean II */ + self->lap = NULL; + self->dlsap_sel = LSAP_ANY; + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + break; + default: + /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we + * are *not* yet bound to the IrLAP link. Jean II */ + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); + break; + } + return ret; +} + +/* + * Function irlmp_state_connect_pend (event, skb, info) + * + * CONNECT_PEND + * + */ +static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + struct sk_buff *tx_skb; + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); + + switch (event) { + case LM_CONNECT_REQUEST: + /* Keep state */ + break; + case LM_CONNECT_RESPONSE: + pr_debug("%s(), LM_CONNECT_RESPONSE, no indication issued yet\n", + __func__); + /* Keep state */ + break; + case LM_DISCONNECT_REQUEST: + pr_debug("%s(), LM_DISCONNECT_REQUEST, not yet bound to IrLAP connection\n", + __func__); + /* Keep state */ + break; + case LM_LAP_CONNECT_CONFIRM: + pr_debug("%s(), LS_CONNECT_CONFIRM\n", __func__); + irlmp_next_lsap_state(self, LSAP_CONNECT); + + tx_skb = self->conn_skb; + self->conn_skb = NULL; + + irlmp_connect_indication(self, tx_skb); + /* Drop reference count - see irlmp_connect_indication(). */ + dev_kfree_skb(tx_skb); + break; + case LM_WATCHDOG_TIMEOUT: + /* Will happen in some rare cases because of a race condition. + * Just make sure we don't stay there forever... + * Jean II */ + pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); + + /* Go back to disconnected mode, keep the socket waiting */ + self->lap = NULL; + self->dlsap_sel = LSAP_ANY; + if(self->conn_skb) + dev_kfree_skb(self->conn_skb); + self->conn_skb = NULL; + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + break; + default: + /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we + * are *not* yet bound to the IrLAP link. Jean II */ + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); + break; + } + return ret; +} + +/* + * Function irlmp_state_dtr (self, event, skb) + * + * DATA_TRANSFER_READY + * + */ +static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + LM_REASON reason; + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); + IRDA_ASSERT(self->lap != NULL, return -1;); + + switch (event) { + case LM_DATA_REQUEST: /* Optimize for the common case */ + irlmp_send_data_pdu(self->lap, self->dlsap_sel, + self->slsap_sel, FALSE, skb); + break; + case LM_DATA_INDICATION: /* Optimize for the common case */ + irlmp_data_indication(self, skb); + break; + case LM_UDATA_REQUEST: + IRDA_ASSERT(skb != NULL, return -1;); + irlmp_send_data_pdu(self->lap, self->dlsap_sel, + self->slsap_sel, TRUE, skb); + break; + case LM_UDATA_INDICATION: + irlmp_udata_indication(self, skb); + break; + case LM_CONNECT_REQUEST: + pr_debug("%s(), LM_CONNECT_REQUEST, error, LSAP already connected\n", + __func__); + /* Keep state */ + break; + case LM_CONNECT_RESPONSE: + pr_debug("%s(), LM_CONNECT_RESPONSE, error, LSAP already connected\n", + __func__); + /* Keep state */ + break; + case LM_DISCONNECT_REQUEST: + irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, self->slsap_sel, + DISCONNECT, skb); + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + /* Called only from irlmp_disconnect_request(), will + * unbind from LAP over there. Jean II */ + + /* Try to close the LAP connection if its still there */ + if (self->lap) { + pr_debug("%s(), trying to close IrLAP\n", + __func__); + irlmp_do_lap_event(self->lap, + LM_LAP_DISCONNECT_REQUEST, + NULL); + } + break; + case LM_LAP_DISCONNECT_INDICATION: + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + + reason = irlmp_convert_lap_reason(self->lap->reason); + + irlmp_disconnect_indication(self, reason, NULL); + break; + case LM_DISCONNECT_INDICATION: + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + + IRDA_ASSERT(self->lap != NULL, return -1;); + IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); + + IRDA_ASSERT(skb != NULL, return -1;); + IRDA_ASSERT(skb->len > 3, return -1;); + reason = skb->data[3]; + + /* Try to close the LAP connection */ + pr_debug("%s(), trying to close IrLAP\n", __func__); + irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); + + irlmp_disconnect_indication(self, reason, skb); + break; + default: + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); + break; + } + return ret; +} + +/* + * Function irlmp_state_setup (event, skb, info) + * + * SETUP, Station Control has set up the underlying IrLAP connection. + * An LSAP connection request has been transmitted to the peer + * LSAP-Connection Control FSM and we are awaiting reply. + */ +static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + LM_REASON reason; + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); + + switch (event) { + case LM_CONNECT_CONFIRM: + irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY); + + del_timer(&self->watchdog_timer); + + irlmp_connect_confirm(self, skb); + break; + case LM_DISCONNECT_INDICATION: + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + + IRDA_ASSERT(self->lap != NULL, return -1;); + IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); + + IRDA_ASSERT(skb != NULL, return -1;); + IRDA_ASSERT(skb->len > 3, return -1;); + reason = skb->data[3]; + + /* Try to close the LAP connection */ + pr_debug("%s(), trying to close IrLAP\n", __func__); + irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); + + irlmp_disconnect_indication(self, reason, skb); + break; + case LM_LAP_DISCONNECT_INDICATION: + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + + del_timer(&self->watchdog_timer); + + IRDA_ASSERT(self->lap != NULL, return -1;); + IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); + + reason = irlmp_convert_lap_reason(self->lap->reason); + + irlmp_disconnect_indication(self, reason, skb); + break; + case LM_WATCHDOG_TIMEOUT: + pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); + + IRDA_ASSERT(self->lap != NULL, return -1;); + irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + + irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL); + break; + default: + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); + break; + } + return ret; +} + +/* + * Function irlmp_state_setup_pend (event, skb, info) + * + * SETUP_PEND, An LM_CONNECT_REQUEST has been received from the service + * user to set up an LSAP connection. A request has been sent to the + * LAP FSM to set up the underlying IrLAP connection, and we + * are awaiting confirm. + */ +static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event, + struct sk_buff *skb) +{ + struct sk_buff *tx_skb; + LM_REASON reason; + int ret = 0; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(irlmp != NULL, return -1;); + + switch (event) { + case LM_LAP_CONNECT_CONFIRM: + IRDA_ASSERT(self->conn_skb != NULL, return -1;); + + tx_skb = self->conn_skb; + self->conn_skb = NULL; + + irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, + self->slsap_sel, CONNECT_CMD, tx_skb); + /* Drop reference count - see irlap_data_request(). */ + dev_kfree_skb(tx_skb); + + irlmp_next_lsap_state(self, LSAP_SETUP); + break; + case LM_WATCHDOG_TIMEOUT: + pr_debug("%s() : WATCHDOG_TIMEOUT !\n", __func__); + + IRDA_ASSERT(self->lap != NULL, return -1;); + irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + + irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL); + break; + case LM_LAP_DISCONNECT_INDICATION: /* LS_Disconnect.indication */ + del_timer( &self->watchdog_timer); + + irlmp_next_lsap_state(self, LSAP_DISCONNECTED); + + reason = irlmp_convert_lap_reason(self->lap->reason); + + irlmp_disconnect_indication(self, reason, NULL); + break; + default: + pr_debug("%s(), Unknown event %s on LSAP %#02x\n", + __func__, irlmp_event[event], self->slsap_sel); + break; + } + return ret; +} diff --git a/drivers/staging/irda/net/irlmp_frame.c b/drivers/staging/irda/net/irlmp_frame.c new file mode 100644 index 000000000000..38b0f994bc7b --- /dev/null +++ b/drivers/staging/irda/net/irlmp_frame.c @@ -0,0 +1,476 @@ +/********************************************************************* + * + * Filename: irlmp_frame.c + * Version: 0.9 + * Description: IrLMP frame implementation + * Status: Experimental. + * Author: Dag Brattli + * Created at: Tue Aug 19 02:09:59 1997 + * Modified at: Mon Dec 13 13:41:12 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999 Dag Brattli + * All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include + +#include +#include +#include +#include +#include +#include + +static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap, + __u8 slsap, int status, hashbin_t *); + +inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, + int expedited, struct sk_buff *skb) +{ + skb->data[0] = dlsap; + skb->data[1] = slsap; + + if (expedited) { + pr_debug("%s(), sending expedited data\n", __func__); + irlap_data_request(self->irlap, skb, TRUE); + } else + irlap_data_request(self->irlap, skb, FALSE); +} + +/* + * Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb) + * + * Send Link Control Frame to IrLAP + */ +void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, + __u8 opcode, struct sk_buff *skb) +{ + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + frame = skb->data; + + frame[0] = dlsap | CONTROL_BIT; + frame[1] = slsap; + + frame[2] = opcode; + + if (opcode == DISCONNECT) + frame[3] = 0x01; /* Service user request */ + else + frame[3] = 0x00; /* rsvd */ + + irlap_data_request(self->irlap, skb, FALSE); +} + +/* + * Function irlmp_input (skb) + * + * Used by IrLAP to pass received data frames to IrLMP layer + * + */ +void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, + int unreliable) +{ + struct lsap_cb *lsap; + __u8 slsap_sel; /* Source (this) LSAP address */ + __u8 dlsap_sel; /* Destination LSAP address */ + __u8 *fp; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); + IRDA_ASSERT(skb->len > 2, return;); + + fp = skb->data; + + /* + * The next statements may be confusing, but we do this so that + * destination LSAP of received frame is source LSAP in our view + */ + slsap_sel = fp[0] & LSAP_MASK; + dlsap_sel = fp[1]; + + /* + * Check if this is an incoming connection, since we must deal with + * it in a different way than other established connections. + */ + if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) { + pr_debug("%s(), incoming connection, source LSAP=%d, dest LSAP=%d\n", + __func__, slsap_sel, dlsap_sel); + + /* Try to find LSAP among the unconnected LSAPs */ + lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD, + irlmp->unconnected_lsaps); + + /* Maybe LSAP was already connected, so try one more time */ + if (!lsap) { + pr_debug("%s(), incoming connection for LSAP already connected\n", + __func__); + lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, + self->lsaps); + } + } else + lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, + self->lsaps); + + if (lsap == NULL) { + pr_debug("IrLMP, Sorry, no LSAP for received frame!\n"); + pr_debug("%s(), slsap_sel = %02x, dlsap_sel = %02x\n", + __func__, slsap_sel, dlsap_sel); + if (fp[0] & CONTROL_BIT) { + pr_debug("%s(), received control frame %02x\n", + __func__, fp[2]); + } else { + pr_debug("%s(), received data frame\n", __func__); + } + return; + } + + /* + * Check if we received a control frame? + */ + if (fp[0] & CONTROL_BIT) { + switch (fp[2]) { + case CONNECT_CMD: + lsap->lap = self; + irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb); + break; + case CONNECT_CNF: + irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb); + break; + case DISCONNECT: + pr_debug("%s(), Disconnect indication!\n", + __func__); + irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION, + skb); + break; + case ACCESSMODE_CMD: + pr_debug("Access mode cmd not implemented!\n"); + break; + case ACCESSMODE_CNF: + pr_debug("Access mode cnf not implemented!\n"); + break; + default: + pr_debug("%s(), Unknown control frame %02x\n", + __func__, fp[2]); + break; + } + } else if (unreliable) { + /* Optimize and bypass the state machine if possible */ + if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY) + irlmp_udata_indication(lsap, skb); + else + irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb); + } else { + /* Optimize and bypass the state machine if possible */ + if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY) + irlmp_data_indication(lsap, skb); + else + irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb); + } +} + +/* + * Function irlmp_link_unitdata_indication (self, skb) + * + * + * + */ +#ifdef CONFIG_IRDA_ULTRA +void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb) +{ + struct lsap_cb *lsap; + __u8 slsap_sel; /* Source (this) LSAP address */ + __u8 dlsap_sel; /* Destination LSAP address */ + __u8 pid; /* Protocol identifier */ + __u8 *fp; + unsigned long flags; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); + IRDA_ASSERT(skb->len > 2, return;); + + fp = skb->data; + + /* + * The next statements may be confusing, but we do this so that + * destination LSAP of received frame is source LSAP in our view + */ + slsap_sel = fp[0] & LSAP_MASK; + dlsap_sel = fp[1]; + pid = fp[2]; + + if (pid & 0x80) { + pr_debug("%s(), extension in PID not supp!\n", + __func__); + return; + } + + /* Check if frame is addressed to the connectionless LSAP */ + if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) { + pr_debug("%s(), dropping frame!\n", __func__); + return; + } + + /* Search the connectionless LSAP */ + spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); + lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps); + while (lsap != NULL) { + /* + * Check if source LSAP and dest LSAP selectors and PID match. + */ + if ((lsap->slsap_sel == slsap_sel) && + (lsap->dlsap_sel == dlsap_sel) && + (lsap->pid == pid)) + { + break; + } + lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps); + } + spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); + + if (lsap) + irlmp_connless_data_indication(lsap, skb); + else { + pr_debug("%s(), found no matching LSAP!\n", __func__); + } +} +#endif /* CONFIG_IRDA_ULTRA */ + +/* + * Function irlmp_link_disconnect_indication (reason, userdata) + * + * IrLAP has disconnected + * + */ +void irlmp_link_disconnect_indication(struct lap_cb *lap, + struct irlap_cb *irlap, + LAP_REASON reason, + struct sk_buff *skb) +{ + IRDA_ASSERT(lap != NULL, return;); + IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); + + lap->reason = reason; + lap->daddr = DEV_ADDR_ANY; + + /* FIXME: must do something with the skb if any */ + + /* + * Inform station state machine + */ + irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL); +} + +/* + * Function irlmp_link_connect_indication (qos) + * + * Incoming LAP connection! + * + */ +void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr, + __u32 daddr, struct qos_info *qos, + struct sk_buff *skb) +{ + /* Copy QoS settings for this session */ + self->qos = qos; + + /* Update destination device address */ + self->daddr = daddr; + IRDA_ASSERT(self->saddr == saddr, return;); + + irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb); +} + +/* + * Function irlmp_link_connect_confirm (qos) + * + * LAP connection confirmed! + * + */ +void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, + struct sk_buff *skb) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); + IRDA_ASSERT(qos != NULL, return;); + + /* Don't need use the skb for now */ + + /* Copy QoS settings for this session */ + self->qos = qos; + + irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL); +} + +/* + * Function irlmp_link_discovery_indication (self, log) + * + * Device is discovering us + * + * It's not an answer to our own discoveries, just another device trying + * to perform discovery, but we don't want to miss the opportunity + * to exploit this information, because : + * o We may not actively perform discovery (just passive discovery) + * o This type of discovery is much more reliable. In some cases, it + * seem that less than 50% of our discoveries get an answer, while + * we always get ~100% of these. + * o Make faster discovery, statistically divide time of discovery + * events by 2 (important for the latency aspect and user feel) + * o Even is we do active discovery, the other node might not + * answer our discoveries (ex: Palm). The Palm will just perform + * one active discovery and connect directly to us. + * + * However, when both devices discover each other, they might attempt to + * connect to each other following the discovery event, and it would create + * collisions on the medium (SNRM battle). + * The "fix" for that is to disable all connection requests in IrLAP + * for 100ms after a discovery indication by setting the media_busy flag. + * Previously, we used to postpone the event which was quite ugly. Now + * that IrLAP takes care of this problem, just pass the event up... + * + * Jean II + */ +void irlmp_link_discovery_indication(struct lap_cb *self, + discovery_t *discovery) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); + + /* Add to main log, cleanup */ + irlmp_add_discovery(irlmp->cachelog, discovery); + + /* Just handle it the same way as a discovery confirm, + * bypass the LM_LAP state machine (see below) */ + irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_PASSIVE); +} + +/* + * Function irlmp_link_discovery_confirm (self, log) + * + * Called by IrLAP with a list of discoveries after the discovery + * request has been carried out. A NULL log is received if IrLAP + * was unable to carry out the discovery request + * + */ +void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); + + /* Add to main log, cleanup */ + irlmp_add_discovery_log(irlmp->cachelog, log); + + /* Propagate event to various LSAPs registered for it. + * We bypass the LM_LAP state machine because + * 1) We do it regardless of the LM_LAP state + * 2) It doesn't affect the LM_LAP state + * 3) Faster, slimer, simpler, ... + * Jean II */ + irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_ACTIVE); +} + +#ifdef CONFIG_IRDA_CACHE_LAST_LSAP +static inline void irlmp_update_cache(struct lap_cb *lap, + struct lsap_cb *lsap) +{ + /* Prevent concurrent read to get garbage */ + lap->cache.valid = FALSE; + /* Update cache entry */ + lap->cache.dlsap_sel = lsap->dlsap_sel; + lap->cache.slsap_sel = lsap->slsap_sel; + lap->cache.lsap = lsap; + lap->cache.valid = TRUE; +} +#endif + +/* + * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue) + * + * Find handle associated with destination and source LSAP + * + * Any IrDA connection (LSAP/TSAP) is uniquely identified by + * 3 parameters, the local lsap, the remote lsap and the remote address. + * We may initiate multiple connections to the same remote service + * (they will have different local lsap), a remote device may initiate + * multiple connections to the same local service (they will have + * different remote lsap), or multiple devices may connect to the same + * service and may use the same remote lsap (and they will have + * different remote address). + * So, where is the remote address ? Each LAP connection is made with + * a single remote device, so imply a specific remote address. + * Jean II + */ +static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel, + __u8 slsap_sel, int status, + hashbin_t *queue) +{ + struct lsap_cb *lsap; + unsigned long flags; + + /* + * Optimize for the common case. We assume that the last frame + * received is in the same connection as the last one, so check in + * cache first to avoid the linear search + */ +#ifdef CONFIG_IRDA_CACHE_LAST_LSAP + if ((self->cache.valid) && + (self->cache.slsap_sel == slsap_sel) && + (self->cache.dlsap_sel == dlsap_sel)) + { + return self->cache.lsap; + } +#endif + + spin_lock_irqsave(&queue->hb_spinlock, flags); + + lsap = (struct lsap_cb *) hashbin_get_first(queue); + while (lsap != NULL) { + /* + * If this is an incoming connection, then the destination + * LSAP selector may have been specified as LM_ANY so that + * any client can connect. In that case we only need to check + * if the source LSAP (in our view!) match! + */ + if ((status == CONNECT_CMD) && + (lsap->slsap_sel == slsap_sel) && + (lsap->dlsap_sel == LSAP_ANY)) { + /* This is where the dest lsap sel is set on incoming + * lsaps */ + lsap->dlsap_sel = dlsap_sel; + break; + } + /* + * Check if source LSAP and dest LSAP selectors match. + */ + if ((lsap->slsap_sel == slsap_sel) && + (lsap->dlsap_sel == dlsap_sel)) + break; + + lsap = (struct lsap_cb *) hashbin_get_next(queue); + } +#ifdef CONFIG_IRDA_CACHE_LAST_LSAP + if(lsap) + irlmp_update_cache(self, lsap); +#endif + spin_unlock_irqrestore(&queue->hb_spinlock, flags); + + /* Return what we've found or NULL */ + return lsap; +} diff --git a/drivers/staging/irda/net/irmod.c b/drivers/staging/irda/net/irmod.c new file mode 100644 index 000000000000..c5e35b85c477 --- /dev/null +++ b/drivers/staging/irda/net/irmod.c @@ -0,0 +1,199 @@ +/********************************************************************* + * + * Filename: irmod.c + * Version: 0.9 + * Description: IrDA stack main entry points + * Status: Experimental. + * Author: Dag Brattli + * Created at: Mon Dec 15 13:55:39 1997 + * Modified at: Wed Jan 5 15:12:41 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1997, 1999-2000 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2004 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +/* + * This file contains the main entry points of the IrDA stack. + * They are in this file and not af_irda.c because some developpers + * are using the IrDA stack without the socket API (compiling out + * af_irda.c). + * Jean II + */ + +#include +#include + +#include +#include /* notify_t */ +#include /* irlap_init */ +#include /* irlmp_init */ +#include /* iriap_init */ +#include /* irttp_init */ +#include /* irda_device_init */ + +/* Packet type handler. + * Tell the kernel how IrDA packets should be handled. + */ +static struct packet_type irda_packet_type __read_mostly = { + .type = cpu_to_be16(ETH_P_IRDA), + .func = irlap_driver_rcv, /* Packet type handler irlap_frame.c */ +}; + +/* + * Function irda_notify_init (notify) + * + * Used for initializing the notify structure + * + */ +void irda_notify_init(notify_t *notify) +{ + notify->data_indication = NULL; + notify->udata_indication = NULL; + notify->connect_confirm = NULL; + notify->connect_indication = NULL; + notify->disconnect_indication = NULL; + notify->flow_indication = NULL; + notify->status_indication = NULL; + notify->instance = NULL; + strlcpy(notify->name, "Unknown", sizeof(notify->name)); +} +EXPORT_SYMBOL(irda_notify_init); + +/* + * Function irda_init (void) + * + * Protocol stack initialisation entry point. + * Initialise the various components of the IrDA stack + */ +static int __init irda_init(void) +{ + int ret = 0; + + /* Lower layer of the stack */ + irlmp_init(); + irlap_init(); + + /* Driver/dongle support */ + irda_device_init(); + + /* Higher layers of the stack */ + iriap_init(); + irttp_init(); + ret = irsock_init(); + if (ret < 0) + goto out_err_1; + + /* Add IrDA packet type (Start receiving packets) */ + dev_add_pack(&irda_packet_type); + + /* External APIs */ +#ifdef CONFIG_PROC_FS + irda_proc_register(); +#endif +#ifdef CONFIG_SYSCTL + ret = irda_sysctl_register(); + if (ret < 0) + goto out_err_2; +#endif + + ret = irda_nl_register(); + if (ret < 0) + goto out_err_3; + + return 0; + + out_err_3: +#ifdef CONFIG_SYSCTL + irda_sysctl_unregister(); + out_err_2: +#endif +#ifdef CONFIG_PROC_FS + irda_proc_unregister(); +#endif + + /* Remove IrDA packet type (stop receiving packets) */ + dev_remove_pack(&irda_packet_type); + + /* Remove higher layers */ + irsock_cleanup(); + out_err_1: + irttp_cleanup(); + iriap_cleanup(); + + /* Remove lower layers */ + irda_device_cleanup(); + irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */ + + /* Remove middle layer */ + irlmp_cleanup(); + + + return ret; +} + +/* + * Function irda_cleanup (void) + * + * Protocol stack cleanup/removal entry point. + * Cleanup the various components of the IrDA stack + */ +static void __exit irda_cleanup(void) +{ + /* Remove External APIs */ + irda_nl_unregister(); + +#ifdef CONFIG_SYSCTL + irda_sysctl_unregister(); +#endif +#ifdef CONFIG_PROC_FS + irda_proc_unregister(); +#endif + + /* Remove IrDA packet type (stop receiving packets) */ + dev_remove_pack(&irda_packet_type); + + /* Remove higher layers */ + irsock_cleanup(); + irttp_cleanup(); + iriap_cleanup(); + + /* Remove lower layers */ + irda_device_cleanup(); + irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */ + + /* Remove middle layer */ + irlmp_cleanup(); +} + +/* + * The IrDA stack must be initialised *before* drivers get initialised, + * and *before* higher protocols (IrLAN/IrCOMM/IrNET) get initialised, + * otherwise bad things will happen (hashbins will be NULL for example). + * Those modules are at module_init()/device_initcall() level. + * + * On the other hand, it needs to be initialised *after* the basic + * networking, the /proc/net filesystem and sysctl module. Those are + * currently initialised in .../init/main.c (before initcalls). + * Also, IrDA drivers needs to be initialised *after* the random number + * generator (main stack and higher layer init don't need it anymore). + * + * Jean II + */ +subsys_initcall(irda_init); +module_exit(irda_cleanup); + +MODULE_AUTHOR("Dag Brattli & Jean Tourrilhes "); +MODULE_DESCRIPTION("The Linux IrDA Protocol Stack"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_NETPROTO(PF_IRDA); diff --git a/drivers/staging/irda/net/irnet/Kconfig b/drivers/staging/irda/net/irnet/Kconfig new file mode 100644 index 000000000000..28c557f0fdd2 --- /dev/null +++ b/drivers/staging/irda/net/irnet/Kconfig @@ -0,0 +1,13 @@ +config IRNET + tristate "IrNET protocol" + depends on IRDA && PPP + help + Say Y here if you want to build support for the IrNET protocol. + To compile it as a module, choose M here: the module will be + called irnet. IrNET is a PPP driver, so you will also need a + working PPP subsystem (driver, daemon and config)... + + IrNET is an alternate way to transfer TCP/IP traffic over IrDA. It + uses synchronous PPP over a set of point to point IrDA sockets. You + can use it between Linux machine or with W2k. + diff --git a/drivers/staging/irda/net/irnet/Makefile b/drivers/staging/irda/net/irnet/Makefile new file mode 100644 index 000000000000..61c365c8a2a0 --- /dev/null +++ b/drivers/staging/irda/net/irnet/Makefile @@ -0,0 +1,7 @@ +# +# Makefile for the Linux IrDA IrNET protocol layer. +# + +obj-$(CONFIG_IRNET) += irnet.o + +irnet-y := irnet_ppp.o irnet_irda.o diff --git a/drivers/staging/irda/net/irnet/irnet.h b/drivers/staging/irda/net/irnet/irnet.h new file mode 100644 index 000000000000..9d451f8ed47a --- /dev/null +++ b/drivers/staging/irda/net/irnet/irnet.h @@ -0,0 +1,522 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - + * + * This file contains definitions and declarations global to the IrNET module, + * all grouped in one place... + * This file is a *private* header, so other modules don't want to know + * what's in there... + * + * Note : as most part of the Linux kernel, this module is available + * under the GNU General Public License (GPL). + */ + +#ifndef IRNET_H +#define IRNET_H + +/************************** DOCUMENTATION ***************************/ +/* + * What is IrNET + * ------------- + * IrNET is a protocol allowing to carry TCP/IP traffic between two + * IrDA peers in an efficient fashion. It is a thin layer, passing PPP + * packets to IrTTP and vice versa. It uses PPP in synchronous mode, + * because IrTTP offer a reliable sequenced packet service (as opposed + * to a byte stream). In fact, you could see IrNET as carrying TCP/IP + * in a IrDA socket, using PPP to provide the glue. + * + * The main difference with traditional PPP over IrCOMM is that we + * avoid the framing and serial emulation which are a performance + * bottleneck. It also allows multipoint communications in a sensible + * fashion. + * + * The main difference with IrLAN is that we use PPP for the link + * management, which is more standard, interoperable and flexible than + * the IrLAN protocol. For example, PPP adds authentication, + * encryption, compression, header compression and automated routing + * setup. And, as IrNET let PPP do the hard work, the implementation + * is much simpler than IrLAN. + * + * The Linux implementation + * ------------------------ + * IrNET is written on top of the Linux-IrDA stack, and interface with + * the generic Linux PPP driver. Because IrNET depend on recent + * changes of the PPP driver interface, IrNET will work only with very + * recent kernel (2.3.99-pre6 and up). + * + * The present implementation offer the following features : + * o simple user interface using pppd + * o efficient implementation (interface directly to PPP and IrTTP) + * o addressing (you can specify the name of the IrNET recipient) + * o multipoint operation (limited by IrLAP specification) + * o information in /proc/net/irda/irnet + * o IrNET events on /dev/irnet (for user space daemon) + * o IrNET daemon (irnetd) to automatically handle incoming requests + * o Windows 2000 compatibility (tested, but need more work) + * Currently missing : + * o Lot's of testing (that's your job) + * o Connection retries (may be too hard to do) + * o Check pppd persist mode + * o User space daemon (to automatically handle incoming requests) + * + * The setup is not currently the most easy, but this should get much + * better when everything will get integrated... + * + * Acknowledgements + * ---------------- + * This module is based on : + * o The PPP driver (ppp_synctty/ppp_generic) by Paul Mackerras + * o The IrLAN protocol (irlan_common/XXX) by Dag Brattli + * o The IrSock interface (af_irda) by Dag Brattli + * o Some other bits from the kernel and my drivers... + * Infinite thanks to those brave souls for providing the infrastructure + * upon which IrNET is built. + * + * Thanks to all my colleagues in HP for helping me. In particular, + * thanks to Salil Pradhan and Bill Serra for W2k testing... + * Thanks to Luiz Magalhaes for irnetd and much testing... + * + * Thanks to Alan Cox for answering lot's of my stupid questions, and + * to Paul Mackerras answering my questions on how to best integrate + * IrNET and pppd. + * + * Jean II + * + * Note on some implementations choices... + * ------------------------------------ + * 1) Direct interface vs tty/socket + * I could have used a tty interface to hook to ppp and use the full + * socket API to connect to IrDA. The code would have been easier to + * maintain, and maybe the code would have been smaller... + * Instead, we hook directly to ppp_generic and to IrTTP, which make + * things more complicated... + * + * The first reason is flexibility : this allow us to create IrNET + * instances on demand (no /dev/ircommX crap) and to allow linkname + * specification on pppd command line... + * + * Second reason is speed optimisation. If you look closely at the + * transmit and receive paths, you will notice that they are "super lean" + * (that's why they look ugly), with no function calls and as little data + * copy and modification as I could... + * + * 2) irnetd in user space + * irnetd is implemented in user space, which is necessary to call pppd. + * This also give maximum benefits in term of flexibility and customability, + * and allow to offer the event channel, useful for other stuff like debug. + * + * On the other hand, this require a loose coordination between the + * present module and irnetd. One critical area is how incoming request + * are handled. + * When irnet receive an incoming request, it send an event to irnetd and + * drop the incoming IrNET socket. + * irnetd start a pppd instance, which create a new IrNET socket. This new + * socket is then connected in the originating node to the pppd instance. + * At this point, in the originating node, the first socket is closed. + * + * I admit, this is a bit messy and waste some resources. The alternative + * is caching incoming socket, and that's also quite messy and waste + * resources. + * We also make connection time slower. For example, on a 115 kb/s link it + * adds 60ms to the connection time (770 ms). However, this is slower than + * the time it takes to fire up pppd on my P133... + * + * + * History : + * ------- + * + * v1 - 15.5.00 - Jean II + * o Basic IrNET (hook to ppp_generic & IrTTP - incl. multipoint) + * o control channel on /dev/irnet (set name/address) + * o event channel on /dev/irnet (for user space daemon) + * + * v2 - 5.6.00 - Jean II + * o Enable DROP_NOT_READY to avoid PPP timeouts & other weirdness... + * o Add DISCONNECT_TO event and rename DISCONNECT_FROM. + * o Set official device number alloaction on /dev/irnet + * + * v3 - 30.8.00 - Jean II + * o Update to latest Linux-IrDA changes : + * - queue_t => irda_queue_t + * o Update to ppp-2.4.0 : + * - move irda_irnet_connect from PPPIOCATTACH to TIOCSETD + * o Add EXPIRE event (depend on new IrDA-Linux patch) + * o Switch from `hashbin_remove' to `hashbin_remove_this' to fix + * a multilink bug... (depend on new IrDA-Linux patch) + * o fix a self->daddr to self->raddr in irda_irnet_connect to fix + * another multilink bug (darn !) + * o Remove LINKNAME_IOCTL cruft + * + * v3b - 31.8.00 - Jean II + * o Dump discovery log at event channel startup + * + * v4 - 28.9.00 - Jean II + * o Fix interaction between poll/select and dump discovery log + * o Add IRNET_BLOCKED_LINK event (depend on new IrDA-Linux patch) + * o Add IRNET_NOANSWER_FROM event (mostly to help support) + * o Release flow control in disconnect_indication + * o Block packets while connecting (speed up connections) + * + * v5 - 11.01.01 - Jean II + * o Init self->max_header_size, just in case... + * o Set up ap->chan.hdrlen, to get zero copy on tx side working. + * o avoid tx->ttp->flow->ppp->tx->... loop, by checking flow state + * Thanks to Christian Gennerat for finding this bug ! + * --- + * o Declare the proper MTU/MRU that we can support + * (but PPP doesn't read the MTU value :-() + * o Declare hashbin HB_NOLOCK instead of HB_LOCAL to avoid + * disabling and enabling irq twice + * + * v6 - 31.05.01 - Jean II + * o Print source address in Found, Discovery, Expiry & Request events + * o Print requested source address in /proc/net/irnet + * o Change control channel input. Allow multiple commands in one line. + * o Add saddr command to change ap->rsaddr (and use that in IrDA) + * --- + * o Make the IrDA connection procedure totally asynchronous. + * Heavy rewrite of the IAS query code and the whole connection + * procedure. Now, irnet_connect() no longer need to be called from + * a process context... + * o Enable IrDA connect retries in ppp_irnet_send(). The good thing + * is that IrDA connect retries are directly driven by PPP LCP + * retries (we retry for each LCP packet), so that everything + * is transparently controlled from pppd lcp-max-configure. + * o Add ttp_connect flag to prevent rentry on the connect procedure + * o Test and fixups to eliminate side effects of retries + * + * v7 - 22.08.01 - Jean II + * o Cleanup : Change "saddr = 0x0" to "saddr = DEV_ADDR_ANY" + * o Fix bug in BLOCK_WHEN_CONNECT introduced in v6 : due to the + * asynchronous IAS query, self->tsap is NULL when PPP send the + * first packet. This was preventing "connect-delay 0" to work. + * Change the test in ppp_irnet_send() to self->ttp_connect. + * + * v8 - 1.11.01 - Jean II + * o Tighten the use of self->ttp_connect and self->ttp_open to + * prevent various race conditions. + * o Avoid leaking discovery log and skb + * o Replace "self" with "server" in irnet_connect_indication() to + * better detect cut'n'paste error ;-) + * + * v9 - 29.11.01 - Jean II + * o Fix event generation in disconnect indication that I broke in v8 + * It was always generation "No-Answer" because I was testing ttp_open + * just after clearing it. *blush*. + * o Use newly created irttp_listen() to fix potential crash when LAP + * destroyed before irnet module removed. + * + * v10 - 4.3.2 - Jean II + * o When receiving a disconnect indication, don't reenable the + * PPP Tx queue, this will trigger a reconnect. Instead, close + * the channel, which will kill pppd... + * + * v11 - 20.3.02 - Jean II + * o Oops ! v10 fix disabled IrNET retries and passive behaviour. + * Better fix in irnet_disconnect_indication() : + * - if connected, kill pppd via hangup. + * - if not connected, reenable ppp Tx, which trigger IrNET retry. + * + * v12 - 10.4.02 - Jean II + * o Fix race condition in irnet_connect_indication(). + * If the socket was already trying to connect, drop old connection + * and use new one only if acting as primary. See comments. + * + * v13 - 30.5.02 - Jean II + * o Update module init code + * + * v14 - 20.2.03 - Jean II + * o Add discovery hint bits in the control channel. + * o Remove obsolete MOD_INC/DEC_USE_COUNT in favor of .owner + * + * v15 - 7.4.03 - Jean II + * o Replace spin_lock_irqsave() with spin_lock_bh() so that we can + * use ppp_unit_number(). It's probably also better overall... + * o Disable call to ppp_unregister_channel(), because we can't do it. + */ + +/***************************** INCLUDES *****************************/ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include /* isspace() */ +#include /* skip_spaces() */ +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/***************************** OPTIONS *****************************/ +/* + * Define or undefine to compile or not some optional part of the + * IrNET driver... + * Note : the present defaults make sense, play with that at your + * own risk... + */ +/* IrDA side of the business... */ +#define DISCOVERY_NOMASK /* To enable W2k compatibility... */ +#define ADVERTISE_HINT /* Advertise IrLAN hint bit */ +#define ALLOW_SIMULT_CONNECT /* This seem to work, cross fingers... */ +#define DISCOVERY_EVENTS /* Query the discovery log to post events */ +#define INITIAL_DISCOVERY /* Dump current discovery log as events */ +#undef STREAM_COMPAT /* Not needed - potentially messy */ +#undef CONNECT_INDIC_KICK /* Might mess IrDA, not needed */ +#undef FAIL_SEND_DISCONNECT /* Might mess IrDA, not needed */ +#undef PASS_CONNECT_PACKETS /* Not needed ? Safe */ +#undef MISSING_PPP_API /* Stuff I wish I could do */ + +/* PPP side of the business */ +#define BLOCK_WHEN_CONNECT /* Block packets when connecting */ +#define CONNECT_IN_SEND /* Retry IrDA connection procedure */ +#undef FLUSH_TO_PPP /* Not sure about this one, let's play safe */ +#undef SECURE_DEVIRNET /* Bah... */ + +/****************************** DEBUG ******************************/ + +/* + * This set of flags enable and disable all the various warning, + * error and debug message of this driver. + * Each section can be enabled and disabled independently + */ +/* In the PPP part */ +#define DEBUG_CTRL_TRACE 0 /* Control channel */ +#define DEBUG_CTRL_INFO 0 /* various info */ +#define DEBUG_CTRL_ERROR 1 /* problems */ +#define DEBUG_FS_TRACE 0 /* filesystem callbacks */ +#define DEBUG_FS_INFO 0 /* various info */ +#define DEBUG_FS_ERROR 1 /* problems */ +#define DEBUG_PPP_TRACE 0 /* PPP related functions */ +#define DEBUG_PPP_INFO 0 /* various info */ +#define DEBUG_PPP_ERROR 1 /* problems */ +#define DEBUG_MODULE_TRACE 0 /* module insertion/removal */ +#define DEBUG_MODULE_ERROR 1 /* problems */ + +/* In the IrDA part */ +#define DEBUG_IRDA_SR_TRACE 0 /* IRDA subroutines */ +#define DEBUG_IRDA_SR_INFO 0 /* various info */ +#define DEBUG_IRDA_SR_ERROR 1 /* problems */ +#define DEBUG_IRDA_SOCK_TRACE 0 /* IRDA main socket functions */ +#define DEBUG_IRDA_SOCK_INFO 0 /* various info */ +#define DEBUG_IRDA_SOCK_ERROR 1 /* problems */ +#define DEBUG_IRDA_SERV_TRACE 0 /* The IrNET server */ +#define DEBUG_IRDA_SERV_INFO 0 /* various info */ +#define DEBUG_IRDA_SERV_ERROR 1 /* problems */ +#define DEBUG_IRDA_TCB_TRACE 0 /* IRDA IrTTP callbacks */ +#define DEBUG_IRDA_CB_INFO 0 /* various info */ +#define DEBUG_IRDA_CB_ERROR 1 /* problems */ +#define DEBUG_IRDA_OCB_TRACE 0 /* IRDA other callbacks */ +#define DEBUG_IRDA_OCB_INFO 0 /* various info */ +#define DEBUG_IRDA_OCB_ERROR 1 /* problems */ + +#define DEBUG_ASSERT 0 /* Verify all assertions */ + +/* + * These are the macros we are using to actually print the debug + * statements. Don't look at it, it's ugly... + * + * One of the trick is that, as the DEBUG_XXX are constant, the + * compiler will optimise away the if() in all cases. + */ +/* All error messages (will show up in the normal logs) */ +#define DERROR(dbg, format, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_INFO "irnet: %s(): " format, __func__ , ##args);} + +/* Normal debug message (will show up in /var/log/debug) */ +#define DEBUG(dbg, format, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_DEBUG "irnet: %s(): " format, __func__ , ##args);} + +/* Entering a function (trace) */ +#define DENTER(dbg, format, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_DEBUG "irnet: -> %s" format, __func__ , ##args);} + +/* Entering and exiting a function in one go (trace) */ +#define DPASS(dbg, format, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_DEBUG "irnet: <>%s" format, __func__ , ##args);} + +/* Exiting a function (trace) */ +#define DEXIT(dbg, format, args...) \ + {if(DEBUG_##dbg) \ + printk(KERN_DEBUG "irnet: <-%s()" format, __func__ , ##args);} + +/* Exit a function with debug */ +#define DRETURN(ret, dbg, args...) \ + {DEXIT(dbg, ": " args);\ + return ret; } + +/* Exit a function on failed condition */ +#define DABORT(cond, ret, dbg, args...) \ + {if(cond) {\ + DERROR(dbg, args);\ + return ret; }} + +/* Invalid assertion, print out an error and exit... */ +#define DASSERT(cond, ret, dbg, args...) \ + {if((DEBUG_ASSERT) && !(cond)) {\ + DERROR(dbg, "Invalid assertion: " args);\ + return ret; }} + +/************************ CONSTANTS & MACROS ************************/ + +/* Paranoia */ +#define IRNET_MAGIC 0xB00754 + +/* Number of control events in the control channel buffer... */ +#define IRNET_MAX_EVENTS 8 /* Should be more than enough... */ + +/****************************** TYPES ******************************/ + +/* + * This is the main structure where we store all the data pertaining to + * one instance of irnet. + * Note : in irnet functions, a pointer this structure is usually called + * "ap" or "self". If the code is borrowed from the IrDA stack, it tend + * to be called "self", and if it is borrowed from the PPP driver it is + * "ap". Apart from that, it's exactly the same structure ;-) + */ +typedef struct irnet_socket +{ + /* ------------------- Instance management ------------------- */ + /* We manage a linked list of IrNET socket instances */ + irda_queue_t q; /* Must be first - for hasbin */ + int magic; /* Paranoia */ + + /* --------------------- FileSystem part --------------------- */ + /* "pppd" interact directly with us on a /dev/ file */ + struct file * file; /* File descriptor of this instance */ + /* TTY stuff - to keep "pppd" happy */ + struct ktermios termios; /* Various tty flags */ + /* Stuff for the control channel */ + int event_index; /* Last read in the event log */ + + /* ------------------------- PPP part ------------------------- */ + /* We interface directly to the ppp_generic driver in the kernel */ + int ppp_open; /* registered with ppp_generic */ + struct ppp_channel chan; /* Interface to generic ppp layer */ + + int mru; /* Max size of PPP payload */ + u32 xaccm[8]; /* Asynchronous character map (just */ + u32 raccm; /* to please pppd - dummy) */ + unsigned int flags; /* PPP flags (compression, ...) */ + unsigned int rbits; /* Unused receive flags ??? */ + struct work_struct disconnect_work; /* Process context disconnection */ + /* ------------------------ IrTTP part ------------------------ */ + /* We create a pseudo "socket" over the IrDA tranport */ + unsigned long ttp_open; /* Set when IrTTP is ready */ + unsigned long ttp_connect; /* Set when IrTTP is connecting */ + struct tsap_cb * tsap; /* IrTTP instance (the connection) */ + + char rname[NICKNAME_MAX_LEN + 1]; + /* IrDA nickname of destination */ + __u32 rdaddr; /* Requested peer IrDA address */ + __u32 rsaddr; /* Requested local IrDA address */ + __u32 daddr; /* actual peer IrDA address */ + __u32 saddr; /* my local IrDA address */ + __u8 dtsap_sel; /* Remote TSAP selector */ + __u8 stsap_sel; /* Local TSAP selector */ + + __u32 max_sdu_size_rx;/* Socket parameters used for IrTTP */ + __u32 max_sdu_size_tx; + __u32 max_data_size; + __u8 max_header_size; + LOCAL_FLOW tx_flow; /* State of the Tx path in IrTTP */ + + /* ------------------- IrLMP and IrIAS part ------------------- */ + /* Used for IrDA Discovery and socket name resolution */ + void * ckey; /* IrLMP client handle */ + __u16 mask; /* Hint bits mask (filter discov.)*/ + int nslots; /* Number of slots for discovery */ + + struct iriap_cb * iriap; /* Used to query remote IAS */ + int errno; /* status of the IAS query */ + + /* -------------------- Discovery log part -------------------- */ + /* Used by initial discovery on the control channel + * and by irnet_discover_daddr_and_lsap_sel() */ + struct irda_device_info *discoveries; /* Copy of the discovery log */ + int disco_index; /* Last read in the discovery log */ + int disco_number; /* Size of the discovery log */ + + struct mutex lock; + +} irnet_socket; + +/* + * This is the various event that we will generate on the control channel + */ +typedef enum irnet_event +{ + IRNET_DISCOVER, /* New IrNET node discovered */ + IRNET_EXPIRE, /* IrNET node expired */ + IRNET_CONNECT_TO, /* IrNET socket has connected to other node */ + IRNET_CONNECT_FROM, /* Other node has connected to IrNET socket */ + IRNET_REQUEST_FROM, /* Non satisfied connection request */ + IRNET_NOANSWER_FROM, /* Failed connection request */ + IRNET_BLOCKED_LINK, /* Link (IrLAP) is blocked for > 3s */ + IRNET_DISCONNECT_FROM, /* IrNET socket has disconnected */ + IRNET_DISCONNECT_TO /* Closing IrNET socket */ +} irnet_event; + +/* + * This is the storage for an event and its arguments + */ +typedef struct irnet_log +{ + irnet_event event; + int unit; + __u32 saddr; + __u32 daddr; + char name[NICKNAME_MAX_LEN + 1]; /* 21 + 1 */ + __u16_host_order hints; /* Discovery hint bits */ +} irnet_log; + +/* + * This is the storage for all events and related stuff... + */ +typedef struct irnet_ctrl_channel +{ + irnet_log log[IRNET_MAX_EVENTS]; /* Event log */ + int index; /* Current index in log */ + spinlock_t spinlock; /* Serialize access to the event log */ + wait_queue_head_t rwait; /* processes blocked on read (or poll) */ +} irnet_ctrl_channel; + +/**************************** PROTOTYPES ****************************/ +/* + * Global functions of the IrNET module + * Note : we list here also functions called from one file to the other. + */ + +/* -------------------------- IRDA PART -------------------------- */ +int irda_irnet_create(irnet_socket *); /* Initialise an IrNET socket */ +int irda_irnet_connect(irnet_socket *); /* Try to connect over IrDA */ +void irda_irnet_destroy(irnet_socket *); /* Teardown an IrNET socket */ +int irda_irnet_init(void); /* Initialise IrDA part of IrNET */ +void irda_irnet_cleanup(void); /* Teardown IrDA part of IrNET */ + +/**************************** VARIABLES ****************************/ + +/* Control channel stuff - allocated in irnet_irda.h */ +extern struct irnet_ctrl_channel irnet_events; + +#endif /* IRNET_H */ diff --git a/drivers/staging/irda/net/irnet/irnet_irda.c b/drivers/staging/irda/net/irnet/irnet_irda.c new file mode 100644 index 000000000000..e390bceeb2f8 --- /dev/null +++ b/drivers/staging/irda/net/irnet/irnet_irda.c @@ -0,0 +1,1885 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - + * + * This file implement the IRDA interface of IrNET. + * Basically, we sit on top of IrTTP. We set up IrTTP, IrIAS properly, + * and exchange frames with IrTTP. + */ + +#include "irnet_irda.h" /* Private header */ +#include +#include +#include +#include + +/* + * PPP disconnect work: we need to make sure we're in + * process context when calling ppp_unregister_channel(). + */ +static void irnet_ppp_disconnect(struct work_struct *work) +{ + irnet_socket * self = + container_of(work, irnet_socket, disconnect_work); + + if (self == NULL) + return; + /* + * If we were connected, cleanup & close the PPP + * channel, which will kill pppd (hangup) and the rest. + */ + if (self->ppp_open && !self->ttp_open && !self->ttp_connect) { + ppp_unregister_channel(&self->chan); + self->ppp_open = 0; + } +} + +/************************* CONTROL CHANNEL *************************/ +/* + * When ppp is not active, /dev/irnet act as a control channel. + * Writing allow to set up the IrDA destination of the IrNET channel, + * and any application may be read events happening on IrNET... + */ + +/*------------------------------------------------------------------*/ +/* + * Post an event to the control channel... + * Put the event in the log, and then wait all process blocked on read + * so they can read the log... + */ +static void +irnet_post_event(irnet_socket * ap, + irnet_event event, + __u32 saddr, + __u32 daddr, + char * name, + __u16 hints) +{ + int index; /* In the log */ + + DENTER(CTRL_TRACE, "(ap=0x%p, event=%d, daddr=%08x, name=``%s'')\n", + ap, event, daddr, name); + + /* Protect this section via spinlock. + * Note : as we are the only event producer, we only need to exclude + * ourself when touching the log, which is nice and easy. + */ + spin_lock_bh(&irnet_events.spinlock); + + /* Copy the event in the log */ + index = irnet_events.index; + irnet_events.log[index].event = event; + irnet_events.log[index].daddr = daddr; + irnet_events.log[index].saddr = saddr; + /* Try to copy IrDA nickname */ + if(name) + strcpy(irnet_events.log[index].name, name); + else + irnet_events.log[index].name[0] = '\0'; + /* Copy hints */ + irnet_events.log[index].hints.word = hints; + /* Try to get ppp unit number */ + if((ap != (irnet_socket *) NULL) && (ap->ppp_open)) + irnet_events.log[index].unit = ppp_unit_number(&ap->chan); + else + irnet_events.log[index].unit = -1; + + /* Increment the index + * Note that we increment the index only after the event is written, + * to make sure that the readers don't get garbage... */ + irnet_events.index = (index + 1) % IRNET_MAX_EVENTS; + + DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index); + + /* Spin lock end */ + spin_unlock_bh(&irnet_events.spinlock); + + /* Now : wake up everybody waiting for events... */ + wake_up_interruptible_all(&irnet_events.rwait); + + DEXIT(CTRL_TRACE, "\n"); +} + +/************************* IRDA SUBROUTINES *************************/ +/* + * These are a bunch of subroutines called from other functions + * down there, mostly common code or to improve readability... + * + * Note : we duplicate quite heavily some routines of af_irda.c, + * because our input structure (self) is quite different + * (struct irnet instead of struct irda_sock), which make sharing + * the same code impossible (at least, without templates). + */ + +/*------------------------------------------------------------------*/ +/* + * Function irda_open_tsap (self) + * + * Open local Transport Service Access Point (TSAP) + * + * Create a IrTTP instance for us and set all the IrTTP callbacks. + */ +static inline int +irnet_open_tsap(irnet_socket * self) +{ + notify_t notify; /* Callback structure */ + + DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); + + DABORT(self->tsap != NULL, -EBUSY, IRDA_SR_ERROR, "Already busy !\n"); + + /* Initialize IrTTP callbacks to be used by the IrDA stack */ + irda_notify_init(¬ify); + notify.connect_confirm = irnet_connect_confirm; + notify.connect_indication = irnet_connect_indication; + notify.disconnect_indication = irnet_disconnect_indication; + notify.data_indication = irnet_data_indication; + /*notify.udata_indication = NULL;*/ + notify.flow_indication = irnet_flow_indication; + notify.status_indication = irnet_status_indication; + notify.instance = self; + strlcpy(notify.name, IRNET_NOTIFY_NAME, sizeof(notify.name)); + + /* Open an IrTTP instance */ + self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, + ¬ify); + DABORT(self->tsap == NULL, -ENOMEM, + IRDA_SR_ERROR, "Unable to allocate TSAP !\n"); + + /* Remember which TSAP selector we actually got */ + self->stsap_sel = self->tsap->stsap_sel; + + DEXIT(IRDA_SR_TRACE, " - tsap=0x%p, sel=0x%X\n", + self->tsap, self->stsap_sel); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_ias_to_tsap (self, result, value) + * + * Examine an IAS object and extract TSAP + * + * We do an IAP query to find the TSAP associated with the IrNET service. + * When IrIAP pass us the result of the query, this function look at + * the return values to check for failures and extract the TSAP if + * possible. + * Also deallocate value + * The failure is in self->errno + * Return TSAP or -1 + */ +static inline __u8 +irnet_ias_to_tsap(irnet_socket * self, + int result, + struct ias_value * value) +{ + __u8 dtsap_sel = 0; /* TSAP we are looking for */ + + DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); + + /* By default, no error */ + self->errno = 0; + + /* Check if request succeeded */ + switch(result) + { + /* Standard errors : service not available */ + case IAS_CLASS_UNKNOWN: + case IAS_ATTRIB_UNKNOWN: + DEBUG(IRDA_SR_INFO, "IAS object doesn't exist ! (%d)\n", result); + self->errno = -EADDRNOTAVAIL; + break; + + /* Other errors, most likely IrDA stack failure */ + default : + DEBUG(IRDA_SR_INFO, "IAS query failed ! (%d)\n", result); + self->errno = -EHOSTUNREACH; + break; + + /* Success : we got what we wanted */ + case IAS_SUCCESS: + break; + } + + /* Check what was returned to us */ + if(value != NULL) + { + /* What type of argument have we got ? */ + switch(value->type) + { + case IAS_INTEGER: + DEBUG(IRDA_SR_INFO, "result=%d\n", value->t.integer); + if(value->t.integer != -1) + /* Get the remote TSAP selector */ + dtsap_sel = value->t.integer; + else + self->errno = -EADDRNOTAVAIL; + break; + default: + self->errno = -EADDRNOTAVAIL; + DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", value->type); + break; + } + + /* Cleanup */ + irias_delete_value(value); + } + else /* value == NULL */ + { + /* Nothing returned to us - usually result != SUCCESS */ + if(!(self->errno)) + { + DERROR(IRDA_SR_ERROR, + "IrDA bug : result == SUCCESS && value == NULL\n"); + self->errno = -EHOSTUNREACH; + } + } + DEXIT(IRDA_SR_TRACE, "\n"); + + /* Return the TSAP */ + return dtsap_sel; +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_find_lsap_sel (self) + * + * Try to lookup LSAP selector in remote LM-IAS + * + * Basically, we start a IAP query, and then go to sleep. When the query + * return, irnet_getvalue_confirm will wake us up, and we can examine the + * result of the query... + * Note that in some case, the query fail even before we go to sleep, + * creating some races... + */ +static inline int +irnet_find_lsap_sel(irnet_socket * self) +{ + DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); + + /* This should not happen */ + DABORT(self->iriap, -EBUSY, IRDA_SR_ERROR, "busy with a previous query.\n"); + + /* Create an IAP instance, will be closed in irnet_getvalue_confirm() */ + self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, + irnet_getvalue_confirm); + + /* Treat unexpected signals as disconnect */ + self->errno = -EHOSTUNREACH; + + /* Query remote LM-IAS */ + iriap_getvaluebyclass_request(self->iriap, self->rsaddr, self->daddr, + IRNET_SERVICE_NAME, IRNET_IAS_VALUE); + + /* The above request is non-blocking. + * After a while, IrDA will call us back in irnet_getvalue_confirm() + * We will then call irnet_ias_to_tsap() and finish the + * connection procedure */ + + DEXIT(IRDA_SR_TRACE, "\n"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_connect_tsap (self) + * + * Initialise the TTP socket and initiate TTP connection + * + */ +static inline int +irnet_connect_tsap(irnet_socket * self) +{ + int err; + + DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); + + /* Open a local TSAP (an IrTTP instance) */ + err = irnet_open_tsap(self); + if(err != 0) + { + clear_bit(0, &self->ttp_connect); + DERROR(IRDA_SR_ERROR, "connect aborted!\n"); + return err; + } + + /* Connect to remote device */ + err = irttp_connect_request(self->tsap, self->dtsap_sel, + self->rsaddr, self->daddr, NULL, + self->max_sdu_size_rx, NULL); + if(err != 0) + { + clear_bit(0, &self->ttp_connect); + DERROR(IRDA_SR_ERROR, "connect aborted!\n"); + return err; + } + + /* The above call is non-blocking. + * After a while, the IrDA stack will either call us back in + * irnet_connect_confirm() or irnet_disconnect_indication() + * See you there ;-) */ + + DEXIT(IRDA_SR_TRACE, "\n"); + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_discover_next_daddr (self) + * + * Query the IrNET TSAP of the next device in the log. + * + * Used in the TSAP discovery procedure. + */ +static inline int +irnet_discover_next_daddr(irnet_socket * self) +{ + /* Close the last instance of IrIAP, and open a new one. + * We can't reuse the IrIAP instance in the IrIAP callback */ + if(self->iriap) + { + iriap_close(self->iriap); + self->iriap = NULL; + } + /* Create a new IAP instance */ + self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, + irnet_discovervalue_confirm); + if(self->iriap == NULL) + return -ENOMEM; + + /* Next discovery - before the call to avoid races */ + self->disco_index++; + + /* Check if we have one more address to try */ + if(self->disco_index < self->disco_number) + { + /* Query remote LM-IAS */ + iriap_getvaluebyclass_request(self->iriap, + self->discoveries[self->disco_index].saddr, + self->discoveries[self->disco_index].daddr, + IRNET_SERVICE_NAME, IRNET_IAS_VALUE); + /* The above request is non-blocking. + * After a while, IrDA will call us back in irnet_discovervalue_confirm() + * We will then call irnet_ias_to_tsap() and come back here again... */ + return 0; + } + else + return 1; +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_discover_daddr_and_lsap_sel (self) + * + * This try to find a device with the requested service. + * + * Initiate a TSAP discovery procedure. + * It basically look into the discovery log. For each address in the list, + * it queries the LM-IAS of the device to find if this device offer + * the requested service. + * If there is more than one node supporting the service, we complain + * to the user (it should move devices around). + * If we find one node which have the requested TSAP, we connect to it. + * + * This function just start the whole procedure. It request the discovery + * log and submit the first IAS query. + * The bulk of the job is handled in irnet_discovervalue_confirm() + * + * Note : this procedure fails if there is more than one device in range + * on the same dongle, because IrLMP doesn't disconnect the LAP when the + * last LSAP is closed. Moreover, we would need to wait the LAP + * disconnection... + */ +static inline int +irnet_discover_daddr_and_lsap_sel(irnet_socket * self) +{ + int ret; + + DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); + + /* Ask lmp for the current discovery log */ + self->discoveries = irlmp_get_discoveries(&self->disco_number, self->mask, + DISCOVERY_DEFAULT_SLOTS); + + /* Check if the we got some results */ + if(self->discoveries == NULL) + { + self->disco_number = -1; + clear_bit(0, &self->ttp_connect); + DRETURN(-ENETUNREACH, IRDA_SR_INFO, "No Cachelog...\n"); + } + DEBUG(IRDA_SR_INFO, "Got the log (0x%p), size is %d\n", + self->discoveries, self->disco_number); + + /* Start with the first discovery */ + self->disco_index = -1; + self->daddr = DEV_ADDR_ANY; + + /* This will fail if the log is empty - this is non-blocking */ + ret = irnet_discover_next_daddr(self); + if(ret) + { + /* Close IAP */ + if(self->iriap) + iriap_close(self->iriap); + self->iriap = NULL; + + /* Cleanup our copy of the discovery log */ + kfree(self->discoveries); + self->discoveries = NULL; + + clear_bit(0, &self->ttp_connect); + DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n"); + } + + /* Follow me in irnet_discovervalue_confirm() */ + + DEXIT(IRDA_SR_TRACE, "\n"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_dname_to_daddr (self) + * + * Convert an IrDA nickname to a valid IrDA address + * + * It basically look into the discovery log until there is a match. + */ +static inline int +irnet_dname_to_daddr(irnet_socket * self) +{ + struct irda_device_info *discoveries; /* Copy of the discovery log */ + int number; /* Number of nodes in the log */ + int i; + + DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); + + /* Ask lmp for the current discovery log */ + discoveries = irlmp_get_discoveries(&number, 0xffff, + DISCOVERY_DEFAULT_SLOTS); + /* Check if the we got some results */ + if(discoveries == NULL) + DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n"); + + /* + * Now, check all discovered devices (if any), and connect + * client only about the services that the client is + * interested in... + */ + for(i = 0; i < number; i++) + { + /* Does the name match ? */ + if(!strncmp(discoveries[i].info, self->rname, NICKNAME_MAX_LEN)) + { + /* Yes !!! Get it.. */ + self->daddr = discoveries[i].daddr; + DEBUG(IRDA_SR_INFO, "discovered device ``%s'' at address 0x%08x.\n", + self->rname, self->daddr); + kfree(discoveries); + DEXIT(IRDA_SR_TRACE, "\n"); + return 0; + } + } + /* No luck ! */ + DEBUG(IRDA_SR_INFO, "cannot discover device ``%s'' !!!\n", self->rname); + kfree(discoveries); + return -EADDRNOTAVAIL; +} + + +/************************* SOCKET ROUTINES *************************/ +/* + * This are the main operations on IrNET sockets, basically to create + * and destroy IrNET sockets. These are called from the PPP part... + */ + +/*------------------------------------------------------------------*/ +/* + * Create a IrNET instance : just initialise some parameters... + */ +int +irda_irnet_create(irnet_socket * self) +{ + DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self); + + self->magic = IRNET_MAGIC; /* Paranoia */ + + self->ttp_open = 0; /* Prevent higher layer from accessing IrTTP */ + self->ttp_connect = 0; /* Not connecting yet */ + self->rname[0] = '\0'; /* May be set via control channel */ + self->rdaddr = DEV_ADDR_ANY; /* May be set via control channel */ + self->rsaddr = DEV_ADDR_ANY; /* May be set via control channel */ + self->daddr = DEV_ADDR_ANY; /* Until we get connected */ + self->saddr = DEV_ADDR_ANY; /* Until we get connected */ + self->max_sdu_size_rx = TTP_SAR_UNBOUND; + + /* Register as a client with IrLMP */ + self->ckey = irlmp_register_client(0, NULL, NULL, NULL); +#ifdef DISCOVERY_NOMASK + self->mask = 0xffff; /* For W2k compatibility */ +#else /* DISCOVERY_NOMASK */ + self->mask = irlmp_service_to_hint(S_LAN); +#endif /* DISCOVERY_NOMASK */ + self->tx_flow = FLOW_START; /* Flow control from IrTTP */ + + INIT_WORK(&self->disconnect_work, irnet_ppp_disconnect); + + DEXIT(IRDA_SOCK_TRACE, "\n"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Connect to the other side : + * o convert device name to an address + * o find the socket number (dlsap) + * o Establish the connection + * + * Note : We no longer mimic af_irda. The IAS query for finding the TSAP + * is done asynchronously, like the TTP connection. This allow us to + * call this function from any context (not only process). + * The downside is that following what's happening in there is tricky + * because it involve various functions all over the place... + */ +int +irda_irnet_connect(irnet_socket * self) +{ + int err; + + DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self); + + /* Check if we are already trying to connect. + * Because irda_irnet_connect() can be called directly by pppd plus + * packet retries in ppp_generic and connect may take time, plus we may + * race with irnet_connect_indication(), we need to be careful there... */ + if(test_and_set_bit(0, &self->ttp_connect)) + DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\n"); + if((self->iriap != NULL) || (self->tsap != NULL)) + DERROR(IRDA_SOCK_ERROR, "Socket not cleaned up...\n"); + + /* Insert ourselves in the hashbin so that the IrNET server can find us. + * Notes : 4th arg is string of 32 char max and must be null terminated + * When 4th arg is used (string), 3rd arg isn't (int) + * Can't re-insert (MUST remove first) so check for that... */ + if((irnet_server.running) && (self->q.q_next == NULL)) + { + spin_lock_bh(&irnet_server.spinlock); + hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname); + spin_unlock_bh(&irnet_server.spinlock); + DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname); + } + + /* If we don't have anything (no address, no name) */ + if((self->rdaddr == DEV_ADDR_ANY) && (self->rname[0] == '\0')) + { + /* Try to find a suitable address */ + if((err = irnet_discover_daddr_and_lsap_sel(self)) != 0) + DRETURN(err, IRDA_SOCK_INFO, "auto-connect failed!\n"); + /* In most cases, the call above is non-blocking */ + } + else + { + /* If we have only the name (no address), try to get an address */ + if(self->rdaddr == DEV_ADDR_ANY) + { + if((err = irnet_dname_to_daddr(self)) != 0) + DRETURN(err, IRDA_SOCK_INFO, "name connect failed!\n"); + } + else + /* Use the requested destination address */ + self->daddr = self->rdaddr; + + /* Query remote LM-IAS to find LSAP selector */ + irnet_find_lsap_sel(self); + /* The above call is non blocking */ + } + + /* At this point, we are waiting for the IrDA stack to call us back, + * or we have already failed. + * We will finish the connection procedure in irnet_connect_tsap(). + */ + DEXIT(IRDA_SOCK_TRACE, "\n"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_irnet_destroy(self) + * + * Destroy irnet instance + * + * Note : this need to be called from a process context. + */ +void +irda_irnet_destroy(irnet_socket * self) +{ + DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self); + if(self == NULL) + return; + + /* Remove ourselves from hashbin (if we are queued in hashbin) + * Note : `irnet_server.running' protect us from calls in hashbin_delete() */ + if((irnet_server.running) && (self->q.q_next != NULL)) + { + struct irnet_socket * entry; + DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n"); + spin_lock_bh(&irnet_server.spinlock); + entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self); + self->q.q_next = NULL; + spin_unlock_bh(&irnet_server.spinlock); + DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n"); + } + + /* If we were connected, post a message */ + if(test_bit(0, &self->ttp_open)) + { + /* Note : as the disconnect comes from ppp_generic, the unit number + * doesn't exist anymore when we post the event, so we need to pass + * NULL as the first arg... */ + irnet_post_event(NULL, IRNET_DISCONNECT_TO, + self->saddr, self->daddr, self->rname, 0); + } + + /* Prevent various IrDA callbacks from messing up things + * Need to be first */ + clear_bit(0, &self->ttp_connect); + + /* Prevent higher layer from accessing IrTTP */ + clear_bit(0, &self->ttp_open); + + /* Unregister with IrLMP */ + irlmp_unregister_client(self->ckey); + + /* Unregister with LM-IAS */ + if(self->iriap) + { + iriap_close(self->iriap); + self->iriap = NULL; + } + + /* Cleanup eventual discoveries from connection attempt or control channel */ + if(self->discoveries != NULL) + { + /* Cleanup our copy of the discovery log */ + kfree(self->discoveries); + self->discoveries = NULL; + } + + /* Close our IrTTP connection */ + if(self->tsap) + { + DEBUG(IRDA_SOCK_INFO, "Closing our TTP connection.\n"); + irttp_disconnect_request(self->tsap, NULL, P_NORMAL); + irttp_close_tsap(self->tsap); + self->tsap = NULL; + } + self->stsap_sel = 0; + + DEXIT(IRDA_SOCK_TRACE, "\n"); +} + + +/************************** SERVER SOCKET **************************/ +/* + * The IrNET service is composed of one server socket and a variable + * number of regular IrNET sockets. The server socket is supposed to + * handle incoming connections and redirect them to one IrNET sockets. + * It's a superset of the regular IrNET socket, but has a very distinct + * behaviour... + */ + +/*------------------------------------------------------------------*/ +/* + * Function irnet_daddr_to_dname (self) + * + * Convert an IrDA address to a IrDA nickname + * + * It basically look into the discovery log until there is a match. + */ +static inline int +irnet_daddr_to_dname(irnet_socket * self) +{ + struct irda_device_info *discoveries; /* Copy of the discovery log */ + int number; /* Number of nodes in the log */ + int i; + + DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self); + + /* Ask lmp for the current discovery log */ + discoveries = irlmp_get_discoveries(&number, 0xffff, + DISCOVERY_DEFAULT_SLOTS); + /* Check if the we got some results */ + if (discoveries == NULL) + DRETURN(-ENETUNREACH, IRDA_SERV_INFO, "Cachelog empty...\n"); + + /* Now, check all discovered devices (if any) */ + for(i = 0; i < number; i++) + { + /* Does the name match ? */ + if(discoveries[i].daddr == self->daddr) + { + /* Yes !!! Get it.. */ + strlcpy(self->rname, discoveries[i].info, sizeof(self->rname)); + self->rname[sizeof(self->rname) - 1] = '\0'; + DEBUG(IRDA_SERV_INFO, "Device 0x%08x is in fact ``%s''.\n", + self->daddr, self->rname); + kfree(discoveries); + DEXIT(IRDA_SERV_TRACE, "\n"); + return 0; + } + } + /* No luck ! */ + DEXIT(IRDA_SERV_INFO, ": cannot discover device 0x%08x !!!\n", self->daddr); + kfree(discoveries); + return -EADDRNOTAVAIL; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_find_socket (self) + * + * Find the correct IrNET socket + * + * Look into the list of IrNET sockets and finds one with the right + * properties... + */ +static inline irnet_socket * +irnet_find_socket(irnet_socket * self) +{ + irnet_socket * new = (irnet_socket *) NULL; + int err; + + DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self); + + /* Get the addresses of the requester */ + self->daddr = irttp_get_daddr(self->tsap); + self->saddr = irttp_get_saddr(self->tsap); + + /* Try to get the IrDA nickname of the requester */ + err = irnet_daddr_to_dname(self); + + /* Protect access to the instance list */ + spin_lock_bh(&irnet_server.spinlock); + + /* So now, try to get an socket having specifically + * requested that nickname */ + if(err == 0) + { + new = (irnet_socket *) hashbin_find(irnet_server.list, + 0, self->rname); + if(new) + DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches rname ``%s''.\n", + new, new->rname); + } + + /* If no name matches, try to find an socket by the destination address */ + /* It can be either the requested destination address (set via the + * control channel), or the current destination address if the + * socket is in the middle of a connection request */ + if(new == (irnet_socket *) NULL) + { + new = (irnet_socket *) hashbin_get_first(irnet_server.list); + while(new !=(irnet_socket *) NULL) + { + /* Does it have the same address ? */ + if((new->rdaddr == self->daddr) || (new->daddr == self->daddr)) + { + /* Yes !!! Get it.. */ + DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches daddr %#08x.\n", + new, self->daddr); + break; + } + new = (irnet_socket *) hashbin_get_next(irnet_server.list); + } + } + + /* If we don't have any socket, get the first unconnected socket */ + if(new == (irnet_socket *) NULL) + { + new = (irnet_socket *) hashbin_get_first(irnet_server.list); + while(new !=(irnet_socket *) NULL) + { + /* Is it available ? */ + if(!(test_bit(0, &new->ttp_open)) && (new->rdaddr == DEV_ADDR_ANY) && + (new->rname[0] == '\0') && (new->ppp_open)) + { + /* Yes !!! Get it.. */ + DEBUG(IRDA_SERV_INFO, "Socket 0x%p is free.\n", + new); + break; + } + new = (irnet_socket *) hashbin_get_next(irnet_server.list); + } + } + + /* Spin lock end */ + spin_unlock_bh(&irnet_server.spinlock); + + DEXIT(IRDA_SERV_TRACE, " - new = 0x%p\n", new); + return new; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_connect_socket (self) + * + * Connect an incoming connection to the socket + * + */ +static inline int +irnet_connect_socket(irnet_socket * server, + irnet_socket * new, + struct qos_info * qos, + __u32 max_sdu_size, + __u8 max_header_size) +{ + DENTER(IRDA_SERV_TRACE, "(server=0x%p, new=0x%p)\n", + server, new); + + /* Now attach up the new socket */ + new->tsap = irttp_dup(server->tsap, new); + DABORT(new->tsap == NULL, -1, IRDA_SERV_ERROR, "dup failed!\n"); + + /* Set up all the relevant parameters on the new socket */ + new->stsap_sel = new->tsap->stsap_sel; + new->dtsap_sel = new->tsap->dtsap_sel; + new->saddr = irttp_get_saddr(new->tsap); + new->daddr = irttp_get_daddr(new->tsap); + + new->max_header_size = max_header_size; + new->max_sdu_size_tx = max_sdu_size; + new->max_data_size = max_sdu_size; +#ifdef STREAM_COMPAT + /* If we want to receive "stream sockets" */ + if(max_sdu_size == 0) + new->max_data_size = irttp_get_max_seg_size(new->tsap); +#endif /* STREAM_COMPAT */ + + /* Clean up the original one to keep it in listen state */ + irttp_listen(server->tsap); + + /* Send a connection response on the new socket */ + irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL); + + /* Allow PPP to send its junk over the new socket... */ + set_bit(0, &new->ttp_open); + + /* Not connecting anymore, and clean up last possible remains + * of connection attempts on the socket */ + clear_bit(0, &new->ttp_connect); + if(new->iriap) + { + iriap_close(new->iriap); + new->iriap = NULL; + } + if(new->discoveries != NULL) + { + kfree(new->discoveries); + new->discoveries = NULL; + } + +#ifdef CONNECT_INDIC_KICK + /* As currently we don't block packets in ppp_irnet_send() while passive, + * this is not really needed... + * Also, not doing it give IrDA a chance to finish the setup properly + * before being swamped with packets... */ + ppp_output_wakeup(&new->chan); +#endif /* CONNECT_INDIC_KICK */ + + /* Notify the control channel */ + irnet_post_event(new, IRNET_CONNECT_FROM, + new->saddr, new->daddr, server->rname, 0); + + DEXIT(IRDA_SERV_TRACE, "\n"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_disconnect_server (self) + * + * Cleanup the server socket when the incoming connection abort + * + */ +static inline void +irnet_disconnect_server(irnet_socket * self, + struct sk_buff *skb) +{ + DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self); + + /* Put the received packet in the black hole */ + kfree_skb(skb); + +#ifdef FAIL_SEND_DISCONNECT + /* Tell the other party we don't want to be connected */ + /* Hum... Is it the right thing to do ? And do we need to send + * a connect response before ? It looks ok without this... */ + irttp_disconnect_request(self->tsap, NULL, P_NORMAL); +#endif /* FAIL_SEND_DISCONNECT */ + + /* Notify the control channel (see irnet_find_socket()) */ + irnet_post_event(NULL, IRNET_REQUEST_FROM, + self->saddr, self->daddr, self->rname, 0); + + /* Clean up the server to keep it in listen state */ + irttp_listen(self->tsap); + + DEXIT(IRDA_SERV_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_setup_server (self) + * + * Create a IrTTP server and set it up... + * + * Register the IrLAN hint bit, create a IrTTP instance for us, + * set all the IrTTP callbacks and create an IrIAS entry... + */ +static inline int +irnet_setup_server(void) +{ + __u16 hints; + + DENTER(IRDA_SERV_TRACE, "()\n"); + + /* Initialise the regular socket part of the server */ + irda_irnet_create(&irnet_server.s); + + /* Open a local TSAP (an IrTTP instance) for the server */ + irnet_open_tsap(&irnet_server.s); + + /* PPP part setup */ + irnet_server.s.ppp_open = 0; + irnet_server.s.chan.private = NULL; + irnet_server.s.file = NULL; + + /* Get the hint bit corresponding to IrLAN */ + /* Note : we overload the IrLAN hint bit. As it is only a "hint", and as + * we provide roughly the same functionality as IrLAN, this is ok. + * In fact, the situation is similar as JetSend overloading the Obex hint + */ + hints = irlmp_service_to_hint(S_LAN); + +#ifdef ADVERTISE_HINT + /* Register with IrLMP as a service (advertise our hint bit) */ + irnet_server.skey = irlmp_register_service(hints); +#endif /* ADVERTISE_HINT */ + + /* Register with LM-IAS (so that people can connect to us) */ + irnet_server.ias_obj = irias_new_object(IRNET_SERVICE_NAME, jiffies); + irias_add_integer_attrib(irnet_server.ias_obj, IRNET_IAS_VALUE, + irnet_server.s.stsap_sel, IAS_KERNEL_ATTR); + irias_insert_object(irnet_server.ias_obj); + +#ifdef DISCOVERY_EVENTS + /* Tell IrLMP we want to be notified of newly discovered nodes */ + irlmp_update_client(irnet_server.s.ckey, hints, + irnet_discovery_indication, irnet_expiry_indication, + (void *) &irnet_server.s); +#endif + + DEXIT(IRDA_SERV_TRACE, " - self=0x%p\n", &irnet_server.s); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Function irda_destroy_server (self) + * + * Destroy the IrTTP server... + * + * Reverse of the previous function... + */ +static inline void +irnet_destroy_server(void) +{ + DENTER(IRDA_SERV_TRACE, "()\n"); + +#ifdef ADVERTISE_HINT + /* Unregister with IrLMP */ + irlmp_unregister_service(irnet_server.skey); +#endif /* ADVERTISE_HINT */ + + /* Unregister with LM-IAS */ + if(irnet_server.ias_obj) + irias_delete_object(irnet_server.ias_obj); + + /* Cleanup the socket part */ + irda_irnet_destroy(&irnet_server.s); + + DEXIT(IRDA_SERV_TRACE, "\n"); +} + + +/************************ IRDA-TTP CALLBACKS ************************/ +/* + * When we create a IrTTP instance, we pass to it a set of callbacks + * that IrTTP will call in case of various events. + * We take care of those events here. + */ + +/*------------------------------------------------------------------*/ +/* + * Function irnet_data_indication (instance, sap, skb) + * + * Received some data from TinyTP. Just queue it on the receive queue + * + */ +static int +irnet_data_indication(void * instance, + void * sap, + struct sk_buff *skb) +{ + irnet_socket * ap = (irnet_socket *) instance; + unsigned char * p; + int code = 0; + + DENTER(IRDA_TCB_TRACE, "(self/ap=0x%p, skb=0x%p)\n", + ap, skb); + DASSERT(skb != NULL, 0, IRDA_CB_ERROR, "skb is NULL !!!\n"); + + /* Check is ppp is ready to receive our packet */ + if(!ap->ppp_open) + { + DERROR(IRDA_CB_ERROR, "PPP not ready, dropping packet...\n"); + /* When we return error, TTP will need to requeue the skb and + * will stop the sender. IrTTP will stall until we send it a + * flow control request... */ + return -ENOMEM; + } + + /* strip address/control field if present */ + p = skb->data; + if((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI)) + { + /* chop off address/control */ + if(skb->len < 3) + goto err_exit; + p = skb_pull(skb, 2); + } + + /* decompress protocol field if compressed */ + if(p[0] & 1) + { + /* protocol is compressed */ + *(u8 *)skb_push(skb, 1) = 0; + } + else + if(skb->len < 2) + goto err_exit; + + /* pass to generic ppp layer */ + /* Note : how do I know if ppp can accept or not the packet ? This is + * essential if I want to manage flow control smoothly... */ + ppp_input(&ap->chan, skb); + + DEXIT(IRDA_TCB_TRACE, "\n"); + return 0; + + err_exit: + DERROR(IRDA_CB_ERROR, "Packet too small, dropping...\n"); + kfree_skb(skb); + ppp_input_error(&ap->chan, code); + return 0; /* Don't return an error code, only for flow control... */ +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_disconnect_indication (instance, sap, reason, skb) + * + * Connection has been closed. Chech reason to find out why + * + * Note : there are many cases where we come here : + * o attempted to connect, timeout + * o connected, link is broken, LAP has timeout + * o connected, other side close the link + * o connection request on the server not handled + */ +static void +irnet_disconnect_indication(void * instance, + void * sap, + LM_REASON reason, + struct sk_buff *skb) +{ + irnet_socket * self = (irnet_socket *) instance; + int test_open; + int test_connect; + + DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self); + DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); + + /* Don't care about it, but let's not leak it */ + if(skb) + dev_kfree_skb(skb); + + /* Prevent higher layer from accessing IrTTP */ + test_open = test_and_clear_bit(0, &self->ttp_open); + /* Not connecting anymore... + * (note : TSAP is open, so IAP callbacks are no longer pending...) */ + test_connect = test_and_clear_bit(0, &self->ttp_connect); + + /* If both self->ttp_open and self->ttp_connect are NULL, it mean that we + * have a race condition with irda_irnet_destroy() or + * irnet_connect_indication(), so don't mess up tsap... + */ + if(!(test_open || test_connect)) + { + DERROR(IRDA_CB_ERROR, "Race condition detected...\n"); + return; + } + + /* If we were active, notify the control channel */ + if(test_open) + irnet_post_event(self, IRNET_DISCONNECT_FROM, + self->saddr, self->daddr, self->rname, 0); + else + /* If we were trying to connect, notify the control channel */ + if((self->tsap) && (self != &irnet_server.s)) + irnet_post_event(self, IRNET_NOANSWER_FROM, + self->saddr, self->daddr, self->rname, 0); + + /* Close our IrTTP connection, cleanup tsap */ + if((self->tsap) && (self != &irnet_server.s)) + { + DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n"); + irttp_close_tsap(self->tsap); + self->tsap = NULL; + } + /* Cleanup the socket in case we want to reconnect in ppp_output_wakeup() */ + self->stsap_sel = 0; + self->daddr = DEV_ADDR_ANY; + self->tx_flow = FLOW_START; + + /* Deal with the ppp instance if it's still alive */ + if(self->ppp_open) + { + if(test_open) + { + /* ppp_unregister_channel() wants a user context. */ + schedule_work(&self->disconnect_work); + } + else + { + /* If we were trying to connect, flush (drain) ppp_generic + * Tx queue (most often we have blocked it), which will + * trigger an other attempt to connect. If we are passive, + * this will empty the Tx queue after last try. */ + ppp_output_wakeup(&self->chan); + } + } + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_connect_confirm (instance, sap, qos, max_sdu_size, skb) + * + * Connections has been confirmed by the remote device + * + */ +static void +irnet_connect_confirm(void * instance, + void * sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + irnet_socket * self = (irnet_socket *) instance; + + DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self); + + /* Check if socket is closing down (via irda_irnet_destroy()) */ + if(! test_bit(0, &self->ttp_connect)) + { + DERROR(IRDA_CB_ERROR, "Socket no longer connecting. Ouch !\n"); + return; + } + + /* How much header space do we need to reserve */ + self->max_header_size = max_header_size; + + /* IrTTP max SDU size in transmit direction */ + self->max_sdu_size_tx = max_sdu_size; + self->max_data_size = max_sdu_size; +#ifdef STREAM_COMPAT + if(max_sdu_size == 0) + self->max_data_size = irttp_get_max_seg_size(self->tsap); +#endif /* STREAM_COMPAT */ + + /* At this point, IrLMP has assigned our source address */ + self->saddr = irttp_get_saddr(self->tsap); + + /* Allow higher layer to access IrTTP */ + set_bit(0, &self->ttp_open); + clear_bit(0, &self->ttp_connect); /* Not racy, IrDA traffic is serial */ + /* Give a kick in the ass of ppp_generic so that he sends us some data */ + ppp_output_wakeup(&self->chan); + + /* Check size of received packet */ + if(skb->len > 0) + { +#ifdef PASS_CONNECT_PACKETS + DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n"); + /* Try to pass it to PPP */ + irnet_data_indication(instance, sap, skb); +#else /* PASS_CONNECT_PACKETS */ + DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n"); + kfree_skb(skb); /* Note : will be optimised with other kfree... */ +#endif /* PASS_CONNECT_PACKETS */ + } + else + kfree_skb(skb); + + /* Notify the control channel */ + irnet_post_event(self, IRNET_CONNECT_TO, + self->saddr, self->daddr, self->rname, 0); + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_flow_indication (instance, sap, flow) + * + * Used by TinyTP to tell us if it can accept more data or not + * + */ +static void +irnet_flow_indication(void * instance, + void * sap, + LOCAL_FLOW flow) +{ + irnet_socket * self = (irnet_socket *) instance; + LOCAL_FLOW oldflow = self->tx_flow; + + DENTER(IRDA_TCB_TRACE, "(self=0x%p, flow=%d)\n", self, flow); + + /* Update our state */ + self->tx_flow = flow; + + /* Check what IrTTP want us to do... */ + switch(flow) + { + case FLOW_START: + DEBUG(IRDA_CB_INFO, "IrTTP wants us to start again\n"); + /* Check if we really need to wake up PPP */ + if(oldflow == FLOW_STOP) + ppp_output_wakeup(&self->chan); + else + DEBUG(IRDA_CB_INFO, "But we were already transmitting !!!\n"); + break; + case FLOW_STOP: + DEBUG(IRDA_CB_INFO, "IrTTP wants us to slow down\n"); + break; + default: + DEBUG(IRDA_CB_INFO, "Unknown flow command!\n"); + break; + } + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_status_indication (instance, sap, reason, skb) + * + * Link (IrLAP) status report. + * + */ +static void +irnet_status_indication(void * instance, + LINK_STATUS link, + LOCK_STATUS lock) +{ + irnet_socket * self = (irnet_socket *) instance; + + DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self); + DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); + + /* We can only get this event if we are connected */ + switch(link) + { + case STATUS_NO_ACTIVITY: + irnet_post_event(self, IRNET_BLOCKED_LINK, + self->saddr, self->daddr, self->rname, 0); + break; + default: + DEBUG(IRDA_CB_INFO, "Unknown status...\n"); + } + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_connect_indication(instance, sap, qos, max_sdu_size, userdata) + * + * Incoming connection + * + * In theory, this function is called only on the server socket. + * Some other node is attempting to connect to the IrNET service, and has + * sent a connection request on our server socket. + * We just redirect the connection to the relevant IrNET socket. + * + * Note : we also make sure that between 2 irnet nodes, there can + * exist only one irnet connection. + */ +static void +irnet_connect_indication(void * instance, + void * sap, + struct qos_info *qos, + __u32 max_sdu_size, + __u8 max_header_size, + struct sk_buff *skb) +{ + irnet_socket * server = &irnet_server.s; + irnet_socket * new = (irnet_socket *) NULL; + + DENTER(IRDA_TCB_TRACE, "(server=0x%p)\n", server); + DASSERT(instance == &irnet_server, , IRDA_CB_ERROR, + "Invalid instance (0x%p) !!!\n", instance); + DASSERT(sap == irnet_server.s.tsap, , IRDA_CB_ERROR, "Invalid sap !!!\n"); + + /* Try to find the most appropriate IrNET socket */ + new = irnet_find_socket(server); + + /* After all this hard work, do we have an socket ? */ + if(new == (irnet_socket *) NULL) + { + DEXIT(IRDA_CB_INFO, ": No socket waiting for this connection.\n"); + irnet_disconnect_server(server, skb); + return; + } + + /* Is the socket already busy ? */ + if(test_bit(0, &new->ttp_open)) + { + DEXIT(IRDA_CB_INFO, ": Socket already connected.\n"); + irnet_disconnect_server(server, skb); + return; + } + + /* The following code is a bit tricky, so need comments ;-) + */ + /* If ttp_connect is set, the socket is trying to connect to the other + * end and may have sent a IrTTP connection request and is waiting for + * a connection response (that may never come). + * Now, the pain is that the socket may have opened a tsap and is + * waiting on it, while the other end is trying to connect to it on + * another tsap. + * Because IrNET can be peer to peer, we need to workaround this. + * Furthermore, the way the irnetd script is implemented, the + * target will create a second IrNET connection back to the + * originator and expect the originator to bind this new connection + * to the original PPPD instance. + * And of course, if we don't use irnetd, we can have a race when + * both side try to connect simultaneously, which could leave both + * connections half closed (yuck). + * Conclusions : + * 1) The "originator" must accept the new connection and get rid + * of the old one so that irnetd works + * 2) One side must deny the new connection to avoid races, + * but both side must agree on which side it is... + * Most often, the originator is primary at the LAP layer. + * Jean II + */ + /* Now, let's look at the way I wrote the test... + * We need to clear up the ttp_connect flag atomically to prevent + * irnet_disconnect_indication() to mess up the tsap we are going to close. + * We want to clear the ttp_connect flag only if we close the tsap, + * otherwise we will never close it, so we need to check for primary + * *before* doing the test on the flag. + * And of course, ALLOW_SIMULT_CONNECT can disable this entirely... + * Jean II + */ + + /* Socket already connecting ? On primary ? */ + if(0 +#ifdef ALLOW_SIMULT_CONNECT + || ((irttp_is_primary(server->tsap) == 1) && /* primary */ + (test_and_clear_bit(0, &new->ttp_connect))) +#endif /* ALLOW_SIMULT_CONNECT */ + ) + { + DERROR(IRDA_CB_ERROR, "Socket already connecting, but going to reuse it !\n"); + + /* Cleanup the old TSAP if necessary - IrIAP will be cleaned up later */ + if(new->tsap != NULL) + { + /* Close the old connection the new socket was attempting, + * so that we can hook it up to the new connection. + * It's now safe to do it... */ + irttp_close_tsap(new->tsap); + new->tsap = NULL; + } + } + else + { + /* Three options : + * 1) socket was not connecting or connected : ttp_connect should be 0. + * 2) we don't want to connect the socket because we are secondary or + * ALLOW_SIMULT_CONNECT is undefined. ttp_connect should be 1. + * 3) we are half way in irnet_disconnect_indication(), and it's a + * nice race condition... Fortunately, we can detect that by checking + * if tsap is still alive. On the other hand, we can't be in + * irda_irnet_destroy() otherwise we would not have found this + * socket in the hashbin. + * Jean II */ + if((test_bit(0, &new->ttp_connect)) || (new->tsap != NULL)) + { + /* Don't mess this socket, somebody else in in charge... */ + DERROR(IRDA_CB_ERROR, "Race condition detected, socket in use, abort connect...\n"); + irnet_disconnect_server(server, skb); + return; + } + } + + /* So : at this point, we have a socket, and it is idle. Good ! */ + irnet_connect_socket(server, new, qos, max_sdu_size, max_header_size); + + /* Check size of received packet */ + if(skb->len > 0) + { +#ifdef PASS_CONNECT_PACKETS + DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n"); + /* Try to pass it to PPP */ + irnet_data_indication(new, new->tsap, skb); +#else /* PASS_CONNECT_PACKETS */ + DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n"); + kfree_skb(skb); /* Note : will be optimised with other kfree... */ +#endif /* PASS_CONNECT_PACKETS */ + } + else + kfree_skb(skb); + + DEXIT(IRDA_TCB_TRACE, "\n"); +} + + +/********************** IRDA-IAS/LMP CALLBACKS **********************/ +/* + * These are the callbacks called by other layers of the IrDA stack, + * mainly LMP for discovery and IAS for name queries. + */ + +/*------------------------------------------------------------------*/ +/* + * Function irnet_getvalue_confirm (result, obj_id, value, priv) + * + * Got answer from remote LM-IAS, just connect + * + * This is the reply to a IAS query we were doing to find the TSAP of + * the device we want to connect to. + * If we have found a valid TSAP, just initiate the TTP connection + * on this TSAP. + */ +static void +irnet_getvalue_confirm(int result, + __u16 obj_id, + struct ias_value *value, + void * priv) +{ + irnet_socket * self = (irnet_socket *) priv; + + DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); + DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n"); + + /* Check if already connected (via irnet_connect_socket()) + * or socket is closing down (via irda_irnet_destroy()) */ + if(! test_bit(0, &self->ttp_connect)) + { + DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n"); + return; + } + + /* We probably don't need to make any more queries */ + iriap_close(self->iriap); + self->iriap = NULL; + + /* Post process the IAS reply */ + self->dtsap_sel = irnet_ias_to_tsap(self, result, value); + + /* If error, just go out */ + if(self->errno) + { + clear_bit(0, &self->ttp_connect); + DERROR(IRDA_OCB_ERROR, "IAS connect failed ! (0x%X)\n", self->errno); + return; + } + + DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n", + self->daddr, self->dtsap_sel); + + /* Start up TTP - non blocking */ + irnet_connect_tsap(self); + + DEXIT(IRDA_OCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_discovervalue_confirm (result, obj_id, value, priv) + * + * Handle the TSAP discovery procedure state machine. + * Got answer from remote LM-IAS, try next device + * + * We are doing a TSAP discovery procedure, and we got an answer to + * a IAS query we were doing to find the TSAP on one of the address + * in the discovery log. + * + * If we have found a valid TSAP for the first time, save it. If it's + * not the first time we found one, complain. + * + * If we have more addresses in the log, just initiate a new query. + * Note that those query may fail (see irnet_discover_daddr_and_lsap_sel()) + * + * Otherwise, wrap up the procedure (cleanup), check if we have found + * any device and connect to it. + */ +static void +irnet_discovervalue_confirm(int result, + __u16 obj_id, + struct ias_value *value, + void * priv) +{ + irnet_socket * self = (irnet_socket *) priv; + __u8 dtsap_sel; /* TSAP we are looking for */ + + DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); + DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n"); + + /* Check if already connected (via irnet_connect_socket()) + * or socket is closing down (via irda_irnet_destroy()) */ + if(! test_bit(0, &self->ttp_connect)) + { + DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n"); + return; + } + + /* Post process the IAS reply */ + dtsap_sel = irnet_ias_to_tsap(self, result, value); + + /* Have we got something ? */ + if(self->errno == 0) + { + /* We found the requested service */ + if(self->daddr != DEV_ADDR_ANY) + { + DERROR(IRDA_OCB_ERROR, "More than one device in range supports IrNET...\n"); + } + else + { + /* First time we found that one, save it ! */ + self->daddr = self->discoveries[self->disco_index].daddr; + self->dtsap_sel = dtsap_sel; + } + } + + /* If no failure */ + if((self->errno == -EADDRNOTAVAIL) || (self->errno == 0)) + { + int ret; + + /* Search the next node */ + ret = irnet_discover_next_daddr(self); + if(!ret) + { + /* In this case, the above request was non-blocking. + * We will return here after a while... */ + return; + } + /* In this case, we have processed the last discovery item */ + } + + /* No more queries to be done (failure or last one) */ + + /* We probably don't need to make any more queries */ + iriap_close(self->iriap); + self->iriap = NULL; + + /* No more items : remove the log and signal termination */ + DEBUG(IRDA_OCB_INFO, "Cleaning up log (0x%p)\n", + self->discoveries); + if(self->discoveries != NULL) + { + /* Cleanup our copy of the discovery log */ + kfree(self->discoveries); + self->discoveries = NULL; + } + self->disco_number = -1; + + /* Check out what we found */ + if(self->daddr == DEV_ADDR_ANY) + { + self->daddr = DEV_ADDR_ANY; + clear_bit(0, &self->ttp_connect); + DEXIT(IRDA_OCB_TRACE, ": cannot discover IrNET in any device !!!\n"); + return; + } + + /* We have a valid address - just connect */ + + DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n", + self->daddr, self->dtsap_sel); + + /* Start up TTP - non blocking */ + irnet_connect_tsap(self); + + DEXIT(IRDA_OCB_TRACE, "\n"); +} + +#ifdef DISCOVERY_EVENTS +/*------------------------------------------------------------------*/ +/* + * Function irnet_discovery_indication (discovery) + * + * Got a discovery indication from IrLMP, post an event + * + * Note : IrLMP take care of matching the hint mask for us, and also + * check if it is a "new" node for us... + * + * As IrLMP filter on the IrLAN hint bit, we get both IrLAN and IrNET + * nodes, so it's only at connection time that we will know if the + * node support IrNET, IrLAN or both. The other solution is to check + * in IAS the PNP ids and service name. + * Note : even if a node support IrNET (or IrLAN), it's no guarantee + * that we will be able to connect to it, the node might already be + * busy... + * + * One last thing : in some case, this function will trigger duplicate + * discovery events. On the other hand, we should catch all + * discoveries properly (i.e. not miss one). Filtering duplicate here + * is to messy, so we leave that to user space... + */ +static void +irnet_discovery_indication(discinfo_t * discovery, + DISCOVERY_MODE mode, + void * priv) +{ + irnet_socket * self = &irnet_server.s; + + DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); + DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR, + "Invalid instance (0x%p) !!!\n", priv); + + DEBUG(IRDA_OCB_INFO, "Discovered new IrNET/IrLAN node %s...\n", + discovery->info); + + /* Notify the control channel */ + irnet_post_event(NULL, IRNET_DISCOVER, + discovery->saddr, discovery->daddr, discovery->info, + get_unaligned((__u16 *)discovery->hints)); + + DEXIT(IRDA_OCB_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_expiry_indication (expiry) + * + * Got a expiry indication from IrLMP, post an event + * + * Note : IrLMP take care of matching the hint mask for us, we only + * check if it is a "new" node... + */ +static void +irnet_expiry_indication(discinfo_t * expiry, + DISCOVERY_MODE mode, + void * priv) +{ + irnet_socket * self = &irnet_server.s; + + DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); + DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR, + "Invalid instance (0x%p) !!!\n", priv); + + DEBUG(IRDA_OCB_INFO, "IrNET/IrLAN node %s expired...\n", + expiry->info); + + /* Notify the control channel */ + irnet_post_event(NULL, IRNET_EXPIRE, + expiry->saddr, expiry->daddr, expiry->info, + get_unaligned((__u16 *)expiry->hints)); + + DEXIT(IRDA_OCB_TRACE, "\n"); +} +#endif /* DISCOVERY_EVENTS */ + + +/*********************** PROC ENTRY CALLBACKS ***********************/ +/* + * We create a instance in the /proc filesystem, and here we take care + * of that... + */ + +#ifdef CONFIG_PROC_FS +static int +irnet_proc_show(struct seq_file *m, void *v) +{ + irnet_socket * self; + char * state; + int i = 0; + + /* Get the IrNET server information... */ + seq_printf(m, "IrNET server - "); + seq_printf(m, "IrDA state: %s, ", + (irnet_server.running ? "running" : "dead")); + seq_printf(m, "stsap_sel: %02x, ", irnet_server.s.stsap_sel); + seq_printf(m, "dtsap_sel: %02x\n", irnet_server.s.dtsap_sel); + + /* Do we need to continue ? */ + if(!irnet_server.running) + return 0; + + /* Protect access to the instance list */ + spin_lock_bh(&irnet_server.spinlock); + + /* Get the sockets one by one... */ + self = (irnet_socket *) hashbin_get_first(irnet_server.list); + while(self != NULL) + { + /* Start printing info about the socket. */ + seq_printf(m, "\nIrNET socket %d - ", i++); + + /* First, get the requested configuration */ + seq_printf(m, "Requested IrDA name: \"%s\", ", self->rname); + seq_printf(m, "daddr: %08x, ", self->rdaddr); + seq_printf(m, "saddr: %08x\n", self->rsaddr); + + /* Second, get all the PPP info */ + seq_printf(m, " PPP state: %s", + (self->ppp_open ? "registered" : "unregistered")); + if(self->ppp_open) + { + seq_printf(m, ", unit: ppp%d", + ppp_unit_number(&self->chan)); + seq_printf(m, ", channel: %d", + ppp_channel_index(&self->chan)); + seq_printf(m, ", mru: %d", + self->mru); + /* Maybe add self->flags ? Later... */ + } + + /* Then, get all the IrDA specific info... */ + if(self->ttp_open) + state = "connected"; + else + if(self->tsap != NULL) + state = "connecting"; + else + if(self->iriap != NULL) + state = "searching"; + else + if(self->ttp_connect) + state = "weird"; + else + state = "idle"; + seq_printf(m, "\n IrDA state: %s, ", state); + seq_printf(m, "daddr: %08x, ", self->daddr); + seq_printf(m, "stsap_sel: %02x, ", self->stsap_sel); + seq_printf(m, "dtsap_sel: %02x\n", self->dtsap_sel); + + /* Next socket, please... */ + self = (irnet_socket *) hashbin_get_next(irnet_server.list); + } + + /* Spin lock end */ + spin_unlock_bh(&irnet_server.spinlock); + + return 0; +} + +static int irnet_proc_open(struct inode *inode, struct file *file) +{ + return single_open(file, irnet_proc_show, NULL); +} + +static const struct file_operations irnet_proc_fops = { + .owner = THIS_MODULE, + .open = irnet_proc_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; +#endif /* PROC_FS */ + + +/********************** CONFIGURATION/CLEANUP **********************/ +/* + * Initialisation and teardown of the IrDA part, called at module + * insertion and removal... + */ + +/*------------------------------------------------------------------*/ +/* + * Prepare the IrNET layer for operation... + */ +int __init +irda_irnet_init(void) +{ + int err = 0; + + DENTER(MODULE_TRACE, "()\n"); + + /* Pure paranoia - should be redundant */ + memset(&irnet_server, 0, sizeof(struct irnet_root)); + + /* Setup start of irnet instance list */ + irnet_server.list = hashbin_new(HB_NOLOCK); + DABORT(irnet_server.list == NULL, -ENOMEM, + MODULE_ERROR, "Can't allocate hashbin!\n"); + /* Init spinlock for instance list */ + spin_lock_init(&irnet_server.spinlock); + + /* Initialise control channel */ + init_waitqueue_head(&irnet_events.rwait); + irnet_events.index = 0; + /* Init spinlock for event logging */ + spin_lock_init(&irnet_events.spinlock); + +#ifdef CONFIG_PROC_FS + /* Add a /proc file for irnet infos */ + proc_create("irnet", 0, proc_irda, &irnet_proc_fops); +#endif /* CONFIG_PROC_FS */ + + /* Setup the IrNET server */ + err = irnet_setup_server(); + + if(!err) + /* We are no longer functional... */ + irnet_server.running = 1; + + DEXIT(MODULE_TRACE, "\n"); + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Cleanup at exit... + */ +void __exit +irda_irnet_cleanup(void) +{ + DENTER(MODULE_TRACE, "()\n"); + + /* We are no longer there... */ + irnet_server.running = 0; + +#ifdef CONFIG_PROC_FS + /* Remove our /proc file */ + remove_proc_entry("irnet", proc_irda); +#endif /* CONFIG_PROC_FS */ + + /* Remove our IrNET server from existence */ + irnet_destroy_server(); + + /* Remove all instances of IrNET socket still present */ + hashbin_delete(irnet_server.list, (FREE_FUNC) irda_irnet_destroy); + + DEXIT(MODULE_TRACE, "\n"); +} diff --git a/drivers/staging/irda/net/irnet/irnet_irda.h b/drivers/staging/irda/net/irnet/irnet_irda.h new file mode 100644 index 000000000000..3e408952a3f1 --- /dev/null +++ b/drivers/staging/irda/net/irnet/irnet_irda.h @@ -0,0 +1,178 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - + * + * This file contains all definitions and declarations necessary for the + * IRDA part of the IrNET module (dealing with IrTTP, IrIAS and co). + * This file is a private header, so other modules don't want to know + * what's in there... + */ + +#ifndef IRNET_IRDA_H +#define IRNET_IRDA_H + +/***************************** INCLUDES *****************************/ +/* Please add other headers in irnet.h */ + +#include "irnet.h" /* Module global include */ + +/************************ CONSTANTS & MACROS ************************/ + +/* + * Name of the service (socket name) used by IrNET + */ +/* IAS object name (or part of it) */ +#define IRNET_SERVICE_NAME "IrNetv1" +/* IAS attribute */ +#define IRNET_IAS_VALUE "IrDA:TinyTP:LsapSel" +/* LMP notify name for client (only for /proc/net/irda/irlmp) */ +#define IRNET_NOTIFY_NAME "IrNET socket" +/* LMP notify name for server (only for /proc/net/irda/irlmp) */ +#define IRNET_NOTIFY_NAME_SERV "IrNET server" + +/****************************** TYPES ******************************/ + +/* + * This is the main structure where we store all the data pertaining to + * the IrNET server (listen for connection requests) and the root + * of the IrNET socket list + */ +typedef struct irnet_root +{ + irnet_socket s; /* To pretend we are a client... */ + + /* Generic stuff */ + int magic; /* Paranoia */ + int running; /* Are we operational ? */ + + /* Link list of all IrNET instances opened */ + hashbin_t * list; + spinlock_t spinlock; /* Serialize access to the list */ + /* Note : the way hashbin has been designed is absolutely not + * reentrant, beware... So, we blindly protect all with spinlock */ + + /* Handle for the hint bit advertised in IrLMP */ + void * skey; + + /* Server socket part */ + struct ias_object * ias_obj; /* Our service name + lsap in IAS */ + +} irnet_root; + + +/**************************** PROTOTYPES ****************************/ + +/* ----------------------- CONTROL CHANNEL ----------------------- */ +static void + irnet_post_event(irnet_socket *, + irnet_event, + __u32, + __u32, + char *, + __u16); +/* ----------------------- IRDA SUBROUTINES ----------------------- */ +static inline int + irnet_open_tsap(irnet_socket *); +static inline __u8 + irnet_ias_to_tsap(irnet_socket *, + int, + struct ias_value *); +static inline int + irnet_find_lsap_sel(irnet_socket *); +static inline int + irnet_connect_tsap(irnet_socket *); +static inline int + irnet_discover_next_daddr(irnet_socket *); +static inline int + irnet_discover_daddr_and_lsap_sel(irnet_socket *); +static inline int + irnet_dname_to_daddr(irnet_socket *); +/* ------------------------ SERVER SOCKET ------------------------ */ +static inline int + irnet_daddr_to_dname(irnet_socket *); +static inline irnet_socket * + irnet_find_socket(irnet_socket *); +static inline int + irnet_connect_socket(irnet_socket *, + irnet_socket *, + struct qos_info *, + __u32, + __u8); +static inline void + irnet_disconnect_server(irnet_socket *, + struct sk_buff *); +static inline int + irnet_setup_server(void); +static inline void + irnet_destroy_server(void); +/* ---------------------- IRDA-TTP CALLBACKS ---------------------- */ +static int + irnet_data_indication(void *, /* instance */ + void *, /* sap */ + struct sk_buff *); +static void + irnet_disconnect_indication(void *, + void *, + LM_REASON, + struct sk_buff *); +static void + irnet_connect_confirm(void *, + void *, + struct qos_info *, + __u32, + __u8, + struct sk_buff *); +static void + irnet_flow_indication(void *, + void *, + LOCAL_FLOW); +static void + irnet_status_indication(void *, + LINK_STATUS, + LOCK_STATUS); +static void + irnet_connect_indication(void *, + void *, + struct qos_info *, + __u32, + __u8, + struct sk_buff *); +/* -------------------- IRDA-IAS/LMP CALLBACKS -------------------- */ +static void + irnet_getvalue_confirm(int, + __u16, + struct ias_value *, + void *); +static void + irnet_discovervalue_confirm(int, + __u16, + struct ias_value *, + void *); +#ifdef DISCOVERY_EVENTS +static void + irnet_discovery_indication(discinfo_t *, + DISCOVERY_MODE, + void *); +static void + irnet_expiry_indication(discinfo_t *, + DISCOVERY_MODE, + void *); +#endif + +/**************************** VARIABLES ****************************/ + +/* + * The IrNET server. Listen to connection requests and co... + */ +static struct irnet_root irnet_server; + +/* Control channel stuff (note : extern) */ +struct irnet_ctrl_channel irnet_events; + +/* The /proc/net/irda directory, defined elsewhere... */ +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry *proc_irda; +#endif /* CONFIG_PROC_FS */ + +#endif /* IRNET_IRDA_H */ diff --git a/drivers/staging/irda/net/irnet/irnet_ppp.c b/drivers/staging/irda/net/irnet/irnet_ppp.c new file mode 100644 index 000000000000..7025dcb853d0 --- /dev/null +++ b/drivers/staging/irda/net/irnet/irnet_ppp.c @@ -0,0 +1,1189 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - + * + * This file implement the PPP interface and /dev/irnet character device. + * The PPP interface hook to the ppp_generic module, handle all our + * relationship to the PPP code in the kernel (and by extension to pppd), + * and exchange PPP frames with this module (send/receive). + * The /dev/irnet device is used primarily for 2 functions : + * 1) as a stub for pppd (the ppp daemon), so that we can appropriately + * generate PPP sessions (we pretend we are a tty). + * 2) as a control channel (write commands, read events) + */ + +#include +#include + +#include "irnet_ppp.h" /* Private header */ +/* Please put other headers in irnet.h - Thanks */ + +/* Generic PPP callbacks (to call us) */ +static const struct ppp_channel_ops irnet_ppp_ops = { + .start_xmit = ppp_irnet_send, + .ioctl = ppp_irnet_ioctl +}; + +/************************* CONTROL CHANNEL *************************/ +/* + * When a pppd instance is not active on /dev/irnet, it acts as a control + * channel. + * Writing allow to set up the IrDA destination of the IrNET channel, + * and any application may be read events happening in IrNET... + */ + +/*------------------------------------------------------------------*/ +/* + * Write is used to send a command to configure a IrNET channel + * before it is open by pppd. The syntax is : "command argument" + * Currently there is only two defined commands : + * o name : set the requested IrDA nickname of the IrNET peer. + * o addr : set the requested IrDA address of the IrNET peer. + * Note : the code is crude, but effective... + */ +static inline ssize_t +irnet_ctrl_write(irnet_socket * ap, + const char __user *buf, + size_t count) +{ + char command[IRNET_MAX_COMMAND]; + char * start; /* Current command being processed */ + char * next; /* Next command to process */ + int length; /* Length of current command */ + + DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count); + + /* Check for overflow... */ + DABORT(count >= IRNET_MAX_COMMAND, -ENOMEM, + CTRL_ERROR, "Too much data !!!\n"); + + /* Get the data in the driver */ + if(copy_from_user(command, buf, count)) + { + DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); + return -EFAULT; + } + + /* Safe terminate the string */ + command[count] = '\0'; + DEBUG(CTRL_INFO, "Command line received is ``%s'' (%zd).\n", + command, count); + + /* Check every commands in the command line */ + next = command; + while(next != NULL) + { + /* Look at the next command */ + start = next; + + /* Scrap whitespaces before the command */ + start = skip_spaces(start); + + /* ',' is our command separator */ + next = strchr(start, ','); + if(next) + { + *next = '\0'; /* Terminate command */ + length = next - start; /* Length */ + next++; /* Skip the '\0' */ + } + else + length = strlen(start); + + DEBUG(CTRL_INFO, "Found command ``%s'' (%d).\n", start, length); + + /* Check if we recognised one of the known command + * We can't use "switch" with strings, so hack with "continue" */ + + /* First command : name -> Requested IrDA nickname */ + if(!strncmp(start, "name", 4)) + { + /* Copy the name only if is included and not "any" */ + if((length > 5) && (strcmp(start + 5, "any"))) + { + /* Strip out trailing whitespaces */ + while(isspace(start[length - 1])) + length--; + + DABORT(length < 5 || length > NICKNAME_MAX_LEN + 5, + -EINVAL, CTRL_ERROR, "Invalid nickname.\n"); + + /* Copy the name for later reuse */ + memcpy(ap->rname, start + 5, length - 5); + ap->rname[length - 5] = '\0'; + } + else + ap->rname[0] = '\0'; + DEBUG(CTRL_INFO, "Got rname = ``%s''\n", ap->rname); + + /* Restart the loop */ + continue; + } + + /* Second command : addr, daddr -> Requested IrDA destination address + * Also process : saddr -> Requested IrDA source address */ + if((!strncmp(start, "addr", 4)) || + (!strncmp(start, "daddr", 5)) || + (!strncmp(start, "saddr", 5))) + { + __u32 addr = DEV_ADDR_ANY; + + /* Copy the address only if is included and not "any" */ + if((length > 5) && (strcmp(start + 5, "any"))) + { + char * begp = start + 5; + char * endp; + + /* Scrap whitespaces before the command */ + begp = skip_spaces(begp); + + /* Convert argument to a number (last arg is the base) */ + addr = simple_strtoul(begp, &endp, 16); + /* Has it worked ? (endp should be start + length) */ + DABORT(endp <= (start + 5), -EINVAL, + CTRL_ERROR, "Invalid address.\n"); + } + /* Which type of address ? */ + if(start[0] == 's') + { + /* Save it */ + ap->rsaddr = addr; + DEBUG(CTRL_INFO, "Got rsaddr = %08x\n", ap->rsaddr); + } + else + { + /* Save it */ + ap->rdaddr = addr; + DEBUG(CTRL_INFO, "Got rdaddr = %08x\n", ap->rdaddr); + } + + /* Restart the loop */ + continue; + } + + /* Other possible command : connect N (number of retries) */ + + /* No command matched -> Failed... */ + DABORT(1, -EINVAL, CTRL_ERROR, "Not a recognised IrNET command.\n"); + } + + /* Success : we have parsed all commands successfully */ + return count; +} + +#ifdef INITIAL_DISCOVERY +/*------------------------------------------------------------------*/ +/* + * Function irnet_get_discovery_log (self) + * + * Query the content on the discovery log if not done + * + * This function query the current content of the discovery log + * at the startup of the event channel and save it in the internal struct. + */ +static void +irnet_get_discovery_log(irnet_socket * ap) +{ + __u16 mask = irlmp_service_to_hint(S_LAN); + + /* Ask IrLMP for the current discovery log */ + ap->discoveries = irlmp_get_discoveries(&ap->disco_number, mask, + DISCOVERY_DEFAULT_SLOTS); + + /* Check if the we got some results */ + if(ap->discoveries == NULL) + ap->disco_number = -1; + + DEBUG(CTRL_INFO, "Got the log (0x%p), size is %d\n", + ap->discoveries, ap->disco_number); +} + +/*------------------------------------------------------------------*/ +/* + * Function irnet_read_discovery_log (self, event) + * + * Read the content on the discovery log + * + * This function dump the current content of the discovery log + * at the startup of the event channel. + * Return 1 if wrote an event on the control channel... + * + * State of the ap->disco_XXX variables : + * Socket creation : discoveries = NULL ; disco_index = 0 ; disco_number = 0 + * While reading : discoveries = ptr ; disco_index = X ; disco_number = Y + * After reading : discoveries = NULL ; disco_index = Y ; disco_number = -1 + */ +static inline int +irnet_read_discovery_log(irnet_socket *ap, char *event, int buf_size) +{ + int done_event = 0; + + DENTER(CTRL_TRACE, "(ap=0x%p, event=0x%p)\n", + ap, event); + + /* Test if we have some work to do or we have already finished */ + if(ap->disco_number == -1) + { + DEBUG(CTRL_INFO, "Already done\n"); + return 0; + } + + /* Test if it's the first time and therefore we need to get the log */ + if(ap->discoveries == NULL) + irnet_get_discovery_log(ap); + + /* Check if we have more item to dump */ + if(ap->disco_index < ap->disco_number) + { + /* Write an event */ + snprintf(event, buf_size, + "Found %08x (%s) behind %08x {hints %02X-%02X}\n", + ap->discoveries[ap->disco_index].daddr, + ap->discoveries[ap->disco_index].info, + ap->discoveries[ap->disco_index].saddr, + ap->discoveries[ap->disco_index].hints[0], + ap->discoveries[ap->disco_index].hints[1]); + DEBUG(CTRL_INFO, "Writing discovery %d : %s\n", + ap->disco_index, ap->discoveries[ap->disco_index].info); + + /* We have an event */ + done_event = 1; + /* Next discovery */ + ap->disco_index++; + } + + /* Check if we have done the last item */ + if(ap->disco_index >= ap->disco_number) + { + /* No more items : remove the log and signal termination */ + DEBUG(CTRL_INFO, "Cleaning up log (0x%p)\n", + ap->discoveries); + if(ap->discoveries != NULL) + { + /* Cleanup our copy of the discovery log */ + kfree(ap->discoveries); + ap->discoveries = NULL; + } + ap->disco_number = -1; + } + + return done_event; +} +#endif /* INITIAL_DISCOVERY */ + +/*------------------------------------------------------------------*/ +/* + * Read is used to get IrNET events + */ +static inline ssize_t +irnet_ctrl_read(irnet_socket * ap, + struct file * file, + char __user * buf, + size_t count) +{ + DECLARE_WAITQUEUE(wait, current); + char event[75]; + ssize_t ret = 0; + + DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count); + +#ifdef INITIAL_DISCOVERY + /* Check if we have read the log */ + if (irnet_read_discovery_log(ap, event, sizeof(event))) + { + count = min(strlen(event), count); + if (copy_to_user(buf, event, count)) + { + DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); + return -EFAULT; + } + + DEXIT(CTRL_TRACE, "\n"); + return count; + } +#endif /* INITIAL_DISCOVERY */ + + /* Put ourselves on the wait queue to be woken up */ + add_wait_queue(&irnet_events.rwait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + for(;;) + { + /* If there is unread events */ + ret = 0; + if(ap->event_index != irnet_events.index) + break; + ret = -EAGAIN; + if(file->f_flags & O_NONBLOCK) + break; + ret = -ERESTARTSYS; + if(signal_pending(current)) + break; + /* Yield and wait to be woken up */ + schedule(); + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&irnet_events.rwait, &wait); + + /* Did we got it ? */ + if(ret != 0) + { + /* No, return the error code */ + DEXIT(CTRL_TRACE, " - ret %zd\n", ret); + return ret; + } + + /* Which event is it ? */ + switch(irnet_events.log[ap->event_index].event) + { + case IRNET_DISCOVER: + snprintf(event, sizeof(event), + "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].saddr, + irnet_events.log[ap->event_index].hints.byte[0], + irnet_events.log[ap->event_index].hints.byte[1]); + break; + case IRNET_EXPIRE: + snprintf(event, sizeof(event), + "Expired %08x (%s) behind %08x {hints %02X-%02X}\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].saddr, + irnet_events.log[ap->event_index].hints.byte[0], + irnet_events.log[ap->event_index].hints.byte[1]); + break; + case IRNET_CONNECT_TO: + snprintf(event, sizeof(event), "Connected to %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_CONNECT_FROM: + snprintf(event, sizeof(event), "Connection from %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_REQUEST_FROM: + snprintf(event, sizeof(event), "Request from %08x (%s) behind %08x\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].saddr); + break; + case IRNET_NOANSWER_FROM: + snprintf(event, sizeof(event), "No-answer from %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_BLOCKED_LINK: + snprintf(event, sizeof(event), "Blocked link with %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_DISCONNECT_FROM: + snprintf(event, sizeof(event), "Disconnection from %08x (%s) on ppp%d\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name, + irnet_events.log[ap->event_index].unit); + break; + case IRNET_DISCONNECT_TO: + snprintf(event, sizeof(event), "Disconnected to %08x (%s)\n", + irnet_events.log[ap->event_index].daddr, + irnet_events.log[ap->event_index].name); + break; + default: + snprintf(event, sizeof(event), "Bug\n"); + } + /* Increment our event index */ + ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS; + + DEBUG(CTRL_INFO, "Event is :%s", event); + + count = min(strlen(event), count); + if (copy_to_user(buf, event, count)) + { + DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); + return -EFAULT; + } + + DEXIT(CTRL_TRACE, "\n"); + return count; +} + +/*------------------------------------------------------------------*/ +/* + * Poll : called when someone do a select on /dev/irnet. + * Just check if there are new events... + */ +static inline unsigned int +irnet_ctrl_poll(irnet_socket * ap, + struct file * file, + poll_table * wait) +{ + unsigned int mask; + + DENTER(CTRL_TRACE, "(ap=0x%p)\n", ap); + + poll_wait(file, &irnet_events.rwait, wait); + mask = POLLOUT | POLLWRNORM; + /* If there is unread events */ + if(ap->event_index != irnet_events.index) + mask |= POLLIN | POLLRDNORM; +#ifdef INITIAL_DISCOVERY + if(ap->disco_number != -1) + { + /* Test if it's the first time and therefore we need to get the log */ + if(ap->discoveries == NULL) + irnet_get_discovery_log(ap); + /* Recheck */ + if(ap->disco_number != -1) + mask |= POLLIN | POLLRDNORM; + } +#endif /* INITIAL_DISCOVERY */ + + DEXIT(CTRL_TRACE, " - mask=0x%X\n", mask); + return mask; +} + + +/*********************** FILESYSTEM CALLBACKS ***********************/ +/* + * Implement the usual open, read, write functions that will be called + * by the file system when some action is performed on /dev/irnet. + * Most of those actions will in fact be performed by "pppd" or + * the control channel, we just act as a redirector... + */ + +/*------------------------------------------------------------------*/ +/* + * Open : when somebody open /dev/irnet + * We basically create a new instance of irnet and initialise it. + */ +static int +dev_irnet_open(struct inode * inode, + struct file * file) +{ + struct irnet_socket * ap; + int err; + + DENTER(FS_TRACE, "(file=0x%p)\n", file); + +#ifdef SECURE_DEVIRNET + /* This could (should?) be enforced by the permissions on /dev/irnet. */ + if(!capable(CAP_NET_ADMIN)) + return -EPERM; +#endif /* SECURE_DEVIRNET */ + + /* Allocate a private structure for this IrNET instance */ + ap = kzalloc(sizeof(*ap), GFP_KERNEL); + DABORT(ap == NULL, -ENOMEM, FS_ERROR, "Can't allocate struct irnet...\n"); + + /* initialize the irnet structure */ + ap->file = file; + + /* PPP channel setup */ + ap->ppp_open = 0; + ap->chan.private = ap; + ap->chan.ops = &irnet_ppp_ops; + ap->chan.mtu = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN); + ap->chan.hdrlen = 2 + TTP_MAX_HEADER; /* for A/C + Max IrDA hdr */ + /* PPP parameters */ + ap->mru = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN); + ap->xaccm[0] = ~0U; + ap->xaccm[3] = 0x60000000U; + ap->raccm = ~0U; + + /* Setup the IrDA part... */ + err = irda_irnet_create(ap); + if(err) + { + DERROR(FS_ERROR, "Can't setup IrDA link...\n"); + kfree(ap); + + return err; + } + + /* For the control channel */ + ap->event_index = irnet_events.index; /* Cancel all past events */ + + mutex_init(&ap->lock); + + /* Put our stuff where we will be able to find it later */ + file->private_data = ap; + + DEXIT(FS_TRACE, " - ap=0x%p\n", ap); + + return 0; +} + + +/*------------------------------------------------------------------*/ +/* + * Close : when somebody close /dev/irnet + * Destroy the instance of /dev/irnet + */ +static int +dev_irnet_close(struct inode * inode, + struct file * file) +{ + irnet_socket * ap = file->private_data; + + DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n", + file, ap); + DABORT(ap == NULL, 0, FS_ERROR, "ap is NULL !!!\n"); + + /* Detach ourselves */ + file->private_data = NULL; + + /* Close IrDA stuff */ + irda_irnet_destroy(ap); + + /* Disconnect from the generic PPP layer if not already done */ + if(ap->ppp_open) + { + DERROR(FS_ERROR, "Channel still registered - deregistering !\n"); + ap->ppp_open = 0; + ppp_unregister_channel(&ap->chan); + } + + kfree(ap); + + DEXIT(FS_TRACE, "\n"); + return 0; +} + +/*------------------------------------------------------------------*/ +/* + * Write does nothing. + * (we receive packet from ppp_generic through ppp_irnet_send()) + */ +static ssize_t +dev_irnet_write(struct file * file, + const char __user *buf, + size_t count, + loff_t * ppos) +{ + irnet_socket * ap = file->private_data; + + DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n", + file, ap, count); + DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); + + /* If we are connected to ppp_generic, let it handle the job */ + if(ap->ppp_open) + return -EAGAIN; + else + return irnet_ctrl_write(ap, buf, count); +} + +/*------------------------------------------------------------------*/ +/* + * Read doesn't do much either. + * (pppd poll us, but ultimately reads through /dev/ppp) + */ +static ssize_t +dev_irnet_read(struct file * file, + char __user * buf, + size_t count, + loff_t * ppos) +{ + irnet_socket * ap = file->private_data; + + DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n", + file, ap, count); + DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); + + /* If we are connected to ppp_generic, let it handle the job */ + if(ap->ppp_open) + return -EAGAIN; + else + return irnet_ctrl_read(ap, file, buf, count); +} + +/*------------------------------------------------------------------*/ +/* + * Poll : called when someone do a select on /dev/irnet + */ +static unsigned int +dev_irnet_poll(struct file * file, + poll_table * wait) +{ + irnet_socket * ap = file->private_data; + unsigned int mask; + + DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n", + file, ap); + + mask = POLLOUT | POLLWRNORM; + DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n"); + + /* If we are connected to ppp_generic, let it handle the job */ + if(!ap->ppp_open) + mask |= irnet_ctrl_poll(ap, file, wait); + + DEXIT(FS_TRACE, " - mask=0x%X\n", mask); + return mask; +} + +/*------------------------------------------------------------------*/ +/* + * IOCtl : Called when someone does some ioctls on /dev/irnet + * This is the way pppd configure us and control us while the PPP + * instance is active. + */ +static long +dev_irnet_ioctl( + struct file * file, + unsigned int cmd, + unsigned long arg) +{ + irnet_socket * ap = file->private_data; + int err; + int val; + void __user *argp = (void __user *)arg; + + DENTER(FS_TRACE, "(file=0x%p, ap=0x%p, cmd=0x%X)\n", + file, ap, cmd); + + /* Basic checks... */ + DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); +#ifdef SECURE_DEVIRNET + if(!capable(CAP_NET_ADMIN)) + return -EPERM; +#endif /* SECURE_DEVIRNET */ + + err = -EFAULT; + switch(cmd) + { + /* Set discipline (should be N_SYNC_PPP or N_TTY) */ + case TIOCSETD: + if(get_user(val, (int __user *)argp)) + break; + if((val == N_SYNC_PPP) || (val == N_PPP)) + { + DEBUG(FS_INFO, "Entering PPP discipline.\n"); + /* PPP channel setup (ap->chan in configured in dev_irnet_open())*/ + if (mutex_lock_interruptible(&ap->lock)) + return -EINTR; + + err = ppp_register_channel(&ap->chan); + if(err == 0) + { + /* Our ppp side is active */ + ap->ppp_open = 1; + + DEBUG(FS_INFO, "Trying to establish a connection.\n"); + /* Setup the IrDA link now - may fail... */ + irda_irnet_connect(ap); + } + else + DERROR(FS_ERROR, "Can't setup PPP channel...\n"); + + mutex_unlock(&ap->lock); + } + else + { + /* In theory, should be N_TTY */ + DEBUG(FS_INFO, "Exiting PPP discipline.\n"); + /* Disconnect from the generic PPP layer */ + if (mutex_lock_interruptible(&ap->lock)) + return -EINTR; + + if(ap->ppp_open) + { + ap->ppp_open = 0; + ppp_unregister_channel(&ap->chan); + } + else + DERROR(FS_ERROR, "Channel not registered !\n"); + err = 0; + + mutex_unlock(&ap->lock); + } + break; + + /* Query PPP channel and unit number */ + case PPPIOCGCHAN: + if (mutex_lock_interruptible(&ap->lock)) + return -EINTR; + + if(ap->ppp_open && !put_user(ppp_channel_index(&ap->chan), + (int __user *)argp)) + err = 0; + + mutex_unlock(&ap->lock); + break; + case PPPIOCGUNIT: + if (mutex_lock_interruptible(&ap->lock)) + return -EINTR; + + if(ap->ppp_open && !put_user(ppp_unit_number(&ap->chan), + (int __user *)argp)) + err = 0; + + mutex_unlock(&ap->lock); + break; + + /* All these ioctls can be passed both directly and from ppp_generic, + * so we just deal with them in one place... + */ + case PPPIOCGFLAGS: + case PPPIOCSFLAGS: + case PPPIOCGASYNCMAP: + case PPPIOCSASYNCMAP: + case PPPIOCGRASYNCMAP: + case PPPIOCSRASYNCMAP: + case PPPIOCGXASYNCMAP: + case PPPIOCSXASYNCMAP: + case PPPIOCGMRU: + case PPPIOCSMRU: + DEBUG(FS_INFO, "Standard PPP ioctl.\n"); + if(!capable(CAP_NET_ADMIN)) + err = -EPERM; + else { + if (mutex_lock_interruptible(&ap->lock)) + return -EINTR; + + err = ppp_irnet_ioctl(&ap->chan, cmd, arg); + + mutex_unlock(&ap->lock); + } + break; + + /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */ + /* Get termios */ + case TCGETS: + DEBUG(FS_INFO, "Get termios.\n"); + if (mutex_lock_interruptible(&ap->lock)) + return -EINTR; + +#ifndef TCGETS2 + if(!kernel_termios_to_user_termios((struct termios __user *)argp, &ap->termios)) + err = 0; +#else + if(kernel_termios_to_user_termios_1((struct termios __user *)argp, &ap->termios)) + err = 0; +#endif + + mutex_unlock(&ap->lock); + break; + /* Set termios */ + case TCSETSF: + DEBUG(FS_INFO, "Set termios.\n"); + if (mutex_lock_interruptible(&ap->lock)) + return -EINTR; + +#ifndef TCGETS2 + if(!user_termios_to_kernel_termios(&ap->termios, (struct termios __user *)argp)) + err = 0; +#else + if(!user_termios_to_kernel_termios_1(&ap->termios, (struct termios __user *)argp)) + err = 0; +#endif + + mutex_unlock(&ap->lock); + break; + + /* Set DTR/RTS */ + case TIOCMBIS: + case TIOCMBIC: + /* Set exclusive/non-exclusive mode */ + case TIOCEXCL: + case TIOCNXCL: + DEBUG(FS_INFO, "TTY compatibility.\n"); + err = 0; + break; + + case TCGETA: + DEBUG(FS_INFO, "TCGETA\n"); + break; + + case TCFLSH: + DEBUG(FS_INFO, "TCFLSH\n"); + /* Note : this will flush buffers in PPP, so it *must* be done + * We should also worry that we don't accept junk here and that + * we get rid of our own buffers */ +#ifdef FLUSH_TO_PPP + if (mutex_lock_interruptible(&ap->lock)) + return -EINTR; + ppp_output_wakeup(&ap->chan); + mutex_unlock(&ap->lock); +#endif /* FLUSH_TO_PPP */ + err = 0; + break; + + case FIONREAD: + DEBUG(FS_INFO, "FIONREAD\n"); + val = 0; + if(put_user(val, (int __user *)argp)) + break; + err = 0; + break; + + default: + DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd); + err = -ENOTTY; + } + + DEXIT(FS_TRACE, " - err = 0x%X\n", err); + return err; +} + +/************************** PPP CALLBACKS **************************/ +/* + * This are the functions that the generic PPP driver in the kernel + * will call to communicate to us. + */ + +/*------------------------------------------------------------------*/ +/* + * Prepare the ppp frame for transmission over the IrDA socket. + * We make sure that the header space is enough, and we change ppp header + * according to flags passed by pppd. + * This is not a callback, but just a helper function used in ppp_irnet_send() + */ +static inline struct sk_buff * +irnet_prepare_skb(irnet_socket * ap, + struct sk_buff * skb) +{ + unsigned char * data; + int proto; /* PPP protocol */ + int islcp; /* Protocol == LCP */ + int needaddr; /* Need PPP address */ + + DENTER(PPP_TRACE, "(ap=0x%p, skb=0x%p)\n", + ap, skb); + + /* Extract PPP protocol from the frame */ + data = skb->data; + proto = (data[0] << 8) + data[1]; + + /* LCP packets with codes between 1 (configure-request) + * and 7 (code-reject) must be sent as though no options + * have been negotiated. */ + islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7); + + /* compress protocol field if option enabled */ + if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp)) + skb_pull(skb,1); + + /* Check if we need address/control fields */ + needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp); + + /* Is the skb headroom large enough to contain all IrDA-headers? */ + if((skb_headroom(skb) < (ap->max_header_size + needaddr)) || + (skb_shared(skb))) + { + struct sk_buff * new_skb; + + DEBUG(PPP_INFO, "Reallocating skb\n"); + + /* Create a new skb */ + new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr); + + /* We have to free the original skb anyway */ + dev_kfree_skb(skb); + + /* Did the realloc succeed ? */ + DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n"); + + /* Use the new skb instead */ + skb = new_skb; + } + + /* prepend address/control fields if necessary */ + if(needaddr) + { + skb_push(skb, 2); + skb->data[0] = PPP_ALLSTATIONS; + skb->data[1] = PPP_UI; + } + + DEXIT(PPP_TRACE, "\n"); + + return skb; +} + +/*------------------------------------------------------------------*/ +/* + * Send a packet to the peer over the IrTTP connection. + * Returns 1 iff the packet was accepted. + * Returns 0 iff packet was not consumed. + * If the packet was not accepted, we will call ppp_output_wakeup + * at some later time to reactivate flow control in ppp_generic. + */ +static int +ppp_irnet_send(struct ppp_channel * chan, + struct sk_buff * skb) +{ + irnet_socket * self = (struct irnet_socket *) chan->private; + int ret; + + DENTER(PPP_TRACE, "(channel=0x%p, ap/self=0x%p)\n", + chan, self); + + /* Check if things are somewhat valid... */ + DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n"); + + /* Check if we are connected */ + if(!(test_bit(0, &self->ttp_open))) + { +#ifdef CONNECT_IN_SEND + /* Let's try to connect one more time... */ + /* Note : we won't be connected after this call, but we should be + * ready for next packet... */ + /* If we are already connecting, this will fail */ + irda_irnet_connect(self); +#endif /* CONNECT_IN_SEND */ + + DEBUG(PPP_INFO, "IrTTP not ready ! (%ld-%ld)\n", + self->ttp_open, self->ttp_connect); + + /* Note : we can either drop the packet or block the packet. + * + * Blocking the packet allow us a better connection time, + * because by calling ppp_output_wakeup() we can have + * ppp_generic resending the LCP request immediately to us, + * rather than waiting for one of pppd periodic transmission of + * LCP request. + * + * On the other hand, if we block all packet, all those periodic + * transmissions of pppd accumulate in ppp_generic, creating a + * backlog of LCP request. When we eventually connect later on, + * we have to transmit all this backlog before we can connect + * proper (if we don't timeout before). + * + * The current strategy is as follow : + * While we are attempting to connect, we block packets to get + * a better connection time. + * If we fail to connect, we drain the queue and start dropping packets + */ +#ifdef BLOCK_WHEN_CONNECT + /* If we are attempting to connect */ + if(test_bit(0, &self->ttp_connect)) + { + /* Blocking packet, ppp_generic will retry later */ + return 0; + } +#endif /* BLOCK_WHEN_CONNECT */ + + /* Dropping packet, pppd will retry later */ + dev_kfree_skb(skb); + return 1; + } + + /* Check if the queue can accept any packet, otherwise block */ + if(self->tx_flow != FLOW_START) + DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n", + skb_queue_len(&self->tsap->tx_queue)); + + /* Prepare ppp frame for transmission */ + skb = irnet_prepare_skb(self, skb); + DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n"); + + /* Send the packet to IrTTP */ + ret = irttp_data_request(self->tsap, skb); + if(ret < 0) + { + /* + * > IrTTPs tx queue is full, so we just have to + * > drop the frame! You might think that we should + * > just return -1 and don't deallocate the frame, + * > but that is dangerous since it's possible that + * > we have replaced the original skb with a new + * > one with larger headroom, and that would really + * > confuse do_dev_queue_xmit() in dev.c! I have + * > tried :-) DB + * Correction : we verify the flow control above (self->tx_flow), + * so we come here only if IrTTP doesn't like the packet (empty, + * too large, IrTTP not connected). In those rare cases, it's ok + * to drop it, we don't want to see it here again... + * Jean II + */ + DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret); + /* irttp_data_request already free the packet */ + } + + DEXIT(PPP_TRACE, "\n"); + return 1; /* Packet has been consumed */ +} + +/*------------------------------------------------------------------*/ +/* + * Take care of the ioctls that ppp_generic doesn't want to deal with... + * Note : we are also called from dev_irnet_ioctl(). + */ +static int +ppp_irnet_ioctl(struct ppp_channel * chan, + unsigned int cmd, + unsigned long arg) +{ + irnet_socket * ap = (struct irnet_socket *) chan->private; + int err; + int val; + u32 accm[8]; + void __user *argp = (void __user *)arg; + + DENTER(PPP_TRACE, "(channel=0x%p, ap=0x%p, cmd=0x%X)\n", + chan, ap, cmd); + + /* Basic checks... */ + DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); + + err = -EFAULT; + switch(cmd) + { + /* PPP flags */ + case PPPIOCGFLAGS: + val = ap->flags | ap->rbits; + if(put_user(val, (int __user *) argp)) + break; + err = 0; + break; + case PPPIOCSFLAGS: + if(get_user(val, (int __user *) argp)) + break; + ap->flags = val & ~SC_RCV_BITS; + ap->rbits = val & SC_RCV_BITS; + err = 0; + break; + + /* Async map stuff - all dummy to please pppd */ + case PPPIOCGASYNCMAP: + if(put_user(ap->xaccm[0], (u32 __user *) argp)) + break; + err = 0; + break; + case PPPIOCSASYNCMAP: + if(get_user(ap->xaccm[0], (u32 __user *) argp)) + break; + err = 0; + break; + case PPPIOCGRASYNCMAP: + if(put_user(ap->raccm, (u32 __user *) argp)) + break; + err = 0; + break; + case PPPIOCSRASYNCMAP: + if(get_user(ap->raccm, (u32 __user *) argp)) + break; + err = 0; + break; + case PPPIOCGXASYNCMAP: + if(copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm))) + break; + err = 0; + break; + case PPPIOCSXASYNCMAP: + if(copy_from_user(accm, argp, sizeof(accm))) + break; + accm[2] &= ~0x40000000U; /* can't escape 0x5e */ + accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ + memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); + err = 0; + break; + + /* Max PPP frame size */ + case PPPIOCGMRU: + if(put_user(ap->mru, (int __user *) argp)) + break; + err = 0; + break; + case PPPIOCSMRU: + if(get_user(val, (int __user *) argp)) + break; + if(val < PPP_MRU) + val = PPP_MRU; + ap->mru = val; + err = 0; + break; + + default: + DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd); + err = -ENOIOCTLCMD; + } + + DEXIT(PPP_TRACE, " - err = 0x%X\n", err); + return err; +} + +/************************** INITIALISATION **************************/ +/* + * Module initialisation and all that jazz... + */ + +/*------------------------------------------------------------------*/ +/* + * Hook our device callbacks in the filesystem, to connect our code + * to /dev/irnet + */ +static inline int __init +ppp_irnet_init(void) +{ + int err = 0; + + DENTER(MODULE_TRACE, "()\n"); + + /* Allocate ourselves as a minor in the misc range */ + err = misc_register(&irnet_misc_device); + + DEXIT(MODULE_TRACE, "\n"); + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Cleanup at exit... + */ +static inline void __exit +ppp_irnet_cleanup(void) +{ + DENTER(MODULE_TRACE, "()\n"); + + /* De-allocate /dev/irnet minor in misc range */ + misc_deregister(&irnet_misc_device); + + DEXIT(MODULE_TRACE, "\n"); +} + +/*------------------------------------------------------------------*/ +/* + * Module main entry point + */ +static int __init +irnet_init(void) +{ + int err; + + /* Initialise both parts... */ + err = irda_irnet_init(); + if(!err) + err = ppp_irnet_init(); + return err; +} + +/*------------------------------------------------------------------*/ +/* + * Module exit + */ +static void __exit +irnet_cleanup(void) +{ + irda_irnet_cleanup(); + ppp_irnet_cleanup(); +} + +/*------------------------------------------------------------------*/ +/* + * Module magic + */ +module_init(irnet_init); +module_exit(irnet_cleanup); +MODULE_AUTHOR("Jean Tourrilhes "); +MODULE_DESCRIPTION("IrNET : Synchronous PPP over IrDA"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS_CHARDEV(10, 187); diff --git a/drivers/staging/irda/net/irnet/irnet_ppp.h b/drivers/staging/irda/net/irnet/irnet_ppp.h new file mode 100644 index 000000000000..32061442cc8e --- /dev/null +++ b/drivers/staging/irda/net/irnet/irnet_ppp.h @@ -0,0 +1,116 @@ +/* + * IrNET protocol module : Synchronous PPP over an IrDA socket. + * + * Jean II - HPL `00 - + * + * This file contains all definitions and declarations necessary for the + * PPP part of the IrNET module. + * This file is a private header, so other modules don't want to know + * what's in there... + */ + +#ifndef IRNET_PPP_H +#define IRNET_PPP_H + +/***************************** INCLUDES *****************************/ + +#include "irnet.h" /* Module global include */ +#include + +/************************ CONSTANTS & MACROS ************************/ + +/* IrNET control channel stuff */ +#define IRNET_MAX_COMMAND 256 /* Max length of a command line */ + +/* PPP hardcore stuff */ + +/* Bits in rbits (PPP flags in irnet struct) */ +#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) + +/* Bit numbers in busy */ +#define XMIT_BUSY 0 +#define RECV_BUSY 1 +#define XMIT_WAKEUP 2 +#define XMIT_FULL 3 + +/* Queue management */ +#define PPPSYNC_MAX_RQLEN 32 /* arbitrary */ + +/****************************** TYPES ******************************/ + + +/**************************** PROTOTYPES ****************************/ + +/* ----------------------- CONTROL CHANNEL ----------------------- */ +static inline ssize_t + irnet_ctrl_write(irnet_socket *, + const char *, + size_t); +static inline ssize_t + irnet_ctrl_read(irnet_socket *, + struct file *, + char *, + size_t); +static inline unsigned int + irnet_ctrl_poll(irnet_socket *, + struct file *, + poll_table *); +/* ----------------------- CHARACTER DEVICE ----------------------- */ +static int + dev_irnet_open(struct inode *, /* fs callback : open */ + struct file *), + dev_irnet_close(struct inode *, + struct file *); +static ssize_t + dev_irnet_write(struct file *, + const char __user *, + size_t, + loff_t *), + dev_irnet_read(struct file *, + char __user *, + size_t, + loff_t *); +static unsigned int + dev_irnet_poll(struct file *, + poll_table *); +static long + dev_irnet_ioctl(struct file *, + unsigned int, + unsigned long); +/* ------------------------ PPP INTERFACE ------------------------ */ +static inline struct sk_buff * + irnet_prepare_skb(irnet_socket *, + struct sk_buff *); +static int + ppp_irnet_send(struct ppp_channel *, + struct sk_buff *); +static int + ppp_irnet_ioctl(struct ppp_channel *, + unsigned int, + unsigned long); + +/**************************** VARIABLES ****************************/ + +/* Filesystem callbacks (to call us) */ +static const struct file_operations irnet_device_fops = +{ + .owner = THIS_MODULE, + .read = dev_irnet_read, + .write = dev_irnet_write, + .poll = dev_irnet_poll, + .unlocked_ioctl = dev_irnet_ioctl, + .open = dev_irnet_open, + .release = dev_irnet_close, + .llseek = noop_llseek, + /* Also : llseek, readdir, mmap, flush, fsync, fasync, lock, readv, writev */ +}; + +/* Structure so that the misc major (drivers/char/misc.c) take care of us... */ +static struct miscdevice irnet_misc_device = +{ + .minor = IRNET_MINOR, + .name = "irnet", + .fops = &irnet_device_fops +}; + +#endif /* IRNET_PPP_H */ diff --git a/drivers/staging/irda/net/irnetlink.c b/drivers/staging/irda/net/irnetlink.c new file mode 100644 index 000000000000..7fc340e574cf --- /dev/null +++ b/drivers/staging/irda/net/irnetlink.c @@ -0,0 +1,162 @@ +/* + * IrDA netlink layer, for stack configuration. + * + * Copyright (c) 2007 Samuel Ortiz + * + * Partly based on the 802.11 nelink implementation + * (see net/wireless/nl80211.c) which is: + * Copyright 2006 Johannes Berg + * + * 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. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + + + +static struct genl_family irda_nl_family; + +static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *info) +{ + char * ifname; + + if (!info->attrs[IRDA_NL_ATTR_IFNAME]) + return NULL; + + ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]); + + pr_debug("%s(): Looking for %s\n", __func__, ifname); + + return dev_get_by_name(net, ifname); +} + +static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info) +{ + struct net_device * dev; + struct irlap_cb * irlap; + u32 mode; + + if (!info->attrs[IRDA_NL_ATTR_MODE]) + return -EINVAL; + + mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]); + + pr_debug("%s(): Switching to mode: %d\n", __func__, mode); + + dev = ifname_to_netdev(&init_net, info); + if (!dev) + return -ENODEV; + + irlap = (struct irlap_cb *)dev->atalk_ptr; + if (!irlap) { + dev_put(dev); + return -ENODEV; + } + + irlap->mode = mode; + + dev_put(dev); + + return 0; +} + +static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info) +{ + struct net_device * dev; + struct irlap_cb * irlap; + struct sk_buff *msg; + void *hdr; + int ret = -ENOBUFS; + + dev = ifname_to_netdev(&init_net, info); + if (!dev) + return -ENODEV; + + msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); + if (!msg) { + dev_put(dev); + return -ENOMEM; + } + + irlap = (struct irlap_cb *)dev->atalk_ptr; + if (!irlap) { + ret = -ENODEV; + goto err_out; + } + + hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, + &irda_nl_family, 0, IRDA_NL_CMD_GET_MODE); + if (hdr == NULL) { + ret = -EMSGSIZE; + goto err_out; + } + + if(nla_put_string(msg, IRDA_NL_ATTR_IFNAME, + dev->name)) + goto err_out; + + if(nla_put_u32(msg, IRDA_NL_ATTR_MODE, irlap->mode)) + goto err_out; + + genlmsg_end(msg, hdr); + + return genlmsg_reply(msg, info); + + err_out: + nlmsg_free(msg); + dev_put(dev); + + return ret; +} + +static const struct nla_policy irda_nl_policy[IRDA_NL_ATTR_MAX + 1] = { + [IRDA_NL_ATTR_IFNAME] = { .type = NLA_NUL_STRING, + .len = IFNAMSIZ-1 }, + [IRDA_NL_ATTR_MODE] = { .type = NLA_U32 }, +}; + +static const struct genl_ops irda_nl_ops[] = { + { + .cmd = IRDA_NL_CMD_SET_MODE, + .doit = irda_nl_set_mode, + .policy = irda_nl_policy, + .flags = GENL_ADMIN_PERM, + }, + { + .cmd = IRDA_NL_CMD_GET_MODE, + .doit = irda_nl_get_mode, + .policy = irda_nl_policy, + /* can be retrieved by unprivileged users */ + }, + +}; + +static struct genl_family irda_nl_family __ro_after_init = { + .name = IRDA_NL_NAME, + .hdrsize = 0, + .version = IRDA_NL_VERSION, + .maxattr = IRDA_NL_CMD_MAX, + .module = THIS_MODULE, + .ops = irda_nl_ops, + .n_ops = ARRAY_SIZE(irda_nl_ops), +}; + +int __init irda_nl_register(void) +{ + return genl_register_family(&irda_nl_family); +} + +void irda_nl_unregister(void) +{ + genl_unregister_family(&irda_nl_family); +} diff --git a/drivers/staging/irda/net/irproc.c b/drivers/staging/irda/net/irproc.c new file mode 100644 index 000000000000..77cfdde9d82f --- /dev/null +++ b/drivers/staging/irda/net/irproc.c @@ -0,0 +1,96 @@ +/********************************************************************* + * + * Filename: irproc.c + * Version: 1.0 + * Description: Various entries in the /proc file system + * Status: Experimental. + * Author: Thomas Davis, + * Created at: Sat Feb 21 21:33:24 1998 + * Modified at: Sun Nov 14 08:54:54 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-1999, Dag Brattli + * Copyright (c) 1998, Thomas Davis, , + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * I, Thomas Davis, provide no warranty for any of this software. + * This material is provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include + +#include +#include +#include + +extern const struct file_operations discovery_seq_fops; +extern const struct file_operations irlap_seq_fops; +extern const struct file_operations irlmp_seq_fops; +extern const struct file_operations irttp_seq_fops; +extern const struct file_operations irias_seq_fops; + +struct irda_entry { + const char *name; + const struct file_operations *fops; +}; + +struct proc_dir_entry *proc_irda; +EXPORT_SYMBOL(proc_irda); + +static const struct irda_entry irda_dirs[] = { + {"discovery", &discovery_seq_fops}, + {"irttp", &irttp_seq_fops}, + {"irlmp", &irlmp_seq_fops}, + {"irlap", &irlap_seq_fops}, + {"irias", &irias_seq_fops}, +}; + +/* + * Function irda_proc_register (void) + * + * Register irda entry in /proc file system + * + */ +void __init irda_proc_register(void) +{ + int i; + + proc_irda = proc_mkdir("irda", init_net.proc_net); + if (proc_irda == NULL) + return; + + for (i = 0; i < ARRAY_SIZE(irda_dirs); i++) + (void) proc_create(irda_dirs[i].name, 0, proc_irda, + irda_dirs[i].fops); +} + +/* + * Function irda_proc_unregister (void) + * + * Unregister irda entry in /proc file system + * + */ +void irda_proc_unregister(void) +{ + int i; + + if (proc_irda) { + for (i=0; i + * Created at: Tue Jun 9 13:29:31 1998 + * Modified at: Sun Dec 12 13:48:22 1999 + * Modified by: Dag Brattli + * Modified at: Thu Jan 4 14:29:10 CET 2001 + * Modified by: Marc Zyngier + * + * Copyright (C) 1998-1999, Aage Kvalnes + * Copyright (C) 1998, Dag Brattli, + * All Rights Reserved. + * + * This code is taken from the Vortex Operating System written by Aage + * Kvalnes. Aage has agreed that this code can use the GPL licence, + * although he does not use that licence in his own code. + * + * This copyright does however _not_ include the ELF hash() function + * which I currently don't know which licence or copyright it + * has. Please inform me if you know. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +/* + * NOTE : + * There are various problems with this package : + * o the hash function for ints is pathetic (but could be changed) + * o locking is sometime suspicious (especially during enumeration) + * o most users have only a few elements (== overhead) + * o most users never use search, so don't benefit from hashing + * Problem already fixed : + * o not 64 bit compliant (most users do hashv = (int) self) + * o hashbin_remove() is broken => use hashbin_remove_this() + * I think most users would be better served by a simple linked list + * (like include/linux/list.h) with a global spinlock per list. + * Jean II + */ + +/* + * Notes on the concurrent access to hashbin and other SMP issues + * ------------------------------------------------------------- + * Hashbins are very often in the IrDA stack a global repository of + * information, and therefore used in a very asynchronous manner following + * various events (driver calls, timers, user calls...). + * Therefore, very often it is highly important to consider the + * management of concurrent access to the hashbin and how to guarantee the + * consistency of the operations on it. + * + * First, we need to define the objective of locking : + * 1) Protect user data (content pointed by the hashbin) + * 2) Protect hashbin structure itself (linked list in each bin) + * + * OLD LOCKING + * ----------- + * + * The previous locking strategy, either HB_LOCAL or HB_GLOBAL were + * both inadequate in *both* aspect. + * o HB_GLOBAL was using a spinlock for each bin (local locking). + * o HB_LOCAL was disabling irq on *all* CPUs, so use a single + * global semaphore. + * The problems were : + * A) Global irq disabling is no longer supported by the kernel + * B) No protection for the hashbin struct global data + * o hashbin_delete() + * o hb_current + * C) No protection for user data in some cases + * + * A) HB_LOCAL use global irq disabling, so doesn't work on kernel + * 2.5.X. Even when it is supported (kernel 2.4.X and earlier), its + * performance is not satisfactory on SMP setups. Most hashbins were + * HB_LOCAL, so (A) definitely need fixing. + * B) HB_LOCAL could be modified to fix (B). However, because HB_GLOBAL + * lock only the individual bins, it will never be able to lock the + * global data, so can't do (B). + * C) Some functions return pointer to data that is still in the + * hashbin : + * o hashbin_find() + * o hashbin_get_first() + * o hashbin_get_next() + * As the data is still in the hashbin, it may be changed or free'd + * while the caller is examinimg the data. In those case, locking can't + * be done within the hashbin, but must include use of the data within + * the caller. + * The caller can easily do this with HB_LOCAL (just disable irqs). + * However, this is impossible with HB_GLOBAL because the caller has no + * way to know the proper bin, so don't know which spinlock to use. + * + * Quick summary : can no longer use HB_LOCAL, and HB_GLOBAL is + * fundamentally broken and will never work. + * + * NEW LOCKING + * ----------- + * + * To fix those problems, I've introduce a few changes in the + * hashbin locking : + * 1) New HB_LOCK scheme + * 2) hashbin->hb_spinlock + * 3) New hashbin usage policy + * + * HB_LOCK : + * ------- + * HB_LOCK is a locking scheme intermediate between the old HB_LOCAL + * and HB_GLOBAL. It uses a single spinlock to protect the whole content + * of the hashbin. As it is a single spinlock, it can protect the global + * data of the hashbin and not only the bins themselves. + * HB_LOCK can only protect some of the hashbin calls, so it only lock + * call that can be made 100% safe and leave other call unprotected. + * HB_LOCK in theory is slower than HB_GLOBAL, but as the hashbin + * content is always small contention is not high, so it doesn't matter + * much. HB_LOCK is probably faster than HB_LOCAL. + * + * hashbin->hb_spinlock : + * -------------------- + * The spinlock that HB_LOCK uses is available for caller, so that + * the caller can protect unprotected calls (see below). + * If the caller want to do entirely its own locking (HB_NOLOCK), he + * can do so and may use safely this spinlock. + * Locking is done like this : + * spin_lock_irqsave(&hashbin->hb_spinlock, flags); + * Releasing the lock : + * spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + * + * Safe & Protected calls : + * ---------------------- + * The following calls are safe or protected via HB_LOCK : + * o hashbin_new() -> safe + * o hashbin_delete() + * o hashbin_insert() + * o hashbin_remove_first() + * o hashbin_remove() + * o hashbin_remove_this() + * o HASHBIN_GET_SIZE() -> atomic + * + * The following calls only protect the hashbin itself : + * o hashbin_lock_find() + * o hashbin_find_next() + * + * Unprotected calls : + * ----------------- + * The following calls need to be protected by the caller : + * o hashbin_find() + * o hashbin_get_first() + * o hashbin_get_next() + * + * Locking Policy : + * -------------- + * If the hashbin is used only in a single thread of execution + * (explicitly or implicitely), you can use HB_NOLOCK + * If the calling module already provide concurrent access protection, + * you may use HB_NOLOCK. + * + * In all other cases, you need to use HB_LOCK and lock the hashbin + * every time before calling one of the unprotected calls. You also must + * use the pointer returned by the unprotected call within the locked + * region. + * + * Extra care for enumeration : + * -------------------------- + * hashbin_get_first() and hashbin_get_next() use the hashbin to + * store the current position, in hb_current. + * As long as the hashbin remains locked, this is safe. If you unlock + * the hashbin, the current position may change if anybody else modify + * or enumerate the hashbin. + * Summary : do the full enumeration while locked. + * + * Alternatively, you may use hashbin_find_next(). But, this will + * be slower, is more complex to use and doesn't protect the hashbin + * content. So, care is needed here as well. + * + * Other issues : + * ------------ + * I believe that we are overdoing it by using spin_lock_irqsave() + * and we should use only spin_lock_bh() or similar. But, I don't have + * the balls to try it out. + * Don't believe that because hashbin are now (somewhat) SMP safe + * that the rest of the code is. Higher layers tend to be safest, + * but LAP and LMP would need some serious dedicated love. + * + * Jean II + */ +#include +#include + +#include +#include + +/************************ QUEUE SUBROUTINES ************************/ + +/* + * Hashbin + */ +#define GET_HASHBIN(x) ( x & HASHBIN_MASK ) + +/* + * Function hash (name) + * + * This function hash the input string 'name' using the ELF hash + * function for strings. + */ +static __u32 hash( const char* name) +{ + __u32 h = 0; + __u32 g; + + while(*name) { + h = (h<<4) + *name++; + if ((g = (h & 0xf0000000))) + h ^=g>>24; + h &=~g; + } + return h; +} + +/* + * Function enqueue_first (queue, proc) + * + * Insert item first in queue. + * + */ +static void enqueue_first(irda_queue_t **queue, irda_queue_t* element) +{ + + /* + * Check if queue is empty. + */ + if ( *queue == NULL ) { + /* + * Queue is empty. Insert one element into the queue. + */ + element->q_next = element->q_prev = *queue = element; + + } else { + /* + * Queue is not empty. Insert element into front of queue. + */ + element->q_next = (*queue); + (*queue)->q_prev->q_next = element; + element->q_prev = (*queue)->q_prev; + (*queue)->q_prev = element; + (*queue) = element; + } +} + + +/* + * Function dequeue (queue) + * + * Remove first entry in queue + * + */ +static irda_queue_t *dequeue_first(irda_queue_t **queue) +{ + irda_queue_t *ret; + + pr_debug("dequeue_first()\n"); + + /* + * Set return value + */ + ret = *queue; + + if ( *queue == NULL ) { + /* + * Queue was empty. + */ + } else if ( (*queue)->q_next == *queue ) { + /* + * Queue only contained a single element. It will now be + * empty. + */ + *queue = NULL; + } else { + /* + * Queue contained several element. Remove the first one. + */ + (*queue)->q_prev->q_next = (*queue)->q_next; + (*queue)->q_next->q_prev = (*queue)->q_prev; + *queue = (*queue)->q_next; + } + + /* + * Return the removed entry (or NULL of queue was empty). + */ + return ret; +} + +/* + * Function dequeue_general (queue, element) + * + * + */ +static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element) +{ + irda_queue_t *ret; + + pr_debug("dequeue_general()\n"); + + /* + * Set return value + */ + ret = *queue; + + if ( *queue == NULL ) { + /* + * Queue was empty. + */ + } else if ( (*queue)->q_next == *queue ) { + /* + * Queue only contained a single element. It will now be + * empty. + */ + *queue = NULL; + + } else { + /* + * Remove specific element. + */ + element->q_prev->q_next = element->q_next; + element->q_next->q_prev = element->q_prev; + if ( (*queue) == element) + (*queue) = element->q_next; + } + + /* + * Return the removed entry (or NULL of queue was empty). + */ + return ret; +} + +/************************ HASHBIN MANAGEMENT ************************/ + +/* + * Function hashbin_create ( type, name ) + * + * Create hashbin! + * + */ +hashbin_t *hashbin_new(int type) +{ + hashbin_t* hashbin; + + /* + * Allocate new hashbin + */ + hashbin = kzalloc(sizeof(*hashbin), GFP_ATOMIC); + if (!hashbin) + return NULL; + + /* + * Initialize structure + */ + hashbin->hb_type = type; + hashbin->magic = HB_MAGIC; + //hashbin->hb_current = NULL; + + /* Make sure all spinlock's are unlocked */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_init(&hashbin->hb_spinlock); + } + + return hashbin; +} +EXPORT_SYMBOL(hashbin_new); + + +/* + * Function hashbin_delete (hashbin, free_func) + * + * Destroy hashbin, the free_func can be a user supplied special routine + * for deallocating this structure if it's complex. If not the user can + * just supply kfree, which should take care of the job. + */ +int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) +{ + irda_queue_t* queue; + unsigned long flags = 0; + int i; + + IRDA_ASSERT(hashbin != NULL, return -1;); + IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;); + + /* Synchronize */ + if (hashbin->hb_type & HB_LOCK) + spin_lock_irqsave(&hashbin->hb_spinlock, flags); + + /* + * Free the entries in the hashbin, TODO: use hashbin_clear when + * it has been shown to work + */ + for (i = 0; i < HASHBIN_SIZE; i ++ ) { + while (1) { + queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]); + + if (!queue) + break; + + if (free_func) { + if (hashbin->hb_type & HB_LOCK) + spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + free_func(queue); + if (hashbin->hb_type & HB_LOCK) + spin_lock_irqsave(&hashbin->hb_spinlock, flags); + } + } + } + + /* Cleanup local data */ + hashbin->hb_current = NULL; + hashbin->magic = ~HB_MAGIC; + + /* Release lock */ + if (hashbin->hb_type & HB_LOCK) + spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + + /* + * Free the hashbin structure + */ + kfree(hashbin); + + return 0; +} +EXPORT_SYMBOL(hashbin_delete); + +/********************* HASHBIN LIST OPERATIONS *********************/ + +/* + * Function hashbin_insert (hashbin, entry, name) + * + * Insert an entry into the hashbin + * + */ +void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, + const char* name) +{ + unsigned long flags = 0; + int bin; + + IRDA_ASSERT( hashbin != NULL, return;); + IRDA_ASSERT( hashbin->magic == HB_MAGIC, return;); + + /* + * Locate hashbin + */ + if ( name ) + hashv = hash( name ); + bin = GET_HASHBIN( hashv ); + + /* Synchronize */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_irqsave(&hashbin->hb_spinlock, flags); + } /* Default is no-lock */ + + /* + * Store name and key + */ + entry->q_hash = hashv; + if ( name ) + strlcpy( entry->q_name, name, sizeof(entry->q_name)); + + /* + * Insert new entry first + */ + enqueue_first( (irda_queue_t**) &hashbin->hb_queue[ bin ], + entry); + hashbin->hb_size++; + + /* Release lock */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + } /* Default is no-lock */ +} +EXPORT_SYMBOL(hashbin_insert); + +/* + * Function hashbin_remove_first (hashbin) + * + * Remove first entry of the hashbin + * + * Note : this function no longer use hashbin_remove(), but does things + * similar to hashbin_remove_this(), so can be considered safe. + * Jean II + */ +void *hashbin_remove_first( hashbin_t *hashbin) +{ + unsigned long flags = 0; + irda_queue_t *entry = NULL; + + /* Synchronize */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_irqsave(&hashbin->hb_spinlock, flags); + } /* Default is no-lock */ + + entry = hashbin_get_first( hashbin); + if ( entry != NULL) { + int bin; + long hashv; + /* + * Locate hashbin + */ + hashv = entry->q_hash; + bin = GET_HASHBIN( hashv ); + + /* + * Dequeue the entry... + */ + dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], + entry); + hashbin->hb_size--; + entry->q_next = NULL; + entry->q_prev = NULL; + + /* + * Check if this item is the currently selected item, and in + * that case we must reset hb_current + */ + if ( entry == hashbin->hb_current) + hashbin->hb_current = NULL; + } + + /* Release lock */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + } /* Default is no-lock */ + + return entry; +} + + +/* + * Function hashbin_remove (hashbin, hashv, name) + * + * Remove entry with the given name + * + * The use of this function is highly discouraged, because the whole + * concept behind hashbin_remove() is broken. In many cases, it's not + * possible to guarantee the unicity of the index (either hashv or name), + * leading to removing the WRONG entry. + * The only simple safe use is : + * hashbin_remove(hasbin, (int) self, NULL); + * In other case, you must think hard to guarantee unicity of the index. + * Jean II + */ +void* hashbin_remove( hashbin_t* hashbin, long hashv, const char* name) +{ + int bin, found = FALSE; + unsigned long flags = 0; + irda_queue_t* entry; + + IRDA_ASSERT( hashbin != NULL, return NULL;); + IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); + + /* + * Locate hashbin + */ + if ( name ) + hashv = hash( name ); + bin = GET_HASHBIN( hashv ); + + /* Synchronize */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_irqsave(&hashbin->hb_spinlock, flags); + } /* Default is no-lock */ + + /* + * Search for entry + */ + entry = hashbin->hb_queue[ bin ]; + if ( entry ) { + do { + /* + * Check for key + */ + if ( entry->q_hash == hashv ) { + /* + * Name compare too? + */ + if ( name ) { + if ( strcmp( entry->q_name, name) == 0) + { + found = TRUE; + break; + } + } else { + found = TRUE; + break; + } + } + entry = entry->q_next; + } while ( entry != hashbin->hb_queue[ bin ] ); + } + + /* + * If entry was found, dequeue it + */ + if ( found ) { + dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], + entry); + hashbin->hb_size--; + + /* + * Check if this item is the currently selected item, and in + * that case we must reset hb_current + */ + if ( entry == hashbin->hb_current) + hashbin->hb_current = NULL; + } + + /* Release lock */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + } /* Default is no-lock */ + + + /* Return */ + if ( found ) + return entry; + else + return NULL; + +} +EXPORT_SYMBOL(hashbin_remove); + +/* + * Function hashbin_remove_this (hashbin, entry) + * + * Remove entry with the given name + * + * In some cases, the user of hashbin can't guarantee the unicity + * of either the hashv or name. + * In those cases, using the above function is guaranteed to cause troubles, + * so we use this one instead... + * And by the way, it's also faster, because we skip the search phase ;-) + */ +void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry) +{ + unsigned long flags = 0; + int bin; + long hashv; + + IRDA_ASSERT( hashbin != NULL, return NULL;); + IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); + IRDA_ASSERT( entry != NULL, return NULL;); + + /* Synchronize */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_lock_irqsave(&hashbin->hb_spinlock, flags); + } /* Default is no-lock */ + + /* Check if valid and not already removed... */ + if((entry->q_next == NULL) || (entry->q_prev == NULL)) { + entry = NULL; + goto out; + } + + /* + * Locate hashbin + */ + hashv = entry->q_hash; + bin = GET_HASHBIN( hashv ); + + /* + * Dequeue the entry... + */ + dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], + entry); + hashbin->hb_size--; + entry->q_next = NULL; + entry->q_prev = NULL; + + /* + * Check if this item is the currently selected item, and in + * that case we must reset hb_current + */ + if ( entry == hashbin->hb_current) + hashbin->hb_current = NULL; +out: + /* Release lock */ + if ( hashbin->hb_type & HB_LOCK ) { + spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + } /* Default is no-lock */ + + return entry; +} +EXPORT_SYMBOL(hashbin_remove_this); + +/*********************** HASHBIN ENUMERATION ***********************/ + +/* + * Function hashbin_common_find (hashbin, hashv, name) + * + * Find item with the given hashv or name + * + */ +void* hashbin_find( hashbin_t* hashbin, long hashv, const char* name ) +{ + int bin; + irda_queue_t* entry; + + pr_debug("hashbin_find()\n"); + + IRDA_ASSERT( hashbin != NULL, return NULL;); + IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); + + /* + * Locate hashbin + */ + if ( name ) + hashv = hash( name ); + bin = GET_HASHBIN( hashv ); + + /* + * Search for entry + */ + entry = hashbin->hb_queue[ bin]; + if ( entry ) { + do { + /* + * Check for key + */ + if ( entry->q_hash == hashv ) { + /* + * Name compare too? + */ + if ( name ) { + if ( strcmp( entry->q_name, name ) == 0 ) { + return entry; + } + } else { + return entry; + } + } + entry = entry->q_next; + } while ( entry != hashbin->hb_queue[ bin ] ); + } + + return NULL; +} +EXPORT_SYMBOL(hashbin_find); + +/* + * Function hashbin_lock_find (hashbin, hashv, name) + * + * Find item with the given hashv or name + * + * Same, but with spinlock protection... + * I call it safe, but it's only safe with respect to the hashbin, not its + * content. - Jean II + */ +void* hashbin_lock_find( hashbin_t* hashbin, long hashv, const char* name ) +{ + unsigned long flags = 0; + irda_queue_t* entry; + + /* Synchronize */ + spin_lock_irqsave(&hashbin->hb_spinlock, flags); + + /* + * Search for entry + */ + entry = hashbin_find(hashbin, hashv, name); + + /* Release lock */ + spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + + return entry; +} +EXPORT_SYMBOL(hashbin_lock_find); + +/* + * Function hashbin_find (hashbin, hashv, name, pnext) + * + * Find an item with the given hashv or name, and its successor + * + * This function allow to do concurrent enumerations without the + * need to lock over the whole session, because the caller keep the + * context of the search. On the other hand, it might fail and return + * NULL if the entry is removed. - Jean II + */ +void* hashbin_find_next( hashbin_t* hashbin, long hashv, const char* name, + void ** pnext) +{ + unsigned long flags = 0; + irda_queue_t* entry; + + /* Synchronize */ + spin_lock_irqsave(&hashbin->hb_spinlock, flags); + + /* + * Search for current entry + * This allow to check if the current item is still in the + * hashbin or has been removed. + */ + entry = hashbin_find(hashbin, hashv, name); + + /* + * Trick hashbin_get_next() to return what we want + */ + if(entry) { + hashbin->hb_current = entry; + *pnext = hashbin_get_next( hashbin ); + } else + *pnext = NULL; + + /* Release lock */ + spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); + + return entry; +} + +/* + * Function hashbin_get_first (hashbin) + * + * Get a pointer to first element in hashbin, this function must be + * called before any calls to hashbin_get_next()! + * + */ +irda_queue_t *hashbin_get_first( hashbin_t* hashbin) +{ + irda_queue_t *entry; + int i; + + IRDA_ASSERT( hashbin != NULL, return NULL;); + IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); + + if ( hashbin == NULL) + return NULL; + + for ( i = 0; i < HASHBIN_SIZE; i ++ ) { + entry = hashbin->hb_queue[ i]; + if ( entry) { + hashbin->hb_current = entry; + return entry; + } + } + /* + * Did not find any item in hashbin + */ + return NULL; +} +EXPORT_SYMBOL(hashbin_get_first); + +/* + * Function hashbin_get_next (hashbin) + * + * Get next item in hashbin. A series of hashbin_get_next() calls must + * be started by a call to hashbin_get_first(). The function returns + * NULL when all items have been traversed + * + * The context of the search is stored within the hashbin, so you must + * protect yourself from concurrent enumerations. - Jean II + */ +irda_queue_t *hashbin_get_next( hashbin_t *hashbin) +{ + irda_queue_t* entry; + int bin; + int i; + + IRDA_ASSERT( hashbin != NULL, return NULL;); + IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); + + if ( hashbin->hb_current == NULL) { + IRDA_ASSERT( hashbin->hb_current != NULL, return NULL;); + return NULL; + } + entry = hashbin->hb_current->q_next; + bin = GET_HASHBIN( entry->q_hash); + + /* + * Make sure that we are not back at the beginning of the queue + * again + */ + if ( entry != hashbin->hb_queue[ bin ]) { + hashbin->hb_current = entry; + + return entry; + } + + /* + * Check that this is not the last queue in hashbin + */ + if ( bin >= HASHBIN_SIZE) + return NULL; + + /* + * Move to next queue in hashbin + */ + bin++; + for ( i = bin; i < HASHBIN_SIZE; i++ ) { + entry = hashbin->hb_queue[ i]; + if ( entry) { + hashbin->hb_current = entry; + + return entry; + } + } + return NULL; +} +EXPORT_SYMBOL(hashbin_get_next); diff --git a/drivers/staging/irda/net/irsysctl.c b/drivers/staging/irda/net/irsysctl.c new file mode 100644 index 000000000000..873da5e7d428 --- /dev/null +++ b/drivers/staging/irda/net/irsysctl.c @@ -0,0 +1,258 @@ +/********************************************************************* + * + * Filename: irsysctl.c + * Version: 1.0 + * Description: Sysctl interface for IrDA + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sun May 24 22:12:06 1998 + * Modified at: Fri Jun 4 02:50:15 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved. + * Copyright (c) 2000-2001 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include + +#include /* irda_debug */ +#include +#include +#include + +extern int sysctl_discovery; +extern int sysctl_discovery_slots; +extern int sysctl_discovery_timeout; +extern int sysctl_slot_timeout; +extern int sysctl_fast_poll_increase; +extern char sysctl_devname[]; +extern int sysctl_max_baud_rate; +extern unsigned int sysctl_min_tx_turn_time; +extern unsigned int sysctl_max_tx_data_size; +extern unsigned int sysctl_max_tx_window; +extern int sysctl_max_noreply_time; +extern int sysctl_warn_noreply_time; +extern int sysctl_lap_keepalive_time; + +extern struct irlmp_cb *irlmp; + +/* this is needed for the proc_dointvec_minmax - Jean II */ +static int max_discovery_slots = 16; /* ??? */ +static int min_discovery_slots = 1; +/* IrLAP 6.13.2 says 25ms to 10+70ms - allow higher since some devices + * seems to require it. (from Dag's comment) */ +static int max_slot_timeout = 160; +static int min_slot_timeout = 20; +static int max_max_baud_rate = 16000000; /* See qos.c - IrLAP spec */ +static int min_max_baud_rate = 2400; +static int max_min_tx_turn_time = 10000; /* See qos.c - IrLAP spec */ +static int min_min_tx_turn_time; +static int max_max_tx_data_size = 2048; /* See qos.c - IrLAP spec */ +static int min_max_tx_data_size = 64; +static int max_max_tx_window = 7; /* See qos.c - IrLAP spec */ +static int min_max_tx_window = 1; +static int max_max_noreply_time = 40; /* See qos.c - IrLAP spec */ +static int min_max_noreply_time = 3; +static int max_warn_noreply_time = 3; /* 3s == standard */ +static int min_warn_noreply_time = 1; /* 1s == min WD_TIMER */ +static int max_lap_keepalive_time = 10000; /* 10s */ +static int min_lap_keepalive_time = 100; /* 100us */ +/* For other sysctl, I've no idea of the range. Maybe Dag could help + * us on that - Jean II */ + +static int do_devname(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret; + + ret = proc_dostring(table, write, buffer, lenp, ppos); + if (ret == 0 && write) { + struct ias_value *val; + + val = irias_new_string_value(sysctl_devname); + if (val) + irias_object_change_attribute("Device", "DeviceName", val); + } + return ret; +} + + +static int do_discovery(struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret; + + ret = proc_dointvec(table, write, buffer, lenp, ppos); + if (ret) + return ret; + + if (irlmp == NULL) + return -ENODEV; + + if (sysctl_discovery) + irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout*HZ); + else + del_timer_sync(&irlmp->discovery_timer); + + return ret; +} + +/* One file */ +static struct ctl_table irda_table[] = { + { + .procname = "discovery", + .data = &sysctl_discovery, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = do_discovery, + }, + { + .procname = "devname", + .data = sysctl_devname, + .maxlen = 65, + .mode = 0644, + .proc_handler = do_devname, + }, +#ifdef CONFIG_IRDA_FAST_RR + { + .procname = "fast_poll_increase", + .data = &sysctl_fast_poll_increase, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, +#endif + { + .procname = "discovery_slots", + .data = &sysctl_discovery_slots, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &min_discovery_slots, + .extra2 = &max_discovery_slots + }, + { + .procname = "discovery_timeout", + .data = &sysctl_discovery_timeout, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec + }, + { + .procname = "slot_timeout", + .data = &sysctl_slot_timeout, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &min_slot_timeout, + .extra2 = &max_slot_timeout + }, + { + .procname = "max_baud_rate", + .data = &sysctl_max_baud_rate, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &min_max_baud_rate, + .extra2 = &max_max_baud_rate + }, + { + .procname = "min_tx_turn_time", + .data = &sysctl_min_tx_turn_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &min_min_tx_turn_time, + .extra2 = &max_min_tx_turn_time + }, + { + .procname = "max_tx_data_size", + .data = &sysctl_max_tx_data_size, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &min_max_tx_data_size, + .extra2 = &max_max_tx_data_size + }, + { + .procname = "max_tx_window", + .data = &sysctl_max_tx_window, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &min_max_tx_window, + .extra2 = &max_max_tx_window + }, + { + .procname = "max_noreply_time", + .data = &sysctl_max_noreply_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &min_max_noreply_time, + .extra2 = &max_max_noreply_time + }, + { + .procname = "warn_noreply_time", + .data = &sysctl_warn_noreply_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &min_warn_noreply_time, + .extra2 = &max_warn_noreply_time + }, + { + .procname = "lap_keepalive_time", + .data = &sysctl_lap_keepalive_time, + .maxlen = sizeof(int), + .mode = 0644, + .proc_handler = proc_dointvec_minmax, + .extra1 = &min_lap_keepalive_time, + .extra2 = &max_lap_keepalive_time + }, + { } +}; + +static struct ctl_table_header *irda_table_header; + +/* + * Function irda_sysctl_register (void) + * + * Register our sysctl interface + * + */ +int __init irda_sysctl_register(void) +{ + irda_table_header = register_net_sysctl(&init_net, "net/irda", irda_table); + if (!irda_table_header) + return -ENOMEM; + + return 0; +} + +/* + * Function irda_sysctl_unregister (void) + * + * Unregister our sysctl interface + * + */ +void irda_sysctl_unregister(void) +{ + unregister_net_sysctl_table(irda_table_header); +} + + + diff --git a/drivers/staging/irda/net/irttp.c b/drivers/staging/irda/net/irttp.c new file mode 100644 index 000000000000..b6ab41d5b3a3 --- /dev/null +++ b/drivers/staging/irda/net/irttp.c @@ -0,0 +1,1891 @@ +/********************************************************************* + * + * Filename: irttp.c + * Version: 1.2 + * Description: Tiny Transport Protocol (TTP) implementation + * Status: Stable + * Author: Dag Brattli + * Created at: Sun Aug 31 20:14:31 1997 + * Modified at: Wed Jan 5 11:31:27 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-2000 Dag Brattli , + * All Rights Reserved. + * Copyright (c) 2000-2003 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +static struct irttp_cb *irttp; + +static void __irttp_close_tsap(struct tsap_cb *self); + +static int irttp_data_indication(void *instance, void *sap, + struct sk_buff *skb); +static int irttp_udata_indication(void *instance, void *sap, + struct sk_buff *skb); +static void irttp_disconnect_indication(void *instance, void *sap, + LM_REASON reason, struct sk_buff *); +static void irttp_connect_indication(void *instance, void *sap, + struct qos_info *qos, __u32 max_sdu_size, + __u8 header_size, struct sk_buff *skb); +static void irttp_connect_confirm(void *instance, void *sap, + struct qos_info *qos, __u32 max_sdu_size, + __u8 header_size, struct sk_buff *skb); +static void irttp_run_tx_queue(struct tsap_cb *self); +static void irttp_run_rx_queue(struct tsap_cb *self); + +static void irttp_flush_queues(struct tsap_cb *self); +static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb); +static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self); +static void irttp_todo_expired(unsigned long data); +static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, + int get); + +static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow); +static void irttp_status_indication(void *instance, + LINK_STATUS link, LOCK_STATUS lock); + +/* Information for parsing parameters in IrTTP */ +static const pi_minor_info_t pi_minor_call_table[] = { + { NULL, 0 }, /* 0x00 */ + { irttp_param_max_sdu_size, PV_INTEGER | PV_BIG_ENDIAN } /* 0x01 */ +}; +static const pi_major_info_t pi_major_call_table[] = { + { pi_minor_call_table, 2 } +}; +static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 }; + +/************************ GLOBAL PROCEDURES ************************/ + +/* + * Function irttp_init (void) + * + * Initialize the IrTTP layer. Called by module initialization code + * + */ +int __init irttp_init(void) +{ + irttp = kzalloc(sizeof(struct irttp_cb), GFP_KERNEL); + if (irttp == NULL) + return -ENOMEM; + + irttp->magic = TTP_MAGIC; + + irttp->tsaps = hashbin_new(HB_LOCK); + if (!irttp->tsaps) { + net_err_ratelimited("%s: can't allocate IrTTP hashbin!\n", + __func__); + kfree(irttp); + return -ENOMEM; + } + + return 0; +} + +/* + * Function irttp_cleanup (void) + * + * Called by module destruction/cleanup code + * + */ +void irttp_cleanup(void) +{ + /* Check for main structure */ + IRDA_ASSERT(irttp->magic == TTP_MAGIC, return;); + + /* + * Delete hashbin and close all TSAP instances in it + */ + hashbin_delete(irttp->tsaps, (FREE_FUNC) __irttp_close_tsap); + + irttp->magic = 0; + + /* De-allocate main structure */ + kfree(irttp); + + irttp = NULL; +} + +/*************************** SUBROUTINES ***************************/ + +/* + * Function irttp_start_todo_timer (self, timeout) + * + * Start todo timer. + * + * Made it more effient and unsensitive to race conditions - Jean II + */ +static inline void irttp_start_todo_timer(struct tsap_cb *self, int timeout) +{ + /* Set new value for timer */ + mod_timer(&self->todo_timer, jiffies + timeout); +} + +/* + * Function irttp_todo_expired (data) + * + * Todo timer has expired! + * + * One of the restriction of the timer is that it is run only on the timer + * interrupt which run every 10ms. This mean that even if you set the timer + * with a delay of 0, it may take up to 10ms before it's run. + * So, to minimise latency and keep cache fresh, we try to avoid using + * it as much as possible. + * Note : we can't use tasklets, because they can't be asynchronously + * killed (need user context), and we can't guarantee that here... + * Jean II + */ +static void irttp_todo_expired(unsigned long data) +{ + struct tsap_cb *self = (struct tsap_cb *) data; + + /* Check that we still exist */ + if (!self || self->magic != TTP_TSAP_MAGIC) + return; + + pr_debug("%s(instance=%p)\n", __func__, self); + + /* Try to make some progress, especially on Tx side - Jean II */ + irttp_run_rx_queue(self); + irttp_run_tx_queue(self); + + /* Check if time for disconnect */ + if (test_bit(0, &self->disconnect_pend)) { + /* Check if it's possible to disconnect yet */ + if (skb_queue_empty(&self->tx_queue)) { + /* Make sure disconnect is not pending anymore */ + clear_bit(0, &self->disconnect_pend); /* FALSE */ + + /* Note : self->disconnect_skb may be NULL */ + irttp_disconnect_request(self, self->disconnect_skb, + P_NORMAL); + self->disconnect_skb = NULL; + } else { + /* Try again later */ + irttp_start_todo_timer(self, HZ/10); + + /* No reason to try and close now */ + return; + } + } + + /* Check if it's closing time */ + if (self->close_pend) + /* Finish cleanup */ + irttp_close_tsap(self); +} + +/* + * Function irttp_flush_queues (self) + * + * Flushes (removes all frames) in transitt-buffer (tx_list) + */ +static void irttp_flush_queues(struct tsap_cb *self) +{ + struct sk_buff *skb; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + + /* Deallocate frames waiting to be sent */ + while ((skb = skb_dequeue(&self->tx_queue)) != NULL) + dev_kfree_skb(skb); + + /* Deallocate received frames */ + while ((skb = skb_dequeue(&self->rx_queue)) != NULL) + dev_kfree_skb(skb); + + /* Deallocate received fragments */ + while ((skb = skb_dequeue(&self->rx_fragments)) != NULL) + dev_kfree_skb(skb); +} + +/* + * Function irttp_reassemble (self) + * + * Makes a new (continuous) skb of all the fragments in the fragment + * queue + * + */ +static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self) +{ + struct sk_buff *skb, *frag; + int n = 0; /* Fragment index */ + + IRDA_ASSERT(self != NULL, return NULL;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;); + + pr_debug("%s(), self->rx_sdu_size=%d\n", __func__, + self->rx_sdu_size); + + skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size); + if (!skb) + return NULL; + + /* + * Need to reserve space for TTP header in case this skb needs to + * be requeued in case delivery failes + */ + skb_reserve(skb, TTP_HEADER); + skb_put(skb, self->rx_sdu_size); + + /* + * Copy all fragments to a new buffer + */ + while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) { + skb_copy_to_linear_data_offset(skb, n, frag->data, frag->len); + n += frag->len; + + dev_kfree_skb(frag); + } + + pr_debug("%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n", + __func__, n, self->rx_sdu_size, self->rx_max_sdu_size); + /* Note : irttp_run_rx_queue() calculate self->rx_sdu_size + * by summing the size of all fragments, so we should always + * have n == self->rx_sdu_size, except in cases where we + * droped the last fragment (when self->rx_sdu_size exceed + * self->rx_max_sdu_size), where n < self->rx_sdu_size. + * Jean II */ + IRDA_ASSERT(n <= self->rx_sdu_size, n = self->rx_sdu_size;); + + /* Set the new length */ + skb_trim(skb, n); + + self->rx_sdu_size = 0; + + return skb; +} + +/* + * Function irttp_fragment_skb (skb) + * + * Fragments a frame and queues all the fragments for transmission + * + */ +static inline void irttp_fragment_skb(struct tsap_cb *self, + struct sk_buff *skb) +{ + struct sk_buff *frag; + __u8 *frame; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + /* + * Split frame into a number of segments + */ + while (skb->len > self->max_seg_size) { + pr_debug("%s(), fragmenting ...\n", __func__); + + /* Make new segment */ + frag = alloc_skb(self->max_seg_size+self->max_header_size, + GFP_ATOMIC); + if (!frag) + return; + + skb_reserve(frag, self->max_header_size); + + /* Copy data from the original skb into this fragment. */ + skb_copy_from_linear_data(skb, skb_put(frag, self->max_seg_size), + self->max_seg_size); + + /* Insert TTP header, with the more bit set */ + frame = skb_push(frag, TTP_HEADER); + frame[0] = TTP_MORE; + + /* Hide the copied data from the original skb */ + skb_pull(skb, self->max_seg_size); + + /* Queue fragment */ + skb_queue_tail(&self->tx_queue, frag); + } + /* Queue what is left of the original skb */ + pr_debug("%s(), queuing last segment\n", __func__); + + frame = skb_push(skb, TTP_HEADER); + frame[0] = 0x00; /* Clear more bit */ + + /* Queue fragment */ + skb_queue_tail(&self->tx_queue, skb); +} + +/* + * Function irttp_param_max_sdu_size (self, param) + * + * Handle the MaxSduSize parameter in the connect frames, this function + * will be called both when this parameter needs to be inserted into, and + * extracted from the connect frames + */ +static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, + int get) +{ + struct tsap_cb *self; + + self = instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); + + if (get) + param->pv.i = self->tx_max_sdu_size; + else + self->tx_max_sdu_size = param->pv.i; + + pr_debug("%s(), MaxSduSize=%d\n", __func__, param->pv.i); + + return 0; +} + +/*************************** CLIENT CALLS ***************************/ +/************************** LMP CALLBACKS **************************/ +/* Everything is happily mixed up. Waiting for next clean up - Jean II */ + +/* + * Initialization, that has to be done on new tsap + * instance allocation and on duplication + */ +static void irttp_init_tsap(struct tsap_cb *tsap) +{ + spin_lock_init(&tsap->lock); + init_timer(&tsap->todo_timer); + + skb_queue_head_init(&tsap->rx_queue); + skb_queue_head_init(&tsap->tx_queue); + skb_queue_head_init(&tsap->rx_fragments); +} + +/* + * Function irttp_open_tsap (stsap, notify) + * + * Create TSAP connection endpoint, + */ +struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) +{ + struct tsap_cb *self; + struct lsap_cb *lsap; + notify_t ttp_notify; + + IRDA_ASSERT(irttp->magic == TTP_MAGIC, return NULL;); + + /* The IrLMP spec (IrLMP 1.1 p10) says that we have the right to + * use only 0x01-0x6F. Of course, we can use LSAP_ANY as well. + * JeanII */ + if ((stsap_sel != LSAP_ANY) && + ((stsap_sel < 0x01) || (stsap_sel >= 0x70))) { + pr_debug("%s(), invalid tsap!\n", __func__); + return NULL; + } + + self = kzalloc(sizeof(struct tsap_cb), GFP_ATOMIC); + if (self == NULL) + return NULL; + + /* Initialize internal objects */ + irttp_init_tsap(self); + + /* Initialise todo timer */ + self->todo_timer.data = (unsigned long) self; + self->todo_timer.function = &irttp_todo_expired; + + /* Initialize callbacks for IrLMP to use */ + irda_notify_init(&ttp_notify); + ttp_notify.connect_confirm = irttp_connect_confirm; + ttp_notify.connect_indication = irttp_connect_indication; + ttp_notify.disconnect_indication = irttp_disconnect_indication; + ttp_notify.data_indication = irttp_data_indication; + ttp_notify.udata_indication = irttp_udata_indication; + ttp_notify.flow_indication = irttp_flow_indication; + if (notify->status_indication != NULL) + ttp_notify.status_indication = irttp_status_indication; + ttp_notify.instance = self; + strncpy(ttp_notify.name, notify->name, NOTIFY_MAX_NAME); + + self->magic = TTP_TSAP_MAGIC; + self->connected = FALSE; + + /* + * Create LSAP at IrLMP layer + */ + lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0); + if (lsap == NULL) { + pr_debug("%s: unable to allocate LSAP!!\n", __func__); + __irttp_close_tsap(self); + return NULL; + } + + /* + * If user specified LSAP_ANY as source TSAP selector, then IrLMP + * will replace it with whatever source selector which is free, so + * the stsap_sel we have might not be valid anymore + */ + self->stsap_sel = lsap->slsap_sel; + pr_debug("%s(), stsap_sel=%02x\n", __func__, self->stsap_sel); + + self->notify = *notify; + self->lsap = lsap; + + hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (long) self, NULL); + + if (credit > TTP_RX_MAX_CREDIT) + self->initial_credit = TTP_RX_MAX_CREDIT; + else + self->initial_credit = credit; + + return self; +} +EXPORT_SYMBOL(irttp_open_tsap); + +/* + * Function irttp_close (handle) + * + * Remove an instance of a TSAP. This function should only deal with the + * deallocation of the TSAP, and resetting of the TSAPs values; + * + */ +static void __irttp_close_tsap(struct tsap_cb *self) +{ + /* First make sure we're connected. */ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + + irttp_flush_queues(self); + + del_timer(&self->todo_timer); + + /* This one won't be cleaned up if we are disconnect_pend + close_pend + * and we receive a disconnect_indication */ + if (self->disconnect_skb) + dev_kfree_skb(self->disconnect_skb); + + self->connected = FALSE; + self->magic = ~TTP_TSAP_MAGIC; + + kfree(self); +} + +/* + * Function irttp_close (self) + * + * Remove TSAP from list of all TSAPs and then deallocate all resources + * associated with this TSAP + * + * Note : because we *free* the tsap structure, it is the responsibility + * of the caller to make sure we are called only once and to deal with + * possible race conditions. - Jean II + */ +int irttp_close_tsap(struct tsap_cb *self) +{ + struct tsap_cb *tsap; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); + + /* Make sure tsap has been disconnected */ + if (self->connected) { + /* Check if disconnect is not pending */ + if (!test_bit(0, &self->disconnect_pend)) { + net_warn_ratelimited("%s: TSAP still connected!\n", + __func__); + irttp_disconnect_request(self, NULL, P_NORMAL); + } + self->close_pend = TRUE; + irttp_start_todo_timer(self, HZ/10); + + return 0; /* Will be back! */ + } + + tsap = hashbin_remove(irttp->tsaps, (long) self, NULL); + + IRDA_ASSERT(tsap == self, return -1;); + + /* Close corresponding LSAP */ + if (self->lsap) { + irlmp_close_lsap(self->lsap); + self->lsap = NULL; + } + + __irttp_close_tsap(self); + + return 0; +} +EXPORT_SYMBOL(irttp_close_tsap); + +/* + * Function irttp_udata_request (self, skb) + * + * Send unreliable data on this TSAP + * + */ +int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb) +{ + int ret; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); + IRDA_ASSERT(skb != NULL, return -1;); + + /* Take shortcut on zero byte packets */ + if (skb->len == 0) { + ret = 0; + goto err; + } + + /* Check that nothing bad happens */ + if (!self->connected) { + net_warn_ratelimited("%s(), Not connected\n", __func__); + ret = -ENOTCONN; + goto err; + } + + if (skb->len > self->max_seg_size) { + net_err_ratelimited("%s(), UData is too large for IrLAP!\n", + __func__); + ret = -EMSGSIZE; + goto err; + } + + irlmp_udata_request(self->lsap, skb); + self->stats.tx_packets++; + + return 0; + +err: + dev_kfree_skb(skb); + return ret; +} +EXPORT_SYMBOL(irttp_udata_request); + + +/* + * Function irttp_data_request (handle, skb) + * + * Queue frame for transmission. If SAR is enabled, fragement the frame + * and queue the fragments for transmission + */ +int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) +{ + __u8 *frame; + int ret; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); + IRDA_ASSERT(skb != NULL, return -1;); + + pr_debug("%s() : queue len = %d\n", __func__, + skb_queue_len(&self->tx_queue)); + + /* Take shortcut on zero byte packets */ + if (skb->len == 0) { + ret = 0; + goto err; + } + + /* Check that nothing bad happens */ + if (!self->connected) { + net_warn_ratelimited("%s: Not connected\n", __func__); + ret = -ENOTCONN; + goto err; + } + + /* + * Check if SAR is disabled, and the frame is larger than what fits + * inside an IrLAP frame + */ + if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) { + net_err_ratelimited("%s: SAR disabled, and data is too large for IrLAP!\n", + __func__); + ret = -EMSGSIZE; + goto err; + } + + /* + * Check if SAR is enabled, and the frame is larger than the + * TxMaxSduSize + */ + if ((self->tx_max_sdu_size != 0) && + (self->tx_max_sdu_size != TTP_SAR_UNBOUND) && + (skb->len > self->tx_max_sdu_size)) { + net_err_ratelimited("%s: SAR enabled, but data is larger than TxMaxSduSize!\n", + __func__); + ret = -EMSGSIZE; + goto err; + } + /* + * Check if transmit queue is full + */ + if (skb_queue_len(&self->tx_queue) >= TTP_TX_MAX_QUEUE) { + /* + * Give it a chance to empty itself + */ + irttp_run_tx_queue(self); + + /* Drop packet. This error code should trigger the caller + * to resend the data in the client code - Jean II */ + ret = -ENOBUFS; + goto err; + } + + /* Queue frame, or queue frame segments */ + if ((self->tx_max_sdu_size == 0) || (skb->len < self->max_seg_size)) { + /* Queue frame */ + IRDA_ASSERT(skb_headroom(skb) >= TTP_HEADER, return -1;); + frame = skb_push(skb, TTP_HEADER); + frame[0] = 0x00; /* Clear more bit */ + + skb_queue_tail(&self->tx_queue, skb); + } else { + /* + * Fragment the frame, this function will also queue the + * fragments, we don't care about the fact the transmit + * queue may be overfilled by all the segments for a little + * while + */ + irttp_fragment_skb(self, skb); + } + + /* Check if we can accept more data from client */ + if ((!self->tx_sdu_busy) && + (skb_queue_len(&self->tx_queue) > TTP_TX_HIGH_THRESHOLD)) { + /* Tx queue filling up, so stop client. */ + if (self->notify.flow_indication) { + self->notify.flow_indication(self->notify.instance, + self, FLOW_STOP); + } + /* self->tx_sdu_busy is the state of the client. + * Update state after notifying client to avoid + * race condition with irttp_flow_indication(). + * If the queue empty itself after our test but before + * we set the flag, we will fix ourselves below in + * irttp_run_tx_queue(). + * Jean II */ + self->tx_sdu_busy = TRUE; + } + + /* Try to make some progress */ + irttp_run_tx_queue(self); + + return 0; + +err: + dev_kfree_skb(skb); + return ret; +} +EXPORT_SYMBOL(irttp_data_request); + +/* + * Function irttp_run_tx_queue (self) + * + * Transmit packets queued for transmission (if possible) + * + */ +static void irttp_run_tx_queue(struct tsap_cb *self) +{ + struct sk_buff *skb; + unsigned long flags; + int n; + + pr_debug("%s() : send_credit = %d, queue_len = %d\n", + __func__, + self->send_credit, skb_queue_len(&self->tx_queue)); + + /* Get exclusive access to the tx queue, otherwise don't touch it */ + if (irda_lock(&self->tx_queue_lock) == FALSE) + return; + + /* Try to send out frames as long as we have credits + * and as long as LAP is not full. If LAP is full, it will + * poll us through irttp_flow_indication() - Jean II */ + while ((self->send_credit > 0) && + (!irlmp_lap_tx_queue_full(self->lsap)) && + (skb = skb_dequeue(&self->tx_queue))) { + /* + * Since we can transmit and receive frames concurrently, + * the code below is a critical region and we must assure that + * nobody messes with the credits while we update them. + */ + spin_lock_irqsave(&self->lock, flags); + + n = self->avail_credit; + self->avail_credit = 0; + + /* Only room for 127 credits in frame */ + if (n > 127) { + self->avail_credit = n-127; + n = 127; + } + self->remote_credit += n; + self->send_credit--; + + spin_unlock_irqrestore(&self->lock, flags); + + /* + * More bit must be set by the data_request() or fragment() + * functions + */ + skb->data[0] |= (n & 0x7f); + + /* Detach from socket. + * The current skb has a reference to the socket that sent + * it (skb->sk). When we pass it to IrLMP, the skb will be + * stored in in IrLAP (self->wx_list). When we are within + * IrLAP, we lose the notion of socket, so we should not + * have a reference to a socket. So, we drop it here. + * + * Why does it matter ? + * When the skb is freed (kfree_skb), if it is associated + * with a socket, it release buffer space on the socket + * (through sock_wfree() and sock_def_write_space()). + * If the socket no longer exist, we may crash. Hard. + * When we close a socket, we make sure that associated packets + * in IrTTP are freed. However, we have no way to cancel + * the packet that we have passed to IrLAP. So, if a packet + * remains in IrLAP (retry on the link or else) after we + * close the socket, we are dead ! + * Jean II */ + if (skb->sk != NULL) { + /* IrSOCK application, IrOBEX, ... */ + skb_orphan(skb); + } + /* IrCOMM over IrTTP, IrLAN, ... */ + + /* Pass the skb to IrLMP - done */ + irlmp_data_request(self->lsap, skb); + self->stats.tx_packets++; + } + + /* Check if we can accept more frames from client. + * We don't want to wait until the todo timer to do that, and we + * can't use tasklets (grr...), so we are obliged to give control + * to client. That's ok, this test will be true not too often + * (max once per LAP window) and we are called from places + * where we can spend a bit of time doing stuff. - Jean II */ + if ((self->tx_sdu_busy) && + (skb_queue_len(&self->tx_queue) < TTP_TX_LOW_THRESHOLD) && + (!self->close_pend)) { + if (self->notify.flow_indication) + self->notify.flow_indication(self->notify.instance, + self, FLOW_START); + + /* self->tx_sdu_busy is the state of the client. + * We don't really have a race here, but it's always safer + * to update our state after the client - Jean II */ + self->tx_sdu_busy = FALSE; + } + + /* Reset lock */ + self->tx_queue_lock = 0; +} + +/* + * Function irttp_give_credit (self) + * + * Send a dataless flowdata TTP-PDU and give available credit to peer + * TSAP + */ +static inline void irttp_give_credit(struct tsap_cb *self) +{ + struct sk_buff *tx_skb = NULL; + unsigned long flags; + int n; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + + pr_debug("%s() send=%d,avail=%d,remote=%d\n", + __func__, + self->send_credit, self->avail_credit, self->remote_credit); + + /* Give credit to peer */ + tx_skb = alloc_skb(TTP_MAX_HEADER, GFP_ATOMIC); + if (!tx_skb) + return; + + /* Reserve space for LMP, and LAP header */ + skb_reserve(tx_skb, LMP_MAX_HEADER); + + /* + * Since we can transmit and receive frames concurrently, + * the code below is a critical region and we must assure that + * nobody messes with the credits while we update them. + */ + spin_lock_irqsave(&self->lock, flags); + + n = self->avail_credit; + self->avail_credit = 0; + + /* Only space for 127 credits in frame */ + if (n > 127) { + self->avail_credit = n - 127; + n = 127; + } + self->remote_credit += n; + + spin_unlock_irqrestore(&self->lock, flags); + + skb_put(tx_skb, 1); + tx_skb->data[0] = (__u8) (n & 0x7f); + + irlmp_data_request(self->lsap, tx_skb); + self->stats.tx_packets++; +} + +/* + * Function irttp_udata_indication (instance, sap, skb) + * + * Received some unit-data (unreliable) + * + */ +static int irttp_udata_indication(void *instance, void *sap, + struct sk_buff *skb) +{ + struct tsap_cb *self; + int err; + + self = instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); + IRDA_ASSERT(skb != NULL, return -1;); + + self->stats.rx_packets++; + + /* Just pass data to layer above */ + if (self->notify.udata_indication) { + err = self->notify.udata_indication(self->notify.instance, + self, skb); + /* Same comment as in irttp_do_data_indication() */ + if (!err) + return 0; + } + /* Either no handler, or handler returns an error */ + dev_kfree_skb(skb); + + return 0; +} + +/* + * Function irttp_data_indication (instance, sap, skb) + * + * Receive segment from IrLMP. + * + */ +static int irttp_data_indication(void *instance, void *sap, + struct sk_buff *skb) +{ + struct tsap_cb *self; + unsigned long flags; + int n; + + self = instance; + + n = skb->data[0] & 0x7f; /* Extract the credits */ + + self->stats.rx_packets++; + + /* Deal with inbound credit + * Since we can transmit and receive frames concurrently, + * the code below is a critical region and we must assure that + * nobody messes with the credits while we update them. + */ + spin_lock_irqsave(&self->lock, flags); + self->send_credit += n; + if (skb->len > 1) + self->remote_credit--; + spin_unlock_irqrestore(&self->lock, flags); + + /* + * Data or dataless packet? Dataless frames contains only the + * TTP_HEADER. + */ + if (skb->len > 1) { + /* + * We don't remove the TTP header, since we must preserve the + * more bit, so the defragment routing knows what to do + */ + skb_queue_tail(&self->rx_queue, skb); + } else { + /* Dataless flowdata TTP-PDU */ + dev_kfree_skb(skb); + } + + + /* Push data to the higher layer. + * We do it synchronously because running the todo timer for each + * receive packet would be too much overhead and latency. + * By passing control to the higher layer, we run the risk that + * it may take time or grab a lock. Most often, the higher layer + * will only put packet in a queue. + * Anyway, packets are only dripping through the IrDA, so we can + * have time before the next packet. + * Further, we are run from NET_BH, so the worse that can happen is + * us missing the optimal time to send back the PF bit in LAP. + * Jean II */ + irttp_run_rx_queue(self); + + /* We now give credits to peer in irttp_run_rx_queue(). + * We need to send credit *NOW*, otherwise we are going + * to miss the next Tx window. The todo timer may take + * a while before it's run... - Jean II */ + + /* + * If the peer device has given us some credits and we didn't have + * anyone from before, then we need to shedule the tx queue. + * We need to do that because our Tx have stopped (so we may not + * get any LAP flow indication) and the user may be stopped as + * well. - Jean II + */ + if (self->send_credit == n) { + /* Restart pushing stuff to LAP */ + irttp_run_tx_queue(self); + /* Note : we don't want to schedule the todo timer + * because it has horrible latency. No tasklets + * because the tasklet API is broken. - Jean II */ + } + + return 0; +} + +/* + * Function irttp_status_indication (self, reason) + * + * Status_indication, just pass to the higher layer... + * + */ +static void irttp_status_indication(void *instance, + LINK_STATUS link, LOCK_STATUS lock) +{ + struct tsap_cb *self; + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + + /* Check if client has already closed the TSAP and gone away */ + if (self->close_pend) + return; + + /* + * Inform service user if he has requested it + */ + if (self->notify.status_indication != NULL) + self->notify.status_indication(self->notify.instance, + link, lock); + else + pr_debug("%s(), no handler\n", __func__); +} + +/* + * Function irttp_flow_indication (self, reason) + * + * Flow_indication : IrLAP tells us to send more data. + * + */ +static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) +{ + struct tsap_cb *self; + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + + pr_debug("%s(instance=%p)\n", __func__, self); + + /* We are "polled" directly from LAP, and the LAP want to fill + * its Tx window. We want to do our best to send it data, so that + * we maximise the window. On the other hand, we want to limit the + * amount of work here so that LAP doesn't hang forever waiting + * for packets. - Jean II */ + + /* Try to send some packets. Currently, LAP calls us every time + * there is one free slot, so we will send only one packet. + * This allow the scheduler to do its round robin - Jean II */ + irttp_run_tx_queue(self); + + /* Note regarding the interraction with higher layer. + * irttp_run_tx_queue() may call the client when its queue + * start to empty, via notify.flow_indication(). Initially. + * I wanted this to happen in a tasklet, to avoid client + * grabbing the CPU, but we can't use tasklets safely. And timer + * is definitely too slow. + * This will happen only once per LAP window, and usually at + * the third packet (unless window is smaller). LAP is still + * doing mtt and sending first packet so it's sort of OK + * to do that. Jean II */ + + /* If we need to send disconnect. try to do it now */ + if (self->disconnect_pend) + irttp_start_todo_timer(self, 0); +} + +/* + * Function irttp_flow_request (self, command) + * + * This function could be used by the upper layers to tell IrTTP to stop + * delivering frames if the receive queues are starting to get full, or + * to tell IrTTP to start delivering frames again. + */ +void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow) +{ + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + + switch (flow) { + case FLOW_STOP: + pr_debug("%s(), flow stop\n", __func__); + self->rx_sdu_busy = TRUE; + break; + case FLOW_START: + pr_debug("%s(), flow start\n", __func__); + self->rx_sdu_busy = FALSE; + + /* Client say he can accept more data, try to free our + * queues ASAP - Jean II */ + irttp_run_rx_queue(self); + + break; + default: + pr_debug("%s(), Unknown flow command!\n", __func__); + } +} +EXPORT_SYMBOL(irttp_flow_request); + +/* + * Function irttp_connect_request (self, dtsap_sel, daddr, qos) + * + * Try to connect to remote destination TSAP selector + * + */ +int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel, + __u32 saddr, __u32 daddr, + struct qos_info *qos, __u32 max_sdu_size, + struct sk_buff *userdata) +{ + struct sk_buff *tx_skb; + __u8 *frame; + __u8 n; + + pr_debug("%s(), max_sdu_size=%d\n", __func__, max_sdu_size); + + IRDA_ASSERT(self != NULL, return -EBADR;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;); + + if (self->connected) { + if (userdata) + dev_kfree_skb(userdata); + return -EISCONN; + } + + /* Any userdata supplied? */ + if (userdata == NULL) { + tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, + GFP_ATOMIC); + if (!tx_skb) + return -ENOMEM; + + /* Reserve space for MUX_CONTROL and LAP header */ + skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER); + } else { + tx_skb = userdata; + /* + * Check that the client has reserved enough space for + * headers + */ + IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, + { dev_kfree_skb(userdata); return -1; }); + } + + /* Initialize connection parameters */ + self->connected = FALSE; + self->avail_credit = 0; + self->rx_max_sdu_size = max_sdu_size; + self->rx_sdu_size = 0; + self->rx_sdu_busy = FALSE; + self->dtsap_sel = dtsap_sel; + + n = self->initial_credit; + + self->remote_credit = 0; + self->send_credit = 0; + + /* + * Give away max 127 credits for now + */ + if (n > 127) { + self->avail_credit = n - 127; + n = 127; + } + + self->remote_credit = n; + + /* SAR enabled? */ + if (max_sdu_size > 0) { + IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), + { dev_kfree_skb(tx_skb); return -1; }); + + /* Insert SAR parameters */ + frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER); + + frame[0] = TTP_PARAMETERS | n; + frame[1] = 0x04; /* Length */ + frame[2] = 0x01; /* MaxSduSize */ + frame[3] = 0x02; /* Value length */ + + put_unaligned(cpu_to_be16((__u16) max_sdu_size), + (__be16 *)(frame+4)); + } else { + /* Insert plain TTP header */ + frame = skb_push(tx_skb, TTP_HEADER); + + /* Insert initial credit in frame */ + frame[0] = n & 0x7f; + } + + /* Connect with IrLMP. No QoS parameters for now */ + return irlmp_connect_request(self->lsap, dtsap_sel, saddr, daddr, qos, + tx_skb); +} +EXPORT_SYMBOL(irttp_connect_request); + +/* + * Function irttp_connect_confirm (handle, qos, skb) + * + * Service user confirms TSAP connection with peer. + * + */ +static void irttp_connect_confirm(void *instance, void *sap, + struct qos_info *qos, __u32 max_seg_size, + __u8 max_header_size, struct sk_buff *skb) +{ + struct tsap_cb *self; + int parameters; + int ret; + __u8 plen; + __u8 n; + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + self->max_seg_size = max_seg_size - TTP_HEADER; + self->max_header_size = max_header_size + TTP_HEADER; + + /* + * Check if we have got some QoS parameters back! This should be the + * negotiated QoS for the link. + */ + if (qos) { + pr_debug("IrTTP, Negotiated BAUD_RATE: %02x\n", + qos->baud_rate.bits); + pr_debug("IrTTP, Negotiated BAUD_RATE: %d bps.\n", + qos->baud_rate.value); + } + + n = skb->data[0] & 0x7f; + + pr_debug("%s(), Initial send_credit=%d\n", __func__, n); + + self->send_credit = n; + self->tx_max_sdu_size = 0; + self->connected = TRUE; + + parameters = skb->data[0] & 0x80; + + IRDA_ASSERT(skb->len >= TTP_HEADER, return;); + skb_pull(skb, TTP_HEADER); + + if (parameters) { + plen = skb->data[0]; + + ret = irda_param_extract_all(self, skb->data+1, + IRDA_MIN(skb->len-1, plen), + ¶m_info); + + /* Any errors in the parameter list? */ + if (ret < 0) { + net_warn_ratelimited("%s: error extracting parameters\n", + __func__); + dev_kfree_skb(skb); + + /* Do not accept this connection attempt */ + return; + } + /* Remove parameters */ + skb_pull(skb, IRDA_MIN(skb->len, plen+1)); + } + + pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, + self->send_credit, self->avail_credit, self->remote_credit); + + pr_debug("%s(), MaxSduSize=%d\n", __func__, + self->tx_max_sdu_size); + + if (self->notify.connect_confirm) { + self->notify.connect_confirm(self->notify.instance, self, qos, + self->tx_max_sdu_size, + self->max_header_size, skb); + } else + dev_kfree_skb(skb); +} + +/* + * Function irttp_connect_indication (handle, skb) + * + * Some other device is connecting to this TSAP + * + */ +static void irttp_connect_indication(void *instance, void *sap, + struct qos_info *qos, __u32 max_seg_size, __u8 max_header_size, + struct sk_buff *skb) +{ + struct tsap_cb *self; + struct lsap_cb *lsap; + int parameters; + int ret; + __u8 plen; + __u8 n; + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + IRDA_ASSERT(skb != NULL, return;); + + lsap = sap; + + self->max_seg_size = max_seg_size - TTP_HEADER; + self->max_header_size = max_header_size+TTP_HEADER; + + pr_debug("%s(), TSAP sel=%02x\n", __func__, self->stsap_sel); + + /* Need to update dtsap_sel if its equal to LSAP_ANY */ + self->dtsap_sel = lsap->dlsap_sel; + + n = skb->data[0] & 0x7f; + + self->send_credit = n; + self->tx_max_sdu_size = 0; + + parameters = skb->data[0] & 0x80; + + IRDA_ASSERT(skb->len >= TTP_HEADER, return;); + skb_pull(skb, TTP_HEADER); + + if (parameters) { + plen = skb->data[0]; + + ret = irda_param_extract_all(self, skb->data+1, + IRDA_MIN(skb->len-1, plen), + ¶m_info); + + /* Any errors in the parameter list? */ + if (ret < 0) { + net_warn_ratelimited("%s: error extracting parameters\n", + __func__); + dev_kfree_skb(skb); + + /* Do not accept this connection attempt */ + return; + } + + /* Remove parameters */ + skb_pull(skb, IRDA_MIN(skb->len, plen+1)); + } + + if (self->notify.connect_indication) { + self->notify.connect_indication(self->notify.instance, self, + qos, self->tx_max_sdu_size, + self->max_header_size, skb); + } else + dev_kfree_skb(skb); +} + +/* + * Function irttp_connect_response (handle, userdata) + * + * Service user is accepting the connection, just pass it down to + * IrLMP! + * + */ +int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, + struct sk_buff *userdata) +{ + struct sk_buff *tx_skb; + __u8 *frame; + int ret; + __u8 n; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); + + pr_debug("%s(), Source TSAP selector=%02x\n", __func__, + self->stsap_sel); + + /* Any userdata supplied? */ + if (userdata == NULL) { + tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, + GFP_ATOMIC); + if (!tx_skb) + return -ENOMEM; + + /* Reserve space for MUX_CONTROL and LAP header */ + skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER); + } else { + tx_skb = userdata; + /* + * Check that the client has reserved enough space for + * headers + */ + IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, + { dev_kfree_skb(userdata); return -1; }); + } + + self->avail_credit = 0; + self->remote_credit = 0; + self->rx_max_sdu_size = max_sdu_size; + self->rx_sdu_size = 0; + self->rx_sdu_busy = FALSE; + + n = self->initial_credit; + + /* Frame has only space for max 127 credits (7 bits) */ + if (n > 127) { + self->avail_credit = n - 127; + n = 127; + } + + self->remote_credit = n; + self->connected = TRUE; + + /* SAR enabled? */ + if (max_sdu_size > 0) { + IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), + { dev_kfree_skb(tx_skb); return -1; }); + + /* Insert TTP header with SAR parameters */ + frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER); + + frame[0] = TTP_PARAMETERS | n; + frame[1] = 0x04; /* Length */ + + /* irda_param_insert(self, IRTTP_MAX_SDU_SIZE, frame+1, */ +/* TTP_SAR_HEADER, ¶m_info) */ + + frame[2] = 0x01; /* MaxSduSize */ + frame[3] = 0x02; /* Value length */ + + put_unaligned(cpu_to_be16((__u16) max_sdu_size), + (__be16 *)(frame+4)); + } else { + /* Insert TTP header */ + frame = skb_push(tx_skb, TTP_HEADER); + + frame[0] = n & 0x7f; + } + + ret = irlmp_connect_response(self->lsap, tx_skb); + + return ret; +} +EXPORT_SYMBOL(irttp_connect_response); + +/* + * Function irttp_dup (self, instance) + * + * Duplicate TSAP, can be used by servers to confirm a connection on a + * new TSAP so it can keep listening on the old one. + */ +struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance) +{ + struct tsap_cb *new; + unsigned long flags; + + /* Protect our access to the old tsap instance */ + spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags); + + /* Find the old instance */ + if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) { + pr_debug("%s(), unable to find TSAP\n", __func__); + spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); + return NULL; + } + + /* Allocate a new instance */ + new = kmemdup(orig, sizeof(struct tsap_cb), GFP_ATOMIC); + if (!new) { + pr_debug("%s(), unable to kmalloc\n", __func__); + spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); + return NULL; + } + spin_lock_init(&new->lock); + + /* We don't need the old instance any more */ + spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); + + /* Try to dup the LSAP (may fail if we were too slow) */ + new->lsap = irlmp_dup(orig->lsap, new); + if (!new->lsap) { + pr_debug("%s(), dup failed!\n", __func__); + kfree(new); + return NULL; + } + + /* Not everything should be copied */ + new->notify.instance = instance; + + /* Initialize internal objects */ + irttp_init_tsap(new); + + /* This is locked */ + hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL); + + return new; +} +EXPORT_SYMBOL(irttp_dup); + +/* + * Function irttp_disconnect_request (self) + * + * Close this connection please! If priority is high, the queued data + * segments, if any, will be deallocated first + * + */ +int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata, + int priority) +{ + int ret; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); + + /* Already disconnected? */ + if (!self->connected) { + pr_debug("%s(), already disconnected!\n", __func__); + if (userdata) + dev_kfree_skb(userdata); + return -1; + } + + /* Disconnect already pending ? + * We need to use an atomic operation to prevent reentry. This + * function may be called from various context, like user, timer + * for following a disconnect_indication() (i.e. net_bh). + * Jean II */ + if (test_and_set_bit(0, &self->disconnect_pend)) { + pr_debug("%s(), disconnect already pending\n", + __func__); + if (userdata) + dev_kfree_skb(userdata); + + /* Try to make some progress */ + irttp_run_tx_queue(self); + return -1; + } + + /* + * Check if there is still data segments in the transmit queue + */ + if (!skb_queue_empty(&self->tx_queue)) { + if (priority == P_HIGH) { + /* + * No need to send the queued data, if we are + * disconnecting right now since the data will + * not have any usable connection to be sent on + */ + pr_debug("%s(): High priority!!()\n", __func__); + irttp_flush_queues(self); + } else if (priority == P_NORMAL) { + /* + * Must delay disconnect until after all data segments + * have been sent and the tx_queue is empty + */ + /* We'll reuse this one later for the disconnect */ + self->disconnect_skb = userdata; /* May be NULL */ + + irttp_run_tx_queue(self); + + irttp_start_todo_timer(self, HZ/10); + return -1; + } + } + /* Note : we don't need to check if self->rx_queue is full and the + * state of self->rx_sdu_busy because the disconnect response will + * be sent at the LMP level (so even if the peer has its Tx queue + * full of data). - Jean II */ + + pr_debug("%s(), Disconnecting ...\n", __func__); + self->connected = FALSE; + + if (!userdata) { + struct sk_buff *tx_skb; + tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); + if (!tx_skb) + return -ENOMEM; + + /* + * Reserve space for MUX and LAP header + */ + skb_reserve(tx_skb, LMP_MAX_HEADER); + + userdata = tx_skb; + } + ret = irlmp_disconnect_request(self->lsap, userdata); + + /* The disconnect is no longer pending */ + clear_bit(0, &self->disconnect_pend); /* FALSE */ + + return ret; +} +EXPORT_SYMBOL(irttp_disconnect_request); + +/* + * Function irttp_disconnect_indication (self, reason) + * + * Disconnect indication, TSAP disconnected by peer? + * + */ +static void irttp_disconnect_indication(void *instance, void *sap, + LM_REASON reason, struct sk_buff *skb) +{ + struct tsap_cb *self; + + self = instance; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); + + /* Prevent higher layer to send more data */ + self->connected = FALSE; + + /* Check if client has already tried to close the TSAP */ + if (self->close_pend) { + /* In this case, the higher layer is probably gone. Don't + * bother it and clean up the remains - Jean II */ + if (skb) + dev_kfree_skb(skb); + irttp_close_tsap(self); + return; + } + + /* If we are here, we assume that is the higher layer is still + * waiting for the disconnect notification and able to process it, + * even if he tried to disconnect. Otherwise, it would have already + * attempted to close the tsap and self->close_pend would be TRUE. + * Jean II */ + + /* No need to notify the client if has already tried to disconnect */ + if (self->notify.disconnect_indication) + self->notify.disconnect_indication(self->notify.instance, self, + reason, skb); + else + if (skb) + dev_kfree_skb(skb); +} + +/* + * Function irttp_do_data_indication (self, skb) + * + * Try to deliver reassembled skb to layer above, and requeue it if that + * for some reason should fail. We mark rx sdu as busy to apply back + * pressure is necessary. + */ +static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb) +{ + int err; + + /* Check if client has already closed the TSAP and gone away */ + if (self->close_pend) { + dev_kfree_skb(skb); + return; + } + + err = self->notify.data_indication(self->notify.instance, self, skb); + + /* Usually the layer above will notify that it's input queue is + * starting to get filled by using the flow request, but this may + * be difficult, so it can instead just refuse to eat it and just + * give an error back + */ + if (err) { + pr_debug("%s() requeueing skb!\n", __func__); + + /* Make sure we take a break */ + self->rx_sdu_busy = TRUE; + + /* Need to push the header in again */ + skb_push(skb, TTP_HEADER); + skb->data[0] = 0x00; /* Make sure MORE bit is cleared */ + + /* Put skb back on queue */ + skb_queue_head(&self->rx_queue, skb); + } +} + +/* + * Function irttp_run_rx_queue (self) + * + * Check if we have any frames to be transmitted, or if we have any + * available credit to give away. + */ +static void irttp_run_rx_queue(struct tsap_cb *self) +{ + struct sk_buff *skb; + int more = 0; + + pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, + self->send_credit, self->avail_credit, self->remote_credit); + + /* Get exclusive access to the rx queue, otherwise don't touch it */ + if (irda_lock(&self->rx_queue_lock) == FALSE) + return; + + /* + * Reassemble all frames in receive queue and deliver them + */ + while (!self->rx_sdu_busy && (skb = skb_dequeue(&self->rx_queue))) { + /* This bit will tell us if it's the last fragment or not */ + more = skb->data[0] & 0x80; + + /* Remove TTP header */ + skb_pull(skb, TTP_HEADER); + + /* Add the length of the remaining data */ + self->rx_sdu_size += skb->len; + + /* + * If SAR is disabled, or user has requested no reassembly + * of received fragments then we just deliver them + * immediately. This can be requested by clients that + * implements byte streams without any message boundaries + */ + if (self->rx_max_sdu_size == TTP_SAR_DISABLE) { + irttp_do_data_indication(self, skb); + self->rx_sdu_size = 0; + + continue; + } + + /* Check if this is a fragment, and not the last fragment */ + if (more) { + /* + * Queue the fragment if we still are within the + * limits of the maximum size of the rx_sdu + */ + if (self->rx_sdu_size <= self->rx_max_sdu_size) { + pr_debug("%s(), queueing frag\n", + __func__); + skb_queue_tail(&self->rx_fragments, skb); + } else { + /* Free the part of the SDU that is too big */ + dev_kfree_skb(skb); + } + continue; + } + /* + * This is the last fragment, so time to reassemble! + */ + if ((self->rx_sdu_size <= self->rx_max_sdu_size) || + (self->rx_max_sdu_size == TTP_SAR_UNBOUND)) { + /* + * A little optimizing. Only queue the fragment if + * there are other fragments. Since if this is the + * last and only fragment, there is no need to + * reassemble :-) + */ + if (!skb_queue_empty(&self->rx_fragments)) { + skb_queue_tail(&self->rx_fragments, + skb); + + skb = irttp_reassemble_skb(self); + } + + /* Now we can deliver the reassembled skb */ + irttp_do_data_indication(self, skb); + } else { + pr_debug("%s(), Truncated frame\n", __func__); + + /* Free the part of the SDU that is too big */ + dev_kfree_skb(skb); + + /* Deliver only the valid but truncated part of SDU */ + skb = irttp_reassemble_skb(self); + + irttp_do_data_indication(self, skb); + } + self->rx_sdu_size = 0; + } + + /* + * It's not trivial to keep track of how many credits are available + * by incrementing at each packet, because delivery may fail + * (irttp_do_data_indication() may requeue the frame) and because + * we need to take care of fragmentation. + * We want the other side to send up to initial_credit packets. + * We have some frames in our queues, and we have already allowed it + * to send remote_credit. + * No need to spinlock, write is atomic and self correcting... + * Jean II + */ + self->avail_credit = (self->initial_credit - + (self->remote_credit + + skb_queue_len(&self->rx_queue) + + skb_queue_len(&self->rx_fragments))); + + /* Do we have too much credits to send to peer ? */ + if ((self->remote_credit <= TTP_RX_MIN_CREDIT) && + (self->avail_credit > 0)) { + /* Send explicit credit frame */ + irttp_give_credit(self); + /* Note : do *NOT* check if tx_queue is non-empty, that + * will produce deadlocks. I repeat : send a credit frame + * even if we have something to send in our Tx queue. + * If we have credits, it means that our Tx queue is blocked. + * + * Let's suppose the peer can't keep up with our Tx. He will + * flow control us by not sending us any credits, and we + * will stop Tx and start accumulating credits here. + * Up to the point where the peer will stop its Tx queue, + * for lack of credits. + * Let's assume the peer application is single threaded. + * It will block on Tx and never consume any Rx buffer. + * Deadlock. Guaranteed. - Jean II + */ + } + + /* Reset lock */ + self->rx_queue_lock = 0; +} + +#ifdef CONFIG_PROC_FS +struct irttp_iter_state { + int id; +}; + +static void *irttp_seq_start(struct seq_file *seq, loff_t *pos) +{ + struct irttp_iter_state *iter = seq->private; + struct tsap_cb *self; + + /* Protect our access to the tsap list */ + spin_lock_irq(&irttp->tsaps->hb_spinlock); + iter->id = 0; + + for (self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps); + self != NULL; + self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps)) { + if (iter->id == *pos) + break; + ++iter->id; + } + + return self; +} + +static void *irttp_seq_next(struct seq_file *seq, void *v, loff_t *pos) +{ + struct irttp_iter_state *iter = seq->private; + + ++*pos; + ++iter->id; + return (void *) hashbin_get_next(irttp->tsaps); +} + +static void irttp_seq_stop(struct seq_file *seq, void *v) +{ + spin_unlock_irq(&irttp->tsaps->hb_spinlock); +} + +static int irttp_seq_show(struct seq_file *seq, void *v) +{ + const struct irttp_iter_state *iter = seq->private; + const struct tsap_cb *self = v; + + seq_printf(seq, "TSAP %d, ", iter->id); + seq_printf(seq, "stsap_sel: %02x, ", + self->stsap_sel); + seq_printf(seq, "dtsap_sel: %02x\n", + self->dtsap_sel); + seq_printf(seq, " connected: %s, ", + self->connected ? "TRUE" : "FALSE"); + seq_printf(seq, "avail credit: %d, ", + self->avail_credit); + seq_printf(seq, "remote credit: %d, ", + self->remote_credit); + seq_printf(seq, "send credit: %d\n", + self->send_credit); + seq_printf(seq, " tx packets: %lu, ", + self->stats.tx_packets); + seq_printf(seq, "rx packets: %lu, ", + self->stats.rx_packets); + seq_printf(seq, "tx_queue len: %u ", + skb_queue_len(&self->tx_queue)); + seq_printf(seq, "rx_queue len: %u\n", + skb_queue_len(&self->rx_queue)); + seq_printf(seq, " tx_sdu_busy: %s, ", + self->tx_sdu_busy ? "TRUE" : "FALSE"); + seq_printf(seq, "rx_sdu_busy: %s\n", + self->rx_sdu_busy ? "TRUE" : "FALSE"); + seq_printf(seq, " max_seg_size: %u, ", + self->max_seg_size); + seq_printf(seq, "tx_max_sdu_size: %u, ", + self->tx_max_sdu_size); + seq_printf(seq, "rx_max_sdu_size: %u\n", + self->rx_max_sdu_size); + + seq_printf(seq, " Used by (%s)\n\n", + self->notify.name); + return 0; +} + +static const struct seq_operations irttp_seq_ops = { + .start = irttp_seq_start, + .next = irttp_seq_next, + .stop = irttp_seq_stop, + .show = irttp_seq_show, +}; + +static int irttp_seq_open(struct inode *inode, struct file *file) +{ + return seq_open_private(file, &irttp_seq_ops, + sizeof(struct irttp_iter_state)); +} + +const struct file_operations irttp_seq_fops = { + .owner = THIS_MODULE, + .open = irttp_seq_open, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release_private, +}; + +#endif /* PROC_FS */ diff --git a/drivers/staging/irda/net/parameters.c b/drivers/staging/irda/net/parameters.c new file mode 100644 index 000000000000..16ce32ffe004 --- /dev/null +++ b/drivers/staging/irda/net/parameters.c @@ -0,0 +1,584 @@ +/********************************************************************* + * + * Filename: parameters.c + * Version: 1.0 + * Description: A more general way to handle (pi,pl,pv) parameters + * Status: Experimental. + * Author: Dag Brattli + * Created at: Mon Jun 7 10:25:11 1999 + * Modified at: Sun Jan 30 14:08:39 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include +#include + +#include +#include + +#include +#include + +static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func); +static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func); +static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func); +static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func); + +static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func); +static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func); + +static int irda_param_unpack(__u8 *buf, char *fmt, ...); + +/* Parameter value call table. Must match PV_TYPE */ +static const PV_HANDLER pv_extract_table[] = { + irda_extract_integer, /* Handler for any length integers */ + irda_extract_integer, /* Handler for 8 bits integers */ + irda_extract_integer, /* Handler for 16 bits integers */ + irda_extract_string, /* Handler for strings */ + irda_extract_integer, /* Handler for 32 bits integers */ + irda_extract_octseq, /* Handler for octet sequences */ + irda_extract_no_value /* Handler for no value parameters */ +}; + +static const PV_HANDLER pv_insert_table[] = { + irda_insert_integer, /* Handler for any length integers */ + irda_insert_integer, /* Handler for 8 bits integers */ + irda_insert_integer, /* Handler for 16 bits integers */ + NULL, /* Handler for strings */ + irda_insert_integer, /* Handler for 32 bits integers */ + NULL, /* Handler for octet sequences */ + irda_insert_no_value /* Handler for no value parameters */ +}; + +/* + * Function irda_insert_no_value (self, buf, len, pi, type, func) + */ +static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func) +{ + irda_param_t p; + int ret; + + p.pi = pi; + p.pl = 0; + + /* Call handler for this parameter */ + ret = (*func)(self, &p, PV_GET); + + /* Extract values anyway, since handler may need them */ + irda_param_pack(buf, "bb", p.pi, p.pl); + + if (ret < 0) + return ret; + + return 2; /* Inserted pl+2 bytes */ +} + +/* + * Function irda_extract_no_value (self, buf, len, type, func) + * + * Extracts a parameter without a pv field (pl=0) + * + */ +static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func) +{ + irda_param_t p; + int ret; + + /* Extract values anyway, since handler may need them */ + irda_param_unpack(buf, "bb", &p.pi, &p.pl); + + /* Call handler for this parameter */ + ret = (*func)(self, &p, PV_PUT); + + if (ret < 0) + return ret; + + return 2; /* Extracted pl+2 bytes */ +} + +/* + * Function irda_insert_integer (self, buf, len, pi, type, func) + */ +static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func) +{ + irda_param_t p; + int n = 0; + int err; + + p.pi = pi; /* In case handler needs to know */ + p.pl = type & PV_MASK; /* The integer type codes the length as well */ + p.pv.i = 0; /* Clear value */ + + /* Call handler for this parameter */ + err = (*func)(self, &p, PV_GET); + if (err < 0) + return err; + + /* + * If parameter length is still 0, then (1) this is an any length + * integer, and (2) the handler function does not care which length + * we choose to use, so we pick the one the gives the fewest bytes. + */ + if (p.pl == 0) { + if (p.pv.i < 0xff) { + pr_debug("%s(), using 1 byte\n", __func__); + p.pl = 1; + } else if (p.pv.i < 0xffff) { + pr_debug("%s(), using 2 bytes\n", __func__); + p.pl = 2; + } else { + pr_debug("%s(), using 4 bytes\n", __func__); + p.pl = 4; /* Default length */ + } + } + /* Check if buffer is long enough for insertion */ + if (len < (2+p.pl)) { + net_warn_ratelimited("%s: buffer too short for insertion!\n", + __func__); + return -1; + } + pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__, + p.pi, p.pl, p.pv.i); + switch (p.pl) { + case 1: + n += irda_param_pack(buf, "bbb", p.pi, p.pl, (__u8) p.pv.i); + break; + case 2: + if (type & PV_BIG_ENDIAN) + p.pv.i = cpu_to_be16((__u16) p.pv.i); + else + p.pv.i = cpu_to_le16((__u16) p.pv.i); + n += irda_param_pack(buf, "bbs", p.pi, p.pl, (__u16) p.pv.i); + break; + case 4: + if (type & PV_BIG_ENDIAN) + cpu_to_be32s(&p.pv.i); + else + cpu_to_le32s(&p.pv.i); + n += irda_param_pack(buf, "bbi", p.pi, p.pl, p.pv.i); + + break; + default: + net_warn_ratelimited("%s: length %d not supported\n", + __func__, p.pl); + /* Skip parameter */ + return -1; + } + + return p.pl+2; /* Inserted pl+2 bytes */ +} + +/* + * Function irda_extract integer (self, buf, len, pi, type, func) + * + * Extract a possibly variable length integer from buffer, and call + * handler for processing of the parameter + */ +static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func) +{ + irda_param_t p; + int n = 0; + int extract_len; /* Real length we extract */ + int err; + + p.pi = pi; /* In case handler needs to know */ + p.pl = buf[1]; /* Extract length of value */ + p.pv.i = 0; /* Clear value */ + extract_len = p.pl; /* Default : extract all */ + + /* Check if buffer is long enough for parsing */ + if (len < (2+p.pl)) { + net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", + __func__, p.pl, len); + return -1; + } + + /* + * Check that the integer length is what we expect it to be. If the + * handler want a 16 bits integer then a 32 bits is not good enough + * PV_INTEGER means that the handler is flexible. + */ + if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) { + net_err_ratelimited("%s: invalid parameter length! Expected %d bytes, but value had %d bytes!\n", + __func__, type & PV_MASK, p.pl); + + /* Most parameters are bit/byte fields or little endian, + * so it's ok to only extract a subset of it (the subset + * that the handler expect). This is necessary, as some + * broken implementations seems to add extra undefined bits. + * If the parameter is shorter than we expect or is big + * endian, we can't play those tricks. Jean II */ + if((p.pl < (type & PV_MASK)) || (type & PV_BIG_ENDIAN)) { + /* Skip parameter */ + return p.pl+2; + } else { + /* Extract subset of it, fallthrough */ + extract_len = type & PV_MASK; + } + } + + + switch (extract_len) { + case 1: + n += irda_param_unpack(buf+2, "b", &p.pv.i); + break; + case 2: + n += irda_param_unpack(buf+2, "s", &p.pv.i); + if (type & PV_BIG_ENDIAN) + p.pv.i = be16_to_cpu((__u16) p.pv.i); + else + p.pv.i = le16_to_cpu((__u16) p.pv.i); + break; + case 4: + n += irda_param_unpack(buf+2, "i", &p.pv.i); + if (type & PV_BIG_ENDIAN) + be32_to_cpus(&p.pv.i); + else + le32_to_cpus(&p.pv.i); + break; + default: + net_warn_ratelimited("%s: length %d not supported\n", + __func__, p.pl); + + /* Skip parameter */ + return p.pl+2; + } + + pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__, + p.pi, p.pl, p.pv.i); + /* Call handler for this parameter */ + err = (*func)(self, &p, PV_PUT); + if (err < 0) + return err; + + return p.pl+2; /* Extracted pl+2 bytes */ +} + +/* + * Function irda_extract_string (self, buf, len, type, func) + */ +static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func) +{ + char str[33]; + irda_param_t p; + int err; + + p.pi = pi; /* In case handler needs to know */ + p.pl = buf[1]; /* Extract length of value */ + if (p.pl > 32) + p.pl = 32; + + pr_debug("%s(), pi=%#x, pl=%d\n", __func__, + p.pi, p.pl); + + /* Check if buffer is long enough for parsing */ + if (len < (2+p.pl)) { + net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", + __func__, p.pl, len); + return -1; + } + + /* Should be safe to copy string like this since we have already + * checked that the buffer is long enough */ + strncpy(str, buf+2, p.pl); + + pr_debug("%s(), str=0x%02x 0x%02x\n", + __func__, (__u8)str[0], (__u8)str[1]); + + /* Null terminate string */ + str[p.pl] = '\0'; + + p.pv.c = str; /* Handler will need to take a copy */ + + /* Call handler for this parameter */ + err = (*func)(self, &p, PV_PUT); + if (err < 0) + return err; + + return p.pl+2; /* Extracted pl+2 bytes */ +} + +/* + * Function irda_extract_octseq (self, buf, len, type, func) + */ +static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi, + PV_TYPE type, PI_HANDLER func) +{ + irda_param_t p; + + p.pi = pi; /* In case handler needs to know */ + p.pl = buf[1]; /* Extract length of value */ + + /* Check if buffer is long enough for parsing */ + if (len < (2+p.pl)) { + net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", + __func__, p.pl, len); + return -1; + } + + pr_debug("%s(), not impl\n", __func__); + + return p.pl+2; /* Extracted pl+2 bytes */ +} + +/* + * Function irda_param_pack (skb, fmt, ...) + * + * Format: + * 'i' = 32 bits integer + * 's' = string + * + */ +int irda_param_pack(__u8 *buf, char *fmt, ...) +{ + irda_pv_t arg; + va_list args; + char *p; + int n = 0; + + va_start(args, fmt); + + for (p = fmt; *p != '\0'; p++) { + switch (*p) { + case 'b': /* 8 bits unsigned byte */ + buf[n++] = (__u8)va_arg(args, int); + break; + case 's': /* 16 bits unsigned short */ + arg.i = (__u16)va_arg(args, int); + put_unaligned((__u16)arg.i, (__u16 *)(buf+n)); n+=2; + break; + case 'i': /* 32 bits unsigned integer */ + arg.i = va_arg(args, __u32); + put_unaligned(arg.i, (__u32 *)(buf+n)); n+=4; + break; +#if 0 + case 'c': /* \0 terminated string */ + arg.c = va_arg(args, char *); + strcpy(buf+n, arg.c); + n += strlen(arg.c) + 1; + break; +#endif + default: + va_end(args); + return -1; + } + } + va_end(args); + + return 0; +} +EXPORT_SYMBOL(irda_param_pack); + +/* + * Function irda_param_unpack (skb, fmt, ...) + */ +static int irda_param_unpack(__u8 *buf, char *fmt, ...) +{ + irda_pv_t arg; + va_list args; + char *p; + int n = 0; + + va_start(args, fmt); + + for (p = fmt; *p != '\0'; p++) { + switch (*p) { + case 'b': /* 8 bits byte */ + arg.ip = va_arg(args, __u32 *); + *arg.ip = buf[n++]; + break; + case 's': /* 16 bits short */ + arg.ip = va_arg(args, __u32 *); + *arg.ip = get_unaligned((__u16 *)(buf+n)); n+=2; + break; + case 'i': /* 32 bits unsigned integer */ + arg.ip = va_arg(args, __u32 *); + *arg.ip = get_unaligned((__u32 *)(buf+n)); n+=4; + break; +#if 0 + case 'c': /* \0 terminated string */ + arg.c = va_arg(args, char *); + strcpy(arg.c, buf+n); + n += strlen(arg.c) + 1; + break; +#endif + default: + va_end(args); + return -1; + } + + } + va_end(args); + + return 0; +} + +/* + * Function irda_param_insert (self, pi, buf, len, info) + * + * Insert the specified parameter (pi) into buffer. Returns number of + * bytes inserted + */ +int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len, + pi_param_info_t *info) +{ + const pi_minor_info_t *pi_minor_info; + __u8 pi_minor; + __u8 pi_major; + int type; + int ret = -1; + int n = 0; + + IRDA_ASSERT(buf != NULL, return ret;); + IRDA_ASSERT(info != NULL, return ret;); + + pi_minor = pi & info->pi_mask; + pi_major = pi >> info->pi_major_offset; + + /* Check if the identifier value (pi) is valid */ + if ((pi_major > info->len-1) || + (pi_minor > info->tables[pi_major].len-1)) + { + pr_debug("%s(), no handler for parameter=0x%02x\n", + __func__, pi); + + /* Skip this parameter */ + return -1; + } + + /* Lookup the info on how to parse this parameter */ + pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor]; + + /* Find expected data type for this parameter identifier (pi)*/ + type = pi_minor_info->type; + + /* Check if handler has been implemented */ + if (!pi_minor_info->func) { + net_info_ratelimited("%s: no handler for pi=%#x\n", + __func__, pi); + /* Skip this parameter */ + return -1; + } + + /* Insert parameter value */ + ret = (*pv_insert_table[type & PV_MASK])(self, buf+n, len, pi, type, + pi_minor_info->func); + return ret; +} +EXPORT_SYMBOL(irda_param_insert); + +/* + * Function irda_param_extract (self, buf, len, info) + * + * Parse all parameters. If len is correct, then everything should be + * safe. Returns the number of bytes that was parsed + * + */ +static int irda_param_extract(void *self, __u8 *buf, int len, + pi_param_info_t *info) +{ + const pi_minor_info_t *pi_minor_info; + __u8 pi_minor; + __u8 pi_major; + int type; + int ret = -1; + int n = 0; + + IRDA_ASSERT(buf != NULL, return ret;); + IRDA_ASSERT(info != NULL, return ret;); + + pi_minor = buf[n] & info->pi_mask; + pi_major = buf[n] >> info->pi_major_offset; + + /* Check if the identifier value (pi) is valid */ + if ((pi_major > info->len-1) || + (pi_minor > info->tables[pi_major].len-1)) + { + pr_debug("%s(), no handler for parameter=0x%02x\n", + __func__, buf[0]); + + /* Skip this parameter */ + return 2 + buf[n + 1]; /* Continue */ + } + + /* Lookup the info on how to parse this parameter */ + pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor]; + + /* Find expected data type for this parameter identifier (pi)*/ + type = pi_minor_info->type; + + pr_debug("%s(), pi=[%d,%d], type=%d\n", __func__, + pi_major, pi_minor, type); + + /* Check if handler has been implemented */ + if (!pi_minor_info->func) { + net_info_ratelimited("%s: no handler for pi=%#x\n", + __func__, buf[n]); + /* Skip this parameter */ + return 2 + buf[n + 1]; /* Continue */ + } + + /* Parse parameter value */ + ret = (*pv_extract_table[type & PV_MASK])(self, buf+n, len, buf[n], + type, pi_minor_info->func); + return ret; +} + +/* + * Function irda_param_extract_all (self, buf, len, info) + * + * Parse all parameters. If len is correct, then everything should be + * safe. Returns the number of bytes that was parsed + * + */ +int irda_param_extract_all(void *self, __u8 *buf, int len, + pi_param_info_t *info) +{ + int ret = -1; + int n = 0; + + IRDA_ASSERT(buf != NULL, return ret;); + IRDA_ASSERT(info != NULL, return ret;); + + /* + * Parse all parameters. Each parameter must be at least two bytes + * long or else there is no point in trying to parse it + */ + while (len > 2) { + ret = irda_param_extract(self, buf+n, len, info); + if (ret < 0) + return ret; + + n += ret; + len -= ret; + } + return n; +} +EXPORT_SYMBOL(irda_param_extract_all); diff --git a/drivers/staging/irda/net/qos.c b/drivers/staging/irda/net/qos.c new file mode 100644 index 000000000000..25ba8509ad3e --- /dev/null +++ b/drivers/staging/irda/net/qos.c @@ -0,0 +1,771 @@ +/********************************************************************* + * + * Filename: qos.c + * Version: 1.0 + * Description: IrLAP QoS parameter negotiation + * Status: Stable + * Author: Dag Brattli + * Created at: Tue Sep 9 00:00:26 1997 + * Modified at: Sun Jan 30 14:29:16 2000 + * Modified by: Dag Brattli + * + * Copyright (c) 1998-2000 Dag Brattli , + * All Rights Reserved. + * Copyright (c) 2000-2001 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * 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, see . + * + ********************************************************************/ + +#include + +#include + +#include +#include +#include +#include +#include + +/* + * Maximum values of the baud rate we negotiate with the other end. + * Most often, you don't have to change that, because Linux-IrDA will + * use the maximum offered by the link layer, which usually works fine. + * In some very rare cases, you may want to limit it to lower speeds... + */ +int sysctl_max_baud_rate = 16000000; +/* + * Maximum value of the lap disconnect timer we negotiate with the other end. + * Most often, the value below represent the best compromise, but some user + * may want to keep the LAP alive longer or shorter in case of link failure. + * Remember that the threshold time (early warning) is fixed to 3s... + */ +int sysctl_max_noreply_time = 12; +/* + * Minimum turn time to be applied before transmitting to the peer. + * Nonzero values (usec) are used as lower limit to the per-connection + * mtt value which was announced by the other end during negotiation. + * Might be helpful if the peer device provides too short mtt. + * Default is 10us which means using the unmodified value given by the + * peer except if it's 0 (0 is likely a bug in the other stack). + */ +unsigned int sysctl_min_tx_turn_time = 10; +/* + * Maximum data size to be used in transmission in payload of LAP frame. + * There is a bit of confusion in the IrDA spec : + * The LAP spec defines the payload of a LAP frame (I field) to be + * 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40). + * On the other hand, the PHY mention frames of 2048 bytes max (IrPHY + * 1.2, chapt 5.3.2.1, p41). But, this number includes the LAP header + * (2 bytes), and CRC (32 bits at 4 Mb/s). So, for the I field (LAP + * payload), that's only 2042 bytes. Oups ! + * My nsc-ircc hardware has troubles receiving 2048 bytes frames at 4 Mb/s, + * so adjust to 2042... I don't know if this bug applies only for 2048 + * bytes frames or all negotiated frame sizes, but you can use the sysctl + * to play with this value anyway. + * Jean II */ +unsigned int sysctl_max_tx_data_size = 2042; +/* + * Maximum transmit window, i.e. number of LAP frames between turn-around. + * This allow to override what the peer told us. Some peers are buggy and + * don't always support what they tell us. + * Jean II */ +unsigned int sysctl_max_tx_window = 7; + +static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get); +static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, + int get); +static int irlap_param_max_turn_time(void *instance, irda_param_t *param, + int get); +static int irlap_param_data_size(void *instance, irda_param_t *param, int get); +static int irlap_param_window_size(void *instance, irda_param_t *param, + int get); +static int irlap_param_additional_bofs(void *instance, irda_param_t *parm, + int get); +static int irlap_param_min_turn_time(void *instance, irda_param_t *param, + int get); + +#ifndef CONFIG_IRDA_DYNAMIC_WINDOW +static __u32 irlap_requested_line_capacity(struct qos_info *qos); +#endif + +static __u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */ +static __u32 baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, + 1152000, 4000000, 16000000 }; /* bps */ +static __u32 data_sizes[] = { 64, 128, 256, 512, 1024, 2048 }; /* bytes */ +static __u32 add_bofs[] = { 48, 24, 12, 5, 3, 2, 1, 0 }; /* bytes */ +static __u32 max_turn_times[] = { 500, 250, 100, 50 }; /* ms */ +static __u32 link_disc_times[] = { 3, 8, 12, 16, 20, 25, 30, 40 }; /* secs */ + +static __u32 max_line_capacities[10][4] = { + /* 500 ms 250 ms 100 ms 50 ms (max turn time) */ + { 100, 0, 0, 0 }, /* 2400 bps */ + { 400, 0, 0, 0 }, /* 9600 bps */ + { 800, 0, 0, 0 }, /* 19200 bps */ + { 1600, 0, 0, 0 }, /* 38400 bps */ + { 2360, 0, 0, 0 }, /* 57600 bps */ + { 4800, 2400, 960, 480 }, /* 115200 bps */ + { 28800, 11520, 5760, 2880 }, /* 576000 bps */ + { 57600, 28800, 11520, 5760 }, /* 1152000 bps */ + { 200000, 100000, 40000, 20000 }, /* 4000000 bps */ + { 800000, 400000, 160000, 80000 }, /* 16000000 bps */ +}; + +static const pi_minor_info_t pi_minor_call_table_type_0[] = { + { NULL, 0 }, +/* 01 */{ irlap_param_baud_rate, PV_INTEGER | PV_LITTLE_ENDIAN }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, + { NULL, 0 }, +/* 08 */{ irlap_param_link_disconnect, PV_INT_8_BITS } +}; + +static const pi_minor_info_t pi_minor_call_table_type_1[] = { + { NULL, 0 }, + { NULL, 0 }, +/* 82 */{ irlap_param_max_turn_time, PV_INT_8_BITS }, +/* 83 */{ irlap_param_data_size, PV_INT_8_BITS }, +/* 84 */{ irlap_param_window_size, PV_INT_8_BITS }, +/* 85 */{ irlap_param_additional_bofs, PV_INT_8_BITS }, +/* 86 */{ irlap_param_min_turn_time, PV_INT_8_BITS }, +}; + +static const pi_major_info_t pi_major_call_table[] = { + { pi_minor_call_table_type_0, 9 }, + { pi_minor_call_table_type_1, 7 }, +}; + +static pi_param_info_t irlap_param_info = { pi_major_call_table, 2, 0x7f, 7 }; + +/* ---------------------- LOCAL SUBROUTINES ---------------------- */ +/* Note : we start with a bunch of local subroutines. + * As the compiler is "one pass", this is the only way to get them to + * inline properly... + * Jean II + */ +/* + * Function value_index (value, array, size) + * + * Returns the index to the value in the specified array + */ +static inline int value_index(__u32 value, __u32 *array, int size) +{ + int i; + + for (i=0; i < size; i++) + if (array[i] == value) + break; + return i; +} + +/* + * Function index_value (index, array) + * + * Returns value to index in array, easy! + * + */ +static inline __u32 index_value(int index, __u32 *array) +{ + return array[index]; +} + +/* + * Function msb_index (word) + * + * Returns index to most significant bit (MSB) in word + * + */ +static int msb_index (__u16 word) +{ + __u16 msb = 0x8000; + int index = 15; /* Current MSB */ + + /* Check for buggy peers. + * Note : there is a small probability that it could be us, but I + * would expect driver authors to catch that pretty early and be + * able to check precisely what's going on. If a end user sees this, + * it's very likely the peer. - Jean II */ + if (word == 0) { + net_warn_ratelimited("%s(), Detected buggy peer, adjust null PV to 0x1!\n", + __func__); + /* The only safe choice (we don't know the array size) */ + word = 0x1; + } + + while (msb) { + if (word & msb) + break; /* Found it! */ + msb >>=1; + index--; + } + return index; +} + +/* + * Function value_lower_bits (value, array) + * + * Returns a bit field marking all possibility lower than value. + */ +static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field) +{ + int i; + __u16 mask = 0x1; + __u16 result = 0x0; + + for (i=0; i < size; i++) { + /* Add the current value to the bit field, shift mask */ + result |= mask; + mask <<= 1; + /* Finished ? */ + if (array[i] >= value) + break; + } + /* Send back a valid index */ + if(i >= size) + i = size - 1; /* Last item */ + *field = result; + return i; +} + +/* + * Function value_highest_bit (value, array) + * + * Returns a bit field marking the highest possibility lower than value. + */ +static inline int value_highest_bit(__u32 value, __u32 *array, int size, __u16 *field) +{ + int i; + __u16 mask = 0x1; + __u16 result = 0x0; + + for (i=0; i < size; i++) { + /* Finished ? */ + if (array[i] <= value) + break; + /* Shift mask */ + mask <<= 1; + } + /* Set the current value to the bit field */ + result |= mask; + /* Send back a valid index */ + if(i >= size) + i = size - 1; /* Last item */ + *field = result; + return i; +} + +/* -------------------------- MAIN CALLS -------------------------- */ + +/* + * Function irda_qos_compute_intersection (qos, new) + * + * Compute the intersection of the old QoS capabilities with new ones + * + */ +void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new) +{ + IRDA_ASSERT(qos != NULL, return;); + IRDA_ASSERT(new != NULL, return;); + + /* Apply */ + qos->baud_rate.bits &= new->baud_rate.bits; + qos->window_size.bits &= new->window_size.bits; + qos->min_turn_time.bits &= new->min_turn_time.bits; + qos->max_turn_time.bits &= new->max_turn_time.bits; + qos->data_size.bits &= new->data_size.bits; + qos->link_disc_time.bits &= new->link_disc_time.bits; + qos->additional_bofs.bits &= new->additional_bofs.bits; + + irda_qos_bits_to_value(qos); +} + +/* + * Function irda_init_max_qos_capabilies (qos) + * + * The purpose of this function is for layers and drivers to be able to + * set the maximum QoS possible and then "and in" their own limitations + * + */ +void irda_init_max_qos_capabilies(struct qos_info *qos) +{ + int i; + /* + * These are the maximum supported values as specified on pages + * 39-43 in IrLAP + */ + + /* Use sysctl to set some configurable values... */ + /* Set configured max speed */ + i = value_lower_bits(sysctl_max_baud_rate, baud_rates, 10, + &qos->baud_rate.bits); + sysctl_max_baud_rate = index_value(i, baud_rates); + + /* Set configured max disc time */ + i = value_lower_bits(sysctl_max_noreply_time, link_disc_times, 8, + &qos->link_disc_time.bits); + sysctl_max_noreply_time = index_value(i, link_disc_times); + + /* LSB is first byte, MSB is second byte */ + qos->baud_rate.bits &= 0x03ff; + + qos->window_size.bits = 0x7f; + qos->min_turn_time.bits = 0xff; + qos->max_turn_time.bits = 0x0f; + qos->data_size.bits = 0x3f; + qos->link_disc_time.bits &= 0xff; + qos->additional_bofs.bits = 0xff; +} +EXPORT_SYMBOL(irda_init_max_qos_capabilies); + +/* + * Function irlap_adjust_qos_settings (qos) + * + * Adjust QoS settings in case some values are not possible to use because + * of other settings + */ +static void irlap_adjust_qos_settings(struct qos_info *qos) +{ + __u32 line_capacity; + int index; + + /* + * Make sure the mintt is sensible. + * Main culprit : Ericsson T39. - Jean II + */ + if (sysctl_min_tx_turn_time > qos->min_turn_time.value) { + int i; + + net_warn_ratelimited("%s(), Detected buggy peer, adjust mtt to %dus!\n", + __func__, sysctl_min_tx_turn_time); + + /* We don't really need bits, but easier this way */ + i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times, + 8, &qos->min_turn_time.bits); + sysctl_min_tx_turn_time = index_value(i, min_turn_times); + qos->min_turn_time.value = sysctl_min_tx_turn_time; + } + + /* + * Not allowed to use a max turn time less than 500 ms if the baudrate + * is less than 115200 + */ + if ((qos->baud_rate.value < 115200) && + (qos->max_turn_time.value < 500)) + { + pr_debug("%s(), adjusting max turn time from %d to 500 ms\n", + __func__, qos->max_turn_time.value); + qos->max_turn_time.value = 500; + } + + /* + * The data size must be adjusted according to the baud rate and max + * turn time + */ + index = value_index(qos->data_size.value, data_sizes, 6); + line_capacity = irlap_max_line_capacity(qos->baud_rate.value, + qos->max_turn_time.value); + +#ifdef CONFIG_IRDA_DYNAMIC_WINDOW + while ((qos->data_size.value > line_capacity) && (index > 0)) { + qos->data_size.value = data_sizes[index--]; + pr_debug("%s(), reducing data size to %d\n", + __func__, qos->data_size.value); + } +#else /* Use method described in section 6.6.11 of IrLAP */ + while (irlap_requested_line_capacity(qos) > line_capacity) { + IRDA_ASSERT(index != 0, return;); + + /* Must be able to send at least one frame */ + if (qos->window_size.value > 1) { + qos->window_size.value--; + pr_debug("%s(), reducing window size to %d\n", + __func__, qos->window_size.value); + } else if (index > 1) { + qos->data_size.value = data_sizes[index--]; + pr_debug("%s(), reducing data size to %d\n", + __func__, qos->data_size.value); + } else { + net_warn_ratelimited("%s(), nothing more we can do!\n", + __func__); + } + } +#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ + /* + * Fix tx data size according to user limits - Jean II + */ + if (qos->data_size.value > sysctl_max_tx_data_size) + /* Allow non discrete adjustement to avoid losing capacity */ + qos->data_size.value = sysctl_max_tx_data_size; + /* + * Override Tx window if user request it. - Jean II + */ + if (qos->window_size.value > sysctl_max_tx_window) + qos->window_size.value = sysctl_max_tx_window; +} + +/* + * Function irlap_negotiate (qos_device, qos_session, skb) + * + * Negotiate QoS values, not really that much negotiation :-) + * We just set the QoS capabilities for the peer station + * + */ +int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb) +{ + int ret; + + ret = irda_param_extract_all(self, skb->data, skb->len, + &irlap_param_info); + + /* Convert the negotiated bits to values */ + irda_qos_bits_to_value(&self->qos_tx); + irda_qos_bits_to_value(&self->qos_rx); + + irlap_adjust_qos_settings(&self->qos_tx); + + pr_debug("Setting BAUD_RATE to %d bps.\n", + self->qos_tx.baud_rate.value); + pr_debug("Setting DATA_SIZE to %d bytes\n", + self->qos_tx.data_size.value); + pr_debug("Setting WINDOW_SIZE to %d\n", + self->qos_tx.window_size.value); + pr_debug("Setting XBOFS to %d\n", + self->qos_tx.additional_bofs.value); + pr_debug("Setting MAX_TURN_TIME to %d ms.\n", + self->qos_tx.max_turn_time.value); + pr_debug("Setting MIN_TURN_TIME to %d usecs.\n", + self->qos_tx.min_turn_time.value); + pr_debug("Setting LINK_DISC to %d secs.\n", + self->qos_tx.link_disc_time.value); + return ret; +} + +/* + * Function irlap_insert_negotiation_params (qos, fp) + * + * Insert QoS negotiaion pararameters into frame + * + */ +int irlap_insert_qos_negotiation_params(struct irlap_cb *self, + struct sk_buff *skb) +{ + int ret; + + /* Insert data rate */ + ret = irda_param_insert(self, PI_BAUD_RATE, skb_tail_pointer(skb), + skb_tailroom(skb), &irlap_param_info); + if (ret < 0) + return ret; + skb_put(skb, ret); + + /* Insert max turnaround time */ + ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb_tail_pointer(skb), + skb_tailroom(skb), &irlap_param_info); + if (ret < 0) + return ret; + skb_put(skb, ret); + + /* Insert data size */ + ret = irda_param_insert(self, PI_DATA_SIZE, skb_tail_pointer(skb), + skb_tailroom(skb), &irlap_param_info); + if (ret < 0) + return ret; + skb_put(skb, ret); + + /* Insert window size */ + ret = irda_param_insert(self, PI_WINDOW_SIZE, skb_tail_pointer(skb), + skb_tailroom(skb), &irlap_param_info); + if (ret < 0) + return ret; + skb_put(skb, ret); + + /* Insert additional BOFs */ + ret = irda_param_insert(self, PI_ADD_BOFS, skb_tail_pointer(skb), + skb_tailroom(skb), &irlap_param_info); + if (ret < 0) + return ret; + skb_put(skb, ret); + + /* Insert minimum turnaround time */ + ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb_tail_pointer(skb), + skb_tailroom(skb), &irlap_param_info); + if (ret < 0) + return ret; + skb_put(skb, ret); + + /* Insert link disconnect/threshold time */ + ret = irda_param_insert(self, PI_LINK_DISC, skb_tail_pointer(skb), + skb_tailroom(skb), &irlap_param_info); + if (ret < 0) + return ret; + skb_put(skb, ret); + + return 0; +} + +/* + * Function irlap_param_baud_rate (instance, param, get) + * + * Negotiate data-rate + * + */ +static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get) +{ + __u16 final; + + struct irlap_cb *self = (struct irlap_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + if (get) { + param->pv.i = self->qos_rx.baud_rate.bits; + pr_debug("%s(), baud rate = 0x%02x\n", + __func__, param->pv.i); + } else { + /* + * Stations must agree on baud rate, so calculate + * intersection + */ + pr_debug("Requested BAUD_RATE: 0x%04x\n", (__u16)param->pv.i); + final = (__u16) param->pv.i & self->qos_rx.baud_rate.bits; + + pr_debug("Final BAUD_RATE: 0x%04x\n", final); + self->qos_tx.baud_rate.bits = final; + self->qos_rx.baud_rate.bits = final; + } + + return 0; +} + +/* + * Function irlap_param_link_disconnect (instance, param, get) + * + * Negotiate link disconnect/threshold time. + * + */ +static int irlap_param_link_disconnect(void *instance, irda_param_t *param, + int get) +{ + __u16 final; + + struct irlap_cb *self = (struct irlap_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + if (get) + param->pv.i = self->qos_rx.link_disc_time.bits; + else { + /* + * Stations must agree on link disconnect/threshold + * time. + */ + pr_debug("LINK_DISC: %02x\n", (__u8)param->pv.i); + final = (__u8) param->pv.i & self->qos_rx.link_disc_time.bits; + + pr_debug("Final LINK_DISC: %02x\n", final); + self->qos_tx.link_disc_time.bits = final; + self->qos_rx.link_disc_time.bits = final; + } + return 0; +} + +/* + * Function irlap_param_max_turn_time (instance, param, get) + * + * Negotiate the maximum turnaround time. This is a type 1 parameter and + * will be negotiated independently for each station + * + */ +static int irlap_param_max_turn_time(void *instance, irda_param_t *param, + int get) +{ + struct irlap_cb *self = (struct irlap_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + if (get) + param->pv.i = self->qos_rx.max_turn_time.bits; + else + self->qos_tx.max_turn_time.bits = (__u8) param->pv.i; + + return 0; +} + +/* + * Function irlap_param_data_size (instance, param, get) + * + * Negotiate the data size. This is a type 1 parameter and + * will be negotiated independently for each station + * + */ +static int irlap_param_data_size(void *instance, irda_param_t *param, int get) +{ + struct irlap_cb *self = (struct irlap_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + if (get) + param->pv.i = self->qos_rx.data_size.bits; + else + self->qos_tx.data_size.bits = (__u8) param->pv.i; + + return 0; +} + +/* + * Function irlap_param_window_size (instance, param, get) + * + * Negotiate the window size. This is a type 1 parameter and + * will be negotiated independently for each station + * + */ +static int irlap_param_window_size(void *instance, irda_param_t *param, + int get) +{ + struct irlap_cb *self = (struct irlap_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + if (get) + param->pv.i = self->qos_rx.window_size.bits; + else + self->qos_tx.window_size.bits = (__u8) param->pv.i; + + return 0; +} + +/* + * Function irlap_param_additional_bofs (instance, param, get) + * + * Negotiate additional BOF characters. This is a type 1 parameter and + * will be negotiated independently for each station. + */ +static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get) +{ + struct irlap_cb *self = (struct irlap_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + if (get) + param->pv.i = self->qos_rx.additional_bofs.bits; + else + self->qos_tx.additional_bofs.bits = (__u8) param->pv.i; + + return 0; +} + +/* + * Function irlap_param_min_turn_time (instance, param, get) + * + * Negotiate the minimum turn around time. This is a type 1 parameter and + * will be negotiated independently for each station + */ +static int irlap_param_min_turn_time(void *instance, irda_param_t *param, + int get) +{ + struct irlap_cb *self = (struct irlap_cb *) instance; + + IRDA_ASSERT(self != NULL, return -1;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); + + if (get) + param->pv.i = self->qos_rx.min_turn_time.bits; + else + self->qos_tx.min_turn_time.bits = (__u8) param->pv.i; + + return 0; +} + +/* + * Function irlap_max_line_capacity (speed, max_turn_time, min_turn_time) + * + * Calculate the maximum line capacity + * + */ +__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time) +{ + __u32 line_capacity; + int i,j; + + pr_debug("%s(), speed=%d, max_turn_time=%d\n", + __func__, speed, max_turn_time); + + i = value_index(speed, baud_rates, 10); + j = value_index(max_turn_time, max_turn_times, 4); + + IRDA_ASSERT(((i >=0) && (i <10)), return 0;); + IRDA_ASSERT(((j >=0) && (j <4)), return 0;); + + line_capacity = max_line_capacities[i][j]; + + pr_debug("%s(), line capacity=%d bytes\n", + __func__, line_capacity); + + return line_capacity; +} + +#ifndef CONFIG_IRDA_DYNAMIC_WINDOW +static __u32 irlap_requested_line_capacity(struct qos_info *qos) +{ + __u32 line_capacity; + + line_capacity = qos->window_size.value * + (qos->data_size.value + 6 + qos->additional_bofs.value) + + irlap_min_turn_time_in_bytes(qos->baud_rate.value, + qos->min_turn_time.value); + + pr_debug("%s(), requested line capacity=%d\n", + __func__, line_capacity); + + return line_capacity; +} +#endif + +void irda_qos_bits_to_value(struct qos_info *qos) +{ + int index; + + IRDA_ASSERT(qos != NULL, return;); + + index = msb_index(qos->baud_rate.bits); + qos->baud_rate.value = baud_rates[index]; + + index = msb_index(qos->data_size.bits); + qos->data_size.value = data_sizes[index]; + + index = msb_index(qos->window_size.bits); + qos->window_size.value = index+1; + + index = msb_index(qos->min_turn_time.bits); + qos->min_turn_time.value = min_turn_times[index]; + + index = msb_index(qos->max_turn_time.bits); + qos->max_turn_time.value = max_turn_times[index]; + + index = msb_index(qos->link_disc_time.bits); + qos->link_disc_time.value = link_disc_times[index]; + + index = msb_index(qos->additional_bofs.bits); + qos->additional_bofs.value = add_bofs[index]; +} +EXPORT_SYMBOL(irda_qos_bits_to_value); diff --git a/drivers/staging/irda/net/timer.c b/drivers/staging/irda/net/timer.c new file mode 100644 index 000000000000..f2280f73b057 --- /dev/null +++ b/drivers/staging/irda/net/timer.c @@ -0,0 +1,231 @@ +/********************************************************************* + * + * Filename: timer.c + * Version: + * Description: + * Status: Experimental. + * Author: Dag Brattli + * Created at: Sat Aug 16 00:59:29 1997 + * Modified at: Wed Dec 8 12:50:34 1999 + * Modified by: Dag Brattli + * + * Copyright (c) 1997, 1999 Dag Brattli , + * All Rights Reserved. + * Copyright (c) 2000-2002 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include + +#include +#include +#include +#include +#include + +extern int sysctl_slot_timeout; + +static void irlap_slot_timer_expired(void* data); +static void irlap_query_timer_expired(void* data); +static void irlap_final_timer_expired(void* data); +static void irlap_wd_timer_expired(void* data); +static void irlap_backoff_timer_expired(void* data); +static void irlap_media_busy_expired(void* data); + +void irlap_start_slot_timer(struct irlap_cb *self, int timeout) +{ + irda_start_timer(&self->slot_timer, timeout, (void *) self, + irlap_slot_timer_expired); +} + +void irlap_start_query_timer(struct irlap_cb *self, int S, int s) +{ + int timeout; + + /* Calculate when the peer discovery should end. Normally, we + * get the end-of-discovery frame, so this is just in case + * we miss it. + * Basically, we multiply the number of remaining slots by our + * slot time, plus add some extra time to properly receive the last + * discovery packet (which is longer due to extra discovery info), + * to avoid messing with for incoming connections requests and + * to accommodate devices that perform discovery slower than us. + * Jean II */ + timeout = msecs_to_jiffies(sysctl_slot_timeout) * (S - s) + + XIDEXTRA_TIMEOUT + SMALLBUSY_TIMEOUT; + + /* Set or re-set the timer. We reset the timer for each received + * discovery query, which allow us to automatically adjust to + * the speed of the peer discovery (faster or slower). Jean II */ + irda_start_timer( &self->query_timer, timeout, (void *) self, + irlap_query_timer_expired); +} + +void irlap_start_final_timer(struct irlap_cb *self, int timeout) +{ + irda_start_timer(&self->final_timer, timeout, (void *) self, + irlap_final_timer_expired); +} + +void irlap_start_wd_timer(struct irlap_cb *self, int timeout) +{ + irda_start_timer(&self->wd_timer, timeout, (void *) self, + irlap_wd_timer_expired); +} + +void irlap_start_backoff_timer(struct irlap_cb *self, int timeout) +{ + irda_start_timer(&self->backoff_timer, timeout, (void *) self, + irlap_backoff_timer_expired); +} + +void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout) +{ + irda_start_timer(&self->media_busy_timer, timeout, + (void *) self, irlap_media_busy_expired); +} + +void irlap_stop_mbusy_timer(struct irlap_cb *self) +{ + /* If timer is activated, kill it! */ + del_timer(&self->media_busy_timer); + + /* If we are in NDM, there is a bunch of events in LAP that + * that be pending due to the media_busy condition, such as + * CONNECT_REQUEST and SEND_UI_FRAME. If we don't generate + * an event, they will wait forever... + * Jean II */ + if (self->state == LAP_NDM) + irlap_do_event(self, MEDIA_BUSY_TIMER_EXPIRED, NULL, NULL); +} + +void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout) +{ + irda_start_timer(&self->watchdog_timer, timeout, (void *) self, + irlmp_watchdog_timer_expired); +} + +void irlmp_start_discovery_timer(struct irlmp_cb *self, int timeout) +{ + irda_start_timer(&self->discovery_timer, timeout, (void *) self, + irlmp_discovery_timer_expired); +} + +void irlmp_start_idle_timer(struct lap_cb *self, int timeout) +{ + irda_start_timer(&self->idle_timer, timeout, (void *) self, + irlmp_idle_timer_expired); +} + +void irlmp_stop_idle_timer(struct lap_cb *self) +{ + /* If timer is activated, kill it! */ + del_timer(&self->idle_timer); +} + +/* + * Function irlap_slot_timer_expired (data) + * + * IrLAP slot timer has expired + * + */ +static void irlap_slot_timer_expired(void *data) +{ + struct irlap_cb *self = (struct irlap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + irlap_do_event(self, SLOT_TIMER_EXPIRED, NULL, NULL); +} + +/* + * Function irlap_query_timer_expired (data) + * + * IrLAP query timer has expired + * + */ +static void irlap_query_timer_expired(void *data) +{ + struct irlap_cb *self = (struct irlap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + irlap_do_event(self, QUERY_TIMER_EXPIRED, NULL, NULL); +} + +/* + * Function irda_final_timer_expired (data) + * + * + * + */ +static void irlap_final_timer_expired(void *data) +{ + struct irlap_cb *self = (struct irlap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + irlap_do_event(self, FINAL_TIMER_EXPIRED, NULL, NULL); +} + +/* + * Function irda_wd_timer_expired (data) + * + * + * + */ +static void irlap_wd_timer_expired(void *data) +{ + struct irlap_cb *self = (struct irlap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + irlap_do_event(self, WD_TIMER_EXPIRED, NULL, NULL); +} + +/* + * Function irda_backoff_timer_expired (data) + * + * + * + */ +static void irlap_backoff_timer_expired(void *data) +{ + struct irlap_cb *self = (struct irlap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + IRDA_ASSERT(self->magic == LAP_MAGIC, return;); + + irlap_do_event(self, BACKOFF_TIMER_EXPIRED, NULL, NULL); +} + + +/* + * Function irtty_media_busy_expired (data) + * + * + */ +static void irlap_media_busy_expired(void *data) +{ + struct irlap_cb *self = (struct irlap_cb *) data; + + IRDA_ASSERT(self != NULL, return;); + + irda_device_set_media_busy(self->netdev, FALSE); + /* Note : the LAP event will be send in irlap_stop_mbusy_timer(), + * to catch other cases where the flag is cleared (for example + * after a discovery) - Jean II */ +} diff --git a/drivers/staging/irda/net/wrapper.c b/drivers/staging/irda/net/wrapper.c new file mode 100644 index 000000000000..40a0f993bf13 --- /dev/null +++ b/drivers/staging/irda/net/wrapper.c @@ -0,0 +1,492 @@ +/********************************************************************* + * + * Filename: wrapper.c + * Version: 1.2 + * Description: IrDA SIR async wrapper layer + * Status: Stable + * Author: Dag Brattli + * Created at: Mon Aug 4 20:40:53 1997 + * Modified at: Fri Jan 28 13:21:09 2000 + * Modified by: Dag Brattli + * Modified at: Fri May 28 3:11 CST 1999 + * Modified by: Horst von Brand + * + * Copyright (c) 1998-2000 Dag Brattli , + * All Rights Reserved. + * Copyright (c) 2000-2002 Jean Tourrilhes + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * Neither Dag Brattli nor University of Tromsø admit liability nor + * provide warranty for any of this software. This material is + * provided "AS-IS" and at no charge. + * + ********************************************************************/ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +/************************** FRAME WRAPPING **************************/ +/* + * Unwrap and unstuff SIR frames + * + * Note : at FIR and MIR, HDLC framing is used and usually handled + * by the controller, so we come here only for SIR... Jean II + */ + +/* + * Function stuff_byte (byte, buf) + * + * Byte stuff one single byte and put the result in buffer pointed to by + * buf. The buffer must at all times be able to have two bytes inserted. + * + * This is in a tight loop, better inline it, so need to be prior to callers. + * (2000 bytes on P6 200MHz, non-inlined ~370us, inline ~170us) - Jean II + */ +static inline int stuff_byte(__u8 byte, __u8 *buf) +{ + switch (byte) { + case BOF: /* FALLTHROUGH */ + case EOF: /* FALLTHROUGH */ + case CE: + /* Insert transparently coded */ + buf[0] = CE; /* Send link escape */ + buf[1] = byte^IRDA_TRANS; /* Complement bit 5 */ + return 2; + /* break; */ + default: + /* Non-special value, no transparency required */ + buf[0] = byte; + return 1; + /* break; */ + } +} + +/* + * Function async_wrap (skb, *tx_buff, buffsize) + * + * Makes a new buffer with wrapping and stuffing, should check that + * we don't get tx buffer overflow. + */ +int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) +{ + struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; + int xbofs; + int i; + int n; + union { + __u16 value; + __u8 bytes[2]; + } fcs; + + /* Initialize variables */ + fcs.value = INIT_FCS; + n = 0; + + /* + * Send XBOF's for required min. turn time and for the negotiated + * additional XBOFS + */ + + if (cb->magic != LAP_MAGIC) { + /* + * This will happen for all frames sent from user-space. + * Nothing to worry about, but we set the default number of + * BOF's + */ + pr_debug("%s(), wrong magic in skb!\n", __func__); + xbofs = 10; + } else + xbofs = cb->xbofs + cb->xbofs_delay; + + pr_debug("%s(), xbofs=%d\n", __func__, xbofs); + + /* Check that we never use more than 115 + 48 xbofs */ + if (xbofs > 163) { + pr_debug("%s(), too many xbofs (%d)\n", __func__, + xbofs); + xbofs = 163; + } + + memset(tx_buff + n, XBOF, xbofs); + n += xbofs; + + /* Start of packet character BOF */ + tx_buff[n++] = BOF; + + /* Insert frame and calc CRC */ + for (i=0; i < skb->len; i++) { + /* + * Check for the possibility of tx buffer overflow. We use + * bufsize-5 since the maximum number of bytes that can be + * transmitted after this point is 5. + */ + if(n >= (buffsize-5)) { + net_err_ratelimited("%s(), tx buffer overflow (n=%d)\n", + __func__, n); + return n; + } + + n += stuff_byte(skb->data[i], tx_buff+n); + fcs.value = irda_fcs(fcs.value, skb->data[i]); + } + + /* Insert CRC in little endian format (LSB first) */ + fcs.value = ~fcs.value; +#ifdef __LITTLE_ENDIAN + n += stuff_byte(fcs.bytes[0], tx_buff+n); + n += stuff_byte(fcs.bytes[1], tx_buff+n); +#else /* ifdef __BIG_ENDIAN */ + n += stuff_byte(fcs.bytes[1], tx_buff+n); + n += stuff_byte(fcs.bytes[0], tx_buff+n); +#endif + tx_buff[n++] = EOF; + + return n; +} +EXPORT_SYMBOL(async_wrap_skb); + +/************************* FRAME UNWRAPPING *************************/ +/* + * Unwrap and unstuff SIR frames + * + * Complete rewrite by Jean II : + * More inline, faster, more compact, more logical. Jean II + * (16 bytes on P6 200MHz, old 5 to 7 us, new 4 to 6 us) + * (24 bytes on P6 200MHz, old 9 to 10 us, new 7 to 8 us) + * (for reference, 115200 b/s is 1 byte every 69 us) + * And reduce wrapper.o by ~900B in the process ;-) + * + * Then, we have the addition of ZeroCopy, which is optional + * (i.e. the driver must initiate it) and improve final processing. + * (2005 B frame + EOF on P6 200MHz, without 30 to 50 us, with 10 to 25 us) + * + * Note : at FIR and MIR, HDLC framing is used and usually handled + * by the controller, so we come here only for SIR... Jean II + */ + +/* + * We can also choose where we want to do the CRC calculation. We can + * do it "inline", as we receive the bytes, or "postponed", when + * receiving the End-Of-Frame. + * (16 bytes on P6 200MHz, inlined 4 to 6 us, postponed 4 to 5 us) + * (24 bytes on P6 200MHz, inlined 7 to 8 us, postponed 5 to 7 us) + * With ZeroCopy : + * (2005 B frame on P6 200MHz, inlined 10 to 25 us, postponed 140 to 180 us) + * Without ZeroCopy : + * (2005 B frame on P6 200MHz, inlined 30 to 50 us, postponed 150 to 180 us) + * (Note : numbers taken with irq disabled) + * + * From those numbers, it's not clear which is the best strategy, because + * we end up running through a lot of data one way or another (i.e. cache + * misses). I personally prefer to avoid the huge latency spike of the + * "postponed" solution, because it come just at the time when we have + * lot's of protocol processing to do and it will hurt our ability to + * reach low link turnaround times... Jean II + */ +//#define POSTPONE_RX_CRC + +/* + * Function async_bump (buf, len, stats) + * + * Got a frame, make a copy of it, and pass it up the stack! We can try + * to inline it since it's only called from state_inside_frame + */ +static inline void +async_bump(struct net_device *dev, + struct net_device_stats *stats, + iobuff_t *rx_buff) +{ + struct sk_buff *newskb; + struct sk_buff *dataskb; + int docopy; + + /* Check if we need to copy the data to a new skb or not. + * If the driver doesn't use ZeroCopy Rx, we have to do it. + * With ZeroCopy Rx, the rx_buff already point to a valid + * skb. But, if the frame is small, it is more efficient to + * copy it to save memory (copy will be fast anyway - that's + * called Rx-copy-break). Jean II */ + docopy = ((rx_buff->skb == NULL) || + (rx_buff->len < IRDA_RX_COPY_THRESHOLD)); + + /* Allocate a new skb */ + newskb = dev_alloc_skb(docopy ? rx_buff->len + 1 : rx_buff->truesize); + if (!newskb) { + stats->rx_dropped++; + /* We could deliver the current skb if doing ZeroCopy Rx, + * but this would stall the Rx path. Better drop the + * packet... Jean II */ + return; + } + + /* Align IP header to 20 bytes (i.e. increase skb->data) + * Note this is only useful with IrLAN, as PPP has a variable + * header size (2 or 1 bytes) - Jean II */ + skb_reserve(newskb, 1); + + if(docopy) { + /* Copy data without CRC (length already checked) */ + skb_copy_to_linear_data(newskb, rx_buff->data, + rx_buff->len - 2); + /* Deliver this skb */ + dataskb = newskb; + } else { + /* We are using ZeroCopy. Deliver old skb */ + dataskb = rx_buff->skb; + /* And hook the new skb to the rx_buff */ + rx_buff->skb = newskb; + rx_buff->head = newskb->data; /* NOT newskb->head */ + //printk(KERN_DEBUG "ZeroCopy : len = %d, dataskb = %p, newskb = %p\n", rx_buff->len, dataskb, newskb); + } + + /* Set proper length on skb (without CRC) */ + skb_put(dataskb, rx_buff->len - 2); + + /* Feed it to IrLAP layer */ + dataskb->dev = dev; + skb_reset_mac_header(dataskb); + dataskb->protocol = htons(ETH_P_IRDA); + + netif_rx(dataskb); + + stats->rx_packets++; + stats->rx_bytes += rx_buff->len; + + /* Clean up rx_buff (redundant with async_unwrap_bof() ???) */ + rx_buff->data = rx_buff->head; + rx_buff->len = 0; +} + +/* + * Function async_unwrap_bof(dev, byte) + * + * Handle Beginning Of Frame character received within a frame + * + */ +static inline void +async_unwrap_bof(struct net_device *dev, + struct net_device_stats *stats, + iobuff_t *rx_buff, __u8 byte) +{ + switch(rx_buff->state) { + case LINK_ESCAPE: + case INSIDE_FRAME: + /* Not supposed to happen, the previous frame is not + * finished - Jean II */ + pr_debug("%s(), Discarding incomplete frame\n", + __func__); + stats->rx_errors++; + stats->rx_missed_errors++; + irda_device_set_media_busy(dev, TRUE); + break; + + case OUTSIDE_FRAME: + case BEGIN_FRAME: + default: + /* We may receive multiple BOF at the start of frame */ + break; + } + + /* Now receiving frame */ + rx_buff->state = BEGIN_FRAME; + rx_buff->in_frame = TRUE; + + /* Time to initialize receive buffer */ + rx_buff->data = rx_buff->head; + rx_buff->len = 0; + rx_buff->fcs = INIT_FCS; +} + +/* + * Function async_unwrap_eof(dev, byte) + * + * Handle End Of Frame character received within a frame + * + */ +static inline void +async_unwrap_eof(struct net_device *dev, + struct net_device_stats *stats, + iobuff_t *rx_buff, __u8 byte) +{ +#ifdef POSTPONE_RX_CRC + int i; +#endif + + switch(rx_buff->state) { + case OUTSIDE_FRAME: + /* Probably missed the BOF */ + stats->rx_errors++; + stats->rx_missed_errors++; + irda_device_set_media_busy(dev, TRUE); + break; + + case BEGIN_FRAME: + case LINK_ESCAPE: + case INSIDE_FRAME: + default: + /* Note : in the case of BEGIN_FRAME and LINK_ESCAPE, + * the fcs will most likely not match and generate an + * error, as expected - Jean II */ + rx_buff->state = OUTSIDE_FRAME; + rx_buff->in_frame = FALSE; + +#ifdef POSTPONE_RX_CRC + /* If we haven't done the CRC as we receive bytes, we + * must do it now... Jean II */ + for(i = 0; i < rx_buff->len; i++) + rx_buff->fcs = irda_fcs(rx_buff->fcs, + rx_buff->data[i]); +#endif + + /* Test FCS and signal success if the frame is good */ + if (rx_buff->fcs == GOOD_FCS) { + /* Deliver frame */ + async_bump(dev, stats, rx_buff); + break; + } else { + /* Wrong CRC, discard frame! */ + irda_device_set_media_busy(dev, TRUE); + + pr_debug("%s(), crc error\n", __func__); + stats->rx_errors++; + stats->rx_crc_errors++; + } + break; + } +} + +/* + * Function async_unwrap_ce(dev, byte) + * + * Handle Character Escape character received within a frame + * + */ +static inline void +async_unwrap_ce(struct net_device *dev, + struct net_device_stats *stats, + iobuff_t *rx_buff, __u8 byte) +{ + switch(rx_buff->state) { + case OUTSIDE_FRAME: + /* Activate carrier sense */ + irda_device_set_media_busy(dev, TRUE); + break; + + case LINK_ESCAPE: + net_warn_ratelimited("%s: state not defined\n", __func__); + break; + + case BEGIN_FRAME: + case INSIDE_FRAME: + default: + /* Stuffed byte coming */ + rx_buff->state = LINK_ESCAPE; + break; + } +} + +/* + * Function async_unwrap_other(dev, byte) + * + * Handle other characters received within a frame + * + */ +static inline void +async_unwrap_other(struct net_device *dev, + struct net_device_stats *stats, + iobuff_t *rx_buff, __u8 byte) +{ + switch(rx_buff->state) { + /* This is on the critical path, case are ordered by + * probability (most frequent first) - Jean II */ + case INSIDE_FRAME: + /* Must be the next byte of the frame */ + if (rx_buff->len < rx_buff->truesize) { + rx_buff->data[rx_buff->len++] = byte; +#ifndef POSTPONE_RX_CRC + rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); +#endif + } else { + pr_debug("%s(), Rx buffer overflow, aborting\n", + __func__); + rx_buff->state = OUTSIDE_FRAME; + } + break; + + case LINK_ESCAPE: + /* + * Stuffed char, complement bit 5 of byte + * following CE, IrLAP p.114 + */ + byte ^= IRDA_TRANS; + if (rx_buff->len < rx_buff->truesize) { + rx_buff->data[rx_buff->len++] = byte; +#ifndef POSTPONE_RX_CRC + rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); +#endif + rx_buff->state = INSIDE_FRAME; + } else { + pr_debug("%s(), Rx buffer overflow, aborting\n", + __func__); + rx_buff->state = OUTSIDE_FRAME; + } + break; + + case OUTSIDE_FRAME: + /* Activate carrier sense */ + if(byte != XBOF) + irda_device_set_media_busy(dev, TRUE); + break; + + case BEGIN_FRAME: + default: + rx_buff->data[rx_buff->len++] = byte; +#ifndef POSTPONE_RX_CRC + rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); +#endif + rx_buff->state = INSIDE_FRAME; + break; + } +} + +/* + * Function async_unwrap_char (dev, rx_buff, byte) + * + * Parse and de-stuff frame received from the IrDA-port + * + * This is the main entry point for SIR drivers. + */ +void async_unwrap_char(struct net_device *dev, + struct net_device_stats *stats, + iobuff_t *rx_buff, __u8 byte) +{ + switch(byte) { + case CE: + async_unwrap_ce(dev, stats, rx_buff, byte); + break; + case BOF: + async_unwrap_bof(dev, stats, rx_buff, byte); + break; + case EOF: + async_unwrap_eof(dev, stats, rx_buff, byte); + break; + default: + async_unwrap_other(dev, stats, rx_buff, byte); + break; + } +} +EXPORT_SYMBOL(async_unwrap_char); + diff --git a/net/Kconfig b/net/Kconfig index 17ca213e1f85..8614a9ccfbef 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -376,7 +376,6 @@ endmenu source "net/ax25/Kconfig" source "net/can/Kconfig" -source "net/irda/Kconfig" source "net/bluetooth/Kconfig" source "net/rxrpc/Kconfig" source "net/kcm/Kconfig" diff --git a/net/Makefile b/net/Makefile index bed80fa398b7..3d3feff3643b 100644 --- a/net/Makefile +++ b/net/Makefile @@ -31,7 +31,6 @@ obj-$(CONFIG_NETROM) += netrom/ obj-$(CONFIG_ROSE) += rose/ obj-$(CONFIG_AX25) += ax25/ obj-$(CONFIG_CAN) += can/ -obj-$(CONFIG_IRDA) += irda/ obj-$(CONFIG_BT) += bluetooth/ obj-$(CONFIG_SUNRPC) += sunrpc/ obj-$(CONFIG_AF_RXRPC) += rxrpc/ diff --git a/net/irda/Kconfig b/net/irda/Kconfig deleted file mode 100644 index c8671a7ffb3c..000000000000 --- a/net/irda/Kconfig +++ /dev/null @@ -1,96 +0,0 @@ -# -# IrDA protocol configuration -# - -menuconfig IRDA - depends on NET && !S390 - tristate "IrDA (infrared) subsystem support" - select CRC_CCITT - ---help--- - Say Y here if you want to build support for the IrDA (TM) protocols. - The Infrared Data Associations (tm) specifies standards for wireless - infrared communication and is supported by most laptops and PDA's. - - To use Linux support for the IrDA (tm) protocols, you will also need - some user-space utilities like irattach. For more information, see - the file . You also want to - read the IR-HOWTO, available at - . - - If you want to exchange bits of data (vCal, vCard) with a PDA, you - will need to install some OBEX application, such as OpenObex : - - - To compile this support as a module, choose M here: the module will - be called irda. - -comment "IrDA protocols" - depends on IRDA - -source "net/irda/irlan/Kconfig" - -source "net/irda/irnet/Kconfig" - -source "net/irda/ircomm/Kconfig" - -config IRDA_ULTRA - bool "Ultra (connectionless) protocol" - depends on IRDA - help - Say Y here to support the connectionless Ultra IRDA protocol. - Ultra allows to exchange data over IrDA with really simple devices - (watch, beacon) without the overhead of the IrDA protocol (no handshaking, - no management frames, simple fixed header). - Ultra is available as a special socket : socket(AF_IRDA, SOCK_DGRAM, 1); - -comment "IrDA options" - depends on IRDA - -config IRDA_CACHE_LAST_LSAP - bool "Cache last LSAP" - depends on IRDA - help - Say Y here if you want IrLMP to cache the last LSAP used. This - makes sense since most frames will be sent/received on the same - connection. Enabling this option will save a hash-lookup per frame. - - If unsure, say Y. - -config IRDA_FAST_RR - bool "Fast RRs (low latency)" - depends on IRDA - ---help--- - Say Y here is you want IrLAP to send fast RR (Receive Ready) frames - when acting as a primary station. - Disabling this option will make latency over IrDA very bad. Enabling - this option will make the IrDA stack send more packet than strictly - necessary, thus reduce your battery life (but not that much). - - Fast RR will make IrLAP send out a RR frame immediately when - receiving a frame if its own transmit queue is currently empty. This - will give a lot of speed improvement when receiving much data since - the secondary station will not have to wait the max. turn around - time (usually 500ms) before it is allowed to transmit the next time. - If the transmit queue of the secondary is also empty, the primary will - start backing-off before sending another RR frame, waiting longer - each time until the back-off reaches the max. turn around time. - This back-off increase in controlled via - /proc/sys/net/irda/fast_poll_increase - - If unsure, say Y. - -config IRDA_DEBUG - bool "Debug information" - depends on IRDA - help - Say Y here if you want the IrDA subsystem to write debug information - to your syslog. You can change the debug level in - /proc/sys/net/irda/debug . - When this option is enabled, the IrDA also perform many extra internal - verifications which will usually prevent the kernel to crash in case of - bugs. - - If unsure, say Y (since it makes it easier to find the bugs). - -source "drivers/net/irda/Kconfig" - diff --git a/net/irda/Makefile b/net/irda/Makefile deleted file mode 100644 index 187f6c563a4b..000000000000 --- a/net/irda/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -# -# Makefile for the Linux IrDA protocol layer. -# - -obj-$(CONFIG_IRDA) += irda.o -obj-$(CONFIG_IRLAN) += irlan/ -obj-$(CONFIG_IRNET) += irnet/ -obj-$(CONFIG_IRCOMM) += ircomm/ - -irda-y := iriap.o iriap_event.o irlmp.o irlmp_event.o irlmp_frame.o \ - irlap.o irlap_event.o irlap_frame.o timer.o qos.o irqueue.o \ - irttp.o irda_device.o irias_object.o wrapper.o af_irda.o \ - discovery.o parameters.o irnetlink.o irmod.o -irda-$(CONFIG_PROC_FS) += irproc.o -irda-$(CONFIG_SYSCTL) += irsysctl.o diff --git a/net/irda/af_irda.c b/net/irda/af_irda.c deleted file mode 100644 index 23fa7c8b09a5..000000000000 --- a/net/irda/af_irda.c +++ /dev/null @@ -1,2695 +0,0 @@ -/********************************************************************* - * - * Filename: af_irda.c - * Version: 0.9 - * Description: IrDA sockets implementation - * Status: Stable - * Author: Dag Brattli - * Created at: Sun May 31 10:12:43 1998 - * Modified at: Sat Dec 25 21:10:23 1999 - * Modified by: Dag Brattli - * Sources: af_netroom.c, af_ax25.c, af_rose.c, af_x25.c etc. - * - * Copyright (c) 1999 Dag Brattli - * Copyright (c) 1999-2003 Jean Tourrilhes - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - * Linux-IrDA now supports four different types of IrDA sockets: - * - * o SOCK_STREAM: TinyTP connections with SAR disabled. The - * max SDU size is 0 for conn. of this type - * o SOCK_SEQPACKET: TinyTP connections with SAR enabled. TTP may - * fragment the messages, but will preserve - * the message boundaries - * o SOCK_DGRAM: IRDAPROTO_UNITDATA: TinyTP connections with Unitdata - * (unreliable) transfers - * IRDAPROTO_ULTRA: Connectionless and unreliable data - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include /* TIOCOUTQ, TIOCINQ */ -#include - -#include -#include - -#include - -static int irda_create(struct net *net, struct socket *sock, int protocol, int kern); - -static const struct proto_ops irda_stream_ops; -static const struct proto_ops irda_seqpacket_ops; -static const struct proto_ops irda_dgram_ops; - -#ifdef CONFIG_IRDA_ULTRA -static const struct proto_ops irda_ultra_ops; -#define ULTRA_MAX_DATA 382 -#endif /* CONFIG_IRDA_ULTRA */ - -#define IRDA_MAX_HEADER (TTP_MAX_HEADER) - -/* - * Function irda_data_indication (instance, sap, skb) - * - * Received some data from TinyTP. Just queue it on the receive queue - * - */ -static int irda_data_indication(void *instance, void *sap, struct sk_buff *skb) -{ - struct irda_sock *self; - struct sock *sk; - int err; - - self = instance; - sk = instance; - - err = sock_queue_rcv_skb(sk, skb); - if (err) { - pr_debug("%s(), error: no more mem!\n", __func__); - self->rx_flow = FLOW_STOP; - - /* When we return error, TTP will need to requeue the skb */ - return err; - } - - return 0; -} - -/* - * Function irda_disconnect_indication (instance, sap, reason, skb) - * - * Connection has been closed. Check reason to find out why - * - */ -static void irda_disconnect_indication(void *instance, void *sap, - LM_REASON reason, struct sk_buff *skb) -{ - struct irda_sock *self; - struct sock *sk; - - self = instance; - - pr_debug("%s(%p)\n", __func__, self); - - /* Don't care about it, but let's not leak it */ - if(skb) - dev_kfree_skb(skb); - - sk = instance; - if (sk == NULL) { - pr_debug("%s(%p) : BUG : sk is NULL\n", - __func__, self); - return; - } - - /* Prevent race conditions with irda_release() and irda_shutdown() */ - bh_lock_sock(sk); - if (!sock_flag(sk, SOCK_DEAD) && sk->sk_state != TCP_CLOSE) { - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - - sk->sk_state_change(sk); - - /* Close our TSAP. - * If we leave it open, IrLMP put it back into the list of - * unconnected LSAPs. The problem is that any incoming request - * can then be matched to this socket (and it will be, because - * it is at the head of the list). This would prevent any - * listening socket waiting on the same TSAP to get those - * requests. Some apps forget to close sockets, or hang to it - * a bit too long, so we may stay in this dead state long - * enough to be noticed... - * Note : all socket function do check sk->sk_state, so we are - * safe... - * Jean II - */ - if (self->tsap) { - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - } - bh_unlock_sock(sk); - - /* Note : once we are there, there is not much you want to do - * with the socket anymore, apart from closing it. - * For example, bind() and connect() won't reset sk->sk_err, - * sk->sk_shutdown and sk->sk_flags to valid values... - * Jean II - */ -} - -/* - * Function irda_connect_confirm (instance, sap, qos, max_sdu_size, skb) - * - * Connections has been confirmed by the remote device - * - */ -static void irda_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, __u8 max_header_size, - struct sk_buff *skb) -{ - struct irda_sock *self; - struct sock *sk; - - self = instance; - - pr_debug("%s(%p)\n", __func__, self); - - sk = instance; - if (sk == NULL) { - dev_kfree_skb(skb); - return; - } - - dev_kfree_skb(skb); - // Should be ??? skb_queue_tail(&sk->sk_receive_queue, skb); - - /* How much header space do we need to reserve */ - self->max_header_size = max_header_size; - - /* IrTTP max SDU size in transmit direction */ - self->max_sdu_size_tx = max_sdu_size; - - /* Find out what the largest chunk of data that we can transmit is */ - switch (sk->sk_type) { - case SOCK_STREAM: - if (max_sdu_size != 0) { - net_err_ratelimited("%s: max_sdu_size must be 0\n", - __func__); - return; - } - self->max_data_size = irttp_get_max_seg_size(self->tsap); - break; - case SOCK_SEQPACKET: - if (max_sdu_size == 0) { - net_err_ratelimited("%s: max_sdu_size cannot be 0\n", - __func__); - return; - } - self->max_data_size = max_sdu_size; - break; - default: - self->max_data_size = irttp_get_max_seg_size(self->tsap); - } - - pr_debug("%s(), max_data_size=%d\n", __func__, - self->max_data_size); - - memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); - - /* We are now connected! */ - sk->sk_state = TCP_ESTABLISHED; - sk->sk_state_change(sk); -} - -/* - * Function irda_connect_indication(instance, sap, qos, max_sdu_size, userdata) - * - * Incoming connection - * - */ -static void irda_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 max_header_size, struct sk_buff *skb) -{ - struct irda_sock *self; - struct sock *sk; - - self = instance; - - pr_debug("%s(%p)\n", __func__, self); - - sk = instance; - if (sk == NULL) { - dev_kfree_skb(skb); - return; - } - - /* How much header space do we need to reserve */ - self->max_header_size = max_header_size; - - /* IrTTP max SDU size in transmit direction */ - self->max_sdu_size_tx = max_sdu_size; - - /* Find out what the largest chunk of data that we can transmit is */ - switch (sk->sk_type) { - case SOCK_STREAM: - if (max_sdu_size != 0) { - net_err_ratelimited("%s: max_sdu_size must be 0\n", - __func__); - kfree_skb(skb); - return; - } - self->max_data_size = irttp_get_max_seg_size(self->tsap); - break; - case SOCK_SEQPACKET: - if (max_sdu_size == 0) { - net_err_ratelimited("%s: max_sdu_size cannot be 0\n", - __func__); - kfree_skb(skb); - return; - } - self->max_data_size = max_sdu_size; - break; - default: - self->max_data_size = irttp_get_max_seg_size(self->tsap); - } - - pr_debug("%s(), max_data_size=%d\n", __func__, - self->max_data_size); - - memcpy(&self->qos_tx, qos, sizeof(struct qos_info)); - - skb_queue_tail(&sk->sk_receive_queue, skb); - sk->sk_state_change(sk); -} - -/* - * Function irda_connect_response (handle) - * - * Accept incoming connection - * - */ -static void irda_connect_response(struct irda_sock *self) -{ - struct sk_buff *skb; - - skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, GFP_KERNEL); - if (skb == NULL) { - pr_debug("%s() Unable to allocate sk_buff!\n", - __func__); - return; - } - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(skb, IRDA_MAX_HEADER); - - irttp_connect_response(self->tsap, self->max_sdu_size_rx, skb); -} - -/* - * Function irda_flow_indication (instance, sap, flow) - * - * Used by TinyTP to tell us if it can accept more data or not - * - */ -static void irda_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) -{ - struct irda_sock *self; - struct sock *sk; - - self = instance; - sk = instance; - BUG_ON(sk == NULL); - - switch (flow) { - case FLOW_STOP: - pr_debug("%s(), IrTTP wants us to slow down\n", - __func__); - self->tx_flow = flow; - break; - case FLOW_START: - self->tx_flow = flow; - pr_debug("%s(), IrTTP wants us to start again\n", - __func__); - wake_up_interruptible(sk_sleep(sk)); - break; - default: - pr_debug("%s(), Unknown flow command!\n", __func__); - /* Unknown flow command, better stop */ - self->tx_flow = flow; - break; - } -} - -/* - * Function irda_getvalue_confirm (obj_id, value, priv) - * - * Got answer from remote LM-IAS, just pass object to requester... - * - * Note : duplicate from above, but we need our own version that - * doesn't touch the dtsap_sel and save the full value structure... - */ -static void irda_getvalue_confirm(int result, __u16 obj_id, - struct ias_value *value, void *priv) -{ - struct irda_sock *self; - - self = priv; - if (!self) { - net_warn_ratelimited("%s: lost myself!\n", __func__); - return; - } - - pr_debug("%s(%p)\n", __func__, self); - - /* We probably don't need to make any more queries */ - iriap_close(self->iriap); - self->iriap = NULL; - - /* Check if request succeeded */ - if (result != IAS_SUCCESS) { - pr_debug("%s(), IAS query failed! (%d)\n", __func__, - result); - - self->errno = result; /* We really need it later */ - - /* Wake up any processes waiting for result */ - wake_up_interruptible(&self->query_wait); - - return; - } - - /* Pass the object to the caller (so the caller must delete it) */ - self->ias_result = value; - self->errno = 0; - - /* Wake up any processes waiting for result */ - wake_up_interruptible(&self->query_wait); -} - -/* - * Function irda_selective_discovery_indication (discovery) - * - * Got a selective discovery indication from IrLMP. - * - * IrLMP is telling us that this node is new and matching our hint bit - * filter. Wake up any process waiting for answer... - */ -static void irda_selective_discovery_indication(discinfo_t *discovery, - DISCOVERY_MODE mode, - void *priv) -{ - struct irda_sock *self; - - self = priv; - if (!self) { - net_warn_ratelimited("%s: lost myself!\n", __func__); - return; - } - - /* Pass parameter to the caller */ - self->cachedaddr = discovery->daddr; - - /* Wake up process if its waiting for device to be discovered */ - wake_up_interruptible(&self->query_wait); -} - -/* - * Function irda_discovery_timeout (priv) - * - * Timeout in the selective discovery process - * - * We were waiting for a node to be discovered, but nothing has come up - * so far. Wake up the user and tell him that we failed... - */ -static void irda_discovery_timeout(u_long priv) -{ - struct irda_sock *self; - - self = (struct irda_sock *) priv; - BUG_ON(self == NULL); - - /* Nothing for the caller */ - self->cachelog = NULL; - self->cachedaddr = 0; - self->errno = -ETIME; - - /* Wake up process if its still waiting... */ - wake_up_interruptible(&self->query_wait); -} - -/* - * Function irda_open_tsap (self) - * - * Open local Transport Service Access Point (TSAP) - * - */ -static int irda_open_tsap(struct irda_sock *self, __u8 tsap_sel, char *name) -{ - notify_t notify; - - if (self->tsap) { - pr_debug("%s: busy!\n", __func__); - return -EBUSY; - } - - /* Initialize callbacks to be used by the IrDA stack */ - irda_notify_init(¬ify); - notify.connect_confirm = irda_connect_confirm; - notify.connect_indication = irda_connect_indication; - notify.disconnect_indication = irda_disconnect_indication; - notify.data_indication = irda_data_indication; - notify.udata_indication = irda_data_indication; - notify.flow_indication = irda_flow_indication; - notify.instance = self; - strncpy(notify.name, name, NOTIFY_MAX_NAME); - - self->tsap = irttp_open_tsap(tsap_sel, DEFAULT_INITIAL_CREDIT, - ¬ify); - if (self->tsap == NULL) { - pr_debug("%s(), Unable to allocate TSAP!\n", - __func__); - return -ENOMEM; - } - /* Remember which TSAP selector we actually got */ - self->stsap_sel = self->tsap->stsap_sel; - - return 0; -} - -/* - * Function irda_open_lsap (self) - * - * Open local Link Service Access Point (LSAP). Used for opening Ultra - * sockets - */ -#ifdef CONFIG_IRDA_ULTRA -static int irda_open_lsap(struct irda_sock *self, int pid) -{ - notify_t notify; - - if (self->lsap) { - net_warn_ratelimited("%s(), busy!\n", __func__); - return -EBUSY; - } - - /* Initialize callbacks to be used by the IrDA stack */ - irda_notify_init(¬ify); - notify.udata_indication = irda_data_indication; - notify.instance = self; - strncpy(notify.name, "Ultra", NOTIFY_MAX_NAME); - - self->lsap = irlmp_open_lsap(LSAP_CONNLESS, ¬ify, pid); - if (self->lsap == NULL) { - pr_debug("%s(), Unable to allocate LSAP!\n", __func__); - return -ENOMEM; - } - - return 0; -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irda_find_lsap_sel (self, name) - * - * Try to lookup LSAP selector in remote LM-IAS - * - * Basically, we start a IAP query, and then go to sleep. When the query - * return, irda_getvalue_confirm will wake us up, and we can examine the - * result of the query... - * Note that in some case, the query fail even before we go to sleep, - * creating some races... - */ -static int irda_find_lsap_sel(struct irda_sock *self, char *name) -{ - pr_debug("%s(%p, %s)\n", __func__, self, name); - - if (self->iriap) { - net_warn_ratelimited("%s(): busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irda_getvalue_confirm); - if(self->iriap == NULL) - return -ENOMEM; - - /* Treat unexpected wakeup as disconnect */ - self->errno = -EHOSTUNREACH; - - /* Query remote LM-IAS */ - iriap_getvaluebyclass_request(self->iriap, self->saddr, self->daddr, - name, "IrDA:TinyTP:LsapSel"); - - /* Wait for answer, if not yet finished (or failed) */ - if (wait_event_interruptible(self->query_wait, (self->iriap==NULL))) - /* Treat signals as disconnect */ - return -EHOSTUNREACH; - - /* Check what happened */ - if (self->errno) - { - /* Requested object/attribute doesn't exist */ - if((self->errno == IAS_CLASS_UNKNOWN) || - (self->errno == IAS_ATTRIB_UNKNOWN)) - return -EADDRNOTAVAIL; - else - return -EHOSTUNREACH; - } - - /* Get the remote TSAP selector */ - switch (self->ias_result->type) { - case IAS_INTEGER: - pr_debug("%s() int=%d\n", - __func__, self->ias_result->t.integer); - - if (self->ias_result->t.integer != -1) - self->dtsap_sel = self->ias_result->t.integer; - else - self->dtsap_sel = 0; - break; - default: - self->dtsap_sel = 0; - pr_debug("%s(), bad type!\n", __func__); - break; - } - if (self->ias_result) - irias_delete_value(self->ias_result); - - if (self->dtsap_sel) - return 0; - - return -EADDRNOTAVAIL; -} - -/* - * Function irda_discover_daddr_and_lsap_sel (self, name) - * - * This try to find a device with the requested service. - * - * It basically look into the discovery log. For each address in the list, - * it queries the LM-IAS of the device to find if this device offer - * the requested service. - * If there is more than one node supporting the service, we complain - * to the user (it should move devices around). - * The, we set both the destination address and the lsap selector to point - * on the service on the unique device we have found. - * - * Note : this function fails if there is more than one device in range, - * because IrLMP doesn't disconnect the LAP when the last LSAP is closed. - * Moreover, we would need to wait the LAP disconnection... - */ -static int irda_discover_daddr_and_lsap_sel(struct irda_sock *self, char *name) -{ - discinfo_t *discoveries; /* Copy of the discovery log */ - int number; /* Number of nodes in the log */ - int i; - int err = -ENETUNREACH; - __u32 daddr = DEV_ADDR_ANY; /* Address we found the service on */ - __u8 dtsap_sel = 0x0; /* TSAP associated with it */ - - pr_debug("%s(), name=%s\n", __func__, name); - - /* Ask lmp for the current discovery log - * Note : we have to use irlmp_get_discoveries(), as opposed - * to play with the cachelog directly, because while we are - * making our ias query, le log might change... */ - discoveries = irlmp_get_discoveries(&number, self->mask.word, - self->nslots); - /* Check if the we got some results */ - if (discoveries == NULL) - return -ENETUNREACH; /* No nodes discovered */ - - /* - * Now, check all discovered devices (if any), and connect - * client only about the services that the client is - * interested in... - */ - for(i = 0; i < number; i++) { - /* Try the address in the log */ - self->daddr = discoveries[i].daddr; - self->saddr = 0x0; - pr_debug("%s(), trying daddr = %08x\n", - __func__, self->daddr); - - /* Query remote LM-IAS for this service */ - err = irda_find_lsap_sel(self, name); - switch (err) { - case 0: - /* We found the requested service */ - if(daddr != DEV_ADDR_ANY) { - pr_debug("%s(), discovered service ''%s'' in two different devices !!!\n", - __func__, name); - self->daddr = DEV_ADDR_ANY; - kfree(discoveries); - return -ENOTUNIQ; - } - /* First time we found that one, save it ! */ - daddr = self->daddr; - dtsap_sel = self->dtsap_sel; - break; - case -EADDRNOTAVAIL: - /* Requested service simply doesn't exist on this node */ - break; - default: - /* Something bad did happen :-( */ - pr_debug("%s(), unexpected IAS query failure\n", - __func__); - self->daddr = DEV_ADDR_ANY; - kfree(discoveries); - return -EHOSTUNREACH; - } - } - /* Cleanup our copy of the discovery log */ - kfree(discoveries); - - /* Check out what we found */ - if(daddr == DEV_ADDR_ANY) { - pr_debug("%s(), cannot discover service ''%s'' in any device !!!\n", - __func__, name); - self->daddr = DEV_ADDR_ANY; - return -EADDRNOTAVAIL; - } - - /* Revert back to discovered device & service */ - self->daddr = daddr; - self->saddr = 0x0; - self->dtsap_sel = dtsap_sel; - - pr_debug("%s(), discovered requested service ''%s'' at address %08x\n", - __func__, name, self->daddr); - - return 0; -} - -/* - * Function irda_getname (sock, uaddr, uaddr_len, peer) - * - * Return the our own, or peers socket address (sockaddr_irda) - * - */ -static int irda_getname(struct socket *sock, struct sockaddr *uaddr, - int *uaddr_len, int peer) -{ - struct sockaddr_irda saddr; - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - - memset(&saddr, 0, sizeof(saddr)); - if (peer) { - if (sk->sk_state != TCP_ESTABLISHED) - return -ENOTCONN; - - saddr.sir_family = AF_IRDA; - saddr.sir_lsap_sel = self->dtsap_sel; - saddr.sir_addr = self->daddr; - } else { - saddr.sir_family = AF_IRDA; - saddr.sir_lsap_sel = self->stsap_sel; - saddr.sir_addr = self->saddr; - } - - pr_debug("%s(), tsap_sel = %#x\n", __func__, saddr.sir_lsap_sel); - pr_debug("%s(), addr = %08x\n", __func__, saddr.sir_addr); - - /* uaddr_len come to us uninitialised */ - *uaddr_len = sizeof (struct sockaddr_irda); - memcpy(uaddr, &saddr, *uaddr_len); - - return 0; -} - -/* - * Function irda_listen (sock, backlog) - * - * Just move to the listen state - * - */ -static int irda_listen(struct socket *sock, int backlog) -{ - struct sock *sk = sock->sk; - int err = -EOPNOTSUPP; - - lock_sock(sk); - - if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) && - (sk->sk_type != SOCK_DGRAM)) - goto out; - - if (sk->sk_state != TCP_LISTEN) { - sk->sk_max_ack_backlog = backlog; - sk->sk_state = TCP_LISTEN; - - err = 0; - } -out: - release_sock(sk); - - return err; -} - -/* - * Function irda_bind (sock, uaddr, addr_len) - * - * Used by servers to register their well known TSAP - * - */ -static int irda_bind(struct socket *sock, struct sockaddr *uaddr, int addr_len) -{ - struct sock *sk = sock->sk; - struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; - struct irda_sock *self = irda_sk(sk); - int err; - - pr_debug("%s(%p)\n", __func__, self); - - if (addr_len != sizeof(struct sockaddr_irda)) - return -EINVAL; - - lock_sock(sk); -#ifdef CONFIG_IRDA_ULTRA - /* Special care for Ultra sockets */ - if ((sk->sk_type == SOCK_DGRAM) && - (sk->sk_protocol == IRDAPROTO_ULTRA)) { - self->pid = addr->sir_lsap_sel; - err = -EOPNOTSUPP; - if (self->pid & 0x80) { - pr_debug("%s(), extension in PID not supp!\n", - __func__); - goto out; - } - err = irda_open_lsap(self, self->pid); - if (err < 0) - goto out; - - /* Pretend we are connected */ - sock->state = SS_CONNECTED; - sk->sk_state = TCP_ESTABLISHED; - err = 0; - - goto out; - } -#endif /* CONFIG_IRDA_ULTRA */ - - self->ias_obj = irias_new_object(addr->sir_name, jiffies); - err = -ENOMEM; - if (self->ias_obj == NULL) - goto out; - - err = irda_open_tsap(self, addr->sir_lsap_sel, addr->sir_name); - if (err < 0) { - irias_delete_object(self->ias_obj); - self->ias_obj = NULL; - goto out; - } - - /* Register with LM-IAS */ - irias_add_integer_attrib(self->ias_obj, "IrDA:TinyTP:LsapSel", - self->stsap_sel, IAS_KERNEL_ATTR); - irias_insert_object(self->ias_obj); - - err = 0; -out: - release_sock(sk); - return err; -} - -/* - * Function irda_accept (sock, newsock, flags) - * - * Wait for incoming connection - * - */ -static int irda_accept(struct socket *sock, struct socket *newsock, int flags, - bool kern) -{ - struct sock *sk = sock->sk; - struct irda_sock *new, *self = irda_sk(sk); - struct sock *newsk; - struct sk_buff *skb = NULL; - int err; - - err = irda_create(sock_net(sk), newsock, sk->sk_protocol, kern); - if (err) - return err; - - err = -EINVAL; - - lock_sock(sk); - if (sock->state != SS_UNCONNECTED) - goto out; - - err = -EOPNOTSUPP; - if ((sk->sk_type != SOCK_STREAM) && (sk->sk_type != SOCK_SEQPACKET) && - (sk->sk_type != SOCK_DGRAM)) - goto out; - - err = -EINVAL; - if (sk->sk_state != TCP_LISTEN) - goto out; - - /* - * The read queue this time is holding sockets ready to use - * hooked into the SABM we saved - */ - - /* - * We can perform the accept only if there is incoming data - * on the listening socket. - * So, we will block the caller until we receive any data. - * If the caller was waiting on select() or poll() before - * calling us, the data is waiting for us ;-) - * Jean II - */ - while (1) { - skb = skb_dequeue(&sk->sk_receive_queue); - if (skb) - break; - - /* Non blocking operation */ - err = -EWOULDBLOCK; - if (flags & O_NONBLOCK) - goto out; - - err = wait_event_interruptible(*(sk_sleep(sk)), - skb_peek(&sk->sk_receive_queue)); - if (err) - goto out; - } - - newsk = newsock->sk; - err = -EIO; - if (newsk == NULL) - goto out; - - newsk->sk_state = TCP_ESTABLISHED; - - new = irda_sk(newsk); - - /* Now attach up the new socket */ - new->tsap = irttp_dup(self->tsap, new); - err = -EPERM; /* value does not seem to make sense. -arnd */ - if (!new->tsap) { - pr_debug("%s(), dup failed!\n", __func__); - goto out; - } - - new->stsap_sel = new->tsap->stsap_sel; - new->dtsap_sel = new->tsap->dtsap_sel; - new->saddr = irttp_get_saddr(new->tsap); - new->daddr = irttp_get_daddr(new->tsap); - - new->max_sdu_size_tx = self->max_sdu_size_tx; - new->max_sdu_size_rx = self->max_sdu_size_rx; - new->max_data_size = self->max_data_size; - new->max_header_size = self->max_header_size; - - memcpy(&new->qos_tx, &self->qos_tx, sizeof(struct qos_info)); - - /* Clean up the original one to keep it in listen state */ - irttp_listen(self->tsap); - - sk->sk_ack_backlog--; - - newsock->state = SS_CONNECTED; - - irda_connect_response(new); - err = 0; -out: - kfree_skb(skb); - release_sock(sk); - return err; -} - -/* - * Function irda_connect (sock, uaddr, addr_len, flags) - * - * Connect to a IrDA device - * - * The main difference with a "standard" connect is that with IrDA we need - * to resolve the service name into a TSAP selector (in TCP, port number - * doesn't have to be resolved). - * Because of this service name resolution, we can offer "auto-connect", - * where we connect to a service without specifying a destination address. - * - * Note : by consulting "errno", the user space caller may learn the cause - * of the failure. Most of them are visible in the function, others may come - * from subroutines called and are listed here : - * o EBUSY : already processing a connect - * o EHOSTUNREACH : bad addr->sir_addr argument - * o EADDRNOTAVAIL : bad addr->sir_name argument - * o ENOTUNIQ : more than one node has addr->sir_name (auto-connect) - * o ENETUNREACH : no node found on the network (auto-connect) - */ -static int irda_connect(struct socket *sock, struct sockaddr *uaddr, - int addr_len, int flags) -{ - struct sock *sk = sock->sk; - struct sockaddr_irda *addr = (struct sockaddr_irda *) uaddr; - struct irda_sock *self = irda_sk(sk); - int err; - - pr_debug("%s(%p)\n", __func__, self); - - lock_sock(sk); - /* Don't allow connect for Ultra sockets */ - err = -ESOCKTNOSUPPORT; - if ((sk->sk_type == SOCK_DGRAM) && (sk->sk_protocol == IRDAPROTO_ULTRA)) - goto out; - - if (sk->sk_state == TCP_ESTABLISHED && sock->state == SS_CONNECTING) { - sock->state = SS_CONNECTED; - err = 0; - goto out; /* Connect completed during a ERESTARTSYS event */ - } - - if (sk->sk_state == TCP_CLOSE && sock->state == SS_CONNECTING) { - sock->state = SS_UNCONNECTED; - err = -ECONNREFUSED; - goto out; - } - - err = -EISCONN; /* No reconnect on a seqpacket socket */ - if (sk->sk_state == TCP_ESTABLISHED) - goto out; - - sk->sk_state = TCP_CLOSE; - sock->state = SS_UNCONNECTED; - - err = -EINVAL; - if (addr_len != sizeof(struct sockaddr_irda)) - goto out; - - /* Check if user supplied any destination device address */ - if ((!addr->sir_addr) || (addr->sir_addr == DEV_ADDR_ANY)) { - /* Try to find one suitable */ - err = irda_discover_daddr_and_lsap_sel(self, addr->sir_name); - if (err) { - pr_debug("%s(), auto-connect failed!\n", __func__); - goto out; - } - } else { - /* Use the one provided by the user */ - self->daddr = addr->sir_addr; - pr_debug("%s(), daddr = %08x\n", __func__, self->daddr); - - /* If we don't have a valid service name, we assume the - * user want to connect on a specific LSAP. Prevent - * the use of invalid LSAPs (IrLMP 1.1 p10). Jean II */ - if((addr->sir_name[0] != '\0') || - (addr->sir_lsap_sel >= 0x70)) { - /* Query remote LM-IAS using service name */ - err = irda_find_lsap_sel(self, addr->sir_name); - if (err) { - pr_debug("%s(), connect failed!\n", __func__); - goto out; - } - } else { - /* Directly connect to the remote LSAP - * specified by the sir_lsap field. - * Please use with caution, in IrDA LSAPs are - * dynamic and there is no "well-known" LSAP. */ - self->dtsap_sel = addr->sir_lsap_sel; - } - } - - /* Check if we have opened a local TSAP */ - if (!self->tsap) { - err = irda_open_tsap(self, LSAP_ANY, addr->sir_name); - if (err) - goto out; - } - - /* Move to connecting socket, start sending Connect Requests */ - sock->state = SS_CONNECTING; - sk->sk_state = TCP_SYN_SENT; - - /* Connect to remote device */ - err = irttp_connect_request(self->tsap, self->dtsap_sel, - self->saddr, self->daddr, NULL, - self->max_sdu_size_rx, NULL); - if (err) { - pr_debug("%s(), connect failed!\n", __func__); - goto out; - } - - /* Now the loop */ - err = -EINPROGRESS; - if (sk->sk_state != TCP_ESTABLISHED && (flags & O_NONBLOCK)) - goto out; - - err = -ERESTARTSYS; - if (wait_event_interruptible(*(sk_sleep(sk)), - (sk->sk_state != TCP_SYN_SENT))) - goto out; - - if (sk->sk_state != TCP_ESTABLISHED) { - sock->state = SS_UNCONNECTED; - err = sock_error(sk); - if (!err) - err = -ECONNRESET; - goto out; - } - - sock->state = SS_CONNECTED; - - /* At this point, IrLMP has assigned our source address */ - self->saddr = irttp_get_saddr(self->tsap); - err = 0; -out: - release_sock(sk); - return err; -} - -static struct proto irda_proto = { - .name = "IRDA", - .owner = THIS_MODULE, - .obj_size = sizeof(struct irda_sock), -}; - -/* - * Function irda_create (sock, protocol) - * - * Create IrDA socket - * - */ -static int irda_create(struct net *net, struct socket *sock, int protocol, - int kern) -{ - struct sock *sk; - struct irda_sock *self; - - if (protocol < 0 || protocol > SK_PROTOCOL_MAX) - return -EINVAL; - - if (net != &init_net) - return -EAFNOSUPPORT; - - /* Check for valid socket type */ - switch (sock->type) { - case SOCK_STREAM: /* For TTP connections with SAR disabled */ - case SOCK_SEQPACKET: /* For TTP connections with SAR enabled */ - case SOCK_DGRAM: /* For TTP Unitdata or LMP Ultra transfers */ - break; - default: - return -ESOCKTNOSUPPORT; - } - - /* Allocate networking socket */ - sk = sk_alloc(net, PF_IRDA, GFP_KERNEL, &irda_proto, kern); - if (sk == NULL) - return -ENOMEM; - - self = irda_sk(sk); - pr_debug("%s() : self is %p\n", __func__, self); - - init_waitqueue_head(&self->query_wait); - - switch (sock->type) { - case SOCK_STREAM: - sock->ops = &irda_stream_ops; - self->max_sdu_size_rx = TTP_SAR_DISABLE; - break; - case SOCK_SEQPACKET: - sock->ops = &irda_seqpacket_ops; - self->max_sdu_size_rx = TTP_SAR_UNBOUND; - break; - case SOCK_DGRAM: - switch (protocol) { -#ifdef CONFIG_IRDA_ULTRA - case IRDAPROTO_ULTRA: - sock->ops = &irda_ultra_ops; - /* Initialise now, because we may send on unbound - * sockets. Jean II */ - self->max_data_size = ULTRA_MAX_DATA - LMP_PID_HEADER; - self->max_header_size = IRDA_MAX_HEADER + LMP_PID_HEADER; - break; -#endif /* CONFIG_IRDA_ULTRA */ - case IRDAPROTO_UNITDATA: - sock->ops = &irda_dgram_ops; - /* We let Unitdata conn. be like seqpack conn. */ - self->max_sdu_size_rx = TTP_SAR_UNBOUND; - break; - default: - sk_free(sk); - return -ESOCKTNOSUPPORT; - } - break; - default: - sk_free(sk); - return -ESOCKTNOSUPPORT; - } - - /* Initialise networking socket struct */ - sock_init_data(sock, sk); /* Note : set sk->sk_refcnt to 1 */ - sk->sk_family = PF_IRDA; - sk->sk_protocol = protocol; - - /* Register as a client with IrLMP */ - self->ckey = irlmp_register_client(0, NULL, NULL, NULL); - self->mask.word = 0xffff; - self->rx_flow = self->tx_flow = FLOW_START; - self->nslots = DISCOVERY_DEFAULT_SLOTS; - self->daddr = DEV_ADDR_ANY; /* Until we get connected */ - self->saddr = 0x0; /* so IrLMP assign us any link */ - return 0; -} - -/* - * Function irda_destroy_socket (self) - * - * Destroy socket - * - */ -static void irda_destroy_socket(struct irda_sock *self) -{ - pr_debug("%s(%p)\n", __func__, self); - - /* Unregister with IrLMP */ - irlmp_unregister_client(self->ckey); - irlmp_unregister_service(self->skey); - - /* Unregister with LM-IAS */ - if (self->ias_obj) { - irias_delete_object(self->ias_obj); - self->ias_obj = NULL; - } - - if (self->iriap) { - iriap_close(self->iriap); - self->iriap = NULL; - } - - if (self->tsap) { - irttp_disconnect_request(self->tsap, NULL, P_NORMAL); - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } -#ifdef CONFIG_IRDA_ULTRA - if (self->lsap) { - irlmp_close_lsap(self->lsap); - self->lsap = NULL; - } -#endif /* CONFIG_IRDA_ULTRA */ -} - -/* - * Function irda_release (sock) - */ -static int irda_release(struct socket *sock) -{ - struct sock *sk = sock->sk; - - if (sk == NULL) - return 0; - - lock_sock(sk); - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - - /* Destroy IrDA socket */ - irda_destroy_socket(irda_sk(sk)); - - sock_orphan(sk); - sock->sk = NULL; - release_sock(sk); - - /* Purge queues (see sock_init_data()) */ - skb_queue_purge(&sk->sk_receive_queue); - - /* Destroy networking socket if we are the last reference on it, - * i.e. if(sk->sk_refcnt == 0) -> sk_free(sk) */ - sock_put(sk); - - /* Notes on socket locking and deallocation... - Jean II - * In theory we should put pairs of sock_hold() / sock_put() to - * prevent the socket to be destroyed whenever there is an - * outstanding request or outstanding incoming packet or event. - * - * 1) This may include IAS request, both in connect and getsockopt. - * Unfortunately, the situation is a bit more messy than it looks, - * because we close iriap and kfree(self) above. - * - * 2) This may include selective discovery in getsockopt. - * Same stuff as above, irlmp registration and self are gone. - * - * Probably 1 and 2 may not matter, because it's all triggered - * by a process and the socket layer already prevent the - * socket to go away while a process is holding it, through - * sockfd_put() and fput()... - * - * 3) This may include deferred TSAP closure. In particular, - * we may receive a late irda_disconnect_indication() - * Fortunately, (tsap_cb *)->close_pend should protect us - * from that. - * - * I did some testing on SMP, and it looks solid. And the socket - * memory leak is now gone... - Jean II - */ - - return 0; -} - -/* - * Function irda_sendmsg (sock, msg, len) - * - * Send message down to TinyTP. This function is used for both STREAM and - * SEQPACK services. This is possible since it forces the client to - * fragment the message if necessary - */ -static int irda_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) -{ - struct sock *sk = sock->sk; - struct irda_sock *self; - struct sk_buff *skb; - int err = -EPIPE; - - pr_debug("%s(), len=%zd\n", __func__, len); - - /* Note : socket.c set MSG_EOR on SEQPACKET sockets */ - if (msg->msg_flags & ~(MSG_DONTWAIT | MSG_EOR | MSG_CMSG_COMPAT | - MSG_NOSIGNAL)) { - return -EINVAL; - } - - lock_sock(sk); - - if (sk->sk_shutdown & SEND_SHUTDOWN) - goto out_err; - - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - - self = irda_sk(sk); - - /* Check if IrTTP is wants us to slow down */ - - if (wait_event_interruptible(*(sk_sleep(sk)), - (self->tx_flow != FLOW_STOP || sk->sk_state != TCP_ESTABLISHED))) { - err = -ERESTARTSYS; - goto out; - } - - /* Check if we are still connected */ - if (sk->sk_state != TCP_ESTABLISHED) { - err = -ENOTCONN; - goto out; - } - - /* Check that we don't send out too big frames */ - if (len > self->max_data_size) { - pr_debug("%s(), Chopping frame from %zd to %d bytes!\n", - __func__, len, self->max_data_size); - len = self->max_data_size; - } - - skb = sock_alloc_send_skb(sk, len + self->max_header_size + 16, - msg->msg_flags & MSG_DONTWAIT, &err); - if (!skb) - goto out_err; - - skb_reserve(skb, self->max_header_size + 16); - skb_reset_transport_header(skb); - skb_put(skb, len); - err = memcpy_from_msg(skb_transport_header(skb), msg, len); - if (err) { - kfree_skb(skb); - goto out_err; - } - - /* - * Just send the message to TinyTP, and let it deal with possible - * errors. No need to duplicate all that here - */ - err = irttp_data_request(self->tsap, skb); - if (err) { - pr_debug("%s(), err=%d\n", __func__, err); - goto out_err; - } - - release_sock(sk); - /* Tell client how much data we actually sent */ - return len; - -out_err: - err = sk_stream_error(sk, msg->msg_flags, err); -out: - release_sock(sk); - return err; - -} - -/* - * Function irda_recvmsg_dgram (sock, msg, size, flags) - * - * Try to receive message and copy it to user. The frame is discarded - * after being read, regardless of how much the user actually read - */ -static int irda_recvmsg_dgram(struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - struct sk_buff *skb; - size_t copied; - int err; - - skb = skb_recv_datagram(sk, flags & ~MSG_DONTWAIT, - flags & MSG_DONTWAIT, &err); - if (!skb) - return err; - - skb_reset_transport_header(skb); - copied = skb->len; - - if (copied > size) { - pr_debug("%s(), Received truncated frame (%zd < %zd)!\n", - __func__, copied, size); - copied = size; - msg->msg_flags |= MSG_TRUNC; - } - skb_copy_datagram_msg(skb, 0, msg, copied); - - skb_free_datagram(sk, skb); - - /* - * Check if we have previously stopped IrTTP and we know - * have more free space in our rx_queue. If so tell IrTTP - * to start delivering frames again before our rx_queue gets - * empty - */ - if (self->rx_flow == FLOW_STOP) { - if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) { - pr_debug("%s(), Starting IrTTP\n", __func__); - self->rx_flow = FLOW_START; - irttp_flow_request(self->tsap, FLOW_START); - } - } - - return copied; -} - -/* - * Function irda_recvmsg_stream (sock, msg, size, flags) - */ -static int irda_recvmsg_stream(struct socket *sock, struct msghdr *msg, - size_t size, int flags) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - int noblock = flags & MSG_DONTWAIT; - size_t copied = 0; - int target, err; - long timeo; - - if ((err = sock_error(sk)) < 0) - return err; - - if (sock->flags & __SO_ACCEPTCON) - return -EINVAL; - - err =-EOPNOTSUPP; - if (flags & MSG_OOB) - return -EOPNOTSUPP; - - err = 0; - target = sock_rcvlowat(sk, flags & MSG_WAITALL, size); - timeo = sock_rcvtimeo(sk, noblock); - - do { - int chunk; - struct sk_buff *skb = skb_dequeue(&sk->sk_receive_queue); - - if (skb == NULL) { - DEFINE_WAIT(wait); - err = 0; - - if (copied >= target) - break; - - prepare_to_wait_exclusive(sk_sleep(sk), &wait, TASK_INTERRUPTIBLE); - - /* - * POSIX 1003.1g mandates this order. - */ - err = sock_error(sk); - if (err) - ; - else if (sk->sk_shutdown & RCV_SHUTDOWN) - ; - else if (noblock) - err = -EAGAIN; - else if (signal_pending(current)) - err = sock_intr_errno(timeo); - else if (sk->sk_state != TCP_ESTABLISHED) - err = -ENOTCONN; - else if (skb_peek(&sk->sk_receive_queue) == NULL) - /* Wait process until data arrives */ - schedule(); - - finish_wait(sk_sleep(sk), &wait); - - if (err) - return err; - if (sk->sk_shutdown & RCV_SHUTDOWN) - break; - - continue; - } - - chunk = min_t(unsigned int, skb->len, size); - if (memcpy_to_msg(msg, skb->data, chunk)) { - skb_queue_head(&sk->sk_receive_queue, skb); - if (copied == 0) - copied = -EFAULT; - break; - } - copied += chunk; - size -= chunk; - - /* Mark read part of skb as used */ - if (!(flags & MSG_PEEK)) { - skb_pull(skb, chunk); - - /* put the skb back if we didn't use it up.. */ - if (skb->len) { - pr_debug("%s(), back on q!\n", - __func__); - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - - kfree_skb(skb); - } else { - pr_debug("%s() questionable!?\n", __func__); - - /* put message back and return */ - skb_queue_head(&sk->sk_receive_queue, skb); - break; - } - } while (size); - - /* - * Check if we have previously stopped IrTTP and we know - * have more free space in our rx_queue. If so tell IrTTP - * to start delivering frames again before our rx_queue gets - * empty - */ - if (self->rx_flow == FLOW_STOP) { - if ((atomic_read(&sk->sk_rmem_alloc) << 2) <= sk->sk_rcvbuf) { - pr_debug("%s(), Starting IrTTP\n", __func__); - self->rx_flow = FLOW_START; - irttp_flow_request(self->tsap, FLOW_START); - } - } - - return copied; -} - -/* - * Function irda_sendmsg_dgram (sock, msg, len) - * - * Send message down to TinyTP for the unreliable sequenced - * packet service... - * - */ -static int irda_sendmsg_dgram(struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct irda_sock *self; - struct sk_buff *skb; - int err; - - pr_debug("%s(), len=%zd\n", __func__, len); - - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) - return -EINVAL; - - lock_sock(sk); - - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - err = -EPIPE; - goto out; - } - - err = -ENOTCONN; - if (sk->sk_state != TCP_ESTABLISHED) - goto out; - - self = irda_sk(sk); - - /* - * Check that we don't send out too big frames. This is an unreliable - * service, so we have no fragmentation and no coalescence - */ - if (len > self->max_data_size) { - pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n", - __func__, len, self->max_data_size); - len = self->max_data_size; - } - - skb = sock_alloc_send_skb(sk, len + self->max_header_size, - msg->msg_flags & MSG_DONTWAIT, &err); - err = -ENOBUFS; - if (!skb) - goto out; - - skb_reserve(skb, self->max_header_size); - skb_reset_transport_header(skb); - - pr_debug("%s(), appending user data\n", __func__); - skb_put(skb, len); - err = memcpy_from_msg(skb_transport_header(skb), msg, len); - if (err) { - kfree_skb(skb); - goto out; - } - - /* - * Just send the message to TinyTP, and let it deal with possible - * errors. No need to duplicate all that here - */ - err = irttp_udata_request(self->tsap, skb); - if (err) { - pr_debug("%s(), err=%d\n", __func__, err); - goto out; - } - - release_sock(sk); - return len; - -out: - release_sock(sk); - return err; -} - -/* - * Function irda_sendmsg_ultra (sock, msg, len) - * - * Send message down to IrLMP for the unreliable Ultra - * packet service... - */ -#ifdef CONFIG_IRDA_ULTRA -static int irda_sendmsg_ultra(struct socket *sock, struct msghdr *msg, - size_t len) -{ - struct sock *sk = sock->sk; - struct irda_sock *self; - __u8 pid = 0; - int bound = 0; - struct sk_buff *skb; - int err; - - pr_debug("%s(), len=%zd\n", __func__, len); - - err = -EINVAL; - if (msg->msg_flags & ~(MSG_DONTWAIT|MSG_CMSG_COMPAT)) - return -EINVAL; - - lock_sock(sk); - - err = -EPIPE; - if (sk->sk_shutdown & SEND_SHUTDOWN) { - send_sig(SIGPIPE, current, 0); - goto out; - } - - self = irda_sk(sk); - - /* Check if an address was specified with sendto. Jean II */ - if (msg->msg_name) { - DECLARE_SOCKADDR(struct sockaddr_irda *, addr, msg->msg_name); - err = -EINVAL; - /* Check address, extract pid. Jean II */ - if (msg->msg_namelen < sizeof(*addr)) - goto out; - if (addr->sir_family != AF_IRDA) - goto out; - - pid = addr->sir_lsap_sel; - if (pid & 0x80) { - pr_debug("%s(), extension in PID not supp!\n", - __func__); - err = -EOPNOTSUPP; - goto out; - } - } else { - /* Check that the socket is properly bound to an Ultra - * port. Jean II */ - if ((self->lsap == NULL) || - (sk->sk_state != TCP_ESTABLISHED)) { - pr_debug("%s(), socket not bound to Ultra PID.\n", - __func__); - err = -ENOTCONN; - goto out; - } - /* Use PID from socket */ - bound = 1; - } - - /* - * Check that we don't send out too big frames. This is an unreliable - * service, so we have no fragmentation and no coalescence - */ - if (len > self->max_data_size) { - pr_debug("%s(), Warning too much data! Chopping frame from %zd to %d bytes!\n", - __func__, len, self->max_data_size); - len = self->max_data_size; - } - - skb = sock_alloc_send_skb(sk, len + self->max_header_size, - msg->msg_flags & MSG_DONTWAIT, &err); - err = -ENOBUFS; - if (!skb) - goto out; - - skb_reserve(skb, self->max_header_size); - skb_reset_transport_header(skb); - - pr_debug("%s(), appending user data\n", __func__); - skb_put(skb, len); - err = memcpy_from_msg(skb_transport_header(skb), msg, len); - if (err) { - kfree_skb(skb); - goto out; - } - - err = irlmp_connless_data_request((bound ? self->lsap : NULL), - skb, pid); - if (err) - pr_debug("%s(), err=%d\n", __func__, err); -out: - release_sock(sk); - return err ? : len; -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irda_shutdown (sk, how) - */ -static int irda_shutdown(struct socket *sock, int how) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - - pr_debug("%s(%p)\n", __func__, self); - - lock_sock(sk); - - sk->sk_state = TCP_CLOSE; - sk->sk_shutdown |= SEND_SHUTDOWN; - sk->sk_state_change(sk); - - if (self->iriap) { - iriap_close(self->iriap); - self->iriap = NULL; - } - - if (self->tsap) { - irttp_disconnect_request(self->tsap, NULL, P_NORMAL); - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - - /* A few cleanup so the socket look as good as new... */ - self->rx_flow = self->tx_flow = FLOW_START; /* needed ??? */ - self->daddr = DEV_ADDR_ANY; /* Until we get re-connected */ - self->saddr = 0x0; /* so IrLMP assign us any link */ - - release_sock(sk); - - return 0; -} - -/* - * Function irda_poll (file, sock, wait) - */ -static unsigned int irda_poll(struct file * file, struct socket *sock, - poll_table *wait) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - unsigned int mask; - - poll_wait(file, sk_sleep(sk), wait); - mask = 0; - - /* Exceptional events? */ - if (sk->sk_err) - mask |= POLLERR; - if (sk->sk_shutdown & RCV_SHUTDOWN) { - pr_debug("%s(), POLLHUP\n", __func__); - mask |= POLLHUP; - } - - /* Readable? */ - if (!skb_queue_empty(&sk->sk_receive_queue)) { - pr_debug("Socket is readable\n"); - mask |= POLLIN | POLLRDNORM; - } - - /* Connection-based need to check for termination and startup */ - switch (sk->sk_type) { - case SOCK_STREAM: - if (sk->sk_state == TCP_CLOSE) { - pr_debug("%s(), POLLHUP\n", __func__); - mask |= POLLHUP; - } - - if (sk->sk_state == TCP_ESTABLISHED) { - if ((self->tx_flow == FLOW_START) && - sock_writeable(sk)) - { - mask |= POLLOUT | POLLWRNORM | POLLWRBAND; - } - } - break; - case SOCK_SEQPACKET: - if ((self->tx_flow == FLOW_START) && - sock_writeable(sk)) - { - mask |= POLLOUT | POLLWRNORM | POLLWRBAND; - } - break; - case SOCK_DGRAM: - if (sock_writeable(sk)) - mask |= POLLOUT | POLLWRNORM | POLLWRBAND; - break; - default: - break; - } - - return mask; -} - -/* - * Function irda_ioctl (sock, cmd, arg) - */ -static int irda_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - struct sock *sk = sock->sk; - int err; - - pr_debug("%s(), cmd=%#x\n", __func__, cmd); - - err = -EINVAL; - switch (cmd) { - case TIOCOUTQ: { - long amount; - - amount = sk->sk_sndbuf - sk_wmem_alloc_get(sk); - if (amount < 0) - amount = 0; - err = put_user(amount, (unsigned int __user *)arg); - break; - } - - case TIOCINQ: { - struct sk_buff *skb; - long amount = 0L; - /* These two are safe on a single CPU system as only user tasks fiddle here */ - if ((skb = skb_peek(&sk->sk_receive_queue)) != NULL) - amount = skb->len; - err = put_user(amount, (unsigned int __user *)arg); - break; - } - - case SIOCGSTAMP: - if (sk != NULL) - err = sock_get_timestamp(sk, (struct timeval __user *)arg); - break; - - case SIOCGIFADDR: - case SIOCSIFADDR: - case SIOCGIFDSTADDR: - case SIOCSIFDSTADDR: - case SIOCGIFBRDADDR: - case SIOCSIFBRDADDR: - case SIOCGIFNETMASK: - case SIOCSIFNETMASK: - case SIOCGIFMETRIC: - case SIOCSIFMETRIC: - break; - default: - pr_debug("%s(), doing device ioctl!\n", __func__); - err = -ENOIOCTLCMD; - } - - return err; -} - -#ifdef CONFIG_COMPAT -/* - * Function irda_ioctl (sock, cmd, arg) - */ -static int irda_compat_ioctl(struct socket *sock, unsigned int cmd, unsigned long arg) -{ - /* - * All IRDA's ioctl are standard ones. - */ - return -ENOIOCTLCMD; -} -#endif - -/* - * Function irda_setsockopt (sock, level, optname, optval, optlen) - * - * Set some options for the socket - * - */ -static int irda_setsockopt(struct socket *sock, int level, int optname, - char __user *optval, unsigned int optlen) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - struct irda_ias_set *ias_opt; - struct ias_object *ias_obj; - struct ias_attrib * ias_attr; /* Attribute in IAS object */ - int opt, free_ias = 0, err = 0; - - pr_debug("%s(%p)\n", __func__, self); - - if (level != SOL_IRLMP) - return -ENOPROTOOPT; - - lock_sock(sk); - - switch (optname) { - case IRLMP_IAS_SET: - /* The user want to add an attribute to an existing IAS object - * (in the IAS database) or to create a new object with this - * attribute. - * We first query IAS to know if the object exist, and then - * create the right attribute... - */ - - if (optlen != sizeof(struct irda_ias_set)) { - err = -EINVAL; - goto out; - } - - /* Copy query to the driver. */ - ias_opt = memdup_user(optval, optlen); - if (IS_ERR(ias_opt)) { - err = PTR_ERR(ias_opt); - goto out; - } - - /* Find the object we target. - * If the user gives us an empty string, we use the object - * associated with this socket. This will workaround - * duplicated class name - Jean II */ - if(ias_opt->irda_class_name[0] == '\0') { - if(self->ias_obj == NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - ias_obj = self->ias_obj; - } else - ias_obj = irias_find_object(ias_opt->irda_class_name); - - /* Only ROOT can mess with the global IAS database. - * Users can only add attributes to the object associated - * with the socket they own - Jean II */ - if((!capable(CAP_NET_ADMIN)) && - ((ias_obj == NULL) || (ias_obj != self->ias_obj))) { - kfree(ias_opt); - err = -EPERM; - goto out; - } - - /* If the object doesn't exist, create it */ - if(ias_obj == (struct ias_object *) NULL) { - /* Create a new object */ - ias_obj = irias_new_object(ias_opt->irda_class_name, - jiffies); - if (ias_obj == NULL) { - kfree(ias_opt); - err = -ENOMEM; - goto out; - } - free_ias = 1; - } - - /* Do we have the attribute already ? */ - if(irias_find_attrib(ias_obj, ias_opt->irda_attrib_name)) { - kfree(ias_opt); - if (free_ias) { - kfree(ias_obj->name); - kfree(ias_obj); - } - err = -EINVAL; - goto out; - } - - /* Look at the type */ - switch(ias_opt->irda_attrib_type) { - case IAS_INTEGER: - /* Add an integer attribute */ - irias_add_integer_attrib( - ias_obj, - ias_opt->irda_attrib_name, - ias_opt->attribute.irda_attrib_int, - IAS_USER_ATTR); - break; - case IAS_OCT_SEQ: - /* Check length */ - if(ias_opt->attribute.irda_attrib_octet_seq.len > - IAS_MAX_OCTET_STRING) { - kfree(ias_opt); - if (free_ias) { - kfree(ias_obj->name); - kfree(ias_obj); - } - - err = -EINVAL; - goto out; - } - /* Add an octet sequence attribute */ - irias_add_octseq_attrib( - ias_obj, - ias_opt->irda_attrib_name, - ias_opt->attribute.irda_attrib_octet_seq.octet_seq, - ias_opt->attribute.irda_attrib_octet_seq.len, - IAS_USER_ATTR); - break; - case IAS_STRING: - /* Should check charset & co */ - /* Check length */ - /* The length is encoded in a __u8, and - * IAS_MAX_STRING == 256, so there is no way - * userspace can pass us a string too large. - * Jean II */ - /* NULL terminate the string (avoid troubles) */ - ias_opt->attribute.irda_attrib_string.string[ias_opt->attribute.irda_attrib_string.len] = '\0'; - /* Add a string attribute */ - irias_add_string_attrib( - ias_obj, - ias_opt->irda_attrib_name, - ias_opt->attribute.irda_attrib_string.string, - IAS_USER_ATTR); - break; - default : - kfree(ias_opt); - if (free_ias) { - kfree(ias_obj->name); - kfree(ias_obj); - } - err = -EINVAL; - goto out; - } - irias_insert_object(ias_obj); - kfree(ias_opt); - break; - case IRLMP_IAS_DEL: - /* The user want to delete an object from our local IAS - * database. We just need to query the IAS, check is the - * object is not owned by the kernel and delete it. - */ - - if (optlen != sizeof(struct irda_ias_set)) { - err = -EINVAL; - goto out; - } - - /* Copy query to the driver. */ - ias_opt = memdup_user(optval, optlen); - if (IS_ERR(ias_opt)) { - err = PTR_ERR(ias_opt); - goto out; - } - - /* Find the object we target. - * If the user gives us an empty string, we use the object - * associated with this socket. This will workaround - * duplicated class name - Jean II */ - if(ias_opt->irda_class_name[0] == '\0') - ias_obj = self->ias_obj; - else - ias_obj = irias_find_object(ias_opt->irda_class_name); - if(ias_obj == (struct ias_object *) NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - - /* Only ROOT can mess with the global IAS database. - * Users can only del attributes from the object associated - * with the socket they own - Jean II */ - if((!capable(CAP_NET_ADMIN)) && - ((ias_obj == NULL) || (ias_obj != self->ias_obj))) { - kfree(ias_opt); - err = -EPERM; - goto out; - } - - /* Find the attribute (in the object) we target */ - ias_attr = irias_find_attrib(ias_obj, - ias_opt->irda_attrib_name); - if(ias_attr == (struct ias_attrib *) NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - - /* Check is the user space own the object */ - if(ias_attr->value->owner != IAS_USER_ATTR) { - pr_debug("%s(), attempting to delete a kernel attribute\n", - __func__); - kfree(ias_opt); - err = -EPERM; - goto out; - } - - /* Remove the attribute (and maybe the object) */ - irias_delete_attrib(ias_obj, ias_attr, 1); - kfree(ias_opt); - break; - case IRLMP_MAX_SDU_SIZE: - if (optlen < sizeof(int)) { - err = -EINVAL; - goto out; - } - - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - /* Only possible for a seqpacket service (TTP with SAR) */ - if (sk->sk_type != SOCK_SEQPACKET) { - pr_debug("%s(), setting max_sdu_size = %d\n", - __func__, opt); - self->max_sdu_size_rx = opt; - } else { - net_warn_ratelimited("%s: not allowed to set MAXSDUSIZE for this socket type!\n", - __func__); - err = -ENOPROTOOPT; - goto out; - } - break; - case IRLMP_HINTS_SET: - if (optlen < sizeof(int)) { - err = -EINVAL; - goto out; - } - - /* The input is really a (__u8 hints[2]), easier as an int */ - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - /* Unregister any old registration */ - irlmp_unregister_service(self->skey); - - self->skey = irlmp_register_service((__u16) opt); - break; - case IRLMP_HINT_MASK_SET: - /* As opposed to the previous case which set the hint bits - * that we advertise, this one set the filter we use when - * making a discovery (nodes which don't match any hint - * bit in the mask are not reported). - */ - if (optlen < sizeof(int)) { - err = -EINVAL; - goto out; - } - - /* The input is really a (__u8 hints[2]), easier as an int */ - if (get_user(opt, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - /* Set the new hint mask */ - self->mask.word = (__u16) opt; - /* Mask out extension bits */ - self->mask.word &= 0x7f7f; - /* Check if no bits */ - if(!self->mask.word) - self->mask.word = 0xFFFF; - - break; - default: - err = -ENOPROTOOPT; - break; - } - -out: - release_sock(sk); - - return err; -} - -/* - * Function irda_extract_ias_value(ias_opt, ias_value) - * - * Translate internal IAS value structure to the user space representation - * - * The external representation of IAS values, as we exchange them with - * user space program is quite different from the internal representation, - * as stored in the IAS database (because we need a flat structure for - * crossing kernel boundary). - * This function transform the former in the latter. We also check - * that the value type is valid. - */ -static int irda_extract_ias_value(struct irda_ias_set *ias_opt, - struct ias_value *ias_value) -{ - /* Look at the type */ - switch (ias_value->type) { - case IAS_INTEGER: - /* Copy the integer */ - ias_opt->attribute.irda_attrib_int = ias_value->t.integer; - break; - case IAS_OCT_SEQ: - /* Set length */ - ias_opt->attribute.irda_attrib_octet_seq.len = ias_value->len; - /* Copy over */ - memcpy(ias_opt->attribute.irda_attrib_octet_seq.octet_seq, - ias_value->t.oct_seq, ias_value->len); - break; - case IAS_STRING: - /* Set length */ - ias_opt->attribute.irda_attrib_string.len = ias_value->len; - ias_opt->attribute.irda_attrib_string.charset = ias_value->charset; - /* Copy over */ - memcpy(ias_opt->attribute.irda_attrib_string.string, - ias_value->t.string, ias_value->len); - /* NULL terminate the string (avoid troubles) */ - ias_opt->attribute.irda_attrib_string.string[ias_value->len] = '\0'; - break; - case IAS_MISSING: - default : - return -EINVAL; - } - - /* Copy type over */ - ias_opt->irda_attrib_type = ias_value->type; - - return 0; -} - -/* - * Function irda_getsockopt (sock, level, optname, optval, optlen) - */ -static int irda_getsockopt(struct socket *sock, int level, int optname, - char __user *optval, int __user *optlen) -{ - struct sock *sk = sock->sk; - struct irda_sock *self = irda_sk(sk); - struct irda_device_list list = { 0 }; - struct irda_device_info *discoveries; - struct irda_ias_set * ias_opt; /* IAS get/query params */ - struct ias_object * ias_obj; /* Object in IAS */ - struct ias_attrib * ias_attr; /* Attribute in IAS object */ - int daddr = DEV_ADDR_ANY; /* Dest address for IAS queries */ - int val = 0; - int len = 0; - int err = 0; - int offset, total; - - pr_debug("%s(%p)\n", __func__, self); - - if (level != SOL_IRLMP) - return -ENOPROTOOPT; - - if (get_user(len, optlen)) - return -EFAULT; - - if(len < 0) - return -EINVAL; - - lock_sock(sk); - - switch (optname) { - case IRLMP_ENUMDEVICES: - - /* Offset to first device entry */ - offset = sizeof(struct irda_device_list) - - sizeof(struct irda_device_info); - - if (len < offset) { - err = -EINVAL; - goto out; - } - - /* Ask lmp for the current discovery log */ - discoveries = irlmp_get_discoveries(&list.len, self->mask.word, - self->nslots); - /* Check if the we got some results */ - if (discoveries == NULL) { - err = -EAGAIN; - goto out; /* Didn't find any devices */ - } - - /* Write total list length back to client */ - if (copy_to_user(optval, &list, offset)) - err = -EFAULT; - - /* Copy the list itself - watch for overflow */ - if (list.len > 2048) { - err = -EINVAL; - goto bed; - } - total = offset + (list.len * sizeof(struct irda_device_info)); - if (total > len) - total = len; - if (copy_to_user(optval+offset, discoveries, total - offset)) - err = -EFAULT; - - /* Write total number of bytes used back to client */ - if (put_user(total, optlen)) - err = -EFAULT; -bed: - /* Free up our buffer */ - kfree(discoveries); - break; - case IRLMP_MAX_SDU_SIZE: - val = self->max_data_size; - len = sizeof(int); - if (put_user(len, optlen)) { - err = -EFAULT; - goto out; - } - - if (copy_to_user(optval, &val, len)) { - err = -EFAULT; - goto out; - } - - break; - case IRLMP_IAS_GET: - /* The user want an object from our local IAS database. - * We just need to query the IAS and return the value - * that we found */ - - /* Check that the user has allocated the right space for us */ - if (len != sizeof(struct irda_ias_set)) { - err = -EINVAL; - goto out; - } - - /* Copy query to the driver. */ - ias_opt = memdup_user(optval, len); - if (IS_ERR(ias_opt)) { - err = PTR_ERR(ias_opt); - goto out; - } - - /* Find the object we target. - * If the user gives us an empty string, we use the object - * associated with this socket. This will workaround - * duplicated class name - Jean II */ - if(ias_opt->irda_class_name[0] == '\0') - ias_obj = self->ias_obj; - else - ias_obj = irias_find_object(ias_opt->irda_class_name); - if(ias_obj == (struct ias_object *) NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - - /* Find the attribute (in the object) we target */ - ias_attr = irias_find_attrib(ias_obj, - ias_opt->irda_attrib_name); - if(ias_attr == (struct ias_attrib *) NULL) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - - /* Translate from internal to user structure */ - err = irda_extract_ias_value(ias_opt, ias_attr->value); - if(err) { - kfree(ias_opt); - goto out; - } - - /* Copy reply to the user */ - if (copy_to_user(optval, ias_opt, - sizeof(struct irda_ias_set))) { - kfree(ias_opt); - err = -EFAULT; - goto out; - } - /* Note : don't need to put optlen, we checked it */ - kfree(ias_opt); - break; - case IRLMP_IAS_QUERY: - /* The user want an object from a remote IAS database. - * We need to use IAP to query the remote database and - * then wait for the answer to come back. */ - - /* Check that the user has allocated the right space for us */ - if (len != sizeof(struct irda_ias_set)) { - err = -EINVAL; - goto out; - } - - /* Copy query to the driver. */ - ias_opt = memdup_user(optval, len); - if (IS_ERR(ias_opt)) { - err = PTR_ERR(ias_opt); - goto out; - } - - /* At this point, there are two cases... - * 1) the socket is connected - that's the easy case, we - * just query the device we are connected to... - * 2) the socket is not connected - the user doesn't want - * to connect and/or may not have a valid service name - * (so can't create a fake connection). In this case, - * we assume that the user pass us a valid destination - * address in the requesting structure... - */ - if(self->daddr != DEV_ADDR_ANY) { - /* We are connected - reuse known daddr */ - daddr = self->daddr; - } else { - /* We are not connected, we must specify a valid - * destination address */ - daddr = ias_opt->daddr; - if((!daddr) || (daddr == DEV_ADDR_ANY)) { - kfree(ias_opt); - err = -EINVAL; - goto out; - } - } - - /* Check that we can proceed with IAP */ - if (self->iriap) { - net_warn_ratelimited("%s: busy with a previous query\n", - __func__); - kfree(ias_opt); - err = -EBUSY; - goto out; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irda_getvalue_confirm); - - if (self->iriap == NULL) { - kfree(ias_opt); - err = -ENOMEM; - goto out; - } - - /* Treat unexpected wakeup as disconnect */ - self->errno = -EHOSTUNREACH; - - /* Query remote LM-IAS */ - iriap_getvaluebyclass_request(self->iriap, - self->saddr, daddr, - ias_opt->irda_class_name, - ias_opt->irda_attrib_name); - - /* Wait for answer, if not yet finished (or failed) */ - if (wait_event_interruptible(self->query_wait, - (self->iriap == NULL))) { - /* pending request uses copy of ias_opt-content - * we can free it regardless! */ - kfree(ias_opt); - /* Treat signals as disconnect */ - err = -EHOSTUNREACH; - goto out; - } - - /* Check what happened */ - if (self->errno) - { - kfree(ias_opt); - /* Requested object/attribute doesn't exist */ - if((self->errno == IAS_CLASS_UNKNOWN) || - (self->errno == IAS_ATTRIB_UNKNOWN)) - err = -EADDRNOTAVAIL; - else - err = -EHOSTUNREACH; - - goto out; - } - - /* Translate from internal to user structure */ - err = irda_extract_ias_value(ias_opt, self->ias_result); - if (self->ias_result) - irias_delete_value(self->ias_result); - if (err) { - kfree(ias_opt); - goto out; - } - - /* Copy reply to the user */ - if (copy_to_user(optval, ias_opt, - sizeof(struct irda_ias_set))) { - kfree(ias_opt); - err = -EFAULT; - goto out; - } - /* Note : don't need to put optlen, we checked it */ - kfree(ias_opt); - break; - case IRLMP_WAITDEVICE: - /* This function is just another way of seeing life ;-) - * IRLMP_ENUMDEVICES assumes that you have a static network, - * and that you just want to pick one of the devices present. - * On the other hand, in here we assume that no device is - * present and that at some point in the future a device will - * come into range. When this device arrive, we just wake - * up the caller, so that he has time to connect to it before - * the device goes away... - * Note : once the node has been discovered for more than a - * few second, it won't trigger this function, unless it - * goes away and come back changes its hint bits (so we - * might call it IRLMP_WAITNEWDEVICE). - */ - - /* Check that the user is passing us an int */ - if (len != sizeof(int)) { - err = -EINVAL; - goto out; - } - /* Get timeout in ms (max time we block the caller) */ - if (get_user(val, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - /* Tell IrLMP we want to be notified */ - irlmp_update_client(self->ckey, self->mask.word, - irda_selective_discovery_indication, - NULL, (void *) self); - - /* Do some discovery (and also return cached results) */ - irlmp_discovery_request(self->nslots); - - /* Wait until a node is discovered */ - if (!self->cachedaddr) { - pr_debug("%s(), nothing discovered yet, going to sleep...\n", - __func__); - - /* Set watchdog timer to expire in ms. */ - self->errno = 0; - setup_timer(&self->watchdog, irda_discovery_timeout, - (unsigned long)self); - mod_timer(&self->watchdog, - jiffies + msecs_to_jiffies(val)); - - /* Wait for IR-LMP to call us back */ - err = __wait_event_interruptible(self->query_wait, - (self->cachedaddr != 0 || self->errno == -ETIME)); - - /* If watchdog is still activated, kill it! */ - del_timer(&(self->watchdog)); - - pr_debug("%s(), ...waking up !\n", __func__); - - if (err != 0) - goto out; - } - else - pr_debug("%s(), found immediately !\n", - __func__); - - /* Tell IrLMP that we have been notified */ - irlmp_update_client(self->ckey, self->mask.word, - NULL, NULL, NULL); - - /* Check if the we got some results */ - if (!self->cachedaddr) { - err = -EAGAIN; /* Didn't find any devices */ - goto out; - } - daddr = self->cachedaddr; - /* Cleanup */ - self->cachedaddr = 0; - - /* We return the daddr of the device that trigger the - * wakeup. As irlmp pass us only the new devices, we - * are sure that it's not an old device. - * If the user want more details, he should query - * the whole discovery log and pick one device... - */ - if (put_user(daddr, (int __user *)optval)) { - err = -EFAULT; - goto out; - } - - break; - default: - err = -ENOPROTOOPT; - } - -out: - - release_sock(sk); - - return err; -} - -static const struct net_proto_family irda_family_ops = { - .family = PF_IRDA, - .create = irda_create, - .owner = THIS_MODULE, -}; - -static const struct proto_ops irda_stream_ops = { - .family = PF_IRDA, - .owner = THIS_MODULE, - .release = irda_release, - .bind = irda_bind, - .connect = irda_connect, - .socketpair = sock_no_socketpair, - .accept = irda_accept, - .getname = irda_getname, - .poll = irda_poll, - .ioctl = irda_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = irda_compat_ioctl, -#endif - .listen = irda_listen, - .shutdown = irda_shutdown, - .setsockopt = irda_setsockopt, - .getsockopt = irda_getsockopt, - .sendmsg = irda_sendmsg, - .recvmsg = irda_recvmsg_stream, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; - -static const struct proto_ops irda_seqpacket_ops = { - .family = PF_IRDA, - .owner = THIS_MODULE, - .release = irda_release, - .bind = irda_bind, - .connect = irda_connect, - .socketpair = sock_no_socketpair, - .accept = irda_accept, - .getname = irda_getname, - .poll = datagram_poll, - .ioctl = irda_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = irda_compat_ioctl, -#endif - .listen = irda_listen, - .shutdown = irda_shutdown, - .setsockopt = irda_setsockopt, - .getsockopt = irda_getsockopt, - .sendmsg = irda_sendmsg, - .recvmsg = irda_recvmsg_dgram, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; - -static const struct proto_ops irda_dgram_ops = { - .family = PF_IRDA, - .owner = THIS_MODULE, - .release = irda_release, - .bind = irda_bind, - .connect = irda_connect, - .socketpair = sock_no_socketpair, - .accept = irda_accept, - .getname = irda_getname, - .poll = datagram_poll, - .ioctl = irda_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = irda_compat_ioctl, -#endif - .listen = irda_listen, - .shutdown = irda_shutdown, - .setsockopt = irda_setsockopt, - .getsockopt = irda_getsockopt, - .sendmsg = irda_sendmsg_dgram, - .recvmsg = irda_recvmsg_dgram, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; - -#ifdef CONFIG_IRDA_ULTRA -static const struct proto_ops irda_ultra_ops = { - .family = PF_IRDA, - .owner = THIS_MODULE, - .release = irda_release, - .bind = irda_bind, - .connect = sock_no_connect, - .socketpair = sock_no_socketpair, - .accept = sock_no_accept, - .getname = irda_getname, - .poll = datagram_poll, - .ioctl = irda_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = irda_compat_ioctl, -#endif - .listen = sock_no_listen, - .shutdown = irda_shutdown, - .setsockopt = irda_setsockopt, - .getsockopt = irda_getsockopt, - .sendmsg = irda_sendmsg_ultra, - .recvmsg = irda_recvmsg_dgram, - .mmap = sock_no_mmap, - .sendpage = sock_no_sendpage, -}; -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irsock_init (pro) - * - * Initialize IrDA protocol - * - */ -int __init irsock_init(void) -{ - int rc = proto_register(&irda_proto, 0); - - if (rc == 0) - rc = sock_register(&irda_family_ops); - - return rc; -} - -/* - * Function irsock_cleanup (void) - * - * Remove IrDA protocol - * - */ -void irsock_cleanup(void) -{ - sock_unregister(PF_IRDA); - proto_unregister(&irda_proto); -} diff --git a/net/irda/discovery.c b/net/irda/discovery.c deleted file mode 100644 index 364d70aed068..000000000000 --- a/net/irda/discovery.c +++ /dev/null @@ -1,417 +0,0 @@ -/********************************************************************* - * - * Filename: discovery.c - * Version: 0.1 - * Description: Routines for handling discoveries at the IrLMP layer - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Apr 6 15:33:50 1999 - * Modified at: Sat Oct 9 17:11:31 1999 - * Modified by: Dag Brattli - * Modified at: Fri May 28 3:11 CST 1999 - * Modified by: Horst von Brand - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include - -/* - * Function irlmp_add_discovery (cachelog, discovery) - * - * Add a new discovery to the cachelog, and remove any old discoveries - * from the same device - * - * Note : we try to preserve the time this device was *first* discovered - * (as opposed to the time of last discovery used for cleanup). This is - * used by clients waiting for discovery events to tell if the device - * discovered is "new" or just the same old one. They can't rely there - * on a binary flag (new/old), because not all discovery events are - * propagated to them, and they might not always listen, so they would - * miss some new devices popping up... - * Jean II - */ -void irlmp_add_discovery(hashbin_t *cachelog, discovery_t *new) -{ - discovery_t *discovery, *node; - unsigned long flags; - - /* Set time of first discovery if node is new (see below) */ - new->firststamp = new->timestamp; - - spin_lock_irqsave(&cachelog->hb_spinlock, flags); - - /* - * Remove all discoveries of devices that has previously been - * discovered on the same link with the same name (info), or the - * same daddr. We do this since some devices (mostly PDAs) change - * their device address between every discovery. - */ - discovery = (discovery_t *) hashbin_get_first(cachelog); - while (discovery != NULL ) { - node = discovery; - - /* Be sure to stay one item ahead */ - discovery = (discovery_t *) hashbin_get_next(cachelog); - - if ((node->data.saddr == new->data.saddr) && - ((node->data.daddr == new->data.daddr) || - (strcmp(node->data.info, new->data.info) == 0))) - { - /* This discovery is a previous discovery - * from the same device, so just remove it - */ - hashbin_remove_this(cachelog, (irda_queue_t *) node); - /* Check if hints bits are unchanged */ - if (get_unaligned((__u16 *)node->data.hints) == get_unaligned((__u16 *)new->data.hints)) - /* Set time of first discovery for this node */ - new->firststamp = node->firststamp; - kfree(node); - } - } - - /* Insert the new and updated version */ - hashbin_insert(cachelog, (irda_queue_t *) new, new->data.daddr, NULL); - - spin_unlock_irqrestore(&cachelog->hb_spinlock, flags); -} - -/* - * Function irlmp_add_discovery_log (cachelog, log) - * - * Merge a disovery log into the cachelog. - * - */ -void irlmp_add_discovery_log(hashbin_t *cachelog, hashbin_t *log) -{ - discovery_t *discovery; - - /* - * If log is missing this means that IrLAP was unable to perform the - * discovery, so restart discovery again with just the half timeout - * of the normal one. - */ - /* Well... It means that there was nobody out there - Jean II */ - if (log == NULL) { - /* irlmp_start_discovery_timer(irlmp, 150); */ - return; - } - - /* - * Locking : we are the only owner of this discovery log, so - * no need to lock it. - * We just need to lock the global log in irlmp_add_discovery(). - */ - discovery = (discovery_t *) hashbin_remove_first(log); - while (discovery != NULL) { - irlmp_add_discovery(cachelog, discovery); - - discovery = (discovery_t *) hashbin_remove_first(log); - } - - /* Delete the now empty log */ - hashbin_delete(log, (FREE_FUNC) kfree); -} - -/* - * Function irlmp_expire_discoveries (log, saddr, force) - * - * Go through all discoveries and expire all that has stayed too long - * - * Note : this assume that IrLAP won't change its saddr, which - * currently is a valid assumption... - */ -void irlmp_expire_discoveries(hashbin_t *log, __u32 saddr, int force) -{ - discovery_t * discovery; - discovery_t * curr; - unsigned long flags; - discinfo_t * buffer = NULL; - int n; /* Size of the full log */ - int i = 0; /* How many we expired */ - - IRDA_ASSERT(log != NULL, return;); - spin_lock_irqsave(&log->hb_spinlock, flags); - - discovery = (discovery_t *) hashbin_get_first(log); - while (discovery != NULL) { - /* Be sure to be one item ahead */ - curr = discovery; - discovery = (discovery_t *) hashbin_get_next(log); - - /* Test if it's time to expire this discovery */ - if ((curr->data.saddr == saddr) && - (force || - ((jiffies - curr->timestamp) > DISCOVERY_EXPIRE_TIMEOUT))) - { - /* Create buffer as needed. - * As this function get called a lot and most time - * we don't have anything to put in the log (we are - * quite picky), we can save a lot of overhead - * by not calling kmalloc. Jean II */ - if(buffer == NULL) { - /* Create the client specific buffer */ - n = HASHBIN_GET_SIZE(log); - buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC); - if (buffer == NULL) { - spin_unlock_irqrestore(&log->hb_spinlock, flags); - return; - } - - } - - /* Copy discovery information */ - memcpy(&(buffer[i]), &(curr->data), - sizeof(discinfo_t)); - i++; - - /* Remove it from the log */ - curr = hashbin_remove_this(log, (irda_queue_t *) curr); - kfree(curr); - } - } - - /* Drop the spinlock before calling the higher layers, as - * we can't guarantee they won't call us back and create a - * deadlock. We will work on our own private data, so we - * don't care to be interrupted. - Jean II */ - spin_unlock_irqrestore(&log->hb_spinlock, flags); - - if(buffer == NULL) - return; - - /* Tell IrLMP and registered clients about it */ - irlmp_discovery_expiry(buffer, i); - - /* Free up our buffer */ - kfree(buffer); -} - -#if 0 -/* - * Function irlmp_dump_discoveries (log) - * - * Print out all discoveries in log - * - */ -void irlmp_dump_discoveries(hashbin_t *log) -{ - discovery_t *discovery; - - IRDA_ASSERT(log != NULL, return;); - - discovery = (discovery_t *) hashbin_get_first(log); - while (discovery != NULL) { - pr_debug("Discovery:\n"); - pr_debug(" daddr=%08x\n", discovery->data.daddr); - pr_debug(" saddr=%08x\n", discovery->data.saddr); - pr_debug(" nickname=%s\n", discovery->data.info); - - discovery = (discovery_t *) hashbin_get_next(log); - } -} -#endif - -/* - * Function irlmp_copy_discoveries (log, pn, mask) - * - * Copy all discoveries in a buffer - * - * This function implement a safe way for lmp clients to access the - * discovery log. The basic problem is that we don't want the log - * to change (add/remove) while the client is reading it. If the - * lmp client manipulate directly the hashbin, he is sure to get - * into troubles... - * The idea is that we copy all the current discovery log in a buffer - * which is specific to the client and pass this copy to him. As we - * do this operation with the spinlock grabbed, we are safe... - * Note : we don't want those clients to grab the spinlock, because - * we have no control on how long they will hold it... - * Note : we choose to copy the log in "struct irda_device_info" to - * save space... - * Note : the client must kfree himself() the log... - * Jean II - */ -struct irda_device_info *irlmp_copy_discoveries(hashbin_t *log, int *pn, - __u16 mask, int old_entries) -{ - discovery_t * discovery; - unsigned long flags; - discinfo_t * buffer = NULL; - int j_timeout = (sysctl_discovery_timeout * HZ); - int n; /* Size of the full log */ - int i = 0; /* How many we picked */ - - IRDA_ASSERT(pn != NULL, return NULL;); - IRDA_ASSERT(log != NULL, return NULL;); - - /* Save spin lock */ - spin_lock_irqsave(&log->hb_spinlock, flags); - - discovery = (discovery_t *) hashbin_get_first(log); - while (discovery != NULL) { - /* Mask out the ones we don't want : - * We want to match the discovery mask, and to get only - * the most recent one (unless we want old ones) */ - if ((get_unaligned((__u16 *)discovery->data.hints) & mask) && - ((old_entries) || - ((jiffies - discovery->firststamp) < j_timeout))) { - /* Create buffer as needed. - * As this function get called a lot and most time - * we don't have anything to put in the log (we are - * quite picky), we can save a lot of overhead - * by not calling kmalloc. Jean II */ - if(buffer == NULL) { - /* Create the client specific buffer */ - n = HASHBIN_GET_SIZE(log); - buffer = kmalloc(n * sizeof(struct irda_device_info), GFP_ATOMIC); - if (buffer == NULL) { - spin_unlock_irqrestore(&log->hb_spinlock, flags); - return NULL; - } - - } - - /* Copy discovery information */ - memcpy(&(buffer[i]), &(discovery->data), - sizeof(discinfo_t)); - i++; - } - discovery = (discovery_t *) hashbin_get_next(log); - } - - spin_unlock_irqrestore(&log->hb_spinlock, flags); - - /* Get the actual number of device in the buffer and return */ - *pn = i; - return buffer; -} - -#ifdef CONFIG_PROC_FS -static inline discovery_t *discovery_seq_idx(loff_t pos) - -{ - discovery_t *discovery; - - for (discovery = (discovery_t *) hashbin_get_first(irlmp->cachelog); - discovery != NULL; - discovery = (discovery_t *) hashbin_get_next(irlmp->cachelog)) { - if (pos-- == 0) - break; - } - - return discovery; -} - -static void *discovery_seq_start(struct seq_file *seq, loff_t *pos) -{ - spin_lock_irq(&irlmp->cachelog->hb_spinlock); - return *pos ? discovery_seq_idx(*pos - 1) : SEQ_START_TOKEN; -} - -static void *discovery_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - return (v == SEQ_START_TOKEN) - ? (void *) hashbin_get_first(irlmp->cachelog) - : (void *) hashbin_get_next(irlmp->cachelog); -} - -static void discovery_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&irlmp->cachelog->hb_spinlock); -} - -static int discovery_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_puts(seq, "IrLMP: Discovery log:\n\n"); - else { - const discovery_t *discovery = v; - - seq_printf(seq, "nickname: %s, hint: 0x%02x%02x", - discovery->data.info, - discovery->data.hints[0], - discovery->data.hints[1]); -#if 0 - if ( discovery->data.hints[0] & HINT_PNP) - seq_puts(seq, "PnP Compatible "); - if ( discovery->data.hints[0] & HINT_PDA) - seq_puts(seq, "PDA/Palmtop "); - if ( discovery->data.hints[0] & HINT_COMPUTER) - seq_puts(seq, "Computer "); - if ( discovery->data.hints[0] & HINT_PRINTER) - seq_puts(seq, "Printer "); - if ( discovery->data.hints[0] & HINT_MODEM) - seq_puts(seq, "Modem "); - if ( discovery->data.hints[0] & HINT_FAX) - seq_puts(seq, "Fax "); - if ( discovery->data.hints[0] & HINT_LAN) - seq_puts(seq, "LAN Access "); - - if ( discovery->data.hints[1] & HINT_TELEPHONY) - seq_puts(seq, "Telephony "); - if ( discovery->data.hints[1] & HINT_FILE_SERVER) - seq_puts(seq, "File Server "); - if ( discovery->data.hints[1] & HINT_COMM) - seq_puts(seq, "IrCOMM "); - if ( discovery->data.hints[1] & HINT_OBEX) - seq_puts(seq, "IrOBEX "); -#endif - seq_printf(seq,", saddr: 0x%08x, daddr: 0x%08x\n\n", - discovery->data.saddr, - discovery->data.daddr); - - seq_putc(seq, '\n'); - } - return 0; -} - -static const struct seq_operations discovery_seq_ops = { - .start = discovery_seq_start, - .next = discovery_seq_next, - .stop = discovery_seq_stop, - .show = discovery_seq_show, -}; - -static int discovery_seq_open(struct inode *inode, struct file *file) -{ - IRDA_ASSERT(irlmp != NULL, return -EINVAL;); - - return seq_open(file, &discovery_seq_ops); -} - -const struct file_operations discovery_seq_fops = { - .owner = THIS_MODULE, - .open = discovery_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; -#endif diff --git a/net/irda/ircomm/Kconfig b/net/irda/ircomm/Kconfig deleted file mode 100644 index 19492c1707b7..000000000000 --- a/net/irda/ircomm/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config IRCOMM - tristate "IrCOMM protocol" - depends on IRDA && TTY - help - Say Y here if you want to build support for the IrCOMM protocol. - To compile it as modules, choose M here: the modules will be - called ircomm and ircomm_tty. - IrCOMM implements serial port emulation, and makes it possible to - use all existing applications that understands TTY's with an - infrared link. Thus you should be able to use application like PPP, - minicom and others. - diff --git a/net/irda/ircomm/Makefile b/net/irda/ircomm/Makefile deleted file mode 100644 index ab23b5ba7e33..000000000000 --- a/net/irda/ircomm/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# -# Makefile for the Linux IrDA IrCOMM protocol layer. -# - -obj-$(CONFIG_IRCOMM) += ircomm.o ircomm-tty.o - -ircomm-y := ircomm_core.o ircomm_event.o ircomm_lmp.o ircomm_ttp.o -ircomm-tty-y := ircomm_tty.o ircomm_tty_attach.o ircomm_tty_ioctl.o ircomm_param.o diff --git a/net/irda/ircomm/ircomm_core.c b/net/irda/ircomm/ircomm_core.c deleted file mode 100644 index 3af219545f6d..000000000000 --- a/net/irda/ircomm/ircomm_core.c +++ /dev/null @@ -1,563 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_core.c - * Version: 1.0 - * Description: IrCOMM service interface - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Jun 6 20:37:34 1999 - * Modified at: Tue Dec 21 13:26:41 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -static int __ircomm_close(struct ircomm_cb *self); -static void ircomm_control_indication(struct ircomm_cb *self, - struct sk_buff *skb, int clen); - -#ifdef CONFIG_PROC_FS -extern struct proc_dir_entry *proc_irda; -static int ircomm_seq_open(struct inode *, struct file *); - -static const struct file_operations ircomm_proc_fops = { - .owner = THIS_MODULE, - .open = ircomm_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; -#endif /* CONFIG_PROC_FS */ - -hashbin_t *ircomm = NULL; - -static int __init ircomm_init(void) -{ - ircomm = hashbin_new(HB_LOCK); - if (ircomm == NULL) { - net_err_ratelimited("%s(), can't allocate hashbin!\n", - __func__); - return -ENOMEM; - } - -#ifdef CONFIG_PROC_FS - { struct proc_dir_entry *ent; - ent = proc_create("ircomm", 0, proc_irda, &ircomm_proc_fops); - if (!ent) { - printk(KERN_ERR "ircomm_init: can't create /proc entry!\n"); - return -ENODEV; - } - } -#endif /* CONFIG_PROC_FS */ - - net_info_ratelimited("IrCOMM protocol (Dag Brattli)\n"); - - return 0; -} - -static void __exit ircomm_cleanup(void) -{ - hashbin_delete(ircomm, (FREE_FUNC) __ircomm_close); - -#ifdef CONFIG_PROC_FS - remove_proc_entry("ircomm", proc_irda); -#endif /* CONFIG_PROC_FS */ -} - -/* - * Function ircomm_open (client_notify) - * - * Start a new IrCOMM instance - * - */ -struct ircomm_cb *ircomm_open(notify_t *notify, __u8 service_type, int line) -{ - struct ircomm_cb *self = NULL; - int ret; - - pr_debug("%s(), service_type=0x%02x\n", __func__ , - service_type); - - IRDA_ASSERT(ircomm != NULL, return NULL;); - - self = kzalloc(sizeof(struct ircomm_cb), GFP_KERNEL); - if (self == NULL) - return NULL; - - self->notify = *notify; - self->magic = IRCOMM_MAGIC; - - /* Check if we should use IrLMP or IrTTP */ - if (service_type & IRCOMM_3_WIRE_RAW) { - self->flow_status = FLOW_START; - ret = ircomm_open_lsap(self); - } else - ret = ircomm_open_tsap(self); - - if (ret < 0) { - kfree(self); - return NULL; - } - - self->service_type = service_type; - self->line = line; - - hashbin_insert(ircomm, (irda_queue_t *) self, line, NULL); - - ircomm_next_state(self, IRCOMM_IDLE); - - return self; -} - -EXPORT_SYMBOL(ircomm_open); - -/* - * Function ircomm_close_instance (self) - * - * Remove IrCOMM instance - * - */ -static int __ircomm_close(struct ircomm_cb *self) -{ - /* Disconnect link if any */ - ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, NULL, NULL); - - /* Remove TSAP */ - if (self->tsap) { - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - - /* Remove LSAP */ - if (self->lsap) { - irlmp_close_lsap(self->lsap); - self->lsap = NULL; - } - self->magic = 0; - - kfree(self); - - return 0; -} - -/* - * Function ircomm_close (self) - * - * Closes and removes the specified IrCOMM instance - * - */ -int ircomm_close(struct ircomm_cb *self) -{ - struct ircomm_cb *entry; - - IRDA_ASSERT(self != NULL, return -EIO;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EIO;); - - entry = hashbin_remove(ircomm, self->line, NULL); - - IRDA_ASSERT(entry == self, return -1;); - - return __ircomm_close(self); -} - -EXPORT_SYMBOL(ircomm_close); - -/* - * Function ircomm_connect_request (self, service_type) - * - * Impl. of this function is differ from one of the reference. This - * function does discovery as well as sending connect request - * - */ -int ircomm_connect_request(struct ircomm_cb *self, __u8 dlsap_sel, - __u32 saddr, __u32 daddr, struct sk_buff *skb, - __u8 service_type) -{ - struct ircomm_info info; - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - - self->service_type= service_type; - - info.dlsap_sel = dlsap_sel; - info.saddr = saddr; - info.daddr = daddr; - - ret = ircomm_do_event(self, IRCOMM_CONNECT_REQUEST, skb, &info); - - return ret; -} - -EXPORT_SYMBOL(ircomm_connect_request); - -/* - * Function ircomm_connect_indication (self, qos, skb) - * - * Notify user layer about the incoming connection - * - */ -void ircomm_connect_indication(struct ircomm_cb *self, struct sk_buff *skb, - struct ircomm_info *info) -{ - /* - * If there are any data hiding in the control channel, we must - * deliver it first. The side effect is that the control channel - * will be removed from the skb - */ - if (self->notify.connect_indication) - self->notify.connect_indication(self->notify.instance, self, - info->qos, info->max_data_size, - info->max_header_size, skb); - else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_connect_response (self, userdata, max_sdu_size) - * - * User accepts connection - * - */ -int ircomm_connect_response(struct ircomm_cb *self, struct sk_buff *userdata) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - - ret = ircomm_do_event(self, IRCOMM_CONNECT_RESPONSE, userdata, NULL); - - return ret; -} - -EXPORT_SYMBOL(ircomm_connect_response); - -/* - * Function connect_confirm (self, skb) - * - * Notify user layer that the link is now connected - * - */ -void ircomm_connect_confirm(struct ircomm_cb *self, struct sk_buff *skb, - struct ircomm_info *info) -{ - if (self->notify.connect_confirm ) - self->notify.connect_confirm(self->notify.instance, - self, info->qos, - info->max_data_size, - info->max_header_size, skb); - else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_data_request (self, userdata) - * - * Send IrCOMM data to peer device - * - */ -int ircomm_data_request(struct ircomm_cb *self, struct sk_buff *skb) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -EFAULT;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;); - IRDA_ASSERT(skb != NULL, return -EFAULT;); - - ret = ircomm_do_event(self, IRCOMM_DATA_REQUEST, skb, NULL); - - return ret; -} - -EXPORT_SYMBOL(ircomm_data_request); - -/* - * Function ircomm_data_indication (self, skb) - * - * Data arrived, so deliver it to user - * - */ -void ircomm_data_indication(struct ircomm_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(skb->len > 0, return;); - - if (self->notify.data_indication) - self->notify.data_indication(self->notify.instance, self, skb); - else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_process_data (self, skb) - * - * Data arrived which may contain control channel data - * - */ -void ircomm_process_data(struct ircomm_cb *self, struct sk_buff *skb) -{ - int clen; - - IRDA_ASSERT(skb->len > 0, return;); - - clen = skb->data[0]; - - /* - * Input validation check: a stir4200/mcp2150 combinations sometimes - * results in frames with clen > remaining packet size. These are - * illegal; if we throw away just this frame then it seems to carry on - * fine - */ - if (unlikely(skb->len < (clen + 1))) { - pr_debug("%s() throwing away illegal frame\n", - __func__); - return; - } - - /* - * If there are any data hiding in the control channel, we must - * deliver it first. The side effect is that the control channel - * will be removed from the skb - */ - if (clen > 0) - ircomm_control_indication(self, skb, clen); - - /* Remove control channel from data channel */ - skb_pull(skb, clen+1); - - if (skb->len) - ircomm_data_indication(self, skb); - else { - pr_debug("%s(), data was control info only!\n", - __func__); - } -} - -/* - * Function ircomm_control_request (self, params) - * - * Send control data to peer device - * - */ -int ircomm_control_request(struct ircomm_cb *self, struct sk_buff *skb) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -EFAULT;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EFAULT;); - IRDA_ASSERT(skb != NULL, return -EFAULT;); - - ret = ircomm_do_event(self, IRCOMM_CONTROL_REQUEST, skb, NULL); - - return ret; -} - -EXPORT_SYMBOL(ircomm_control_request); - -/* - * Function ircomm_control_indication (self, skb) - * - * Data has arrived on the control channel - * - */ -static void ircomm_control_indication(struct ircomm_cb *self, - struct sk_buff *skb, int clen) -{ - /* Use udata for delivering data on the control channel */ - if (self->notify.udata_indication) { - struct sk_buff *ctrl_skb; - - /* We don't own the skb, so clone it */ - ctrl_skb = skb_clone(skb, GFP_ATOMIC); - if (!ctrl_skb) - return; - - /* Remove data channel from control channel */ - skb_trim(ctrl_skb, clen+1); - - self->notify.udata_indication(self->notify.instance, self, - ctrl_skb); - - /* Drop reference count - - * see ircomm_tty_control_indication(). */ - dev_kfree_skb(ctrl_skb); - } else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_disconnect_request (self, userdata, priority) - * - * User layer wants to disconnect the IrCOMM connection - * - */ -int ircomm_disconnect_request(struct ircomm_cb *self, struct sk_buff *userdata) -{ - struct ircomm_info info; - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - - ret = ircomm_do_event(self, IRCOMM_DISCONNECT_REQUEST, userdata, - &info); - return ret; -} - -EXPORT_SYMBOL(ircomm_disconnect_request); - -/* - * Function disconnect_indication (self, skb) - * - * Tell user that the link has been disconnected - * - */ -void ircomm_disconnect_indication(struct ircomm_cb *self, struct sk_buff *skb, - struct ircomm_info *info) -{ - IRDA_ASSERT(info != NULL, return;); - - if (self->notify.disconnect_indication) { - self->notify.disconnect_indication(self->notify.instance, self, - info->reason, skb); - } else { - pr_debug("%s(), missing handler\n", __func__); - } -} - -/* - * Function ircomm_flow_request (self, flow) - * - * - * - */ -void ircomm_flow_request(struct ircomm_cb *self, LOCAL_FLOW flow) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - if (self->service_type == IRCOMM_3_WIRE_RAW) - return; - - irttp_flow_request(self->tsap, flow); -} - -EXPORT_SYMBOL(ircomm_flow_request); - -#ifdef CONFIG_PROC_FS -static void *ircomm_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct ircomm_cb *self; - loff_t off = 0; - - spin_lock_irq(&ircomm->hb_spinlock); - - for (self = (struct ircomm_cb *) hashbin_get_first(ircomm); - self != NULL; - self = (struct ircomm_cb *) hashbin_get_next(ircomm)) { - if (off++ == *pos) - break; - - } - return self; -} - -static void *ircomm_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (void *) hashbin_get_next(ircomm); -} - -static void ircomm_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&ircomm->hb_spinlock); -} - -static int ircomm_seq_show(struct seq_file *seq, void *v) -{ - const struct ircomm_cb *self = v; - - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -EINVAL; ); - - if(self->line < 0x10) - seq_printf(seq, "ircomm%d", self->line); - else - seq_printf(seq, "irlpt%d", self->line - 0x10); - - seq_printf(seq, - " state: %s, slsap_sel: %#02x, dlsap_sel: %#02x, mode:", - ircomm_state[ self->state], - self->slsap_sel, self->dlsap_sel); - - if(self->service_type & IRCOMM_3_WIRE_RAW) - seq_printf(seq, " 3-wire-raw"); - if(self->service_type & IRCOMM_3_WIRE) - seq_printf(seq, " 3-wire"); - if(self->service_type & IRCOMM_9_WIRE) - seq_printf(seq, " 9-wire"); - if(self->service_type & IRCOMM_CENTRONICS) - seq_printf(seq, " Centronics"); - seq_putc(seq, '\n'); - - return 0; -} - -static const struct seq_operations ircomm_seq_ops = { - .start = ircomm_seq_start, - .next = ircomm_seq_next, - .stop = ircomm_seq_stop, - .show = ircomm_seq_show, -}; - -static int ircomm_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &ircomm_seq_ops); -} -#endif /* CONFIG_PROC_FS */ - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("IrCOMM protocol"); -MODULE_LICENSE("GPL"); - -module_init(ircomm_init); -module_exit(ircomm_cleanup); diff --git a/net/irda/ircomm/ircomm_event.c b/net/irda/ircomm/ircomm_event.c deleted file mode 100644 index b0730ac9f388..000000000000 --- a/net/irda/ircomm/ircomm_event.c +++ /dev/null @@ -1,246 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_event.c - * Version: 1.0 - * Description: IrCOMM layer state machine - * Status: Stable - * Author: Dag Brattli - * Created at: Sun Jun 6 20:33:11 1999 - * Modified at: Sun Dec 12 13:44:32 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include - -#include -#include - -static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info); -static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info); -static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info); -static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info); - -const char *const ircomm_state[] = { - "IRCOMM_IDLE", - "IRCOMM_WAITI", - "IRCOMM_WAITR", - "IRCOMM_CONN", -}; - -static const char *const ircomm_event[] __maybe_unused = { - "IRCOMM_CONNECT_REQUEST", - "IRCOMM_CONNECT_RESPONSE", - "IRCOMM_TTP_CONNECT_INDICATION", - "IRCOMM_LMP_CONNECT_INDICATION", - "IRCOMM_TTP_CONNECT_CONFIRM", - "IRCOMM_LMP_CONNECT_CONFIRM", - - "IRCOMM_LMP_DISCONNECT_INDICATION", - "IRCOMM_TTP_DISCONNECT_INDICATION", - "IRCOMM_DISCONNECT_REQUEST", - - "IRCOMM_TTP_DATA_INDICATION", - "IRCOMM_LMP_DATA_INDICATION", - "IRCOMM_DATA_REQUEST", - "IRCOMM_CONTROL_REQUEST", - "IRCOMM_CONTROL_INDICATION", -}; - -static int (*state[])(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) = -{ - ircomm_state_idle, - ircomm_state_waiti, - ircomm_state_waitr, - ircomm_state_conn, -}; - -/* - * Function ircomm_state_idle (self, event, skb) - * - * IrCOMM is currently idle - * - */ -static int ircomm_state_idle(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_CONNECT_REQUEST: - ircomm_next_state(self, IRCOMM_WAITI); - ret = self->issue.connect_request(self, skb, info); - break; - case IRCOMM_TTP_CONNECT_INDICATION: - case IRCOMM_LMP_CONNECT_INDICATION: - ircomm_next_state(self, IRCOMM_WAITR); - ircomm_connect_indication(self, skb, info); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_state_waiti (self, event, skb) - * - * The IrCOMM user has requested an IrCOMM connection to the remote - * device and is awaiting confirmation - */ -static int ircomm_state_waiti(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_TTP_CONNECT_CONFIRM: - case IRCOMM_LMP_CONNECT_CONFIRM: - ircomm_next_state(self, IRCOMM_CONN); - ircomm_connect_confirm(self, skb, info); - break; - case IRCOMM_TTP_DISCONNECT_INDICATION: - case IRCOMM_LMP_DISCONNECT_INDICATION: - ircomm_next_state(self, IRCOMM_IDLE); - ircomm_disconnect_indication(self, skb, info); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_state_waitr (self, event, skb) - * - * IrCOMM has received an incoming connection request and is awaiting - * response from the user - */ -static int ircomm_state_waitr(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_CONNECT_RESPONSE: - ircomm_next_state(self, IRCOMM_CONN); - ret = self->issue.connect_response(self, skb); - break; - case IRCOMM_DISCONNECT_REQUEST: - ircomm_next_state(self, IRCOMM_IDLE); - ret = self->issue.disconnect_request(self, skb, info); - break; - case IRCOMM_TTP_DISCONNECT_INDICATION: - case IRCOMM_LMP_DISCONNECT_INDICATION: - ircomm_next_state(self, IRCOMM_IDLE); - ircomm_disconnect_indication(self, skb, info); - break; - default: - pr_debug("%s(), unknown event = %s\n", __func__ , - ircomm_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_state_conn (self, event, skb) - * - * IrCOMM is connected to the peer IrCOMM device - * - */ -static int ircomm_state_conn(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_DATA_REQUEST: - ret = self->issue.data_request(self, skb, 0); - break; - case IRCOMM_TTP_DATA_INDICATION: - ircomm_process_data(self, skb); - break; - case IRCOMM_LMP_DATA_INDICATION: - ircomm_data_indication(self, skb); - break; - case IRCOMM_CONTROL_REQUEST: - /* Just send a separate frame for now */ - ret = self->issue.data_request(self, skb, skb->len); - break; - case IRCOMM_TTP_DISCONNECT_INDICATION: - case IRCOMM_LMP_DISCONNECT_INDICATION: - ircomm_next_state(self, IRCOMM_IDLE); - ircomm_disconnect_indication(self, skb, info); - break; - case IRCOMM_DISCONNECT_REQUEST: - ircomm_next_state(self, IRCOMM_IDLE); - ret = self->issue.disconnect_request(self, skb, info); - break; - default: - pr_debug("%s(), unknown event = %s\n", __func__ , - ircomm_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_do_event (self, event, skb) - * - * Process event - * - */ -int ircomm_do_event(struct ircomm_cb *self, IRCOMM_EVENT event, - struct sk_buff *skb, struct ircomm_info *info) -{ - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_state[self->state], ircomm_event[event]); - - return (*state[self->state])(self, event, skb, info); -} - -/* - * Function ircomm_next_state (self, state) - * - * Switch state - * - */ -void ircomm_next_state(struct ircomm_cb *self, IRCOMM_STATE state) -{ - self->state = state; - - pr_debug("%s: next state=%s, service type=%d\n", __func__ , - ircomm_state[self->state], self->service_type); -} diff --git a/net/irda/ircomm/ircomm_lmp.c b/net/irda/ircomm/ircomm_lmp.c deleted file mode 100644 index e4cc847bb933..000000000000 --- a/net/irda/ircomm/ircomm_lmp.c +++ /dev/null @@ -1,350 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_lmp.c - * Version: 1.0 - * Description: Interface between IrCOMM and IrLMP - * Status: Stable - * Author: Dag Brattli - * Created at: Sun Jun 6 20:48:27 1999 - * Modified at: Sun Dec 12 13:44:17 1999 - * Modified by: Dag Brattli - * Sources: Previous IrLPT work by Thomas Davis - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include /* struct irda_skb_cb */ - -#include -#include - - -/* - * Function ircomm_lmp_connect_request (self, userdata) - * - * - * - */ -static int ircomm_lmp_connect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info) -{ - int ret = 0; - - /* Don't forget to refcount it - should be NULL anyway */ - if(userdata) - skb_get(userdata); - - ret = irlmp_connect_request(self->lsap, info->dlsap_sel, - info->saddr, info->daddr, NULL, userdata); - return ret; -} - -/* - * Function ircomm_lmp_connect_response (self, skb) - * - * - * - */ -static int ircomm_lmp_connect_response(struct ircomm_cb *self, - struct sk_buff *userdata) -{ - struct sk_buff *tx_skb; - - /* Any userdata supplied? */ - if (userdata == NULL) { - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - } else { - /* - * Check that the client has reserved enough space for - * headers - */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_MAX_HEADER, - return -1;); - - /* Don't forget to refcount it - should be NULL anyway */ - skb_get(userdata); - tx_skb = userdata; - } - - return irlmp_connect_response(self->lsap, tx_skb); -} - -static int ircomm_lmp_disconnect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info) -{ - struct sk_buff *tx_skb; - int ret; - - if (!userdata) { - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - userdata = tx_skb; - } else { - /* Don't forget to refcount it - should be NULL anyway */ - skb_get(userdata); - } - - ret = irlmp_disconnect_request(self->lsap, userdata); - - return ret; -} - -/* - * Function ircomm_lmp_flow_control (skb) - * - * This function is called when a data frame we have sent to IrLAP has - * been deallocated. We do this to make sure we don't flood IrLAP with - * frames, since we are not using the IrTTP flow control mechanism - */ -static void ircomm_lmp_flow_control(struct sk_buff *skb) -{ - struct irda_skb_cb *cb; - struct ircomm_cb *self; - int line; - - IRDA_ASSERT(skb != NULL, return;); - - cb = (struct irda_skb_cb *) skb->cb; - - line = cb->line; - - self = (struct ircomm_cb *) hashbin_lock_find(ircomm, line, NULL); - if (!self) { - pr_debug("%s(), didn't find myself\n", __func__); - return; - } - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - self->pkt_count--; - - if ((self->pkt_count < 2) && (self->flow_status == FLOW_STOP)) { - pr_debug("%s(), asking TTY to start again!\n", __func__); - self->flow_status = FLOW_START; - if (self->notify.flow_indication) - self->notify.flow_indication(self->notify.instance, - self, FLOW_START); - } -} - -/* - * Function ircomm_lmp_data_request (self, userdata) - * - * Send data frame to peer device - * - */ -static int ircomm_lmp_data_request(struct ircomm_cb *self, - struct sk_buff *skb, - int not_used) -{ - struct irda_skb_cb *cb; - int ret; - - IRDA_ASSERT(skb != NULL, return -1;); - - cb = (struct irda_skb_cb *) skb->cb; - - cb->line = self->line; - - pr_debug("%s(), sending frame\n", __func__); - - /* Don't forget to refcount it - see ircomm_tty_do_softint() */ - skb_get(skb); - - skb_orphan(skb); - skb->destructor = ircomm_lmp_flow_control; - - if ((self->pkt_count++ > 7) && (self->flow_status == FLOW_START)) { - pr_debug("%s(), asking TTY to slow down!\n", __func__); - self->flow_status = FLOW_STOP; - if (self->notify.flow_indication) - self->notify.flow_indication(self->notify.instance, - self, FLOW_STOP); - } - ret = irlmp_data_request(self->lsap, skb); - if (ret) { - net_err_ratelimited("%s(), failed\n", __func__); - /* irlmp_data_request already free the packet */ - } - - return ret; -} - -/* - * Function ircomm_lmp_data_indication (instance, sap, skb) - * - * Incoming data which we must deliver to the state machine, to check - * we are still connected. - */ -static int ircomm_lmp_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - ircomm_do_event(self, IRCOMM_LMP_DATA_INDICATION, skb, NULL); - - /* Drop reference count - see ircomm_tty_data_indication(). */ - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function ircomm_lmp_connect_confirm (instance, sap, qos, max_sdu_size, - * max_header_size, skb) - * - * Connection has been confirmed by peer device - * - */ -static void ircomm_lmp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_seg_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(qos != NULL, return;); - - info.max_data_size = max_seg_size; - info.max_header_size = max_header_size; - info.qos = qos; - - ircomm_do_event(self, IRCOMM_LMP_CONNECT_CONFIRM, skb, &info); - - /* Drop reference count - see ircomm_tty_connect_confirm(). */ - dev_kfree_skb(skb); -} - -/* - * Function ircomm_lmp_connect_indication (instance, sap, qos, max_sdu_size, - * max_header_size, skb) - * - * Peer device wants to make a connection with us - * - */ -static void ircomm_lmp_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_seg_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *)instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(qos != NULL, return;); - - info.max_data_size = max_seg_size; - info.max_header_size = max_header_size; - info.qos = qos; - - ircomm_do_event(self, IRCOMM_LMP_CONNECT_INDICATION, skb, &info); - - /* Drop reference count - see ircomm_tty_connect_indication(). */ - dev_kfree_skb(skb); -} - -/* - * Function ircomm_lmp_disconnect_indication (instance, sap, reason, skb) - * - * Peer device has closed the connection, or the link went down for some - * other reason - */ -static void ircomm_lmp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - info.reason = reason; - - ircomm_do_event(self, IRCOMM_LMP_DISCONNECT_INDICATION, skb, &info); - - /* Drop reference count - see ircomm_tty_disconnect_indication(). */ - if(skb) - dev_kfree_skb(skb); -} -/* - * Function ircomm_open_lsap (self) - * - * Open LSAP. This function will only be used when using "raw" services - * - */ -int ircomm_open_lsap(struct ircomm_cb *self) -{ - notify_t notify; - - /* Register callbacks */ - irda_notify_init(¬ify); - notify.data_indication = ircomm_lmp_data_indication; - notify.connect_confirm = ircomm_lmp_connect_confirm; - notify.connect_indication = ircomm_lmp_connect_indication; - notify.disconnect_indication = ircomm_lmp_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrCOMM", sizeof(notify.name)); - - self->lsap = irlmp_open_lsap(LSAP_ANY, ¬ify, 0); - if (!self->lsap) { - pr_debug("%sfailed to allocate tsap\n", __func__); - return -1; - } - self->slsap_sel = self->lsap->slsap_sel; - - /* - * Initialize the call-table for issuing commands - */ - self->issue.data_request = ircomm_lmp_data_request; - self->issue.connect_request = ircomm_lmp_connect_request; - self->issue.connect_response = ircomm_lmp_connect_response; - self->issue.disconnect_request = ircomm_lmp_disconnect_request; - - return 0; -} diff --git a/net/irda/ircomm/ircomm_param.c b/net/irda/ircomm/ircomm_param.c deleted file mode 100644 index 5728e76ca6d5..000000000000 --- a/net/irda/ircomm/ircomm_param.c +++ /dev/null @@ -1,501 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_param.c - * Version: 1.0 - * Description: Parameter handling for the IrCOMM protocol - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Jun 7 10:25:11 1999 - * Modified at: Sun Jan 30 14:32:03 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#include - -static int ircomm_param_service_type(void *instance, irda_param_t *param, - int get); -static int ircomm_param_port_type(void *instance, irda_param_t *param, - int get); -static int ircomm_param_port_name(void *instance, irda_param_t *param, - int get); -static int ircomm_param_service_type(void *instance, irda_param_t *param, - int get); -static int ircomm_param_data_rate(void *instance, irda_param_t *param, - int get); -static int ircomm_param_data_format(void *instance, irda_param_t *param, - int get); -static int ircomm_param_flow_control(void *instance, irda_param_t *param, - int get); -static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get); -static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get); -static int ircomm_param_line_status(void *instance, irda_param_t *param, - int get); -static int ircomm_param_dte(void *instance, irda_param_t *param, int get); -static int ircomm_param_dce(void *instance, irda_param_t *param, int get); -static int ircomm_param_poll(void *instance, irda_param_t *param, int get); - -static const pi_minor_info_t pi_minor_call_table_common[] = { - { ircomm_param_service_type, PV_INT_8_BITS }, - { ircomm_param_port_type, PV_INT_8_BITS }, - { ircomm_param_port_name, PV_STRING } -}; -static const pi_minor_info_t pi_minor_call_table_non_raw[] = { - { ircomm_param_data_rate, PV_INT_32_BITS | PV_BIG_ENDIAN }, - { ircomm_param_data_format, PV_INT_8_BITS }, - { ircomm_param_flow_control, PV_INT_8_BITS }, - { ircomm_param_xon_xoff, PV_INT_16_BITS }, - { ircomm_param_enq_ack, PV_INT_16_BITS }, - { ircomm_param_line_status, PV_INT_8_BITS } -}; -static const pi_minor_info_t pi_minor_call_table_9_wire[] = { - { ircomm_param_dte, PV_INT_8_BITS }, - { ircomm_param_dce, PV_INT_8_BITS }, - { ircomm_param_poll, PV_NO_VALUE }, -}; - -static const pi_major_info_t pi_major_call_table[] = { - { pi_minor_call_table_common, 3 }, - { pi_minor_call_table_non_raw, 6 }, - { pi_minor_call_table_9_wire, 3 } -/* { pi_minor_call_table_centronics } */ -}; - -pi_param_info_t ircomm_param_info = { pi_major_call_table, 3, 0x0f, 4 }; - -/* - * Function ircomm_param_request (self, pi, flush) - * - * Queue a parameter for the control channel - * - */ -int ircomm_param_request(struct ircomm_tty_cb *self, __u8 pi, int flush) -{ - unsigned long flags; - struct sk_buff *skb; - int count; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* Make sure we don't send parameters for raw mode */ - if (self->service_type == IRCOMM_3_WIRE_RAW) - return 0; - - spin_lock_irqsave(&self->spinlock, flags); - - skb = self->ctrl_skb; - if (!skb) { - skb = alloc_skb(256, GFP_ATOMIC); - if (!skb) { - spin_unlock_irqrestore(&self->spinlock, flags); - return -ENOMEM; - } - - skb_reserve(skb, self->max_header_size); - self->ctrl_skb = skb; - } - /* - * Inserting is a little bit tricky since we don't know how much - * room we will need. But this should hopefully work OK - */ - count = irda_param_insert(self, pi, skb_tail_pointer(skb), - skb_tailroom(skb), &ircomm_param_info); - if (count < 0) { - net_warn_ratelimited("%s(), no room for parameter!\n", - __func__); - spin_unlock_irqrestore(&self->spinlock, flags); - return -1; - } - skb_put(skb, count); - pr_debug("%s(), skb->len=%d\n", __func__, skb->len); - - spin_unlock_irqrestore(&self->spinlock, flags); - - if (flush) { - /* ircomm_tty_do_softint will take care of the rest */ - schedule_work(&self->tqueue); - } - - return count; -} - -/* - * Function ircomm_param_service_type (self, buf, len) - * - * Handle service type, this function will both be called after the LM-IAS - * query and then the remote device sends its initial parameters - * - */ -static int ircomm_param_service_type(void *instance, irda_param_t *param, - int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - __u8 service_type = (__u8) param->pv.i; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) { - param->pv.i = self->settings.service_type; - return 0; - } - - /* Find all common service types */ - service_type &= self->service_type; - if (!service_type) { - pr_debug("%s(), No common service type to use!\n", __func__); - return -1; - } - pr_debug("%s(), services in common=%02x\n", __func__ , - service_type); - - /* - * Now choose a preferred service type of those available - */ - if (service_type & IRCOMM_CENTRONICS) - self->settings.service_type = IRCOMM_CENTRONICS; - else if (service_type & IRCOMM_9_WIRE) - self->settings.service_type = IRCOMM_9_WIRE; - else if (service_type & IRCOMM_3_WIRE) - self->settings.service_type = IRCOMM_3_WIRE; - else if (service_type & IRCOMM_3_WIRE_RAW) - self->settings.service_type = IRCOMM_3_WIRE_RAW; - - pr_debug("%s(), resulting service type=0x%02x\n", __func__ , - self->settings.service_type); - - /* - * Now the line is ready for some communication. Check if we are a - * server, and send over some initial parameters. - * Client do it in ircomm_tty_state_setup(). - * Note : we may get called from ircomm_tty_getvalue_confirm(), - * therefore before we even have open any socket. And self->client - * is initialised to TRUE only later. So, we check if the link is - * really initialised. - Jean II - */ - if ((self->max_header_size != IRCOMM_TTY_HDR_UNINITIALISED) && - (!self->client) && - (self->settings.service_type != IRCOMM_3_WIRE_RAW)) - { - /* Init connection */ - ircomm_tty_send_initial_parameters(self); - ircomm_tty_link_established(self); - } - - return 0; -} - -/* - * Function ircomm_param_port_type (self, param) - * - * The port type parameter tells if the devices are serial or parallel. - * Since we only advertise serial service, this parameter should only - * be equal to IRCOMM_SERIAL. - */ -static int ircomm_param_port_type(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = IRCOMM_SERIAL; - else { - self->settings.port_type = (__u8) param->pv.i; - - pr_debug("%s(), port type=%d\n", __func__ , - self->settings.port_type); - } - return 0; -} - -/* - * Function ircomm_param_port_name (self, param) - * - * Exchange port name - * - */ -static int ircomm_param_port_name(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) { - pr_debug("%s(), not imp!\n", __func__); - } else { - pr_debug("%s(), port-name=%s\n", __func__ , param->pv.c); - strncpy(self->settings.port_name, param->pv.c, 32); - } - - return 0; -} - -/* - * Function ircomm_param_data_rate (self, param) - * - * Exchange data rate to be used in this settings - * - */ -static int ircomm_param_data_rate(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = self->settings.data_rate; - else - self->settings.data_rate = param->pv.i; - - pr_debug("%s(), data rate = %d\n", __func__ , param->pv.i); - - return 0; -} - -/* - * Function ircomm_param_data_format (self, param) - * - * Exchange data format to be used in this settings - * - */ -static int ircomm_param_data_format(void *instance, irda_param_t *param, - int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = self->settings.data_format; - else - self->settings.data_format = (__u8) param->pv.i; - - return 0; -} - -/* - * Function ircomm_param_flow_control (self, param) - * - * Exchange flow control settings to be used in this settings - * - */ -static int ircomm_param_flow_control(void *instance, irda_param_t *param, - int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = self->settings.flow_control; - else - self->settings.flow_control = (__u8) param->pv.i; - - pr_debug("%s(), flow control = 0x%02x\n", __func__ , (__u8)param->pv.i); - - return 0; -} - -/* - * Function ircomm_param_xon_xoff (self, param) - * - * Exchange XON/XOFF characters - * - */ -static int ircomm_param_xon_xoff(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) { - param->pv.i = self->settings.xonxoff[0]; - param->pv.i |= self->settings.xonxoff[1] << 8; - } else { - self->settings.xonxoff[0] = (__u16) param->pv.i & 0xff; - self->settings.xonxoff[1] = (__u16) param->pv.i >> 8; - } - - pr_debug("%s(), XON/XOFF = 0x%02x,0x%02x\n", __func__ , - param->pv.i & 0xff, param->pv.i >> 8); - - return 0; -} - -/* - * Function ircomm_param_enq_ack (self, param) - * - * Exchange ENQ/ACK characters - * - */ -static int ircomm_param_enq_ack(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) { - param->pv.i = self->settings.enqack[0]; - param->pv.i |= self->settings.enqack[1] << 8; - } else { - self->settings.enqack[0] = (__u16) param->pv.i & 0xff; - self->settings.enqack[1] = (__u16) param->pv.i >> 8; - } - - pr_debug("%s(), ENQ/ACK = 0x%02x,0x%02x\n", __func__ , - param->pv.i & 0xff, param->pv.i >> 8); - - return 0; -} - -/* - * Function ircomm_param_line_status (self, param) - * - * - * - */ -static int ircomm_param_line_status(void *instance, irda_param_t *param, - int get) -{ - pr_debug("%s(), not impl.\n", __func__); - - return 0; -} - -/* - * Function ircomm_param_dte (instance, param) - * - * If we get here, there must be some sort of null-modem connection, and - * we are probably working in server mode as well. - */ -static int ircomm_param_dte(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - __u8 dte; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (get) - param->pv.i = self->settings.dte; - else { - dte = (__u8) param->pv.i; - - self->settings.dce = 0; - - if (dte & IRCOMM_DELTA_DTR) - self->settings.dce |= (IRCOMM_DELTA_DSR| - IRCOMM_DELTA_RI | - IRCOMM_DELTA_CD); - if (dte & IRCOMM_DTR) - self->settings.dce |= (IRCOMM_DSR| - IRCOMM_RI | - IRCOMM_CD); - - if (dte & IRCOMM_DELTA_RTS) - self->settings.dce |= IRCOMM_DELTA_CTS; - if (dte & IRCOMM_RTS) - self->settings.dce |= IRCOMM_CTS; - - /* Take appropriate actions */ - ircomm_tty_check_modem_status(self); - - /* Null modem cable emulator */ - self->settings.null_modem = TRUE; - } - - return 0; -} - -/* - * Function ircomm_param_dce (instance, param) - * - * - * - */ -static int ircomm_param_dce(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - __u8 dce; - - pr_debug("%s(), dce = 0x%02x\n", __func__ , (__u8)param->pv.i); - - dce = (__u8) param->pv.i; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - self->settings.dce = dce; - - /* Check if any of the settings have changed */ - if (dce & 0x0f) { - if (dce & IRCOMM_DELTA_CTS) { - pr_debug("%s(), CTS\n", __func__); - } - } - - ircomm_tty_check_modem_status(self); - - return 0; -} - -/* - * Function ircomm_param_poll (instance, param) - * - * Called when the peer device is polling for the line settings - * - */ -static int ircomm_param_poll(void *instance, irda_param_t *param, int get) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* Poll parameters are always of length 0 (just a signal) */ - if (!get) { - /* Respond with DTE line settings */ - ircomm_param_request(self, IRCOMM_DTE, TRUE); - } - return 0; -} - - - - - diff --git a/net/irda/ircomm/ircomm_ttp.c b/net/irda/ircomm/ircomm_ttp.c deleted file mode 100644 index 4b81e0934770..000000000000 --- a/net/irda/ircomm/ircomm_ttp.c +++ /dev/null @@ -1,350 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_ttp.c - * Version: 1.0 - * Description: Interface between IrCOMM and IrTTP - * Status: Stable - * Author: Dag Brattli - * Created at: Sun Jun 6 20:48:27 1999 - * Modified at: Mon Dec 13 11:35:13 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include - -#include -#include -#include -#include - -#include -#include - -static int ircomm_ttp_data_indication(void *instance, void *sap, - struct sk_buff *skb); -static void ircomm_ttp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); -static void ircomm_ttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); -static void ircomm_ttp_flow_indication(void *instance, void *sap, - LOCAL_FLOW cmd); -static void ircomm_ttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb); -static int ircomm_ttp_data_request(struct ircomm_cb *self, - struct sk_buff *skb, - int clen); -static int ircomm_ttp_connect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info); -static int ircomm_ttp_connect_response(struct ircomm_cb *self, - struct sk_buff *userdata); -static int ircomm_ttp_disconnect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info); - -/* - * Function ircomm_open_tsap (self) - * - * - * - */ -int ircomm_open_tsap(struct ircomm_cb *self) -{ - notify_t notify; - - /* Register callbacks */ - irda_notify_init(¬ify); - notify.data_indication = ircomm_ttp_data_indication; - notify.connect_confirm = ircomm_ttp_connect_confirm; - notify.connect_indication = ircomm_ttp_connect_indication; - notify.flow_indication = ircomm_ttp_flow_indication; - notify.disconnect_indication = ircomm_ttp_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrCOMM", sizeof(notify.name)); - - self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, - ¬ify); - if (!self->tsap) { - pr_debug("%sfailed to allocate tsap\n", __func__); - return -1; - } - self->slsap_sel = self->tsap->stsap_sel; - - /* - * Initialize the call-table for issuing commands - */ - self->issue.data_request = ircomm_ttp_data_request; - self->issue.connect_request = ircomm_ttp_connect_request; - self->issue.connect_response = ircomm_ttp_connect_response; - self->issue.disconnect_request = ircomm_ttp_disconnect_request; - - return 0; -} - -/* - * Function ircomm_ttp_connect_request (self, userdata) - * - * - * - */ -static int ircomm_ttp_connect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info) -{ - int ret = 0; - - /* Don't forget to refcount it - should be NULL anyway */ - if(userdata) - skb_get(userdata); - - ret = irttp_connect_request(self->tsap, info->dlsap_sel, - info->saddr, info->daddr, NULL, - TTP_SAR_DISABLE, userdata); - - return ret; -} - -/* - * Function ircomm_ttp_connect_response (self, skb) - * - * - * - */ -static int ircomm_ttp_connect_response(struct ircomm_cb *self, - struct sk_buff *userdata) -{ - int ret; - - /* Don't forget to refcount it - should be NULL anyway */ - if(userdata) - skb_get(userdata); - - ret = irttp_connect_response(self->tsap, TTP_SAR_DISABLE, userdata); - - return ret; -} - -/* - * Function ircomm_ttp_data_request (self, userdata) - * - * Send IrCOMM data to IrTTP layer. Currently we do not try to combine - * control data with pure data, so they will be sent as separate frames. - * Should not be a big problem though, since control frames are rare. But - * some of them are sent after connection establishment, so this can - * increase the latency a bit. - */ -static int ircomm_ttp_data_request(struct ircomm_cb *self, - struct sk_buff *skb, - int clen) -{ - int ret; - - IRDA_ASSERT(skb != NULL, return -1;); - - pr_debug("%s(), clen=%d\n", __func__ , clen); - - /* - * Insert clen field, currently we either send data only, or control - * only frames, to make things easier and avoid queueing - */ - IRDA_ASSERT(skb_headroom(skb) >= IRCOMM_HEADER_SIZE, return -1;); - - /* Don't forget to refcount it - see ircomm_tty_do_softint() */ - skb_get(skb); - - skb_push(skb, IRCOMM_HEADER_SIZE); - - skb->data[0] = clen; - - ret = irttp_data_request(self->tsap, skb); - if (ret) { - net_err_ratelimited("%s(), failed\n", __func__); - /* irttp_data_request already free the packet */ - } - - return ret; -} - -/* - * Function ircomm_ttp_data_indication (instance, sap, skb) - * - * Incoming data - * - */ -static int ircomm_ttp_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - ircomm_do_event(self, IRCOMM_TTP_DATA_INDICATION, skb, NULL); - - /* Drop reference count - see ircomm_tty_data_indication(). */ - dev_kfree_skb(skb); - - return 0; -} - -static void ircomm_ttp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(qos != NULL, goto out;); - - if (max_sdu_size != TTP_SAR_DISABLE) { - net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n", - __func__); - goto out; - } - - info.max_data_size = irttp_get_max_seg_size(self->tsap) - - IRCOMM_HEADER_SIZE; - info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE; - info.qos = qos; - - ircomm_do_event(self, IRCOMM_TTP_CONNECT_CONFIRM, skb, &info); - -out: - /* Drop reference count - see ircomm_tty_connect_confirm(). */ - dev_kfree_skb(skb); -} - -/* - * Function ircomm_ttp_connect_indication (instance, sap, qos, max_sdu_size, - * max_header_size, skb) - * - * - * - */ -static void ircomm_ttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *)instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(qos != NULL, goto out;); - - if (max_sdu_size != TTP_SAR_DISABLE) { - net_err_ratelimited("%s(), SAR not allowed for IrCOMM!\n", - __func__); - goto out; - } - - info.max_data_size = irttp_get_max_seg_size(self->tsap) - - IRCOMM_HEADER_SIZE; - info.max_header_size = max_header_size + IRCOMM_HEADER_SIZE; - info.qos = qos; - - ircomm_do_event(self, IRCOMM_TTP_CONNECT_INDICATION, skb, &info); - -out: - /* Drop reference count - see ircomm_tty_connect_indication(). */ - dev_kfree_skb(skb); -} - -/* - * Function ircomm_ttp_disconnect_request (self, userdata, info) - * - * - * - */ -static int ircomm_ttp_disconnect_request(struct ircomm_cb *self, - struct sk_buff *userdata, - struct ircomm_info *info) -{ - int ret; - - /* Don't forget to refcount it - should be NULL anyway */ - if(userdata) - skb_get(userdata); - - ret = irttp_disconnect_request(self->tsap, userdata, P_NORMAL); - - return ret; -} - -/* - * Function ircomm_ttp_disconnect_indication (instance, sap, reason, skb) - * - * - * - */ -static void ircomm_ttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - struct ircomm_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - info.reason = reason; - - ircomm_do_event(self, IRCOMM_TTP_DISCONNECT_INDICATION, skb, &info); - - /* Drop reference count - see ircomm_tty_disconnect_indication(). */ - if(skb) - dev_kfree_skb(skb); -} - -/* - * Function ircomm_ttp_flow_indication (instance, sap, cmd) - * - * Layer below is telling us to start or stop the flow of data - * - */ -static void ircomm_ttp_flow_indication(void *instance, void *sap, - LOCAL_FLOW cmd) -{ - struct ircomm_cb *self = (struct ircomm_cb *) instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_MAGIC, return;); - - if (self->notify.flow_indication) - self->notify.flow_indication(self->notify.instance, self, cmd); -} - - diff --git a/net/irda/ircomm/ircomm_tty.c b/net/irda/ircomm/ircomm_tty.c deleted file mode 100644 index ec157c3419b5..000000000000 --- a/net/irda/ircomm/ircomm_tty.c +++ /dev/null @@ -1,1329 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_tty.c - * Version: 1.0 - * Description: IrCOMM serial TTY driver - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Jun 6 21:00:56 1999 - * Modified at: Wed Feb 23 00:09:02 2000 - * Modified by: Dag Brattli - * Sources: serial.c and previous IrCOMM work by Takahide Higuchi - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include /* for MODULE_ALIAS_CHARDEV_MAJOR */ - -#include - -#include -#include - -#include -#include -#include -#include - -static int ircomm_tty_install(struct tty_driver *driver, - struct tty_struct *tty); -static int ircomm_tty_open(struct tty_struct *tty, struct file *filp); -static void ircomm_tty_close(struct tty_struct * tty, struct file *filp); -static int ircomm_tty_write(struct tty_struct * tty, - const unsigned char *buf, int count); -static int ircomm_tty_write_room(struct tty_struct *tty); -static void ircomm_tty_throttle(struct tty_struct *tty); -static void ircomm_tty_unthrottle(struct tty_struct *tty); -static int ircomm_tty_chars_in_buffer(struct tty_struct *tty); -static void ircomm_tty_flush_buffer(struct tty_struct *tty); -static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch); -static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout); -static void ircomm_tty_hangup(struct tty_struct *tty); -static void ircomm_tty_do_softint(struct work_struct *work); -static void ircomm_tty_shutdown(struct ircomm_tty_cb *self); -static void ircomm_tty_stop(struct tty_struct *tty); - -static int ircomm_tty_data_indication(void *instance, void *sap, - struct sk_buff *skb); -static int ircomm_tty_control_indication(void *instance, void *sap, - struct sk_buff *skb); -static void ircomm_tty_flow_indication(void *instance, void *sap, - LOCAL_FLOW cmd); -#ifdef CONFIG_PROC_FS -static const struct file_operations ircomm_tty_proc_fops; -#endif /* CONFIG_PROC_FS */ -static struct tty_driver *driver; - -static hashbin_t *ircomm_tty = NULL; - -static const struct tty_operations ops = { - .install = ircomm_tty_install, - .open = ircomm_tty_open, - .close = ircomm_tty_close, - .write = ircomm_tty_write, - .write_room = ircomm_tty_write_room, - .chars_in_buffer = ircomm_tty_chars_in_buffer, - .flush_buffer = ircomm_tty_flush_buffer, - .ioctl = ircomm_tty_ioctl, /* ircomm_tty_ioctl.c */ - .tiocmget = ircomm_tty_tiocmget, /* ircomm_tty_ioctl.c */ - .tiocmset = ircomm_tty_tiocmset, /* ircomm_tty_ioctl.c */ - .throttle = ircomm_tty_throttle, - .unthrottle = ircomm_tty_unthrottle, - .send_xchar = ircomm_tty_send_xchar, - .set_termios = ircomm_tty_set_termios, - .stop = ircomm_tty_stop, - .start = ircomm_tty_start, - .hangup = ircomm_tty_hangup, - .wait_until_sent = ircomm_tty_wait_until_sent, -#ifdef CONFIG_PROC_FS - .proc_fops = &ircomm_tty_proc_fops, -#endif /* CONFIG_PROC_FS */ -}; - -static void ircomm_port_raise_dtr_rts(struct tty_port *port, int raise) -{ - struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb, - port); - /* - * Here, we use to lock those two guys, but as ircomm_param_request() - * does it itself, I don't see the point (and I see the deadlock). - * Jean II - */ - if (raise) - self->settings.dte |= IRCOMM_RTS | IRCOMM_DTR; - else - self->settings.dte &= ~(IRCOMM_RTS | IRCOMM_DTR); - - ircomm_param_request(self, IRCOMM_DTE, TRUE); -} - -static int ircomm_port_carrier_raised(struct tty_port *port) -{ - struct ircomm_tty_cb *self = container_of(port, struct ircomm_tty_cb, - port); - return self->settings.dce & IRCOMM_CD; -} - -static const struct tty_port_operations ircomm_port_ops = { - .dtr_rts = ircomm_port_raise_dtr_rts, - .carrier_raised = ircomm_port_carrier_raised, -}; - -/* - * Function ircomm_tty_init() - * - * Init IrCOMM TTY layer/driver - * - */ -static int __init ircomm_tty_init(void) -{ - driver = alloc_tty_driver(IRCOMM_TTY_PORTS); - if (!driver) - return -ENOMEM; - ircomm_tty = hashbin_new(HB_LOCK); - if (ircomm_tty == NULL) { - net_err_ratelimited("%s(), can't allocate hashbin!\n", - __func__); - put_tty_driver(driver); - return -ENOMEM; - } - - driver->driver_name = "ircomm"; - driver->name = "ircomm"; - driver->major = IRCOMM_TTY_MAJOR; - driver->minor_start = IRCOMM_TTY_MINOR; - driver->type = TTY_DRIVER_TYPE_SERIAL; - driver->subtype = SERIAL_TYPE_NORMAL; - driver->init_termios = tty_std_termios; - driver->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; - driver->flags = TTY_DRIVER_REAL_RAW; - tty_set_operations(driver, &ops); - if (tty_register_driver(driver)) { - net_err_ratelimited("%s(): Couldn't register serial driver\n", - __func__); - put_tty_driver(driver); - return -1; - } - return 0; -} - -static void __exit __ircomm_tty_cleanup(struct ircomm_tty_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - ircomm_tty_shutdown(self); - - self->magic = 0; - tty_port_destroy(&self->port); - kfree(self); -} - -/* - * Function ircomm_tty_cleanup () - * - * Remove IrCOMM TTY layer/driver - * - */ -static void __exit ircomm_tty_cleanup(void) -{ - int ret; - - ret = tty_unregister_driver(driver); - if (ret) { - net_err_ratelimited("%s(), failed to unregister driver\n", - __func__); - return; - } - - hashbin_delete(ircomm_tty, (FREE_FUNC) __ircomm_tty_cleanup); - put_tty_driver(driver); -} - -/* - * Function ircomm_startup (self) - * - * - * - */ -static int ircomm_tty_startup(struct ircomm_tty_cb *self) -{ - notify_t notify; - int ret = -ENODEV; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* Check if already open */ - if (tty_port_initialized(&self->port)) { - pr_debug("%s(), already open so break out!\n", __func__); - return 0; - } - tty_port_set_initialized(&self->port, 1); - - /* Register with IrCOMM */ - irda_notify_init(¬ify); - /* These callbacks we must handle ourselves */ - notify.data_indication = ircomm_tty_data_indication; - notify.udata_indication = ircomm_tty_control_indication; - notify.flow_indication = ircomm_tty_flow_indication; - - /* Use the ircomm_tty interface for these ones */ - notify.disconnect_indication = ircomm_tty_disconnect_indication; - notify.connect_confirm = ircomm_tty_connect_confirm; - notify.connect_indication = ircomm_tty_connect_indication; - strlcpy(notify.name, "ircomm_tty", sizeof(notify.name)); - notify.instance = self; - - if (!self->ircomm) { - self->ircomm = ircomm_open(¬ify, self->service_type, - self->line); - } - if (!self->ircomm) - goto err; - - self->slsap_sel = self->ircomm->slsap_sel; - - /* Connect IrCOMM link with remote device */ - ret = ircomm_tty_attach_cable(self); - if (ret < 0) { - net_err_ratelimited("%s(), error attaching cable!\n", __func__); - goto err; - } - - return 0; -err: - tty_port_set_initialized(&self->port, 0); - return ret; -} - -/* - * Function ircomm_block_til_ready (self, filp) - * - * - * - */ -static int ircomm_tty_block_til_ready(struct ircomm_tty_cb *self, - struct tty_struct *tty, struct file *filp) -{ - struct tty_port *port = &self->port; - DECLARE_WAITQUEUE(wait, current); - int retval; - int do_clocal = 0; - unsigned long flags; - - /* - * If non-blocking mode is set, or the port is not enabled, - * then make the check up front and then exit. - */ - if (tty_io_error(tty)) { - tty_port_set_active(port, 1); - return 0; - } - - if (filp->f_flags & O_NONBLOCK) { - /* nonblock mode is set */ - if (C_BAUD(tty)) - tty_port_raise_dtr_rts(port); - tty_port_set_active(port, 1); - pr_debug("%s(), O_NONBLOCK requested!\n", __func__); - return 0; - } - - if (C_CLOCAL(tty)) { - pr_debug("%s(), doing CLOCAL!\n", __func__); - do_clocal = 1; - } - - /* Wait for carrier detect and the line to become - * free (i.e., not in use by the callout). While we are in - * this loop, port->count is dropped by one, so that - * mgsl_close() knows when to free things. We restore it upon - * exit, either normal or abnormal. - */ - - retval = 0; - add_wait_queue(&port->open_wait, &wait); - - pr_debug("%s(%d):block_til_ready before block on %s open_count=%d\n", - __FILE__, __LINE__, tty->driver->name, port->count); - - spin_lock_irqsave(&port->lock, flags); - port->count--; - port->blocked_open++; - spin_unlock_irqrestore(&port->lock, flags); - - while (1) { - if (C_BAUD(tty) && tty_port_initialized(port)) - tty_port_raise_dtr_rts(port); - - set_current_state(TASK_INTERRUPTIBLE); - - if (tty_hung_up_p(filp) || !tty_port_initialized(port)) { - retval = (port->flags & ASYNC_HUP_NOTIFY) ? - -EAGAIN : -ERESTARTSYS; - break; - } - - /* - * Check if link is ready now. Even if CLOCAL is - * specified, we cannot return before the IrCOMM link is - * ready - */ - if ((do_clocal || tty_port_carrier_raised(port)) && - self->state == IRCOMM_TTY_READY) - { - break; - } - - if (signal_pending(current)) { - retval = -ERESTARTSYS; - break; - } - - pr_debug("%s(%d):block_til_ready blocking on %s open_count=%d\n", - __FILE__, __LINE__, tty->driver->name, port->count); - - schedule(); - } - - __set_current_state(TASK_RUNNING); - remove_wait_queue(&port->open_wait, &wait); - - spin_lock_irqsave(&port->lock, flags); - if (!tty_hung_up_p(filp)) - port->count++; - port->blocked_open--; - spin_unlock_irqrestore(&port->lock, flags); - - pr_debug("%s(%d):block_til_ready after blocking on %s open_count=%d\n", - __FILE__, __LINE__, tty->driver->name, port->count); - - if (!retval) - tty_port_set_active(port, 1); - - return retval; -} - - -static int ircomm_tty_install(struct tty_driver *driver, struct tty_struct *tty) -{ - struct ircomm_tty_cb *self; - unsigned int line = tty->index; - - /* Check if instance already exists */ - self = hashbin_lock_find(ircomm_tty, line, NULL); - if (!self) { - /* No, so make new instance */ - self = kzalloc(sizeof(struct ircomm_tty_cb), GFP_KERNEL); - if (self == NULL) - return -ENOMEM; - - tty_port_init(&self->port); - self->port.ops = &ircomm_port_ops; - self->magic = IRCOMM_TTY_MAGIC; - self->flow = FLOW_STOP; - - self->line = line; - INIT_WORK(&self->tqueue, ircomm_tty_do_softint); - self->max_header_size = IRCOMM_TTY_HDR_UNINITIALISED; - self->max_data_size = IRCOMM_TTY_DATA_UNINITIALISED; - - /* Init some important stuff */ - init_timer(&self->watchdog_timer); - spin_lock_init(&self->spinlock); - - /* - * Force TTY into raw mode by default which is usually what - * we want for IrCOMM and IrLPT. This way applications will - * not have to twiddle with printcap etc. - * - * Note this is completely usafe and doesn't work properly - */ - tty->termios.c_iflag = 0; - tty->termios.c_oflag = 0; - - /* Insert into hash */ - hashbin_insert(ircomm_tty, (irda_queue_t *) self, line, NULL); - } - - tty->driver_data = self; - - return tty_port_install(&self->port, driver, tty); -} - -/* - * Function ircomm_tty_open (tty, filp) - * - * This routine is called when a particular tty device is opened. This - * routine is mandatory; if this routine is not filled in, the attempted - * open will fail with ENODEV. - */ -static int ircomm_tty_open(struct tty_struct *tty, struct file *filp) -{ - struct ircomm_tty_cb *self = tty->driver_data; - unsigned long flags; - int ret; - - /* ++ is not atomic, so this should be protected - Jean II */ - spin_lock_irqsave(&self->port.lock, flags); - self->port.count++; - spin_unlock_irqrestore(&self->port.lock, flags); - tty_port_tty_set(&self->port, tty); - - pr_debug("%s(), %s%d, count = %d\n", __func__ , tty->driver->name, - self->line, self->port.count); - - /* Not really used by us, but lets do it anyway */ - self->port.low_latency = (self->port.flags & ASYNC_LOW_LATENCY) ? 1 : 0; - - /* Check if this is a "normal" ircomm device, or an irlpt device */ - if (self->line < 0x10) { - self->service_type = IRCOMM_3_WIRE | IRCOMM_9_WIRE; - self->settings.service_type = IRCOMM_9_WIRE; /* 9 wire as default */ - /* Jan Kiszka -> add DSR/RI -> Conform to IrCOMM spec */ - self->settings.dce = IRCOMM_CTS | IRCOMM_CD | IRCOMM_DSR | IRCOMM_RI; /* Default line settings */ - pr_debug("%s(), IrCOMM device\n", __func__); - } else { - pr_debug("%s(), IrLPT device\n", __func__); - self->service_type = IRCOMM_3_WIRE_RAW; - self->settings.service_type = IRCOMM_3_WIRE_RAW; /* Default */ - } - - ret = ircomm_tty_startup(self); - if (ret) - return ret; - - ret = ircomm_tty_block_til_ready(self, tty, filp); - if (ret) { - pr_debug("%s(), returning after block_til_ready with %d\n", - __func__, ret); - - return ret; - } - return 0; -} - -/* - * Function ircomm_tty_close (tty, filp) - * - * This routine is called when a particular tty device is closed. - * - */ -static void ircomm_tty_close(struct tty_struct *tty, struct file *filp) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - struct tty_port *port = &self->port; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - if (tty_port_close_start(port, tty, filp) == 0) - return; - - ircomm_tty_shutdown(self); - - tty_driver_flush_buffer(tty); - - tty_port_close_end(port, tty); - tty_port_tty_set(port, NULL); -} - -/* - * Function ircomm_tty_flush_buffer (tty) - * - * - * - */ -static void ircomm_tty_flush_buffer(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* - * Let do_softint() do this to avoid race condition with - * do_softint() ;-) - */ - schedule_work(&self->tqueue); -} - -/* - * Function ircomm_tty_do_softint (work) - * - * We use this routine to give the write wakeup to the user at at a - * safe time (as fast as possible after write have completed). This - * can be compared to the Tx interrupt. - */ -static void ircomm_tty_do_softint(struct work_struct *work) -{ - struct ircomm_tty_cb *self = - container_of(work, struct ircomm_tty_cb, tqueue); - struct tty_struct *tty; - unsigned long flags; - struct sk_buff *skb, *ctrl_skb; - - if (!self || self->magic != IRCOMM_TTY_MAGIC) - return; - - tty = tty_port_tty_get(&self->port); - if (!tty) - return; - - /* Unlink control buffer */ - spin_lock_irqsave(&self->spinlock, flags); - - ctrl_skb = self->ctrl_skb; - self->ctrl_skb = NULL; - - spin_unlock_irqrestore(&self->spinlock, flags); - - /* Flush control buffer if any */ - if(ctrl_skb) { - if(self->flow == FLOW_START) - ircomm_control_request(self->ircomm, ctrl_skb); - /* Drop reference count - see ircomm_ttp_data_request(). */ - dev_kfree_skb(ctrl_skb); - } - - if (tty->hw_stopped) - goto put; - - /* Unlink transmit buffer */ - spin_lock_irqsave(&self->spinlock, flags); - - skb = self->tx_skb; - self->tx_skb = NULL; - - spin_unlock_irqrestore(&self->spinlock, flags); - - /* Flush transmit buffer if any */ - if (skb) { - ircomm_tty_do_event(self, IRCOMM_TTY_DATA_REQUEST, skb, NULL); - /* Drop reference count - see ircomm_ttp_data_request(). */ - dev_kfree_skb(skb); - } - - /* Check if user (still) wants to be waken up */ - tty_wakeup(tty); -put: - tty_kref_put(tty); -} - -/* - * Function ircomm_tty_write (tty, buf, count) - * - * This routine is called by the kernel to write a series of characters - * to the tty device. The characters may come from user space or kernel - * space. This routine will return the number of characters actually - * accepted for writing. This routine is mandatory. - */ -static int ircomm_tty_write(struct tty_struct *tty, - const unsigned char *buf, int count) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned long flags; - struct sk_buff *skb; - int tailroom = 0; - int len = 0; - int size; - - pr_debug("%s(), count=%d, hw_stopped=%d\n", __func__ , count, - tty->hw_stopped); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* We may receive packets from the TTY even before we have finished - * our setup. Not cool. - * The problem is that we don't know the final header and data size - * to create the proper skb, so any skb we would create would have - * bogus header and data size, so need care. - * We use a bogus header size to safely detect this condition. - * Another problem is that hw_stopped was set to 0 way before it - * should be, so we would drop this skb. It should now be fixed. - * One option is to not accept data until we are properly setup. - * But, I suspect that when it happens, the ppp line discipline - * just "drops" the data, which might screw up connect scripts. - * The second option is to create a "safe skb", with large header - * and small size (see ircomm_tty_open() for values). - * We just need to make sure that when the real values get filled, - * we don't mess up the original "safe skb" (see tx_data_size). - * Jean II */ - if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) { - pr_debug("%s() : not initialised\n", __func__); -#ifdef IRCOMM_NO_TX_BEFORE_INIT - /* We didn't consume anything, TTY will retry */ - return 0; -#endif - } - - if (count < 1) - return 0; - - /* Protect our manipulation of self->tx_skb and related */ - spin_lock_irqsave(&self->spinlock, flags); - - /* Fetch current transmit buffer */ - skb = self->tx_skb; - - /* - * Send out all the data we get, possibly as multiple fragmented - * frames, but this will only happen if the data is larger than the - * max data size. The normal case however is just the opposite, and - * this function may be called multiple times, and will then actually - * defragment the data and send it out as one packet as soon as - * possible, but at a safer point in time - */ - while (count) { - size = count; - - /* Adjust data size to the max data size */ - if (size > self->max_data_size) - size = self->max_data_size; - - /* - * Do we already have a buffer ready for transmit, or do - * we need to allocate a new frame - */ - if (skb) { - /* - * Any room for more data at the end of the current - * transmit buffer? Cannot use skb_tailroom, since - * dev_alloc_skb gives us a larger skb than we - * requested - * Note : use tx_data_size, because max_data_size - * may have changed and we don't want to overwrite - * the skb. - Jean II - */ - if ((tailroom = (self->tx_data_size - skb->len)) > 0) { - /* Adjust data to tailroom */ - if (size > tailroom) - size = tailroom; - } else { - /* - * Current transmit frame is full, so break - * out, so we can send it as soon as possible - */ - break; - } - } else { - /* Prepare a full sized frame */ - skb = alloc_skb(self->max_data_size+ - self->max_header_size, - GFP_ATOMIC); - if (!skb) { - spin_unlock_irqrestore(&self->spinlock, flags); - return -ENOBUFS; - } - skb_reserve(skb, self->max_header_size); - self->tx_skb = skb; - /* Remember skb size because max_data_size may - * change later on - Jean II */ - self->tx_data_size = self->max_data_size; - } - - /* Copy data */ - skb_put_data(skb, buf + len, size); - - count -= size; - len += size; - } - - spin_unlock_irqrestore(&self->spinlock, flags); - - /* - * Schedule a new thread which will transmit the frame as soon - * as possible, but at a safe point in time. We do this so the - * "user" can give us data multiple times, as PPP does (because of - * its 256 byte tx buffer). We will then defragment and send out - * all this data as one single packet. - */ - schedule_work(&self->tqueue); - - return len; -} - -/* - * Function ircomm_tty_write_room (tty) - * - * This routine returns the numbers of characters the tty driver will - * accept for queuing to be written. This number is subject to change as - * output buffers get emptied, or if the output flow control is acted. - */ -static int ircomm_tty_write_room(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned long flags; - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - -#ifdef IRCOMM_NO_TX_BEFORE_INIT - /* max_header_size tells us if the channel is initialised or not. */ - if (self->max_header_size == IRCOMM_TTY_HDR_UNINITIALISED) - /* Don't bother us yet */ - return 0; -#endif - - /* Check if we are allowed to transmit any data. - * hw_stopped is the regular flow control. - * Jean II */ - if (tty->hw_stopped) - ret = 0; - else { - spin_lock_irqsave(&self->spinlock, flags); - if (self->tx_skb) - ret = self->tx_data_size - self->tx_skb->len; - else - ret = self->max_data_size; - spin_unlock_irqrestore(&self->spinlock, flags); - } - pr_debug("%s(), ret=%d\n", __func__ , ret); - - return ret; -} - -/* - * Function ircomm_tty_wait_until_sent (tty, timeout) - * - * This routine waits until the device has written out all of the - * characters in its transmitter FIFO. - */ -static void ircomm_tty_wait_until_sent(struct tty_struct *tty, int timeout) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned long orig_jiffies, poll_time; - unsigned long flags; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - orig_jiffies = jiffies; - - /* Set poll time to 200 ms */ - poll_time = msecs_to_jiffies(200); - if (timeout) - poll_time = min_t(unsigned long, timeout, poll_time); - - spin_lock_irqsave(&self->spinlock, flags); - while (self->tx_skb && self->tx_skb->len) { - spin_unlock_irqrestore(&self->spinlock, flags); - schedule_timeout_interruptible(poll_time); - spin_lock_irqsave(&self->spinlock, flags); - if (signal_pending(current)) - break; - if (timeout && time_after(jiffies, orig_jiffies + timeout)) - break; - } - spin_unlock_irqrestore(&self->spinlock, flags); - __set_current_state(TASK_RUNNING); -} - -/* - * Function ircomm_tty_throttle (tty) - * - * This routine notifies the tty driver that input buffers for the line - * discipline are close to full, and it should somehow signal that no - * more characters should be sent to the tty. - */ -static void ircomm_tty_throttle(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* Software flow control? */ - if (I_IXOFF(tty)) - ircomm_tty_send_xchar(tty, STOP_CHAR(tty)); - - /* Hardware flow control? */ - if (C_CRTSCTS(tty)) { - self->settings.dte &= ~IRCOMM_RTS; - self->settings.dte |= IRCOMM_DELTA_RTS; - - ircomm_param_request(self, IRCOMM_DTE, TRUE); - } - - ircomm_flow_request(self->ircomm, FLOW_STOP); -} - -/* - * Function ircomm_tty_unthrottle (tty) - * - * This routine notifies the tty drivers that it should signals that - * characters can now be sent to the tty without fear of overrunning the - * input buffers of the line disciplines. - */ -static void ircomm_tty_unthrottle(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* Using software flow control? */ - if (I_IXOFF(tty)) - ircomm_tty_send_xchar(tty, START_CHAR(tty)); - - /* Using hardware flow control? */ - if (C_CRTSCTS(tty)) { - self->settings.dte |= (IRCOMM_RTS|IRCOMM_DELTA_RTS); - - ircomm_param_request(self, IRCOMM_DTE, TRUE); - pr_debug("%s(), FLOW_START\n", __func__); - } - ircomm_flow_request(self->ircomm, FLOW_START); -} - -/* - * Function ircomm_tty_chars_in_buffer (tty) - * - * Indicates if there are any data in the buffer - * - */ -static int ircomm_tty_chars_in_buffer(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned long flags; - int len = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - spin_lock_irqsave(&self->spinlock, flags); - - if (self->tx_skb) - len = self->tx_skb->len; - - spin_unlock_irqrestore(&self->spinlock, flags); - - return len; -} - -static void ircomm_tty_shutdown(struct ircomm_tty_cb *self) -{ - unsigned long flags; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - if (!tty_port_initialized(&self->port)) - return; - tty_port_set_initialized(&self->port, 0); - - ircomm_tty_detach_cable(self); - - spin_lock_irqsave(&self->spinlock, flags); - - del_timer(&self->watchdog_timer); - - /* Free parameter buffer */ - if (self->ctrl_skb) { - dev_kfree_skb(self->ctrl_skb); - self->ctrl_skb = NULL; - } - - /* Free transmit buffer */ - if (self->tx_skb) { - dev_kfree_skb(self->tx_skb); - self->tx_skb = NULL; - } - - if (self->ircomm) { - ircomm_close(self->ircomm); - self->ircomm = NULL; - } - - spin_unlock_irqrestore(&self->spinlock, flags); -} - -/* - * Function ircomm_tty_hangup (tty) - * - * This routine notifies the tty driver that it should hangup the tty - * device. - * - */ -static void ircomm_tty_hangup(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - struct tty_port *port = &self->port; - unsigned long flags; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* ircomm_tty_flush_buffer(tty); */ - ircomm_tty_shutdown(self); - - spin_lock_irqsave(&port->lock, flags); - if (port->tty) { - set_bit(TTY_IO_ERROR, &port->tty->flags); - tty_kref_put(port->tty); - } - port->tty = NULL; - port->count = 0; - spin_unlock_irqrestore(&port->lock, flags); - tty_port_set_active(port, 0); - - wake_up_interruptible(&port->open_wait); -} - -/* - * Function ircomm_tty_send_xchar (tty, ch) - * - * This routine is used to send a high-priority XON/XOFF character to - * the device. - */ -static void ircomm_tty_send_xchar(struct tty_struct *tty, char ch) -{ - pr_debug("%s(), not impl\n", __func__); -} - -/* - * Function ircomm_tty_start (tty) - * - * This routine notifies the tty driver that it resume sending - * characters to the tty device. - */ -void ircomm_tty_start(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - ircomm_flow_request(self->ircomm, FLOW_START); -} - -/* - * Function ircomm_tty_stop (tty) - * - * This routine notifies the tty driver that it should stop outputting - * characters to the tty device. - */ -static void ircomm_tty_stop(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - ircomm_flow_request(self->ircomm, FLOW_STOP); -} - -/* - * Function ircomm_check_modem_status (self) - * - * Check for any changes in the DCE's line settings. This function should - * be called whenever the dce parameter settings changes, to update the - * flow control settings and other things - */ -void ircomm_tty_check_modem_status(struct ircomm_tty_cb *self) -{ - struct tty_struct *tty; - int status; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - tty = tty_port_tty_get(&self->port); - - status = self->settings.dce; - - if (status & IRCOMM_DCE_DELTA_ANY) { - /*wake_up_interruptible(&self->delta_msr_wait);*/ - } - if (tty_port_check_carrier(&self->port) && (status & IRCOMM_DELTA_CD)) { - pr_debug("%s(), ircomm%d CD now %s...\n", __func__ , self->line, - (status & IRCOMM_CD) ? "on" : "off"); - - if (status & IRCOMM_CD) { - wake_up_interruptible(&self->port.open_wait); - } else { - pr_debug("%s(), Doing serial hangup..\n", __func__); - if (tty) - tty_hangup(tty); - - /* Hangup will remote the tty, so better break out */ - goto put; - } - } - if (tty && tty_port_cts_enabled(&self->port)) { - if (tty->hw_stopped) { - if (status & IRCOMM_CTS) { - pr_debug("%s(), CTS tx start...\n", __func__); - tty->hw_stopped = 0; - - /* Wake up processes blocked on open */ - wake_up_interruptible(&self->port.open_wait); - - schedule_work(&self->tqueue); - goto put; - } - } else { - if (!(status & IRCOMM_CTS)) { - pr_debug("%s(), CTS tx stop...\n", __func__); - tty->hw_stopped = 1; - } - } - } -put: - tty_kref_put(tty); -} - -/* - * Function ircomm_tty_data_indication (instance, sap, skb) - * - * Handle incoming data, and deliver it to the line discipline - * - */ -static int ircomm_tty_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - tty = tty_port_tty_get(&self->port); - if (!tty) { - pr_debug("%s(), no tty!\n", __func__); - return 0; - } - - /* - * If we receive data when hardware is stopped then something is wrong. - * We try to poll the peers line settings to check if we are up todate. - * Devices like WinCE can do this, and since they don't send any - * params, we can just as well declare the hardware for running. - */ - if (tty->hw_stopped && (self->flow == FLOW_START)) { - pr_debug("%s(), polling for line settings!\n", __func__); - ircomm_param_request(self, IRCOMM_POLL, TRUE); - - /* We can just as well declare the hardware for running */ - ircomm_tty_send_initial_parameters(self); - ircomm_tty_link_established(self); - } - tty_kref_put(tty); - - /* - * Use flip buffer functions since the code may be called from interrupt - * context - */ - tty_insert_flip_string(&self->port, skb->data, skb->len); - tty_flip_buffer_push(&self->port); - - /* No need to kfree_skb - see ircomm_ttp_data_indication() */ - - return 0; -} - -/* - * Function ircomm_tty_control_indication (instance, sap, skb) - * - * Parse all incoming parameters (easy!) - * - */ -static int ircomm_tty_control_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - int clen; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - clen = skb->data[0]; - - irda_param_extract_all(self, skb->data+1, IRDA_MIN(skb->len-1, clen), - &ircomm_param_info); - - /* No need to kfree_skb - see ircomm_control_indication() */ - - return 0; -} - -/* - * Function ircomm_tty_flow_indication (instance, sap, cmd) - * - * This function is called by IrTTP when it wants us to slow down the - * transmission of data. We just mark the hardware as stopped, and wait - * for IrTTP to notify us that things are OK again. - */ -static void ircomm_tty_flow_indication(void *instance, void *sap, - LOCAL_FLOW cmd) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - tty = tty_port_tty_get(&self->port); - - switch (cmd) { - case FLOW_START: - pr_debug("%s(), hw start!\n", __func__); - if (tty) - tty->hw_stopped = 0; - - /* ircomm_tty_do_softint will take care of the rest */ - schedule_work(&self->tqueue); - break; - default: /* If we get here, something is very wrong, better stop */ - case FLOW_STOP: - pr_debug("%s(), hw stopped!\n", __func__); - if (tty) - tty->hw_stopped = 1; - break; - } - - tty_kref_put(tty); - self->flow = cmd; -} - -#ifdef CONFIG_PROC_FS -static void ircomm_tty_line_info(struct ircomm_tty_cb *self, struct seq_file *m) -{ - struct tty_struct *tty; - char sep; - - seq_printf(m, "State: %s\n", ircomm_tty_state[self->state]); - - seq_puts(m, "Service type: "); - if (self->service_type & IRCOMM_9_WIRE) - seq_puts(m, "9_WIRE"); - else if (self->service_type & IRCOMM_3_WIRE) - seq_puts(m, "3_WIRE"); - else if (self->service_type & IRCOMM_3_WIRE_RAW) - seq_puts(m, "3_WIRE_RAW"); - else - seq_puts(m, "No common service type!\n"); - seq_putc(m, '\n'); - - seq_printf(m, "Port name: %s\n", self->settings.port_name); - - seq_printf(m, "DTE status:"); - sep = ' '; - if (self->settings.dte & IRCOMM_RTS) { - seq_printf(m, "%cRTS", sep); - sep = '|'; - } - if (self->settings.dte & IRCOMM_DTR) { - seq_printf(m, "%cDTR", sep); - sep = '|'; - } - seq_putc(m, '\n'); - - seq_puts(m, "DCE status:"); - sep = ' '; - if (self->settings.dce & IRCOMM_CTS) { - seq_printf(m, "%cCTS", sep); - sep = '|'; - } - if (self->settings.dce & IRCOMM_DSR) { - seq_printf(m, "%cDSR", sep); - sep = '|'; - } - if (self->settings.dce & IRCOMM_CD) { - seq_printf(m, "%cCD", sep); - sep = '|'; - } - if (self->settings.dce & IRCOMM_RI) { - seq_printf(m, "%cRI", sep); - sep = '|'; - } - seq_putc(m, '\n'); - - seq_puts(m, "Configuration: "); - if (!self->settings.null_modem) - seq_puts(m, "DTE <-> DCE\n"); - else - seq_puts(m, "DTE <-> DTE (null modem emulation)\n"); - - seq_printf(m, "Data rate: %d\n", self->settings.data_rate); - - seq_puts(m, "Flow control:"); - sep = ' '; - if (self->settings.flow_control & IRCOMM_XON_XOFF_IN) { - seq_printf(m, "%cXON_XOFF_IN", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_XON_XOFF_OUT) { - seq_printf(m, "%cXON_XOFF_OUT", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_RTS_CTS_IN) { - seq_printf(m, "%cRTS_CTS_IN", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_RTS_CTS_OUT) { - seq_printf(m, "%cRTS_CTS_OUT", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_DSR_DTR_IN) { - seq_printf(m, "%cDSR_DTR_IN", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_DSR_DTR_OUT) { - seq_printf(m, "%cDSR_DTR_OUT", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_ENQ_ACK_IN) { - seq_printf(m, "%cENQ_ACK_IN", sep); - sep = '|'; - } - if (self->settings.flow_control & IRCOMM_ENQ_ACK_OUT) { - seq_printf(m, "%cENQ_ACK_OUT", sep); - sep = '|'; - } - seq_putc(m, '\n'); - - seq_puts(m, "Flags:"); - sep = ' '; - if (tty_port_cts_enabled(&self->port)) { - seq_printf(m, "%cASYNC_CTS_FLOW", sep); - sep = '|'; - } - if (tty_port_check_carrier(&self->port)) { - seq_printf(m, "%cASYNC_CHECK_CD", sep); - sep = '|'; - } - if (tty_port_initialized(&self->port)) { - seq_printf(m, "%cASYNC_INITIALIZED", sep); - sep = '|'; - } - if (self->port.flags & ASYNC_LOW_LATENCY) { - seq_printf(m, "%cASYNC_LOW_LATENCY", sep); - sep = '|'; - } - if (tty_port_active(&self->port)) { - seq_printf(m, "%cASYNC_NORMAL_ACTIVE", sep); - sep = '|'; - } - seq_putc(m, '\n'); - - seq_printf(m, "Role: %s\n", self->client ? "client" : "server"); - seq_printf(m, "Open count: %d\n", self->port.count); - seq_printf(m, "Max data size: %d\n", self->max_data_size); - seq_printf(m, "Max header size: %d\n", self->max_header_size); - - tty = tty_port_tty_get(&self->port); - if (tty) { - seq_printf(m, "Hardware: %s\n", - tty->hw_stopped ? "Stopped" : "Running"); - tty_kref_put(tty); - } -} - -static int ircomm_tty_proc_show(struct seq_file *m, void *v) -{ - struct ircomm_tty_cb *self; - unsigned long flags; - - spin_lock_irqsave(&ircomm_tty->hb_spinlock, flags); - - self = (struct ircomm_tty_cb *) hashbin_get_first(ircomm_tty); - while (self != NULL) { - if (self->magic != IRCOMM_TTY_MAGIC) - break; - - ircomm_tty_line_info(self, m); - self = (struct ircomm_tty_cb *) hashbin_get_next(ircomm_tty); - } - spin_unlock_irqrestore(&ircomm_tty->hb_spinlock, flags); - return 0; -} - -static int ircomm_tty_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, ircomm_tty_proc_show, NULL); -} - -static const struct file_operations ircomm_tty_proc_fops = { - .owner = THIS_MODULE, - .open = ircomm_tty_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif /* CONFIG_PROC_FS */ - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("IrCOMM serial TTY driver"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV_MAJOR(IRCOMM_TTY_MAJOR); - -module_init(ircomm_tty_init); -module_exit(ircomm_tty_cleanup); diff --git a/net/irda/ircomm/ircomm_tty_attach.c b/net/irda/ircomm/ircomm_tty_attach.c deleted file mode 100644 index 0a411019c098..000000000000 --- a/net/irda/ircomm/ircomm_tty_attach.c +++ /dev/null @@ -1,987 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_tty_attach.c - * Version: - * Description: Code for attaching the serial driver to IrCOMM - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Jun 5 17:42:00 1999 - * Modified at: Tue Jan 4 14:20:49 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include -#include - -static void ircomm_tty_ias_register(struct ircomm_tty_cb *self); -static void ircomm_tty_discovery_indication(discinfo_t *discovery, - DISCOVERY_MODE mode, - void *priv); -static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, - struct ias_value *value, void *priv); -static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, - int timeout); -static void ircomm_tty_watchdog_timer_expired(void *data); - -static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_search(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); -static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info); - -const char *const ircomm_tty_state[] = { - "IRCOMM_TTY_IDLE", - "IRCOMM_TTY_SEARCH", - "IRCOMM_TTY_QUERY_PARAMETERS", - "IRCOMM_TTY_QUERY_LSAP_SEL", - "IRCOMM_TTY_SETUP", - "IRCOMM_TTY_READY", - "*** ERROR *** ", -}; - -static const char *const ircomm_tty_event[] __maybe_unused = { - "IRCOMM_TTY_ATTACH_CABLE", - "IRCOMM_TTY_DETACH_CABLE", - "IRCOMM_TTY_DATA_REQUEST", - "IRCOMM_TTY_DATA_INDICATION", - "IRCOMM_TTY_DISCOVERY_REQUEST", - "IRCOMM_TTY_DISCOVERY_INDICATION", - "IRCOMM_TTY_CONNECT_CONFIRM", - "IRCOMM_TTY_CONNECT_INDICATION", - "IRCOMM_TTY_DISCONNECT_REQUEST", - "IRCOMM_TTY_DISCONNECT_INDICATION", - "IRCOMM_TTY_WD_TIMER_EXPIRED", - "IRCOMM_TTY_GOT_PARAMETERS", - "IRCOMM_TTY_GOT_LSAPSEL", - "*** ERROR ****", -}; - -static int (*state[])(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, - struct sk_buff *skb, struct ircomm_tty_info *info) = -{ - ircomm_tty_state_idle, - ircomm_tty_state_search, - ircomm_tty_state_query_parameters, - ircomm_tty_state_query_lsap_sel, - ircomm_tty_state_setup, - ircomm_tty_state_ready, -}; - -/* - * Function ircomm_tty_attach_cable (driver) - * - * Try to attach cable (IrCOMM link). This function will only return - * when the link has been connected, or if an error condition occurs. - * If success, the return value is the resulting service type. - */ -int ircomm_tty_attach_cable(struct ircomm_tty_cb *self) -{ - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - /* Check if somebody has already connected to us */ - if (ircomm_is_connected(self->ircomm)) { - pr_debug("%s(), already connected!\n", __func__); - return 0; - } - - /* Make sure nobody tries to write before the link is up */ - tty = tty_port_tty_get(&self->port); - if (tty) { - tty->hw_stopped = 1; - tty_kref_put(tty); - } - - ircomm_tty_ias_register(self); - - ircomm_tty_do_event(self, IRCOMM_TTY_ATTACH_CABLE, NULL, NULL); - - return 0; -} - -/* - * Function ircomm_detach_cable (driver) - * - * Detach cable, or cable has been detached by peer - * - */ -void ircomm_tty_detach_cable(struct ircomm_tty_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - del_timer(&self->watchdog_timer); - - /* Remove discovery handler */ - if (self->ckey) { - irlmp_unregister_client(self->ckey); - self->ckey = NULL; - } - /* Remove IrCOMM hint bits */ - if (self->skey) { - irlmp_unregister_service(self->skey); - self->skey = NULL; - } - - if (self->iriap) { - iriap_close(self->iriap); - self->iriap = NULL; - } - - /* Remove LM-IAS object */ - if (self->obj) { - irias_delete_object(self->obj); - self->obj = NULL; - } - - ircomm_tty_do_event(self, IRCOMM_TTY_DETACH_CABLE, NULL, NULL); - - /* Reset some values */ - self->daddr = self->saddr = 0; - self->dlsap_sel = self->slsap_sel = 0; - - memset(&self->settings, 0, sizeof(struct ircomm_params)); -} - -/* - * Function ircomm_tty_ias_register (self) - * - * Register with LM-IAS depending on which service type we are - * - */ -static void ircomm_tty_ias_register(struct ircomm_tty_cb *self) -{ - __u8 oct_seq[6]; - __u16 hints; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* Compute hint bits based on service */ - hints = irlmp_service_to_hint(S_COMM); - if (self->service_type & IRCOMM_3_WIRE_RAW) - hints |= irlmp_service_to_hint(S_PRINTER); - - /* Advertise IrCOMM hint bit in discovery */ - if (!self->skey) - self->skey = irlmp_register_service(hints); - /* Set up a discovery handler */ - if (!self->ckey) - self->ckey = irlmp_register_client(hints, - ircomm_tty_discovery_indication, - NULL, (void *) self); - - /* If already done, no need to do it again */ - if (self->obj) - return; - - if (self->service_type & IRCOMM_3_WIRE_RAW) { - /* Register IrLPT with LM-IAS */ - self->obj = irias_new_object("IrLPT", IAS_IRLPT_ID); - irias_add_integer_attrib(self->obj, "IrDA:IrLMP:LsapSel", - self->slsap_sel, IAS_KERNEL_ATTR); - } else { - /* Register IrCOMM with LM-IAS */ - self->obj = irias_new_object("IrDA:IrCOMM", IAS_IRCOMM_ID); - irias_add_integer_attrib(self->obj, "IrDA:TinyTP:LsapSel", - self->slsap_sel, IAS_KERNEL_ATTR); - - /* Code the parameters into the buffer */ - irda_param_pack(oct_seq, "bbbbbb", - IRCOMM_SERVICE_TYPE, 1, self->service_type, - IRCOMM_PORT_TYPE, 1, IRCOMM_SERIAL); - - /* Register parameters with LM-IAS */ - irias_add_octseq_attrib(self->obj, "Parameters", oct_seq, 6, - IAS_KERNEL_ATTR); - } - irias_insert_object(self->obj); -} - -/* - * Function ircomm_tty_ias_unregister (self) - * - * Remove our IAS object and client hook while connected. - * - */ -static void ircomm_tty_ias_unregister(struct ircomm_tty_cb *self) -{ - /* Remove LM-IAS object now so it is not reused. - * IrCOMM deals very poorly with multiple incoming connections. - * It should looks a lot more like IrNET, and "dup" a server TSAP - * to the application TSAP (based on various rules). - * This is a cheap workaround allowing multiple clients to - * connect to us. It will not always work. - * Each IrCOMM socket has an IAS entry. Incoming connection will - * pick the first one found. So, when we are fully connected, - * we remove our IAS entries so that the next IAS entry is used. - * We do that for *both* client and server, because a server - * can also create client instances. - * Jean II */ - if (self->obj) { - irias_delete_object(self->obj); - self->obj = NULL; - } - -#if 0 - /* Remove discovery handler. - * While we are connected, we no longer need to receive - * discovery events. This would be the case if there is - * multiple IrLAP interfaces. Jean II */ - if (self->ckey) { - irlmp_unregister_client(self->ckey); - self->ckey = NULL; - } -#endif -} - -/* - * Function ircomm_send_initial_parameters (self) - * - * Send initial parameters to the remote IrCOMM device. These parameters - * must be sent before any data. - */ -int ircomm_tty_send_initial_parameters(struct ircomm_tty_cb *self) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (self->service_type & IRCOMM_3_WIRE_RAW) - return 0; - - /* - * Set default values, but only if the application for some reason - * haven't set them already - */ - pr_debug("%s(), data-rate = %d\n", __func__ , - self->settings.data_rate); - if (!self->settings.data_rate) - self->settings.data_rate = 9600; - pr_debug("%s(), data-format = %d\n", __func__ , - self->settings.data_format); - if (!self->settings.data_format) - self->settings.data_format = IRCOMM_WSIZE_8; /* 8N1 */ - - pr_debug("%s(), flow-control = %d\n", __func__ , - self->settings.flow_control); - /*self->settings.flow_control = IRCOMM_RTS_CTS_IN|IRCOMM_RTS_CTS_OUT;*/ - - /* Do not set delta values for the initial parameters */ - self->settings.dte = IRCOMM_DTR | IRCOMM_RTS; - - /* Only send service type parameter when we are the client */ - if (self->client) - ircomm_param_request(self, IRCOMM_SERVICE_TYPE, FALSE); - ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE); - ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE); - - /* For a 3 wire service, we just flush the last parameter and return */ - if (self->settings.service_type == IRCOMM_3_WIRE) { - ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE); - return 0; - } - - /* Only 9-wire service types continue here */ - ircomm_param_request(self, IRCOMM_FLOW_CONTROL, FALSE); -#if 0 - ircomm_param_request(self, IRCOMM_XON_XOFF, FALSE); - ircomm_param_request(self, IRCOMM_ENQ_ACK, FALSE); -#endif - /* Notify peer that we are ready to receive data */ - ircomm_param_request(self, IRCOMM_DTE, TRUE); - - return 0; -} - -/* - * Function ircomm_tty_discovery_indication (discovery) - * - * Remote device is discovered, try query the remote IAS to see which - * device it is, and which services it has. - * - */ -static void ircomm_tty_discovery_indication(discinfo_t *discovery, - DISCOVERY_MODE mode, - void *priv) -{ - struct ircomm_tty_cb *self; - struct ircomm_tty_info info; - - /* Important note : - * We need to drop all passive discoveries. - * The LSAP management of IrComm is deficient and doesn't deal - * with the case of two instance connecting to each other - * simultaneously (it will deadlock in LMP). - * The proper fix would be to use the same technique as in IrNET, - * to have one server socket and separate instances for the - * connecting/connected socket. - * The workaround is to drop passive discovery, which drastically - * reduce the probability of this happening. - * Jean II */ - if(mode == DISCOVERY_PASSIVE) - return; - - info.daddr = discovery->daddr; - info.saddr = discovery->saddr; - - self = priv; - ircomm_tty_do_event(self, IRCOMM_TTY_DISCOVERY_INDICATION, - NULL, &info); -} - -/* - * Function ircomm_tty_disconnect_indication (instance, sap, reason, skb) - * - * Link disconnected - * - */ -void ircomm_tty_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - tty = tty_port_tty_get(&self->port); - if (!tty) - return; - - /* This will stop control data transfers */ - self->flow = FLOW_STOP; - - /* Stop data transfers */ - tty->hw_stopped = 1; - - ircomm_tty_do_event(self, IRCOMM_TTY_DISCONNECT_INDICATION, NULL, - NULL); - tty_kref_put(tty); -} - -/* - * Function ircomm_tty_getvalue_confirm (result, obj_id, value, priv) - * - * Got result from the IAS query we make - * - */ -static void ircomm_tty_getvalue_confirm(int result, __u16 obj_id, - struct ias_value *value, - void *priv) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) priv; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - /* We probably don't need to make any more queries */ - iriap_close(self->iriap); - self->iriap = NULL; - - /* Check if request succeeded */ - if (result != IAS_SUCCESS) { - pr_debug("%s(), got NULL value!\n", __func__); - return; - } - - switch (value->type) { - case IAS_OCT_SEQ: - pr_debug("%s(), got octet sequence\n", __func__); - - irda_param_extract_all(self, value->t.oct_seq, value->len, - &ircomm_param_info); - - ircomm_tty_do_event(self, IRCOMM_TTY_GOT_PARAMETERS, NULL, - NULL); - break; - case IAS_INTEGER: - /* Got LSAP selector */ - pr_debug("%s(), got lsapsel = %d\n", __func__ , - value->t.integer); - - if (value->t.integer == -1) { - pr_debug("%s(), invalid value!\n", __func__); - } else - self->dlsap_sel = value->t.integer; - - ircomm_tty_do_event(self, IRCOMM_TTY_GOT_LSAPSEL, NULL, NULL); - break; - case IAS_MISSING: - pr_debug("%s(), got IAS_MISSING\n", __func__); - break; - default: - pr_debug("%s(), got unknown type!\n", __func__); - break; - } - irias_delete_value(value); -} - -/* - * Function ircomm_tty_connect_confirm (instance, sap, qos, max_sdu_size, skb) - * - * Connection confirmed - * - */ -void ircomm_tty_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_data_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - self->client = TRUE; - self->max_data_size = max_data_size; - self->max_header_size = max_header_size; - self->flow = FLOW_START; - - ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_CONFIRM, NULL, NULL); - - /* No need to kfree_skb - see ircomm_ttp_connect_confirm() */ -} - -/* - * Function ircomm_tty_connect_indication (instance, sap, qos, max_sdu_size, - * skb) - * - * we are discovered and being requested to connect by remote device ! - * - */ -void ircomm_tty_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_data_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) instance; - int clen; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - self->client = FALSE; - self->max_data_size = max_data_size; - self->max_header_size = max_header_size; - self->flow = FLOW_START; - - clen = skb->data[0]; - if (clen) - irda_param_extract_all(self, skb->data+1, - IRDA_MIN(skb->len, clen), - &ircomm_param_info); - - ircomm_tty_do_event(self, IRCOMM_TTY_CONNECT_INDICATION, NULL, NULL); - - /* No need to kfree_skb - see ircomm_ttp_connect_indication() */ -} - -/* - * Function ircomm_tty_link_established (self) - * - * Called when the IrCOMM link is established - * - */ -void ircomm_tty_link_established(struct ircomm_tty_cb *self) -{ - struct tty_struct *tty; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - tty = tty_port_tty_get(&self->port); - if (!tty) - return; - - del_timer(&self->watchdog_timer); - - /* - * IrCOMM link is now up, and if we are not using hardware - * flow-control, then declare the hardware as running. Otherwise we - * will have to wait for the peer device (DCE) to raise the CTS - * line. - */ - if (tty_port_cts_enabled(&self->port) && - ((self->settings.dce & IRCOMM_CTS) == 0)) { - pr_debug("%s(), waiting for CTS ...\n", __func__); - goto put; - } else { - pr_debug("%s(), starting hardware!\n", __func__); - - tty->hw_stopped = 0; - - /* Wake up processes blocked on open */ - wake_up_interruptible(&self->port.open_wait); - } - - schedule_work(&self->tqueue); -put: - tty_kref_put(tty); -} - -/* - * Function ircomm_tty_start_watchdog_timer (self, timeout) - * - * Start the watchdog timer. This timer is used to make sure that any - * connection attempt is successful, and if not, we will retry after - * the timeout - */ -static void ircomm_tty_start_watchdog_timer(struct ircomm_tty_cb *self, - int timeout) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - irda_start_timer(&self->watchdog_timer, timeout, (void *) self, - ircomm_tty_watchdog_timer_expired); -} - -/* - * Function ircomm_tty_watchdog_timer_expired (data) - * - * Called when the connect procedure have taken to much time. - * - */ -static void ircomm_tty_watchdog_timer_expired(void *data) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - ircomm_tty_do_event(self, IRCOMM_TTY_WD_TIMER_EXPIRED, NULL, NULL); -} - - -/* - * Function ircomm_tty_do_event (self, event, skb) - * - * Process event - * - */ -int ircomm_tty_do_event(struct ircomm_tty_cb *self, IRCOMM_TTY_EVENT event, - struct sk_buff *skb, struct ircomm_tty_info *info) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - return (*state[self->state])(self, event, skb, info); -} - -/* - * Function ircomm_tty_next_state (self, state) - * - * Switch state - * - */ -static inline void ircomm_tty_next_state(struct ircomm_tty_cb *self, IRCOMM_TTY_STATE state) -{ - /* - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return;); - - pr_debug("%s: next state=%s, service type=%d\n", __func__ , - ircomm_tty_state[self->state], self->service_type); - */ - self->state = state; -} - -/* - * Function ircomm_tty_state_idle (self, event, skb, info) - * - * Just hanging around - * - */ -static int ircomm_tty_state_idle(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - switch (event) { - case IRCOMM_TTY_ATTACH_CABLE: - /* Try to discover any remote devices */ - ircomm_tty_start_watchdog_timer(self, 3*HZ); - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - - irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); - break; - case IRCOMM_TTY_DISCOVERY_INDICATION: - self->daddr = info->daddr; - self->saddr = info->saddr; - - if (self->iriap) { - net_warn_ratelimited("%s(), busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - ircomm_tty_getvalue_confirm); - - iriap_getvaluebyclass_request(self->iriap, - self->saddr, self->daddr, - "IrDA:IrCOMM", "Parameters"); - - ircomm_tty_start_watchdog_timer(self, 3*HZ); - ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: - /* Just stay idle */ - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_search (self, event, skb, info) - * - * Trying to discover an IrCOMM device - * - */ -static int ircomm_tty_state_search(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - switch (event) { - case IRCOMM_TTY_DISCOVERY_INDICATION: - self->daddr = info->daddr; - self->saddr = info->saddr; - - if (self->iriap) { - net_warn_ratelimited("%s(), busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - ircomm_tty_getvalue_confirm); - - if (self->service_type == IRCOMM_3_WIRE_RAW) { - iriap_getvaluebyclass_request(self->iriap, self->saddr, - self->daddr, "IrLPT", - "IrDA:IrLMP:LsapSel"); - ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL); - } else { - iriap_getvaluebyclass_request(self->iriap, self->saddr, - self->daddr, - "IrDA:IrCOMM", - "Parameters"); - - ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_PARAMETERS); - } - ircomm_tty_start_watchdog_timer(self, 3*HZ); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: -#if 1 - /* Give up */ -#else - /* Try to discover any remote devices */ - ircomm_tty_start_watchdog_timer(self, 3*HZ); - irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); -#endif - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_query (self, event, skb, info) - * - * Querying the remote LM-IAS for IrCOMM parameters - * - */ -static int ircomm_tty_state_query_parameters(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - switch (event) { - case IRCOMM_TTY_GOT_PARAMETERS: - if (self->iriap) { - net_warn_ratelimited("%s(), busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - ircomm_tty_getvalue_confirm); - - iriap_getvaluebyclass_request(self->iriap, self->saddr, - self->daddr, "IrDA:IrCOMM", - "IrDA:TinyTP:LsapSel"); - - ircomm_tty_start_watchdog_timer(self, 3*HZ); - ircomm_tty_next_state(self, IRCOMM_TTY_QUERY_LSAP_SEL); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: - /* Go back to search mode */ - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_query_lsap_sel (self, event, skb, info) - * - * Query remote LM-IAS for the LSAP selector which we can connect to - * - */ -static int ircomm_tty_state_query_lsap_sel(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - switch (event) { - case IRCOMM_TTY_GOT_LSAPSEL: - /* Connect to remote device */ - ret = ircomm_connect_request(self->ircomm, self->dlsap_sel, - self->saddr, self->daddr, - NULL, self->service_type); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - ircomm_tty_next_state(self, IRCOMM_TTY_SETUP); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: - /* Go back to search mode */ - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_setup (self, event, skb, info) - * - * Trying to connect - * - */ -static int ircomm_tty_state_setup(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - pr_debug("%s: state=%s, event=%s\n", __func__ , - ircomm_tty_state[self->state], ircomm_tty_event[event]); - - switch (event) { - case IRCOMM_TTY_CONNECT_CONFIRM: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* - * Send initial parameters. This will also send out queued - * parameters waiting for the connection to come up - */ - ircomm_tty_send_initial_parameters(self); - ircomm_tty_link_established(self); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_CONNECT_INDICATION: - del_timer(&self->watchdog_timer); - ircomm_tty_ias_unregister(self); - - /* Accept connection */ - ircomm_connect_response(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_READY); - break; - case IRCOMM_TTY_WD_TIMER_EXPIRED: - /* Go back to search mode */ - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - break; - case IRCOMM_TTY_DETACH_CABLE: - /* ircomm_disconnect_request(self->ircomm, NULL); */ - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - -/* - * Function ircomm_tty_state_ready (self, event, skb, info) - * - * IrCOMM is now connected - * - */ -static int ircomm_tty_state_ready(struct ircomm_tty_cb *self, - IRCOMM_TTY_EVENT event, - struct sk_buff *skb, - struct ircomm_tty_info *info) -{ - int ret = 0; - - switch (event) { - case IRCOMM_TTY_DATA_REQUEST: - ret = ircomm_data_request(self->ircomm, skb); - break; - case IRCOMM_TTY_DETACH_CABLE: - ircomm_disconnect_request(self->ircomm, NULL); - ircomm_tty_next_state(self, IRCOMM_TTY_IDLE); - break; - case IRCOMM_TTY_DISCONNECT_INDICATION: - ircomm_tty_ias_register(self); - ircomm_tty_next_state(self, IRCOMM_TTY_SEARCH); - ircomm_tty_start_watchdog_timer(self, 3*HZ); - - if (tty_port_check_carrier(&self->port)) { - /* Drop carrier */ - self->settings.dce = IRCOMM_DELTA_CD; - ircomm_tty_check_modem_status(self); - } else { - pr_debug("%s(), hanging up!\n", __func__); - tty_port_tty_hangup(&self->port, false); - } - break; - default: - pr_debug("%s(), unknown event: %s\n", __func__ , - ircomm_tty_event[event]); - ret = -EINVAL; - } - return ret; -} - diff --git a/net/irda/ircomm/ircomm_tty_ioctl.c b/net/irda/ircomm/ircomm_tty_ioctl.c deleted file mode 100644 index 171c3dee760e..000000000000 --- a/net/irda/ircomm/ircomm_tty_ioctl.c +++ /dev/null @@ -1,291 +0,0 @@ -/********************************************************************* - * - * Filename: ircomm_tty_ioctl.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Jun 10 14:39:09 1999 - * Modified at: Wed Jan 5 14:45:43 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include - -#include - -#include -#include - -#include -#include -#include -#include - -#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) - -/* - * Function ircomm_tty_change_speed (driver) - * - * Change speed of the driver. If the remote device is a DCE, then this - * should make it change the speed of its serial port - */ -static void ircomm_tty_change_speed(struct ircomm_tty_cb *self, - struct tty_struct *tty) -{ - unsigned int cflag, cval; - int baud; - - if (!self->ircomm) - return; - - cflag = tty->termios.c_cflag; - - /* byte size and parity */ - switch (cflag & CSIZE) { - case CS5: cval = IRCOMM_WSIZE_5; break; - case CS6: cval = IRCOMM_WSIZE_6; break; - case CS7: cval = IRCOMM_WSIZE_7; break; - case CS8: cval = IRCOMM_WSIZE_8; break; - default: cval = IRCOMM_WSIZE_5; break; - } - if (cflag & CSTOPB) - cval |= IRCOMM_2_STOP_BIT; - - if (cflag & PARENB) - cval |= IRCOMM_PARITY_ENABLE; - if (!(cflag & PARODD)) - cval |= IRCOMM_PARITY_EVEN; - - /* Determine divisor based on baud rate */ - baud = tty_get_baud_rate(tty); - if (!baud) - baud = 9600; /* B0 transition handled in rs_set_termios */ - - self->settings.data_rate = baud; - ircomm_param_request(self, IRCOMM_DATA_RATE, FALSE); - - /* CTS flow control flag and modem status interrupts */ - tty_port_set_cts_flow(&self->port, cflag & CRTSCTS); - if (cflag & CRTSCTS) { - self->settings.flow_control |= IRCOMM_RTS_CTS_IN; - /* This got me. Bummer. Jean II */ - if (self->service_type == IRCOMM_3_WIRE_RAW) - net_warn_ratelimited("%s(), enabling RTS/CTS on link that doesn't support it (3-wire-raw)\n", - __func__); - } else { - self->settings.flow_control &= ~IRCOMM_RTS_CTS_IN; - } - tty_port_set_check_carrier(&self->port, ~cflag & CLOCAL); - - self->settings.data_format = cval; - - ircomm_param_request(self, IRCOMM_DATA_FORMAT, FALSE); - ircomm_param_request(self, IRCOMM_FLOW_CONTROL, TRUE); -} - -/* - * Function ircomm_tty_set_termios (tty, old_termios) - * - * This routine allows the tty driver to be notified when device's - * termios settings have changed. Note that a well-designed tty driver - * should be prepared to accept the case where old == NULL, and try to - * do something rational. - */ -void ircomm_tty_set_termios(struct tty_struct *tty, - struct ktermios *old_termios) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned int cflag = tty->termios.c_cflag; - - if ((cflag == old_termios->c_cflag) && - (RELEVANT_IFLAG(tty->termios.c_iflag) == - RELEVANT_IFLAG(old_termios->c_iflag))) - { - return; - } - - ircomm_tty_change_speed(self, tty); - - /* Handle transition to B0 status */ - if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { - self->settings.dte &= ~(IRCOMM_DTR|IRCOMM_RTS); - ircomm_param_request(self, IRCOMM_DTE, TRUE); - } - - /* Handle transition away from B0 status */ - if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { - self->settings.dte |= IRCOMM_DTR; - if (!C_CRTSCTS(tty) || !tty_throttled(tty)) - self->settings.dte |= IRCOMM_RTS; - ircomm_param_request(self, IRCOMM_DTE, TRUE); - } - - /* Handle turning off CRTSCTS */ - if ((old_termios->c_cflag & CRTSCTS) && !C_CRTSCTS(tty)) - { - tty->hw_stopped = 0; - ircomm_tty_start(tty); - } -} - -/* - * Function ircomm_tty_tiocmget (tty) - * - * - * - */ -int ircomm_tty_tiocmget(struct tty_struct *tty) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - unsigned int result; - - if (tty_io_error(tty)) - return -EIO; - - result = ((self->settings.dte & IRCOMM_RTS) ? TIOCM_RTS : 0) - | ((self->settings.dte & IRCOMM_DTR) ? TIOCM_DTR : 0) - | ((self->settings.dce & IRCOMM_CD) ? TIOCM_CAR : 0) - | ((self->settings.dce & IRCOMM_RI) ? TIOCM_RNG : 0) - | ((self->settings.dce & IRCOMM_DSR) ? TIOCM_DSR : 0) - | ((self->settings.dce & IRCOMM_CTS) ? TIOCM_CTS : 0); - return result; -} - -/* - * Function ircomm_tty_tiocmset (tty, set, clear) - * - * - * - */ -int ircomm_tty_tiocmset(struct tty_struct *tty, - unsigned int set, unsigned int clear) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - - if (tty_io_error(tty)) - return -EIO; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRCOMM_TTY_MAGIC, return -1;); - - if (set & TIOCM_RTS) - self->settings.dte |= IRCOMM_RTS; - if (set & TIOCM_DTR) - self->settings.dte |= IRCOMM_DTR; - - if (clear & TIOCM_RTS) - self->settings.dte &= ~IRCOMM_RTS; - if (clear & TIOCM_DTR) - self->settings.dte &= ~IRCOMM_DTR; - - if ((set|clear) & TIOCM_RTS) - self->settings.dte |= IRCOMM_DELTA_RTS; - if ((set|clear) & TIOCM_DTR) - self->settings.dte |= IRCOMM_DELTA_DTR; - - ircomm_param_request(self, IRCOMM_DTE, TRUE); - - return 0; -} - -/* - * Function get_serial_info (driver, retinfo) - * - * - * - */ -static int ircomm_tty_get_serial_info(struct ircomm_tty_cb *self, - struct serial_struct __user *retinfo) -{ - struct serial_struct info; - - memset(&info, 0, sizeof(info)); - info.line = self->line; - info.flags = self->port.flags; - info.baud_base = self->settings.data_rate; - info.close_delay = self->port.close_delay; - info.closing_wait = self->port.closing_wait; - - /* For compatibility */ - info.type = PORT_16550A; - - if (copy_to_user(retinfo, &info, sizeof(*retinfo))) - return -EFAULT; - - return 0; -} - -/* - * Function set_serial_info (driver, new_info) - * - * - * - */ -static int ircomm_tty_set_serial_info(struct ircomm_tty_cb *self, - struct serial_struct __user *new_info) -{ - return 0; -} - -/* - * Function ircomm_tty_ioctl (tty, cmd, arg) - * - * - * - */ -int ircomm_tty_ioctl(struct tty_struct *tty, - unsigned int cmd, unsigned long arg) -{ - struct ircomm_tty_cb *self = (struct ircomm_tty_cb *) tty->driver_data; - int ret = 0; - - if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && - (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && - (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { - if (tty_io_error(tty)) - return -EIO; - } - - switch (cmd) { - case TIOCGSERIAL: - ret = ircomm_tty_get_serial_info(self, (struct serial_struct __user *) arg); - break; - case TIOCSSERIAL: - ret = ircomm_tty_set_serial_info(self, (struct serial_struct __user *) arg); - break; - case TIOCMIWAIT: - pr_debug("(), TIOCMIWAIT, not impl!\n"); - break; - - case TIOCGICOUNT: - pr_debug("%s(), TIOCGICOUNT not impl!\n", __func__); - return 0; - default: - ret = -ENOIOCTLCMD; /* ioctls which we must ignore */ - } - return ret; -} - - - diff --git a/net/irda/irda_device.c b/net/irda/irda_device.c deleted file mode 100644 index 890b90d055d5..000000000000 --- a/net/irda/irda_device.c +++ /dev/null @@ -1,316 +0,0 @@ -/********************************************************************* - * - * Filename: irda_device.c - * Version: 0.9 - * Description: Utility functions used by the device drivers - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Oct 9 09:22:27 1999 - * Modified at: Sun Jan 23 17:41:24 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include -#include -#include -#include - -static void __irda_task_delete(struct irda_task *task); - -static hashbin_t *dongles = NULL; -static hashbin_t *tasks = NULL; - -static void irda_task_timer_expired(void *data); - -int __init irda_device_init( void) -{ - dongles = hashbin_new(HB_NOLOCK); - if (dongles == NULL) { - net_warn_ratelimited("IrDA: Can't allocate dongles hashbin!\n"); - return -ENOMEM; - } - spin_lock_init(&dongles->hb_spinlock); - - tasks = hashbin_new(HB_LOCK); - if (tasks == NULL) { - net_warn_ratelimited("IrDA: Can't allocate tasks hashbin!\n"); - hashbin_delete(dongles, NULL); - return -ENOMEM; - } - - /* We no longer initialise the driver ourselves here, we let - * the system do it for us... - Jean II */ - - return 0; -} - -static void leftover_dongle(void *arg) -{ - struct dongle_reg *reg = arg; - net_warn_ratelimited("IrDA: Dongle type %x not unregistered\n", - reg->type); -} - -void irda_device_cleanup(void) -{ - hashbin_delete(tasks, (FREE_FUNC) __irda_task_delete); - - hashbin_delete(dongles, leftover_dongle); -} - -/* - * Function irda_device_set_media_busy (self, status) - * - * Called when we have detected that another station is transmitting - * in contention mode. - */ -void irda_device_set_media_busy(struct net_device *dev, int status) -{ - struct irlap_cb *self; - - pr_debug("%s(%s)\n", __func__, status ? "TRUE" : "FALSE"); - - self = (struct irlap_cb *) dev->atalk_ptr; - - /* Some drivers may enable the receive interrupt before calling - * irlap_open(), or they may disable the receive interrupt - * after calling irlap_close(). - * The IrDA stack is protected from this in irlap_driver_rcv(). - * However, the driver calls directly the wrapper, that calls - * us directly. Make sure we protect ourselves. - * Jean II */ - if (!self || self->magic != LAP_MAGIC) - return; - - if (status) { - self->media_busy = TRUE; - if (status == SMALL) - irlap_start_mbusy_timer(self, SMALLBUSY_TIMEOUT); - else - irlap_start_mbusy_timer(self, MEDIABUSY_TIMEOUT); - pr_debug("Media busy!\n"); - } else { - self->media_busy = FALSE; - irlap_stop_mbusy_timer(self); - } -} -EXPORT_SYMBOL(irda_device_set_media_busy); - - -/* - * Function irda_device_is_receiving (dev) - * - * Check if the device driver is currently receiving data - * - */ -int irda_device_is_receiving(struct net_device *dev) -{ - struct if_irda_req req; - int ret; - - if (!dev->netdev_ops->ndo_do_ioctl) { - net_err_ratelimited("%s: do_ioctl not impl. by device driver\n", - __func__); - return -1; - } - - ret = (dev->netdev_ops->ndo_do_ioctl)(dev, (struct ifreq *) &req, - SIOCGRECEIVING); - if (ret < 0) - return ret; - - return req.ifr_receiving; -} - -static void __irda_task_delete(struct irda_task *task) -{ - del_timer(&task->timer); - - kfree(task); -} - -static void irda_task_delete(struct irda_task *task) -{ - /* Unregister task */ - hashbin_remove(tasks, (long) task, NULL); - - __irda_task_delete(task); -} - -/* - * Function irda_task_kick (task) - * - * Tries to execute a task possible multiple times until the task is either - * finished, or askes for a timeout. When a task is finished, we do post - * processing, and notify the parent task, that is waiting for this task - * to complete. - */ -static int irda_task_kick(struct irda_task *task) -{ - int finished = TRUE; - int count = 0; - int timeout; - - IRDA_ASSERT(task != NULL, return -1;); - IRDA_ASSERT(task->magic == IRDA_TASK_MAGIC, return -1;); - - /* Execute task until it's finished, or askes for a timeout */ - do { - timeout = task->function(task); - if (count++ > 100) { - net_err_ratelimited("%s: error in task handler!\n", - __func__); - irda_task_delete(task); - return TRUE; - } - } while ((timeout == 0) && (task->state != IRDA_TASK_DONE)); - - if (timeout < 0) { - net_err_ratelimited("%s: Error executing task!\n", __func__); - irda_task_delete(task); - return TRUE; - } - - /* Check if we are finished */ - if (task->state == IRDA_TASK_DONE) { - del_timer(&task->timer); - - /* Do post processing */ - if (task->finished) - task->finished(task); - - /* Notify parent */ - if (task->parent) { - /* Check if parent is waiting for us to complete */ - if (task->parent->state == IRDA_TASK_CHILD_WAIT) { - task->parent->state = IRDA_TASK_CHILD_DONE; - - /* Stop timer now that we are here */ - del_timer(&task->parent->timer); - - /* Kick parent task */ - irda_task_kick(task->parent); - } - } - irda_task_delete(task); - } else if (timeout > 0) { - irda_start_timer(&task->timer, timeout, (void *) task, - irda_task_timer_expired); - finished = FALSE; - } else { - pr_debug("%s(), not finished, and no timeout!\n", - __func__); - finished = FALSE; - } - - return finished; -} - -/* - * Function irda_task_timer_expired (data) - * - * Task time has expired. We now try to execute task (again), and restart - * the timer if the task has not finished yet - */ -static void irda_task_timer_expired(void *data) -{ - struct irda_task *task; - - task = data; - - irda_task_kick(task); -} - -/* - * Function irda_device_setup (dev) - * - * This function should be used by low level device drivers in a similar way - * as ether_setup() is used by normal network device drivers - */ -static void irda_device_setup(struct net_device *dev) -{ - dev->hard_header_len = 0; - dev->addr_len = LAP_ALEN; - - dev->type = ARPHRD_IRDA; - dev->tx_queue_len = 8; /* Window size + 1 s-frame */ - - memset(dev->broadcast, 0xff, LAP_ALEN); - - dev->mtu = 2048; - dev->flags = IFF_NOARP; -} - -/* - * Funciton alloc_irdadev - * Allocates and sets up an IRDA device in a manner similar to - * alloc_etherdev. - */ -struct net_device *alloc_irdadev(int sizeof_priv) -{ - return alloc_netdev(sizeof_priv, "irda%d", NET_NAME_UNKNOWN, - irda_device_setup); -} -EXPORT_SYMBOL(alloc_irdadev); - -#ifdef CONFIG_ISA_DMA_API -/* - * Function setup_dma (idev, buffer, count, mode) - * - * Setup the DMA channel. Commonly used by LPC FIR drivers - * - */ -void irda_setup_dma(int channel, dma_addr_t buffer, int count, int mode) -{ - unsigned long flags; - - flags = claim_dma_lock(); - - disable_dma(channel); - clear_dma_ff(channel); - set_dma_mode(channel, mode); - set_dma_addr(channel, buffer); - set_dma_count(channel, count); - enable_dma(channel); - - release_dma_lock(flags); -} -EXPORT_SYMBOL(irda_setup_dma); -#endif diff --git a/net/irda/iriap.c b/net/irda/iriap.c deleted file mode 100644 index 1138eaf5c682..000000000000 --- a/net/irda/iriap.c +++ /dev/null @@ -1,1085 +0,0 @@ -/********************************************************************* - * - * Filename: iriap.c - * Version: 0.8 - * Description: Information Access Protocol (IAP) - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Aug 21 00:02:07 1997 - * Modified at: Sat Dec 25 16:42:42 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include - -/* FIXME: This one should go in irlmp.c */ -static const char *const ias_charset_types[] __maybe_unused = { - "CS_ASCII", - "CS_ISO_8859_1", - "CS_ISO_8859_2", - "CS_ISO_8859_3", - "CS_ISO_8859_4", - "CS_ISO_8859_5", - "CS_ISO_8859_6", - "CS_ISO_8859_7", - "CS_ISO_8859_8", - "CS_ISO_8859_9", - "CS_UNICODE" -}; - -static hashbin_t *iriap = NULL; -static void *service_handle; - -static void __iriap_close(struct iriap_cb *self); -static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode); -static void iriap_disconnect_indication(void *instance, void *sap, - LM_REASON reason, struct sk_buff *skb); -static void iriap_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); -static void iriap_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, __u8 max_header_size, - struct sk_buff *skb); -static int iriap_data_indication(void *instance, void *sap, - struct sk_buff *skb); - -static void iriap_watchdog_timer_expired(void *data); - -static inline void iriap_start_watchdog_timer(struct iriap_cb *self, - int timeout) -{ - irda_start_timer(&self->watchdog_timer, timeout, self, - iriap_watchdog_timer_expired); -} - -static struct lock_class_key irias_objects_key; - -/* - * Function iriap_init (void) - * - * Initializes the IrIAP layer, called by the module initialization code - * in irmod.c - */ -int __init iriap_init(void) -{ - struct ias_object *obj; - struct iriap_cb *server; - __u8 oct_seq[6]; - __u16 hints; - - /* Allocate master array */ - iriap = hashbin_new(HB_LOCK); - if (!iriap) - return -ENOMEM; - - /* Object repository - defined in irias_object.c */ - irias_objects = hashbin_new(HB_LOCK); - if (!irias_objects) { - net_warn_ratelimited("%s: Can't allocate irias_objects hashbin!\n", - __func__); - hashbin_delete(iriap, NULL); - return -ENOMEM; - } - - lockdep_set_class_and_name(&irias_objects->hb_spinlock, &irias_objects_key, - "irias_objects"); - - /* - * Register some default services for IrLMP - */ - hints = irlmp_service_to_hint(S_COMPUTER); - service_handle = irlmp_register_service(hints); - - /* Register the Device object with LM-IAS */ - obj = irias_new_object("Device", IAS_DEVICE_ID); - irias_add_string_attrib(obj, "DeviceName", "Linux", IAS_KERNEL_ATTR); - - oct_seq[0] = 0x01; /* Version 1 */ - oct_seq[1] = 0x00; /* IAS support bits */ - oct_seq[2] = 0x00; /* LM-MUX support bits */ -#ifdef CONFIG_IRDA_ULTRA - oct_seq[2] |= 0x04; /* Connectionless Data support */ -#endif - irias_add_octseq_attrib(obj, "IrLMPSupport", oct_seq, 3, - IAS_KERNEL_ATTR); - irias_insert_object(obj); - - /* - * Register server support with IrLMP so we can accept incoming - * connections - */ - server = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL); - if (!server) { - pr_debug("%s(), unable to open server\n", __func__); - return -1; - } - iriap_register_lsap(server, LSAP_IAS, IAS_SERVER); - - return 0; -} - -/* - * Function iriap_cleanup (void) - * - * Initializes the IrIAP layer, called by the module cleanup code in - * irmod.c - */ -void iriap_cleanup(void) -{ - irlmp_unregister_service(service_handle); - - hashbin_delete(iriap, (FREE_FUNC) __iriap_close); - hashbin_delete(irias_objects, (FREE_FUNC) __irias_delete_object); -} - -/* - * Function iriap_open (void) - * - * Opens an instance of the IrIAP layer, and registers with IrLMP - */ -struct iriap_cb *iriap_open(__u8 slsap_sel, int mode, void *priv, - CONFIRM_CALLBACK callback) -{ - struct iriap_cb *self; - - self = kzalloc(sizeof(*self), GFP_ATOMIC); - if (!self) - return NULL; - - /* - * Initialize instance - */ - - self->magic = IAS_MAGIC; - self->mode = mode; - if (mode == IAS_CLIENT) { - if (iriap_register_lsap(self, slsap_sel, mode)) { - kfree(self); - return NULL; - } - } - - self->confirm = callback; - self->priv = priv; - - /* iriap_getvaluebyclass_request() will construct packets before - * we connect, so this must have a sane value... Jean II */ - self->max_header_size = LMP_MAX_HEADER; - - init_timer(&self->watchdog_timer); - - hashbin_insert(iriap, (irda_queue_t *) self, (long) self, NULL); - - /* Initialize state machines */ - iriap_next_client_state(self, S_DISCONNECT); - iriap_next_call_state(self, S_MAKE_CALL); - iriap_next_server_state(self, R_DISCONNECT); - iriap_next_r_connect_state(self, R_WAITING); - - return self; -} -EXPORT_SYMBOL(iriap_open); - -/* - * Function __iriap_close (self) - * - * Removes (deallocates) the IrIAP instance - * - */ -static void __iriap_close(struct iriap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - del_timer(&self->watchdog_timer); - - if (self->request_skb) - dev_kfree_skb(self->request_skb); - - self->magic = 0; - - kfree(self); -} - -/* - * Function iriap_close (void) - * - * Closes IrIAP and deregisters with IrLMP - */ -void iriap_close(struct iriap_cb *self) -{ - struct iriap_cb *entry; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - if (self->lsap) { - irlmp_close_lsap(self->lsap); - self->lsap = NULL; - } - - entry = (struct iriap_cb *) hashbin_remove(iriap, (long) self, NULL); - IRDA_ASSERT(entry == self, return;); - - __iriap_close(self); -} -EXPORT_SYMBOL(iriap_close); - -static int iriap_register_lsap(struct iriap_cb *self, __u8 slsap_sel, int mode) -{ - notify_t notify; - - irda_notify_init(¬ify); - notify.connect_confirm = iriap_connect_confirm; - notify.connect_indication = iriap_connect_indication; - notify.disconnect_indication = iriap_disconnect_indication; - notify.data_indication = iriap_data_indication; - notify.instance = self; - if (mode == IAS_CLIENT) - strcpy(notify.name, "IrIAS cli"); - else - strcpy(notify.name, "IrIAS srv"); - - self->lsap = irlmp_open_lsap(slsap_sel, ¬ify, 0); - if (self->lsap == NULL) { - net_err_ratelimited("%s: Unable to allocated LSAP!\n", - __func__); - return -1; - } - self->slsap_sel = self->lsap->slsap_sel; - - return 0; -} - -/* - * Function iriap_disconnect_indication (handle, reason) - * - * Got disconnect, so clean up everything associated with this connection - * - */ -static void iriap_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *skb) -{ - struct iriap_cb *self; - - pr_debug("%s(), reason=%s [%d]\n", __func__, - irlmp_reason_str(reason), reason); - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - IRDA_ASSERT(iriap != NULL, return;); - - del_timer(&self->watchdog_timer); - - /* Not needed */ - if (skb) - dev_kfree_skb(skb); - - if (self->mode == IAS_CLIENT) { - pr_debug("%s(), disconnect as client\n", __func__); - - - iriap_do_client_event(self, IAP_LM_DISCONNECT_INDICATION, - NULL); - /* - * Inform service user that the request failed by sending - * it a NULL value. Warning, the client might close us, so - * remember no to use self anymore after calling confirm - */ - if (self->confirm) - self->confirm(IAS_DISCONNECT, 0, NULL, self->priv); - } else { - pr_debug("%s(), disconnect as server\n", __func__); - iriap_do_server_event(self, IAP_LM_DISCONNECT_INDICATION, - NULL); - iriap_close(self); - } -} - -/* - * Function iriap_disconnect_request (handle) - */ -static void iriap_disconnect_request(struct iriap_cb *self) -{ - struct sk_buff *tx_skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (tx_skb == NULL) { - pr_debug("%s(), Could not allocate an sk_buff of length %d\n", - __func__, LMP_MAX_HEADER); - return; - } - - /* - * Reserve space for MUX control and LAP header - */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - irlmp_disconnect_request(self->lsap, tx_skb); -} - -/* - * Function iriap_getvaluebyclass (addr, name, attr) - * - * Retrieve all values from attribute in all objects with given class - * name - */ -int iriap_getvaluebyclass_request(struct iriap_cb *self, - __u32 saddr, __u32 daddr, - char *name, char *attr) -{ - struct sk_buff *tx_skb; - int name_len, attr_len, skb_len; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return -1;); - - /* Client must supply the destination device address */ - if (!daddr) - return -1; - - self->daddr = daddr; - self->saddr = saddr; - - /* - * Save operation, so we know what the later indication is about - */ - self->operation = GET_VALUE_BY_CLASS; - - /* Give ourselves 10 secs to finish this operation */ - iriap_start_watchdog_timer(self, 10*HZ); - - name_len = strlen(name); /* Up to IAS_MAX_CLASSNAME = 60 */ - attr_len = strlen(attr); /* Up to IAS_MAX_ATTRIBNAME = 60 */ - - skb_len = self->max_header_size+2+name_len+1+attr_len+4; - tx_skb = alloc_skb(skb_len, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, self->max_header_size); - skb_put(tx_skb, 3+name_len+attr_len); - frame = tx_skb->data; - - /* Build frame */ - frame[0] = IAP_LST | GET_VALUE_BY_CLASS; - frame[1] = name_len; /* Insert length of name */ - memcpy(frame+2, name, name_len); /* Insert name */ - frame[2+name_len] = attr_len; /* Insert length of attr */ - memcpy(frame+3+name_len, attr, attr_len); /* Insert attr */ - - iriap_do_client_event(self, IAP_CALL_REQUEST_GVBC, tx_skb); - - /* Drop reference count - see state_s_disconnect(). */ - dev_kfree_skb(tx_skb); - - return 0; -} -EXPORT_SYMBOL(iriap_getvaluebyclass_request); - -/* - * Function iriap_getvaluebyclass_confirm (self, skb) - * - * Got result from GetValueByClass command. Parse it and return result - * to service user. - * - */ -static void iriap_getvaluebyclass_confirm(struct iriap_cb *self, - struct sk_buff *skb) -{ - struct ias_value *value; - int charset; - __u32 value_len; - __u32 tmp_cpu32; - __u16 obj_id; - __u16 len; - __u8 type; - __u8 *fp; - int n; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Initialize variables */ - fp = skb->data; - n = 2; - - /* Get length, MSB first */ - len = get_unaligned_be16(fp + n); - n += 2; - - pr_debug("%s(), len=%d\n", __func__, len); - - /* Get object ID, MSB first */ - obj_id = get_unaligned_be16(fp + n); - n += 2; - - type = fp[n++]; - pr_debug("%s(), Value type = %d\n", __func__, type); - - switch (type) { - case IAS_INTEGER: - memcpy(&tmp_cpu32, fp+n, 4); n += 4; - be32_to_cpus(&tmp_cpu32); - value = irias_new_integer_value(tmp_cpu32); - - /* Legal values restricted to 0x01-0x6f, page 15 irttp */ - pr_debug("%s(), lsap=%d\n", __func__, value->t.integer); - break; - case IAS_STRING: - charset = fp[n++]; - - switch (charset) { - case CS_ASCII: - break; -/* case CS_ISO_8859_1: */ -/* case CS_ISO_8859_2: */ -/* case CS_ISO_8859_3: */ -/* case CS_ISO_8859_4: */ -/* case CS_ISO_8859_5: */ -/* case CS_ISO_8859_6: */ -/* case CS_ISO_8859_7: */ -/* case CS_ISO_8859_8: */ -/* case CS_ISO_8859_9: */ -/* case CS_UNICODE: */ - default: - pr_debug("%s(), charset [%d] %s, not supported\n", - __func__, charset, - charset < ARRAY_SIZE(ias_charset_types) ? - ias_charset_types[charset] : - "(unknown)"); - - /* Aborting, close connection! */ - iriap_disconnect_request(self); - return; - /* break; */ - } - value_len = fp[n++]; - pr_debug("%s(), strlen=%d\n", __func__, value_len); - - /* Make sure the string is null-terminated */ - if (n + value_len < skb->len) - fp[n + value_len] = 0x00; - pr_debug("Got string %s\n", fp+n); - - /* Will truncate to IAS_MAX_STRING bytes */ - value = irias_new_string_value(fp+n); - break; - case IAS_OCT_SEQ: - value_len = get_unaligned_be16(fp + n); - n += 2; - - /* Will truncate to IAS_MAX_OCTET_STRING bytes */ - value = irias_new_octseq_value(fp+n, value_len); - break; - default: - value = irias_new_missing_value(); - break; - } - - /* Finished, close connection! */ - iriap_disconnect_request(self); - - /* Warning, the client might close us, so remember no to use self - * anymore after calling confirm - */ - if (self->confirm) - self->confirm(IAS_SUCCESS, obj_id, value, self->priv); - else { - pr_debug("%s(), missing handler!\n", __func__); - irias_delete_value(value); - } -} - -/* - * Function iriap_getvaluebyclass_response () - * - * Send answer back to remote LM-IAS - * - */ -static void iriap_getvaluebyclass_response(struct iriap_cb *self, - __u16 obj_id, - __u8 ret_code, - struct ias_value *value) -{ - struct sk_buff *tx_skb; - int n; - __be32 tmp_be32; - __be16 tmp_be16; - __u8 *fp; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(value != NULL, return;); - IRDA_ASSERT(value->len <= 1024, return;); - - /* Initialize variables */ - n = 0; - - /* - * We must adjust the size of the response after the length of the - * value. We add 32 bytes because of the 6 bytes for the frame and - * max 5 bytes for the value coding. - */ - tx_skb = alloc_skb(value->len + self->max_header_size + 32, - GFP_ATOMIC); - if (!tx_skb) - return; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, self->max_header_size); - skb_put(tx_skb, 6); - - fp = tx_skb->data; - - /* Build frame */ - fp[n++] = GET_VALUE_BY_CLASS | IAP_LST; - fp[n++] = ret_code; - - /* Insert list length (MSB first) */ - tmp_be16 = htons(0x0001); - memcpy(fp+n, &tmp_be16, 2); n += 2; - - /* Insert object identifier ( MSB first) */ - tmp_be16 = cpu_to_be16(obj_id); - memcpy(fp+n, &tmp_be16, 2); n += 2; - - switch (value->type) { - case IAS_STRING: - skb_put(tx_skb, 3 + value->len); - fp[n++] = value->type; - fp[n++] = 0; /* ASCII */ - fp[n++] = (__u8) value->len; - memcpy(fp+n, value->t.string, value->len); n+=value->len; - break; - case IAS_INTEGER: - skb_put(tx_skb, 5); - fp[n++] = value->type; - - tmp_be32 = cpu_to_be32(value->t.integer); - memcpy(fp+n, &tmp_be32, 4); n += 4; - break; - case IAS_OCT_SEQ: - skb_put(tx_skb, 3 + value->len); - fp[n++] = value->type; - - tmp_be16 = cpu_to_be16(value->len); - memcpy(fp+n, &tmp_be16, 2); n += 2; - memcpy(fp+n, value->t.oct_seq, value->len); n+=value->len; - break; - case IAS_MISSING: - pr_debug("%s: sending IAS_MISSING\n", __func__); - skb_put(tx_skb, 1); - fp[n++] = value->type; - break; - default: - pr_debug("%s(), type not implemented!\n", __func__); - break; - } - iriap_do_r_connect_event(self, IAP_CALL_RESPONSE, tx_skb); - - /* Drop reference count - see state_r_execute(). */ - dev_kfree_skb(tx_skb); -} - -/* - * Function iriap_getvaluebyclass_indication (self, skb) - * - * getvaluebyclass is requested from peer LM-IAS - * - */ -static void iriap_getvaluebyclass_indication(struct iriap_cb *self, - struct sk_buff *skb) -{ - struct ias_object *obj; - struct ias_attrib *attrib; - int name_len; - int attr_len; - char name[IAS_MAX_CLASSNAME + 1]; /* 60 bytes */ - char attr[IAS_MAX_ATTRIBNAME + 1]; /* 60 bytes */ - __u8 *fp; - int n; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - fp = skb->data; - n = 1; - - name_len = fp[n++]; - - IRDA_ASSERT(name_len < IAS_MAX_CLASSNAME + 1, return;); - - memcpy(name, fp+n, name_len); n+=name_len; - name[name_len] = '\0'; - - attr_len = fp[n++]; - - IRDA_ASSERT(attr_len < IAS_MAX_ATTRIBNAME + 1, return;); - - memcpy(attr, fp+n, attr_len); n+=attr_len; - attr[attr_len] = '\0'; - - pr_debug("LM-IAS: Looking up %s: %s\n", name, attr); - obj = irias_find_object(name); - - if (obj == NULL) { - pr_debug("LM-IAS: Object %s not found\n", name); - iriap_getvaluebyclass_response(self, 0x1235, IAS_CLASS_UNKNOWN, - &irias_missing); - return; - } - pr_debug("LM-IAS: found %s, id=%d\n", obj->name, obj->id); - - attrib = irias_find_attrib(obj, attr); - if (attrib == NULL) { - pr_debug("LM-IAS: Attribute %s not found\n", attr); - iriap_getvaluebyclass_response(self, obj->id, - IAS_ATTRIB_UNKNOWN, - &irias_missing); - return; - } - - /* We have a match; send the value. */ - iriap_getvaluebyclass_response(self, obj->id, IAS_SUCCESS, - attrib->value); -} - -/* - * Function iriap_send_ack (void) - * - * Currently not used - * - */ -void iriap_send_ack(struct iriap_cb *self) -{ - struct sk_buff *tx_skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - tx_skb = alloc_skb(LMP_MAX_HEADER + 1, GFP_ATOMIC); - if (!tx_skb) - return; - - /* Reserve space for MUX and LAP header */ - skb_reserve(tx_skb, self->max_header_size); - skb_put(tx_skb, 1); - frame = tx_skb->data; - - /* Build frame */ - frame[0] = IAP_LST | IAP_ACK | self->operation; - - irlmp_data_request(self->lsap, tx_skb); -} - -void iriap_connect_request(struct iriap_cb *self) -{ - int ret; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - ret = irlmp_connect_request(self->lsap, LSAP_IAS, - self->saddr, self->daddr, - NULL, NULL); - if (ret < 0) { - pr_debug("%s(), connect failed!\n", __func__); - self->confirm(IAS_DISCONNECT, 0, NULL, self->priv); - } -} - -/* - * Function iriap_connect_confirm (handle, skb) - * - * LSAP connection confirmed! - * - */ -static void iriap_connect_confirm(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct iriap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - self->max_data_size = max_seg_size; - self->max_header_size = max_header_size; - - del_timer(&self->watchdog_timer); - - iriap_do_client_event(self, IAP_LM_CONNECT_CONFIRM, skb); - - /* Drop reference count - see state_s_make_call(). */ - dev_kfree_skb(skb); -} - -/* - * Function iriap_connect_indication ( handle, skb) - * - * Remote LM-IAS is requesting connection - * - */ -static void iriap_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct iriap_cb *self, *new; - - self = instance; - - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(self != NULL, goto out;); - IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;); - - /* Start new server */ - new = iriap_open(LSAP_IAS, IAS_SERVER, NULL, NULL); - if (!new) { - pr_debug("%s(), open failed\n", __func__); - goto out; - } - - /* Now attach up the new "socket" */ - new->lsap = irlmp_dup(self->lsap, new); - if (!new->lsap) { - pr_debug("%s(), dup failed!\n", __func__); - goto out; - } - - new->max_data_size = max_seg_size; - new->max_header_size = max_header_size; - - /* Clean up the original one to keep it in listen state */ - irlmp_listen(self->lsap); - - iriap_do_server_event(new, IAP_LM_CONNECT_INDICATION, skb); - -out: - /* Drop reference count - see state_r_disconnect(). */ - dev_kfree_skb(skb); -} - -/* - * Function iriap_data_indication (handle, skb) - * - * Receives data from connection identified by handle from IrLMP - * - */ -static int iriap_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct iriap_cb *self; - __u8 *frame; - __u8 opcode; - - self = instance; - - IRDA_ASSERT(skb != NULL, return 0;); - IRDA_ASSERT(self != NULL, goto out;); - IRDA_ASSERT(self->magic == IAS_MAGIC, goto out;); - - frame = skb->data; - - if (self->mode == IAS_SERVER) { - /* Call server */ - pr_debug("%s(), Calling server!\n", __func__); - iriap_do_r_connect_event(self, IAP_RECV_F_LST, skb); - goto out; - } - opcode = frame[0]; - if (~opcode & IAP_LST) { - net_warn_ratelimited("%s:, IrIAS multiframe commands or results is not implemented yet!\n", - __func__); - goto out; - } - - /* Check for ack frames since they don't contain any data */ - if (opcode & IAP_ACK) { - pr_debug("%s() Got ack frame!\n", __func__); - goto out; - } - - opcode &= ~IAP_LST; /* Mask away LST bit */ - - switch (opcode) { - case GET_INFO_BASE: - pr_debug("IrLMP GetInfoBaseDetails not implemented!\n"); - break; - case GET_VALUE_BY_CLASS: - iriap_do_call_event(self, IAP_RECV_F_LST, NULL); - - switch (frame[1]) { - case IAS_SUCCESS: - iriap_getvaluebyclass_confirm(self, skb); - break; - case IAS_CLASS_UNKNOWN: - pr_debug("%s(), No such class!\n", __func__); - /* Finished, close connection! */ - iriap_disconnect_request(self); - - /* - * Warning, the client might close us, so remember - * no to use self anymore after calling confirm - */ - if (self->confirm) - self->confirm(IAS_CLASS_UNKNOWN, 0, NULL, - self->priv); - break; - case IAS_ATTRIB_UNKNOWN: - pr_debug("%s(), No such attribute!\n", __func__); - /* Finished, close connection! */ - iriap_disconnect_request(self); - - /* - * Warning, the client might close us, so remember - * no to use self anymore after calling confirm - */ - if (self->confirm) - self->confirm(IAS_ATTRIB_UNKNOWN, 0, NULL, - self->priv); - break; - } - break; - default: - pr_debug("%s(), Unknown op-code: %02x\n", __func__, - opcode); - break; - } - -out: - /* Cleanup - sub-calls will have done skb_get() as needed. */ - dev_kfree_skb(skb); - return 0; -} - -/* - * Function iriap_call_indication (self, skb) - * - * Received call to server from peer LM-IAS - * - */ -void iriap_call_indication(struct iriap_cb *self, struct sk_buff *skb) -{ - __u8 *fp; - __u8 opcode; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - fp = skb->data; - - opcode = fp[0]; - if (~opcode & 0x80) { - net_warn_ratelimited("%s: IrIAS multiframe commands or results is not implemented yet!\n", - __func__); - return; - } - opcode &= 0x7f; /* Mask away LST bit */ - - switch (opcode) { - case GET_INFO_BASE: - net_warn_ratelimited("%s: GetInfoBaseDetails not implemented yet!\n", - __func__); - break; - case GET_VALUE_BY_CLASS: - iriap_getvaluebyclass_indication(self, skb); - break; - } - /* skb will be cleaned up in iriap_data_indication */ -} - -/* - * Function iriap_watchdog_timer_expired (data) - * - * Query has taken too long time, so abort - * - */ -static void iriap_watchdog_timer_expired(void *data) -{ - struct iriap_cb *self = (struct iriap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - /* iriap_close(self); */ -} - -#ifdef CONFIG_PROC_FS - -static const char *const ias_value_types[] = { - "IAS_MISSING", - "IAS_INTEGER", - "IAS_OCT_SEQ", - "IAS_STRING" -}; - -static inline struct ias_object *irias_seq_idx(loff_t pos) -{ - struct ias_object *obj; - - for (obj = (struct ias_object *) hashbin_get_first(irias_objects); - obj; obj = (struct ias_object *) hashbin_get_next(irias_objects)) { - if (pos-- == 0) - break; - } - - return obj; -} - -static void *irias_seq_start(struct seq_file *seq, loff_t *pos) -{ - spin_lock_irq(&irias_objects->hb_spinlock); - - return *pos ? irias_seq_idx(*pos - 1) : SEQ_START_TOKEN; -} - -static void *irias_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - ++*pos; - - return (v == SEQ_START_TOKEN) - ? (void *) hashbin_get_first(irias_objects) - : (void *) hashbin_get_next(irias_objects); -} - -static void irias_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&irias_objects->hb_spinlock); -} - -static int irias_seq_show(struct seq_file *seq, void *v) -{ - if (v == SEQ_START_TOKEN) - seq_puts(seq, "LM-IAS Objects:\n"); - else { - struct ias_object *obj = v; - struct ias_attrib *attrib; - - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -EINVAL;); - - seq_printf(seq, "name: %s, id=%d\n", - obj->name, obj->id); - - /* Careful for priority inversions here ! - * All other uses of attrib spinlock are independent of - * the object spinlock, so we are safe. Jean II */ - spin_lock(&obj->attribs->hb_spinlock); - - /* List all attributes for this object */ - for (attrib = (struct ias_attrib *) hashbin_get_first(obj->attribs); - attrib != NULL; - attrib = (struct ias_attrib *) hashbin_get_next(obj->attribs)) { - - IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, - goto outloop; ); - - seq_printf(seq, " - Attribute name: \"%s\", ", - attrib->name); - seq_printf(seq, "value[%s]: ", - ias_value_types[attrib->value->type]); - - switch (attrib->value->type) { - case IAS_INTEGER: - seq_printf(seq, "%d\n", - attrib->value->t.integer); - break; - case IAS_STRING: - seq_printf(seq, "\"%s\"\n", - attrib->value->t.string); - break; - case IAS_OCT_SEQ: - seq_printf(seq, "octet sequence (%d bytes)\n", - attrib->value->len); - break; - case IAS_MISSING: - seq_puts(seq, "missing\n"); - break; - default: - seq_printf(seq, "type %d?\n", - attrib->value->type); - } - seq_putc(seq, '\n'); - - } - IRDA_ASSERT_LABEL(outloop:) - spin_unlock(&obj->attribs->hb_spinlock); - } - - return 0; -} - -static const struct seq_operations irias_seq_ops = { - .start = irias_seq_start, - .next = irias_seq_next, - .stop = irias_seq_stop, - .show = irias_seq_show, -}; - -static int irias_seq_open(struct inode *inode, struct file *file) -{ - IRDA_ASSERT( irias_objects != NULL, return -EINVAL;); - - return seq_open(file, &irias_seq_ops); -} - -const struct file_operations irias_seq_fops = { - .owner = THIS_MODULE, - .open = irias_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -#endif /* PROC_FS */ diff --git a/net/irda/iriap_event.c b/net/irda/iriap_event.c deleted file mode 100644 index e6098b2e048a..000000000000 --- a/net/irda/iriap_event.c +++ /dev/null @@ -1,496 +0,0 @@ -/********************************************************************* - * - * Filename: iriap_event.c - * Version: 0.1 - * Description: IAP Finite State Machine - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Aug 21 00:02:07 1997 - * Modified at: Wed Mar 1 11:28:34 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -#include -#include -#include -#include - -static void state_s_disconnect (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_connecting (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_call (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); - -static void state_s_make_call (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_calling (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_outstanding (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_replying (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_s_wait_active (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); - -static void state_r_disconnect (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_call (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_waiting (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_wait_active (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_receiving (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_execute (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); -static void state_r_returning (struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb); - -static void (*iriap_state[])(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) = { - /* Client FSM */ - state_s_disconnect, - state_s_connecting, - state_s_call, - - /* S-Call FSM */ - state_s_make_call, - state_s_calling, - state_s_outstanding, - state_s_replying, - state_s_wait_for_call, - state_s_wait_active, - - /* Server FSM */ - state_r_disconnect, - state_r_call, - - /* R-Connect FSM */ - state_r_waiting, - state_r_wait_active, - state_r_receiving, - state_r_execute, - state_r_returning, -}; - -void iriap_next_client_state(struct iriap_cb *self, IRIAP_STATE state) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - self->client_state = state; -} - -void iriap_next_call_state(struct iriap_cb *self, IRIAP_STATE state) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - self->call_state = state; -} - -void iriap_next_server_state(struct iriap_cb *self, IRIAP_STATE state) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - self->server_state = state; -} - -void iriap_next_r_connect_state(struct iriap_cb *self, IRIAP_STATE state) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - self->r_connect_state = state; -} - -void iriap_do_client_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - (*iriap_state[ self->client_state]) (self, event, skb); -} - -void iriap_do_call_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - (*iriap_state[ self->call_state]) (self, event, skb); -} - -void iriap_do_server_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - (*iriap_state[ self->server_state]) (self, event, skb); -} - -void iriap_do_r_connect_event(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - (*iriap_state[ self->r_connect_state]) (self, event, skb); -} - - -/* - * Function state_s_disconnect (event, skb) - * - * S-Disconnect, The device has no LSAP connection to a particular - * remote device. - */ -static void state_s_disconnect(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - switch (event) { - case IAP_CALL_REQUEST_GVBC: - iriap_next_client_state(self, S_CONNECTING); - IRDA_ASSERT(self->request_skb == NULL, return;); - /* Don't forget to refcount it - - * see iriap_getvaluebyclass_request(). */ - skb_get(skb); - self->request_skb = skb; - iriap_connect_request(self); - break; - case IAP_LM_DISCONNECT_INDICATION: - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_s_connecting (self, event, skb) - * - * S-Connecting - * - */ -static void state_s_connecting(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - switch (event) { - case IAP_LM_CONNECT_CONFIRM: - /* - * Jump to S-Call FSM - */ - iriap_do_call_event(self, IAP_CALL_REQUEST, skb); - /* iriap_call_request(self, 0,0,0); */ - iriap_next_client_state(self, S_CALL); - break; - case IAP_LM_DISCONNECT_INDICATION: - /* Abort calls */ - iriap_next_call_state(self, S_MAKE_CALL); - iriap_next_client_state(self, S_DISCONNECT); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_s_call (self, event, skb) - * - * S-Call, The device can process calls to a specific remote - * device. Whenever the LSAP connection is disconnected, this state - * catches that event and clears up - */ -static void state_s_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - - switch (event) { - case IAP_LM_DISCONNECT_INDICATION: - /* Abort calls */ - iriap_next_call_state(self, S_MAKE_CALL); - iriap_next_client_state(self, S_DISCONNECT); - break; - default: - pr_debug("state_s_call: Unknown event %d\n", event); - break; - } -} - -/* - * Function state_s_make_call (event, skb) - * - * S-Make-Call - * - */ -static void state_s_make_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - - IRDA_ASSERT(self != NULL, return;); - - switch (event) { - case IAP_CALL_REQUEST: - /* Already refcounted - see state_s_disconnect() */ - tx_skb = self->request_skb; - self->request_skb = NULL; - - irlmp_data_request(self->lsap, tx_skb); - iriap_next_call_state(self, S_OUTSTANDING); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_s_calling (event, skb) - * - * S-Calling - * - */ -static void state_s_calling(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -/* - * Function state_s_outstanding (event, skb) - * - * S-Outstanding, The device is waiting for a response to a command - * - */ -static void state_s_outstanding(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - - switch (event) { - case IAP_RECV_F_LST: - /*iriap_send_ack(self);*/ - /*LM_Idle_request(idle); */ - - iriap_next_call_state(self, S_WAIT_FOR_CALL); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_s_replying (event, skb) - * - * S-Replying, The device is collecting a multiple part response - */ -static void state_s_replying(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -/* - * Function state_s_wait_for_call (event, skb) - * - * S-Wait-for-Call - * - */ -static void state_s_wait_for_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - - -/* - * Function state_s_wait_active (event, skb) - * - * S-Wait-Active - * - */ -static void state_s_wait_active(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -/************************************************************************** - * - * Server FSM - * - **************************************************************************/ - -/* - * Function state_r_disconnect (self, event, skb) - * - * LM-IAS server is disconnected (not processing any requests!) - * - */ -static void state_r_disconnect(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - - switch (event) { - case IAP_LM_CONNECT_INDICATION: - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (tx_skb == NULL) - return; - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - irlmp_connect_response(self->lsap, tx_skb); - /*LM_Idle_request(idle); */ - - iriap_next_server_state(self, R_CALL); - - /* - * Jump to R-Connect FSM, we skip R-Waiting since we do not - * care about LM_Idle_request()! - */ - iriap_next_r_connect_state(self, R_RECEIVING); - break; - default: - pr_debug("%s(), unknown event %d\n", __func__, event); - break; - } -} - -/* - * Function state_r_call (self, event, skb) - */ -static void state_r_call(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - switch (event) { - case IAP_LM_DISCONNECT_INDICATION: - /* Abort call */ - iriap_next_server_state(self, R_DISCONNECT); - iriap_next_r_connect_state(self, R_WAITING); - break; - default: - pr_debug("%s(), unknown event!\n", __func__); - break; - } -} - -/* - * R-Connect FSM - */ - -/* - * Function state_r_waiting (self, event, skb) - */ -static void state_r_waiting(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -static void state_r_wait_active(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), Not implemented\n", __func__); -} - -/* - * Function state_r_receiving (self, event, skb) - * - * We are receiving a command - * - */ -static void state_r_receiving(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - switch (event) { - case IAP_RECV_F_LST: - iriap_next_r_connect_state(self, R_EXECUTE); - - iriap_call_indication(self, skb); - break; - default: - pr_debug("%s(), unknown event!\n", __func__); - break; - } -} - -/* - * Function state_r_execute (self, event, skb) - * - * The server is processing the request - * - */ -static void state_r_execute(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IAS_MAGIC, return;); - - switch (event) { - case IAP_CALL_RESPONSE: - /* - * Since we don't implement the Waiting state, we return - * to state Receiving instead, DB. - */ - iriap_next_r_connect_state(self, R_RECEIVING); - - /* Don't forget to refcount it - see - * iriap_getvaluebyclass_response(). */ - skb_get(skb); - - irlmp_data_request(self->lsap, skb); - break; - default: - pr_debug("%s(), unknown event!\n", __func__); - break; - } -} - -static void state_r_returning(struct iriap_cb *self, IRIAP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), event=%d\n", __func__, event); - - switch (event) { - case IAP_RECV_F_LST: - break; - default: - break; - } -} diff --git a/net/irda/irias_object.c b/net/irda/irias_object.c deleted file mode 100644 index 53b86d0e1630..000000000000 --- a/net/irda/irias_object.c +++ /dev/null @@ -1,555 +0,0 @@ -/********************************************************************* - * - * Filename: irias_object.c - * Version: 0.3 - * Description: IAS object database and functions - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Oct 1 22:50:04 1998 - * Modified at: Wed Dec 15 11:23:16 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include -#include - -hashbin_t *irias_objects; - -/* - * Used when a missing value needs to be returned - */ -struct ias_value irias_missing = { IAS_MISSING, 0, 0, 0, {0}}; - - -/* - * Function ias_new_object (name, id) - * - * Create a new IAS object - * - */ -struct ias_object *irias_new_object( char *name, int id) -{ - struct ias_object *obj; - - obj = kzalloc(sizeof(struct ias_object), GFP_ATOMIC); - if (obj == NULL) { - net_warn_ratelimited("%s(), Unable to allocate object!\n", - __func__); - return NULL; - } - - obj->magic = IAS_OBJECT_MAGIC; - obj->name = kstrndup(name, IAS_MAX_CLASSNAME, GFP_ATOMIC); - if (!obj->name) { - net_warn_ratelimited("%s(), Unable to allocate name!\n", - __func__); - kfree(obj); - return NULL; - } - obj->id = id; - - /* Locking notes : the attrib spinlock has lower precendence - * than the objects spinlock. Never grap the objects spinlock - * while holding any attrib spinlock (risk of deadlock). Jean II */ - obj->attribs = hashbin_new(HB_LOCK); - - if (obj->attribs == NULL) { - net_warn_ratelimited("%s(), Unable to allocate attribs!\n", - __func__); - kfree(obj->name); - kfree(obj); - return NULL; - } - - return obj; -} -EXPORT_SYMBOL(irias_new_object); - -/* - * Function irias_delete_attrib (attrib) - * - * Delete given attribute and deallocate all its memory - * - */ -static void __irias_delete_attrib(struct ias_attrib *attrib) -{ - IRDA_ASSERT(attrib != NULL, return;); - IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;); - - kfree(attrib->name); - - irias_delete_value(attrib->value); - attrib->magic = ~IAS_ATTRIB_MAGIC; - - kfree(attrib); -} - -void __irias_delete_object(struct ias_object *obj) -{ - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - kfree(obj->name); - - hashbin_delete(obj->attribs, (FREE_FUNC) __irias_delete_attrib); - - obj->magic = ~IAS_OBJECT_MAGIC; - - kfree(obj); -} - -/* - * Function irias_delete_object (obj) - * - * Remove object from hashbin and deallocate all attributes associated with - * with this object and the object itself - * - */ -int irias_delete_object(struct ias_object *obj) -{ - struct ias_object *node; - - IRDA_ASSERT(obj != NULL, return -1;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;); - - /* Remove from list */ - node = hashbin_remove_this(irias_objects, (irda_queue_t *) obj); - if (!node) - pr_debug("%s(), object already removed!\n", - __func__); - - /* Destroy */ - __irias_delete_object(obj); - - return 0; -} -EXPORT_SYMBOL(irias_delete_object); - -/* - * Function irias_delete_attrib (obj) - * - * Remove attribute from hashbin and, if it was the last attribute of - * the object, remove the object as well. - * - */ -int irias_delete_attrib(struct ias_object *obj, struct ias_attrib *attrib, - int cleanobject) -{ - struct ias_attrib *node; - - IRDA_ASSERT(obj != NULL, return -1;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return -1;); - IRDA_ASSERT(attrib != NULL, return -1;); - - /* Remove attribute from object */ - node = hashbin_remove_this(obj->attribs, (irda_queue_t *) attrib); - if (!node) - return 0; /* Already removed or non-existent */ - - /* Deallocate attribute */ - __irias_delete_attrib(node); - - /* Check if object has still some attributes, destroy it if none. - * At first glance, this look dangerous, as the kernel reference - * various IAS objects. However, we only use this function on - * user attributes, not kernel attributes, so there is no risk - * of deleting a kernel object this way. Jean II */ - node = (struct ias_attrib *) hashbin_get_first(obj->attribs); - if (cleanobject && !node) - irias_delete_object(obj); - - return 0; -} - -/* - * Function irias_insert_object (obj) - * - * Insert an object into the LM-IAS database - * - */ -void irias_insert_object(struct ias_object *obj) -{ - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - hashbin_insert(irias_objects, (irda_queue_t *) obj, 0, obj->name); -} -EXPORT_SYMBOL(irias_insert_object); - -/* - * Function irias_find_object (name) - * - * Find object with given name - * - */ -struct ias_object *irias_find_object(char *name) -{ - IRDA_ASSERT(name != NULL, return NULL;); - - /* Unsafe (locking), object might change */ - return hashbin_lock_find(irias_objects, 0, name); -} -EXPORT_SYMBOL(irias_find_object); - -/* - * Function irias_find_attrib (obj, name) - * - * Find named attribute in object - * - */ -struct ias_attrib *irias_find_attrib(struct ias_object *obj, char *name) -{ - struct ias_attrib *attrib; - - IRDA_ASSERT(obj != NULL, return NULL;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return NULL;); - IRDA_ASSERT(name != NULL, return NULL;); - - attrib = hashbin_lock_find(obj->attribs, 0, name); - if (attrib == NULL) - return NULL; - - /* Unsafe (locking), attrib might change */ - return attrib; -} - -/* - * Function irias_add_attribute (obj, attrib) - * - * Add attribute to object - * - */ -static void irias_add_attrib(struct ias_object *obj, struct ias_attrib *attrib, - int owner) -{ - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - IRDA_ASSERT(attrib != NULL, return;); - IRDA_ASSERT(attrib->magic == IAS_ATTRIB_MAGIC, return;); - - /* Set if attrib is owned by kernel or user space */ - attrib->value->owner = owner; - - hashbin_insert(obj->attribs, (irda_queue_t *) attrib, 0, attrib->name); -} - -/* - * Function irias_object_change_attribute (obj_name, attrib_name, new_value) - * - * Change the value of an objects attribute. - * - */ -int irias_object_change_attribute(char *obj_name, char *attrib_name, - struct ias_value *new_value) -{ - struct ias_object *obj; - struct ias_attrib *attrib; - unsigned long flags; - - /* Find object */ - obj = hashbin_lock_find(irias_objects, 0, obj_name); - if (obj == NULL) { - net_warn_ratelimited("%s: Unable to find object: %s\n", - __func__, obj_name); - return -1; - } - - /* Slightly unsafe (obj might get removed under us) */ - spin_lock_irqsave(&obj->attribs->hb_spinlock, flags); - - /* Find attribute */ - attrib = hashbin_find(obj->attribs, 0, attrib_name); - if (attrib == NULL) { - net_warn_ratelimited("%s: Unable to find attribute: %s\n", - __func__, attrib_name); - spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); - return -1; - } - - if ( attrib->value->type != new_value->type) { - pr_debug("%s(), changing value type not allowed!\n", - __func__); - spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); - return -1; - } - - /* Delete old value */ - irias_delete_value(attrib->value); - - /* Insert new value */ - attrib->value = new_value; - - /* Success */ - spin_unlock_irqrestore(&obj->attribs->hb_spinlock, flags); - return 0; -} -EXPORT_SYMBOL(irias_object_change_attribute); - -/* - * Function irias_object_add_integer_attrib (obj, name, value) - * - * Add an integer attribute to an LM-IAS object - * - */ -void irias_add_integer_attrib(struct ias_object *obj, char *name, int value, - int owner) -{ - struct ias_attrib *attrib; - - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - IRDA_ASSERT(name != NULL, return;); - - attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC); - if (attrib == NULL) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - return; - } - - attrib->magic = IAS_ATTRIB_MAGIC; - attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); - - /* Insert value */ - attrib->value = irias_new_integer_value(value); - if (!attrib->name || !attrib->value) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - if (attrib->value) - irias_delete_value(attrib->value); - kfree(attrib->name); - kfree(attrib); - return; - } - - irias_add_attrib(obj, attrib, owner); -} -EXPORT_SYMBOL(irias_add_integer_attrib); - - /* - * Function irias_add_octseq_attrib (obj, name, octet_seq, len) - * - * Add a octet sequence attribute to an LM-IAS object - * - */ - -void irias_add_octseq_attrib(struct ias_object *obj, char *name, __u8 *octets, - int len, int owner) -{ - struct ias_attrib *attrib; - - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - IRDA_ASSERT(name != NULL, return;); - IRDA_ASSERT(octets != NULL, return;); - - attrib = kzalloc(sizeof(struct ias_attrib), GFP_ATOMIC); - if (attrib == NULL) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - return; - } - - attrib->magic = IAS_ATTRIB_MAGIC; - attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); - - attrib->value = irias_new_octseq_value( octets, len); - if (!attrib->name || !attrib->value) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - if (attrib->value) - irias_delete_value(attrib->value); - kfree(attrib->name); - kfree(attrib); - return; - } - - irias_add_attrib(obj, attrib, owner); -} -EXPORT_SYMBOL(irias_add_octseq_attrib); - -/* - * Function irias_object_add_string_attrib (obj, string) - * - * Add a string attribute to an LM-IAS object - * - */ -void irias_add_string_attrib(struct ias_object *obj, char *name, char *value, - int owner) -{ - struct ias_attrib *attrib; - - IRDA_ASSERT(obj != NULL, return;); - IRDA_ASSERT(obj->magic == IAS_OBJECT_MAGIC, return;); - - IRDA_ASSERT(name != NULL, return;); - IRDA_ASSERT(value != NULL, return;); - - attrib = kzalloc(sizeof( struct ias_attrib), GFP_ATOMIC); - if (attrib == NULL) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - return; - } - - attrib->magic = IAS_ATTRIB_MAGIC; - attrib->name = kstrndup(name, IAS_MAX_ATTRIBNAME, GFP_ATOMIC); - - attrib->value = irias_new_string_value(value); - if (!attrib->name || !attrib->value) { - net_warn_ratelimited("%s: Unable to allocate attribute!\n", - __func__); - if (attrib->value) - irias_delete_value(attrib->value); - kfree(attrib->name); - kfree(attrib); - return; - } - - irias_add_attrib(obj, attrib, owner); -} -EXPORT_SYMBOL(irias_add_string_attrib); - -/* - * Function irias_new_integer_value (integer) - * - * Create new IAS integer value - * - */ -struct ias_value *irias_new_integer_value(int integer) -{ - struct ias_value *value; - - value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) - return NULL; - - value->type = IAS_INTEGER; - value->len = 4; - value->t.integer = integer; - - return value; -} -EXPORT_SYMBOL(irias_new_integer_value); - -/* - * Function irias_new_string_value (string) - * - * Create new IAS string value - * - * Per IrLMP 1.1, 4.3.3.2, strings are up to 256 chars - Jean II - */ -struct ias_value *irias_new_string_value(char *string) -{ - struct ias_value *value; - - value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) - return NULL; - - value->type = IAS_STRING; - value->charset = CS_ASCII; - value->t.string = kstrndup(string, IAS_MAX_STRING, GFP_ATOMIC); - if (!value->t.string) { - net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__); - kfree(value); - return NULL; - } - - value->len = strlen(value->t.string); - - return value; -} - -/* - * Function irias_new_octseq_value (octets, len) - * - * Create new IAS octet-sequence value - * - * Per IrLMP 1.1, 4.3.3.2, octet-sequence are up to 1024 bytes - Jean II - */ -struct ias_value *irias_new_octseq_value(__u8 *octseq , int len) -{ - struct ias_value *value; - - value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) - return NULL; - - value->type = IAS_OCT_SEQ; - /* Check length */ - if(len > IAS_MAX_OCTET_STRING) - len = IAS_MAX_OCTET_STRING; - value->len = len; - - value->t.oct_seq = kmemdup(octseq, len, GFP_ATOMIC); - if (value->t.oct_seq == NULL){ - net_warn_ratelimited("%s: Unable to kmalloc!\n", __func__); - kfree(value); - return NULL; - } - return value; -} - -struct ias_value *irias_new_missing_value(void) -{ - struct ias_value *value; - - value = kzalloc(sizeof(struct ias_value), GFP_ATOMIC); - if (value == NULL) - return NULL; - - value->type = IAS_MISSING; - - return value; -} - -/* - * Function irias_delete_value (value) - * - * Delete IAS value - * - */ -void irias_delete_value(struct ias_value *value) -{ - IRDA_ASSERT(value != NULL, return;); - - switch (value->type) { - case IAS_INTEGER: /* Fallthrough */ - case IAS_MISSING: - /* No need to deallocate */ - break; - case IAS_STRING: - /* Deallocate string */ - kfree(value->t.string); - break; - case IAS_OCT_SEQ: - /* Deallocate byte stream */ - kfree(value->t.oct_seq); - break; - default: - pr_debug("%s(), Unknown value type!\n", __func__); - break; - } - kfree(value); -} -EXPORT_SYMBOL(irias_delete_value); diff --git a/net/irda/irlan/Kconfig b/net/irda/irlan/Kconfig deleted file mode 100644 index 951abc2e3a7f..000000000000 --- a/net/irda/irlan/Kconfig +++ /dev/null @@ -1,14 +0,0 @@ -config IRLAN - tristate "IrLAN protocol" - depends on IRDA - help - Say Y here if you want to build support for the IrLAN protocol. - To compile it as a module, choose M here: the module will be called - irlan. IrLAN emulates an Ethernet and makes it possible to put up - a wireless LAN using infrared beams. - - The IrLAN protocol can be used to talk with infrared access points - like the HP NetbeamIR, or the ESI JetEye NET. You can also connect - to another Linux machine running the IrLAN protocol for ad-hoc - networking! - diff --git a/net/irda/irlan/Makefile b/net/irda/irlan/Makefile deleted file mode 100644 index 94eefbc8e6b9..000000000000 --- a/net/irda/irlan/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux IrDA IrLAN protocol layer. -# - -obj-$(CONFIG_IRLAN) += irlan.o - -irlan-y := irlan_common.o irlan_eth.o irlan_event.o irlan_client.o irlan_provider.o irlan_filter.o irlan_provider_event.o irlan_client_event.o diff --git a/net/irda/irlan/irlan_client.c b/net/irda/irlan/irlan_client.c deleted file mode 100644 index c5837a40c78e..000000000000 --- a/net/irda/irlan/irlan_client.c +++ /dev/null @@ -1,559 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_client.c - * Version: 0.9 - * Description: IrDA LAN Access Protocol (IrLAN) Client - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Tue Dec 14 15:47:02 1999 - * Modified by: Dag Brattli - * Sources: skeleton.c by Donald Becker - * slip.c by Laurence Culhane, - * Fred N. van Kempen, - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - -#undef CONFIG_IRLAN_GRATUITOUS_ARP - -static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *); -static int irlan_client_ctrl_data_indication(void *instance, void *sap, - struct sk_buff *skb); -static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *); -static void irlan_check_response_param(struct irlan_cb *self, char *param, - char *value, int val_len); -static void irlan_client_open_ctrl_tsap(struct irlan_cb *self); - -static void irlan_client_kick_timer_expired(void *data) -{ - struct irlan_cb *self = (struct irlan_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* - * If we are in peer mode, the client may not have got the discovery - * indication it needs to make progress. If the client is still in - * IDLE state, we must kick it to, but only if the provider is not IDLE - */ - if ((self->provider.access_type == ACCESS_PEER) && - (self->client.state == IRLAN_IDLE) && - (self->provider.state != IRLAN_IDLE)) { - irlan_client_wakeup(self, self->saddr, self->daddr); - } -} - -static void irlan_client_start_kick_timer(struct irlan_cb *self, int timeout) -{ - irda_start_timer(&self->client.kick_timer, timeout, (void *) self, - irlan_client_kick_timer_expired); -} - -/* - * Function irlan_client_wakeup (self, saddr, daddr) - * - * Wake up client - * - */ -void irlan_client_wakeup(struct irlan_cb *self, __u32 saddr, __u32 daddr) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* - * Check if we are already awake, or if we are a provider in direct - * mode (in that case we must leave the client idle - */ - if ((self->client.state != IRLAN_IDLE) || - (self->provider.access_type == ACCESS_DIRECT)) - { - pr_debug("%s(), already awake!\n", __func__); - return; - } - - /* Addresses may have changed! */ - self->saddr = saddr; - self->daddr = daddr; - - if (self->disconnect_reason == LM_USER_REQUEST) { - pr_debug("%s(), still stopped by user\n", __func__); - return; - } - - /* Open TSAPs */ - irlan_client_open_ctrl_tsap(self); - irlan_open_data_tsap(self); - - irlan_do_client_event(self, IRLAN_DISCOVERY_INDICATION, NULL); - - /* Start kick timer */ - irlan_client_start_kick_timer(self, 2*HZ); -} - -/* - * Function irlan_discovery_indication (daddr) - * - * Remote device with IrLAN server support discovered - * - */ -void irlan_client_discovery_indication(discinfo_t *discovery, - DISCOVERY_MODE mode, - void *priv) -{ - struct irlan_cb *self; - __u32 saddr, daddr; - - IRDA_ASSERT(discovery != NULL, return;); - - /* - * I didn't check it, but I bet that IrLAN suffer from the same - * deficiency as IrComm and doesn't handle two instances - * simultaneously connecting to each other. - * Same workaround, drop passive discoveries. - * Jean II */ - if(mode == DISCOVERY_PASSIVE) - return; - - saddr = discovery->saddr; - daddr = discovery->daddr; - - /* Find instance */ - rcu_read_lock(); - self = irlan_get_any(); - if (self) { - IRDA_ASSERT(self->magic == IRLAN_MAGIC, goto out;); - - pr_debug("%s(), Found instance (%08x)!\n", __func__ , - daddr); - - irlan_client_wakeup(self, saddr, daddr); - } -IRDA_ASSERT_LABEL(out:) - rcu_read_unlock(); -} - -/* - * Function irlan_client_data_indication (handle, skb) - * - * This function gets the data that is received on the control channel - * - */ -static int irlan_client_ctrl_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct irlan_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - irlan_do_client_event(self, IRLAN_DATA_INDICATION, skb); - - /* Ready for a new command */ - pr_debug("%s(), clearing tx_busy\n", __func__); - self->client.tx_busy = FALSE; - - /* Check if we have some queued commands waiting to be sent */ - irlan_run_ctrl_tx_queue(self); - - return 0; -} - -static void irlan_client_ctrl_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *userdata) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - struct sk_buff *skb; - - pr_debug("%s(), reason=%d\n", __func__ , reason); - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - IRDA_ASSERT(tsap != NULL, return;); - IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); - - IRDA_ASSERT(tsap == self->client.tsap_ctrl, return;); - - /* Remove frames queued on the control channel */ - while ((skb = skb_dequeue(&self->client.txq)) != NULL) { - dev_kfree_skb(skb); - } - self->client.tx_busy = FALSE; - - irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); -} - -/* - * Function irlan_client_open_tsaps (self) - * - * Initialize callbacks and open IrTTP TSAPs - * - */ -static void irlan_client_open_ctrl_tsap(struct irlan_cb *self) -{ - struct tsap_cb *tsap; - notify_t notify; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Check if already open */ - if (self->client.tsap_ctrl) - return; - - irda_notify_init(¬ify); - - /* Set up callbacks */ - notify.data_indication = irlan_client_ctrl_data_indication; - notify.connect_confirm = irlan_client_ctrl_connect_confirm; - notify.disconnect_indication = irlan_client_ctrl_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrLAN ctrl (c)", sizeof(notify.name)); - - tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); - if (!tsap) { - pr_debug("%s(), Got no tsap!\n", __func__); - return; - } - self->client.tsap_ctrl = tsap; -} - -/* - * Function irlan_client_connect_confirm (handle, skb) - * - * Connection to peer IrLAN laye confirmed - * - */ -static void irlan_client_ctrl_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct irlan_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - self->client.max_sdu_size = max_sdu_size; - self->client.max_header_size = max_header_size; - - /* TODO: we could set the MTU depending on the max_sdu_size */ - - irlan_do_client_event(self, IRLAN_CONNECT_COMPLETE, NULL); -} - -/* - * Function print_ret_code (code) - * - * Print return code of request to peer IrLAN layer. - * - */ -static void print_ret_code(__u8 code) -{ - switch(code) { - case 0: - printk(KERN_INFO "Success\n"); - break; - case 1: - net_warn_ratelimited("IrLAN: Insufficient resources\n"); - break; - case 2: - net_warn_ratelimited("IrLAN: Invalid command format\n"); - break; - case 3: - net_warn_ratelimited("IrLAN: Command not supported\n"); - break; - case 4: - net_warn_ratelimited("IrLAN: Parameter not supported\n"); - break; - case 5: - net_warn_ratelimited("IrLAN: Value not supported\n"); - break; - case 6: - net_warn_ratelimited("IrLAN: Not open\n"); - break; - case 7: - net_warn_ratelimited("IrLAN: Authentication required\n"); - break; - case 8: - net_warn_ratelimited("IrLAN: Invalid password\n"); - break; - case 9: - net_warn_ratelimited("IrLAN: Protocol error\n"); - break; - case 255: - net_warn_ratelimited("IrLAN: Asynchronous status\n"); - break; - } -} - -/* - * Function irlan_client_parse_response (self, skb) - * - * Extract all parameters from received buffer, then feed them to - * check_params for parsing - */ -void irlan_client_parse_response(struct irlan_cb *self, struct sk_buff *skb) -{ - __u8 *frame; - __u8 *ptr; - int count; - int ret; - __u16 val_len; - int i; - char *name; - char *value; - - IRDA_ASSERT(skb != NULL, return;); - - pr_debug("%s() skb->len=%d\n", __func__ , (int)skb->len); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - if (!skb) { - net_err_ratelimited("%s(), Got NULL skb!\n", __func__); - return; - } - frame = skb->data; - - /* - * Check return code and print it if not success - */ - if (frame[0]) { - print_ret_code(frame[0]); - return; - } - - name = kmalloc(255, GFP_ATOMIC); - if (!name) - return; - value = kmalloc(1016, GFP_ATOMIC); - if (!value) { - kfree(name); - return; - } - - /* How many parameters? */ - count = frame[1]; - - pr_debug("%s(), got %d parameters\n", __func__ , count); - - ptr = frame+2; - - /* For all parameters */ - for (i=0; imagic == IRLAN_MAGIC, return;); - - /* Media type */ - if (strcmp(param, "MEDIA") == 0) { - if (strcmp(value, "802.3") == 0) - self->media = MEDIA_802_3; - else - self->media = MEDIA_802_5; - return; - } - if (strcmp(param, "FILTER_TYPE") == 0) { - if (strcmp(value, "DIRECTED") == 0) - self->client.filter_type |= IRLAN_DIRECTED; - else if (strcmp(value, "FUNCTIONAL") == 0) - self->client.filter_type |= IRLAN_FUNCTIONAL; - else if (strcmp(value, "GROUP") == 0) - self->client.filter_type |= IRLAN_GROUP; - else if (strcmp(value, "MAC_FRAME") == 0) - self->client.filter_type |= IRLAN_MAC_FRAME; - else if (strcmp(value, "MULTICAST") == 0) - self->client.filter_type |= IRLAN_MULTICAST; - else if (strcmp(value, "BROADCAST") == 0) - self->client.filter_type |= IRLAN_BROADCAST; - else if (strcmp(value, "IPX_SOCKET") == 0) - self->client.filter_type |= IRLAN_IPX_SOCKET; - - } - if (strcmp(param, "ACCESS_TYPE") == 0) { - if (strcmp(value, "DIRECT") == 0) - self->client.access_type = ACCESS_DIRECT; - else if (strcmp(value, "PEER") == 0) - self->client.access_type = ACCESS_PEER; - else if (strcmp(value, "HOSTED") == 0) - self->client.access_type = ACCESS_HOSTED; - else { - pr_debug("%s(), unknown access type!\n", __func__); - } - } - /* IRLAN version */ - if (strcmp(param, "IRLAN_VER") == 0) { - pr_debug("IrLAN version %d.%d\n", (__u8)value[0], - (__u8)value[1]); - - self->version[0] = value[0]; - self->version[1] = value[1]; - return; - } - /* Which remote TSAP to use for data channel */ - if (strcmp(param, "DATA_CHAN") == 0) { - self->dtsap_sel_data = value[0]; - pr_debug("Data TSAP = %02x\n", self->dtsap_sel_data); - return; - } - if (strcmp(param, "CON_ARB") == 0) { - memcpy(&tmp_cpu, value, 2); /* Align value */ - le16_to_cpus(&tmp_cpu); /* Convert to host order */ - self->client.recv_arb_val = tmp_cpu; - pr_debug("%s(), receive arb val=%d\n", __func__ , - self->client.recv_arb_val); - } - if (strcmp(param, "MAX_FRAME") == 0) { - memcpy(&tmp_cpu, value, 2); /* Align value */ - le16_to_cpus(&tmp_cpu); /* Convert to host order */ - self->client.max_frame = tmp_cpu; - pr_debug("%s(), max frame=%d\n", __func__ , - self->client.max_frame); - } - - /* RECONNECT_KEY, in case the link goes down! */ - if (strcmp(param, "RECONNECT_KEY") == 0) { - pr_debug("Got reconnect key: "); - /* for (i = 0; i < val_len; i++) */ -/* printk("%02x", value[i]); */ - memcpy(self->client.reconnect_key, value, val_len); - self->client.key_len = val_len; - pr_debug("\n"); - } - /* FILTER_ENTRY, have we got an ethernet address? */ - if (strcmp(param, "FILTER_ENTRY") == 0) { - bytes = value; - pr_debug("Ethernet address = %pM\n", bytes); - for (i = 0; i < 6; i++) - self->dev->dev_addr[i] = bytes[i]; - } -} - -/* - * Function irlan_client_get_value_confirm (obj_id, value) - * - * Got results from remote LM-IAS - * - */ -void irlan_client_get_value_confirm(int result, __u16 obj_id, - struct ias_value *value, void *priv) -{ - struct irlan_cb *self; - - IRDA_ASSERT(priv != NULL, return;); - - self = priv; - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* We probably don't need to make any more queries */ - iriap_close(self->client.iriap); - self->client.iriap = NULL; - - /* Check if request succeeded */ - if (result != IAS_SUCCESS) { - pr_debug("%s(), got NULL value!\n", __func__); - irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, - NULL); - return; - } - - switch (value->type) { - case IAS_INTEGER: - self->dtsap_sel_ctrl = value->t.integer; - - if (value->t.integer != -1) { - irlan_do_client_event(self, IRLAN_IAS_PROVIDER_AVAIL, - NULL); - return; - } - irias_delete_value(value); - break; - default: - pr_debug("%s(), unknown type!\n", __func__); - break; - } - irlan_do_client_event(self, IRLAN_IAS_PROVIDER_NOT_AVAIL, NULL); -} diff --git a/net/irda/irlan/irlan_client_event.c b/net/irda/irlan/irlan_client_event.c deleted file mode 100644 index cc93fabbbb19..000000000000 --- a/net/irda/irlan/irlan_client_event.c +++ /dev/null @@ -1,511 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_client_event.c - * Version: 0.9 - * Description: IrLAN client state machine - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sun Dec 26 21:52:24 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static int irlan_client_state_idle (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_conn (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_info (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_open (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_wait (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_arb (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_data (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_client_state_sync (struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); - -static int (*state[])(struct irlan_cb *, IRLAN_EVENT event, struct sk_buff *) = -{ - irlan_client_state_idle, - irlan_client_state_query, - irlan_client_state_conn, - irlan_client_state_info, - irlan_client_state_media, - irlan_client_state_open, - irlan_client_state_wait, - irlan_client_state_arb, - irlan_client_state_data, - irlan_client_state_close, - irlan_client_state_sync -}; - -void irlan_do_client_event(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - (*state[ self->client.state]) (self, event, skb); -} - -/* - * Function irlan_client_state_idle (event, skb, info) - * - * IDLE, We are waiting for an indication that there is a provider - * available. - */ -static int irlan_client_state_idle(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - switch (event) { - case IRLAN_DISCOVERY_INDICATION: - if (self->client.iriap) { - net_warn_ratelimited("%s(), busy with a previous query\n", - __func__); - return -EBUSY; - } - - self->client.iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irlan_client_get_value_confirm); - /* Get some values from peer IAS */ - irlan_next_client_state(self, IRLAN_QUERY); - iriap_getvaluebyclass_request(self->client.iriap, - self->saddr, self->daddr, - "IrLAN", "IrDA:TinyTP:LsapSel"); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_query (event, skb, info) - * - * QUERY, We have queryed the remote IAS and is ready to connect - * to provider, just waiting for the confirm. - * - */ -static int irlan_client_state_query(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - switch(event) { - case IRLAN_IAS_PROVIDER_AVAIL: - IRDA_ASSERT(self->dtsap_sel_ctrl != 0, return -1;); - - self->client.open_retries = 0; - - irttp_connect_request(self->client.tsap_ctrl, - self->dtsap_sel_ctrl, - self->saddr, self->daddr, NULL, - IRLAN_MTU, NULL); - irlan_next_client_state(self, IRLAN_CONN); - break; - case IRLAN_IAS_PROVIDER_NOT_AVAIL: - pr_debug("%s(), IAS_PROVIDER_NOT_AVAIL\n", __func__); - irlan_next_client_state(self, IRLAN_IDLE); - - /* Give the client a kick! */ - if ((self->provider.access_type == ACCESS_PEER) && - (self->provider.state != IRLAN_IDLE)) - irlan_client_wakeup(self, self->saddr, self->daddr); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_conn (event, skb, info) - * - * CONN, We have connected to a provider but has not issued any - * commands yet. - * - */ -static int irlan_client_state_conn(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch (event) { - case IRLAN_CONNECT_COMPLETE: - /* Send getinfo cmd */ - irlan_get_provider_info(self); - irlan_next_client_state(self, IRLAN_INFO); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_info (self, event, skb, info) - * - * INFO, We have issued a GetInfo command and is awaiting a reply. - */ -static int irlan_client_state_info(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch (event) { - case IRLAN_DATA_INDICATION: - IRDA_ASSERT(skb != NULL, return -1;); - - irlan_client_parse_response(self, skb); - - irlan_next_client_state(self, IRLAN_MEDIA); - - irlan_get_media_char(self); - break; - - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_media (self, event, skb, info) - * - * MEDIA, The irlan_client has issued a GetMedia command and is awaiting a - * reply. - * - */ -static int irlan_client_state_media(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_DATA_INDICATION: - irlan_client_parse_response(self, skb); - irlan_open_data_channel(self); - irlan_next_client_state(self, IRLAN_OPEN); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_open (self, event, skb, info) - * - * OPEN, The irlan_client has issued a OpenData command and is awaiting a - * reply - * - */ -static int irlan_client_state_open(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - struct qos_info qos; - - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_DATA_INDICATION: - irlan_client_parse_response(self, skb); - - /* - * Check if we have got the remote TSAP for data - * communications - */ - IRDA_ASSERT(self->dtsap_sel_data != 0, return -1;); - - /* Check which access type we are dealing with */ - switch (self->client.access_type) { - case ACCESS_PEER: - if (self->provider.state == IRLAN_OPEN) { - - irlan_next_client_state(self, IRLAN_ARB); - irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, - NULL); - } else { - - irlan_next_client_state(self, IRLAN_WAIT); - } - break; - case ACCESS_DIRECT: - case ACCESS_HOSTED: - qos.link_disc_time.bits = 0x01; /* 3 secs */ - - irttp_connect_request(self->tsap_data, - self->dtsap_sel_data, - self->saddr, self->daddr, &qos, - IRLAN_MTU, NULL); - - irlan_next_client_state(self, IRLAN_DATA); - break; - default: - pr_debug("%s(), unknown access type!\n", __func__); - break; - } - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_wait (self, event, skb, info) - * - * WAIT, The irlan_client is waiting for the local provider to enter the - * provider OPEN state. - * - */ -static int irlan_client_state_wait(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_PROVIDER_SIGNAL: - irlan_next_client_state(self, IRLAN_ARB); - irlan_do_client_event(self, IRLAN_CHECK_CON_ARB, NULL); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -static int irlan_client_state_arb(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - struct qos_info qos; - - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_CHECK_CON_ARB: - if (self->client.recv_arb_val == self->provider.send_arb_val) { - irlan_next_client_state(self, IRLAN_CLOSE); - irlan_close_data_channel(self); - } else if (self->client.recv_arb_val < - self->provider.send_arb_val) - { - qos.link_disc_time.bits = 0x01; /* 3 secs */ - - irlan_next_client_state(self, IRLAN_DATA); - irttp_connect_request(self->tsap_data, - self->dtsap_sel_data, - self->saddr, self->daddr, &qos, - IRLAN_MTU, NULL); - } else if (self->client.recv_arb_val > - self->provider.send_arb_val) - { - pr_debug("%s(), lost the battle :-(\n", __func__); - } - break; - case IRLAN_DATA_CONNECT_INDICATION: - irlan_next_client_state(self, IRLAN_DATA); - break; - case IRLAN_LMP_DISCONNECT: - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - case IRLAN_WATCHDOG_TIMEOUT: - pr_debug("%s(), IRLAN_WATCHDOG_TIMEOUT\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_data (self, event, skb, info) - * - * DATA, The data channel is connected, allowing data transfers between - * the local and remote machines. - * - */ -static int irlan_client_state_data(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - switch(event) { - case IRLAN_DATA_INDICATION: - irlan_client_parse_response(self, skb); - break; - case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ - case IRLAN_LAP_DISCONNECT: - irlan_next_client_state(self, IRLAN_IDLE); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_close (self, event, skb, info) - * - * - * - */ -static int irlan_client_state_close(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_client_state_sync (self, event, skb, info) - * - * - * - */ -static int irlan_client_state_sync(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - if (skb) - dev_kfree_skb(skb); - - return 0; -} - - - - - - - - - - - - - diff --git a/net/irda/irlan/irlan_common.c b/net/irda/irlan/irlan_common.c deleted file mode 100644 index 481bbc2a4349..000000000000 --- a/net/irda/irlan/irlan_common.c +++ /dev/null @@ -1,1176 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_common.c - * Version: 0.9 - * Description: IrDA LAN Access Protocol Implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sun Dec 26 21:53:10 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include - - -/* extern char sysctl_devname[]; */ - -/* - * Master structure - */ -static LIST_HEAD(irlans); - -static void *ckey; -static void *skey; - -/* Module parameters */ -static bool eth; /* Use "eth" or "irlan" name for devices */ -static int access = ACCESS_PEER; /* PEER, DIRECT or HOSTED */ - -#ifdef CONFIG_PROC_FS -static const char *const irlan_access[] = { - "UNKNOWN", - "DIRECT", - "PEER", - "HOSTED" -}; - -static const char *const irlan_media[] = { - "UNKNOWN", - "802.3", - "802.5" -}; - -extern struct proc_dir_entry *proc_irda; - -static int irlan_seq_open(struct inode *inode, struct file *file); - -static const struct file_operations irlan_fops = { - .owner = THIS_MODULE, - .open = irlan_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release, -}; - -extern struct proc_dir_entry *proc_irda; -#endif /* CONFIG_PROC_FS */ - -static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr); -static void __irlan_close(struct irlan_cb *self); -static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, - __u8 value_byte, __u16 value_short, - __u8 *value_array, __u16 value_len); -static void irlan_open_unicast_addr(struct irlan_cb *self); -static void irlan_get_unicast_addr(struct irlan_cb *self); -void irlan_close_tsaps(struct irlan_cb *self); - -/* - * Function irlan_init (void) - * - * Initialize IrLAN layer - * - */ -static int __init irlan_init(void) -{ - struct irlan_cb *new; - __u16 hints; - -#ifdef CONFIG_PROC_FS - { struct proc_dir_entry *proc; - proc = proc_create("irlan", 0, proc_irda, &irlan_fops); - if (!proc) { - printk(KERN_ERR "irlan_init: can't create /proc entry!\n"); - return -ENODEV; - } - } -#endif /* CONFIG_PROC_FS */ - - hints = irlmp_service_to_hint(S_LAN); - - /* Register with IrLMP as a client */ - ckey = irlmp_register_client(hints, &irlan_client_discovery_indication, - NULL, NULL); - if (!ckey) - goto err_ckey; - - /* Register with IrLMP as a service */ - skey = irlmp_register_service(hints); - if (!skey) - goto err_skey; - - /* Start the master IrLAN instance (the only one for now) */ - new = irlan_open(DEV_ADDR_ANY, DEV_ADDR_ANY); - if (!new) - goto err_open; - - /* The master will only open its (listen) control TSAP */ - irlan_provider_open_ctrl_tsap(new); - - /* Do some fast discovery! */ - irlmp_discovery_request(DISCOVERY_DEFAULT_SLOTS); - - return 0; - -err_open: - irlmp_unregister_service(skey); -err_skey: - irlmp_unregister_client(ckey); -err_ckey: -#ifdef CONFIG_PROC_FS - remove_proc_entry("irlan", proc_irda); -#endif /* CONFIG_PROC_FS */ - - return -ENOMEM; -} - -static void __exit irlan_cleanup(void) -{ - struct irlan_cb *self, *next; - - irlmp_unregister_client(ckey); - irlmp_unregister_service(skey); - -#ifdef CONFIG_PROC_FS - remove_proc_entry("irlan", proc_irda); -#endif /* CONFIG_PROC_FS */ - - /* Cleanup any leftover network devices */ - rtnl_lock(); - list_for_each_entry_safe(self, next, &irlans, dev_list) { - __irlan_close(self); - } - rtnl_unlock(); -} - -/* - * Function irlan_open (void) - * - * Open new instance of a client/provider, we should only register the - * network device if this instance is ment for a particular client/provider - */ -static struct irlan_cb __init *irlan_open(__u32 saddr, __u32 daddr) -{ - struct net_device *dev; - struct irlan_cb *self; - - /* Create network device with irlan */ - dev = alloc_irlandev(eth ? "eth%d" : "irlan%d"); - if (!dev) - return NULL; - - self = netdev_priv(dev); - self->dev = dev; - - /* - * Initialize local device structure - */ - self->magic = IRLAN_MAGIC; - self->saddr = saddr; - self->daddr = daddr; - - /* Provider access can only be PEER, DIRECT, or HOSTED */ - self->provider.access_type = access; - if (access == ACCESS_DIRECT) { - /* - * Since we are emulating an IrLAN sever we will have to - * give ourself an ethernet address! - */ - dev->dev_addr[0] = 0x40; - dev->dev_addr[1] = 0x00; - dev->dev_addr[2] = 0x00; - dev->dev_addr[3] = 0x00; - get_random_bytes(dev->dev_addr+4, 1); - get_random_bytes(dev->dev_addr+5, 1); - } - - self->media = MEDIA_802_3; - self->disconnect_reason = LM_USER_REQUEST; - init_timer(&self->watchdog_timer); - init_timer(&self->client.kick_timer); - init_waitqueue_head(&self->open_wait); - - skb_queue_head_init(&self->client.txq); - - irlan_next_client_state(self, IRLAN_IDLE); - irlan_next_provider_state(self, IRLAN_IDLE); - - if (register_netdev(dev)) { - pr_debug("%s(), register_netdev() failed!\n", - __func__); - self = NULL; - free_netdev(dev); - } else { - rtnl_lock(); - list_add_rcu(&self->dev_list, &irlans); - rtnl_unlock(); - } - - return self; -} -/* - * Function __irlan_close (self) - * - * This function closes and deallocates the IrLAN client instances. Be - * aware that other functions which calls client_close() must - * remove self from irlans list first. - */ -static void __irlan_close(struct irlan_cb *self) -{ - ASSERT_RTNL(); - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - del_timer_sync(&self->watchdog_timer); - del_timer_sync(&self->client.kick_timer); - - /* Close all open connections and remove TSAPs */ - irlan_close_tsaps(self); - - if (self->client.iriap) - iriap_close(self->client.iriap); - - /* Remove frames queued on the control channel */ - skb_queue_purge(&self->client.txq); - - /* Unregister and free self via destructor */ - unregister_netdevice(self->dev); -} - -/* Find any instance of irlan, used for client discovery wakeup */ -struct irlan_cb *irlan_get_any(void) -{ - struct irlan_cb *self; - - list_for_each_entry_rcu(self, &irlans, dev_list) { - return self; - } - return NULL; -} - -/* - * Function irlan_connect_indication (instance, sap, qos, max_sdu_size, skb) - * - * Here we receive the connect indication for the data channel - * - */ -static void irlan_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - IRDA_ASSERT(tsap == self->tsap_data,return;); - - self->max_sdu_size = max_sdu_size; - self->max_header_size = max_header_size; - - pr_debug("%s: We are now connected!\n", __func__); - - del_timer(&self->watchdog_timer); - - /* If you want to pass the skb to *both* state machines, you will - * need to skb_clone() it, so that you don't free it twice. - * As the state machines don't need it, git rid of it here... - * Jean II */ - if (skb) - dev_kfree_skb(skb); - - irlan_do_provider_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); - irlan_do_client_event(self, IRLAN_DATA_CONNECT_INDICATION, NULL); - - if (self->provider.access_type == ACCESS_PEER) { - /* - * Data channel is open, so we are now allowed to - * configure the remote filter - */ - irlan_get_unicast_addr(self); - irlan_open_unicast_addr(self); - } - /* Ready to transfer Ethernet frames (at last) */ - netif_start_queue(self->dev); /* Clear reason */ -} - -static void irlan_connect_confirm(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct irlan_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - self->max_sdu_size = max_sdu_size; - self->max_header_size = max_header_size; - - /* TODO: we could set the MTU depending on the max_sdu_size */ - - pr_debug("%s: We are now connected!\n", __func__); - del_timer(&self->watchdog_timer); - - /* - * Data channel is open, so we are now allowed to configure the remote - * filter - */ - irlan_get_unicast_addr(self); - irlan_open_unicast_addr(self); - - /* Open broadcast and multicast filter by default */ - irlan_set_broadcast_filter(self, TRUE); - irlan_set_multicast_filter(self, TRUE); - - /* Ready to transfer Ethernet frames */ - netif_start_queue(self->dev); - self->disconnect_reason = 0; /* Clear reason */ - wake_up_interruptible(&self->open_wait); -} - -/* - * Function irlan_client_disconnect_indication (handle) - * - * Callback function for the IrTTP layer. Indicates a disconnection of - * the specified connection (handle) - */ -static void irlan_disconnect_indication(void *instance, - void *sap, LM_REASON reason, - struct sk_buff *userdata) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - - pr_debug("%s(), reason=%d\n", __func__ , reason); - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - IRDA_ASSERT(tsap != NULL, return;); - IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); - - IRDA_ASSERT(tsap == self->tsap_data, return;); - - pr_debug("IrLAN, data channel disconnected by peer!\n"); - - /* Save reason so we know if we should try to reconnect or not */ - self->disconnect_reason = reason; - - switch (reason) { - case LM_USER_REQUEST: /* User request */ - pr_debug("%s(), User requested\n", __func__); - break; - case LM_LAP_DISCONNECT: /* Unexpected IrLAP disconnect */ - pr_debug("%s(), Unexpected IrLAP disconnect\n", __func__); - break; - case LM_CONNECT_FAILURE: /* Failed to establish IrLAP connection */ - pr_debug("%s(), IrLAP connect failed\n", __func__); - break; - case LM_LAP_RESET: /* IrLAP reset */ - pr_debug("%s(), IrLAP reset\n", __func__); - break; - case LM_INIT_DISCONNECT: - pr_debug("%s(), IrLMP connect failed\n", __func__); - break; - default: - net_err_ratelimited("%s(), Unknown disconnect reason\n", - __func__); - break; - } - - /* If you want to pass the skb to *both* state machines, you will - * need to skb_clone() it, so that you don't free it twice. - * As the state machines don't need it, git rid of it here... - * Jean II */ - if (userdata) - dev_kfree_skb(userdata); - - irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); - irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); - - wake_up_interruptible(&self->open_wait); -} - -void irlan_open_data_tsap(struct irlan_cb *self) -{ - struct tsap_cb *tsap; - notify_t notify; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Check if already open */ - if (self->tsap_data) - return; - - irda_notify_init(¬ify); - - notify.data_indication = irlan_eth_receive; - notify.udata_indication = irlan_eth_receive; - notify.connect_indication = irlan_connect_indication; - notify.connect_confirm = irlan_connect_confirm; - notify.flow_indication = irlan_eth_flow_indication; - notify.disconnect_indication = irlan_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrLAN data", sizeof(notify.name)); - - tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, ¬ify); - if (!tsap) { - pr_debug("%s(), Got no tsap!\n", __func__); - return; - } - self->tsap_data = tsap; - - /* - * This is the data TSAP selector which we will pass to the client - * when the client ask for it. - */ - self->stsap_sel_data = self->tsap_data->stsap_sel; -} - -void irlan_close_tsaps(struct irlan_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Disconnect and close all open TSAP connections */ - if (self->tsap_data) { - irttp_disconnect_request(self->tsap_data, NULL, P_NORMAL); - irttp_close_tsap(self->tsap_data); - self->tsap_data = NULL; - } - if (self->client.tsap_ctrl) { - irttp_disconnect_request(self->client.tsap_ctrl, NULL, - P_NORMAL); - irttp_close_tsap(self->client.tsap_ctrl); - self->client.tsap_ctrl = NULL; - } - if (self->provider.tsap_ctrl) { - irttp_disconnect_request(self->provider.tsap_ctrl, NULL, - P_NORMAL); - irttp_close_tsap(self->provider.tsap_ctrl); - self->provider.tsap_ctrl = NULL; - } - self->disconnect_reason = LM_USER_REQUEST; -} - -/* - * Function irlan_ias_register (self, tsap_sel) - * - * Register with LM-IAS - * - */ -void irlan_ias_register(struct irlan_cb *self, __u8 tsap_sel) -{ - struct ias_object *obj; - struct ias_value *new_value; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* - * Check if object has already been registered by a previous provider. - * If that is the case, we just change the value of the attribute - */ - if (!irias_find_object("IrLAN")) { - obj = irias_new_object("IrLAN", IAS_IRLAN_ID); - irias_add_integer_attrib(obj, "IrDA:TinyTP:LsapSel", tsap_sel, - IAS_KERNEL_ATTR); - irias_insert_object(obj); - } else { - new_value = irias_new_integer_value(tsap_sel); - irias_object_change_attribute("IrLAN", "IrDA:TinyTP:LsapSel", - new_value); - } - - /* Register PnP object only if not registered before */ - if (!irias_find_object("PnP")) { - obj = irias_new_object("PnP", IAS_PNP_ID); -#if 0 - irias_add_string_attrib(obj, "Name", sysctl_devname, - IAS_KERNEL_ATTR); -#else - irias_add_string_attrib(obj, "Name", "Linux", IAS_KERNEL_ATTR); -#endif - irias_add_string_attrib(obj, "DeviceID", "HWP19F0", - IAS_KERNEL_ATTR); - irias_add_integer_attrib(obj, "CompCnt", 1, IAS_KERNEL_ATTR); - if (self->provider.access_type == ACCESS_PEER) - irias_add_string_attrib(obj, "Comp#01", "PNP8389", - IAS_KERNEL_ATTR); - else - irias_add_string_attrib(obj, "Comp#01", "PNP8294", - IAS_KERNEL_ATTR); - - irias_add_string_attrib(obj, "Manufacturer", - "Linux-IrDA Project", IAS_KERNEL_ATTR); - irias_insert_object(obj); - } -} - -/* - * Function irlan_run_ctrl_tx_queue (self) - * - * Try to send the next command in the control transmit queue - * - */ -int irlan_run_ctrl_tx_queue(struct irlan_cb *self) -{ - struct sk_buff *skb; - - if (irda_lock(&self->client.tx_busy) == FALSE) - return -EBUSY; - - skb = skb_dequeue(&self->client.txq); - if (!skb) { - self->client.tx_busy = FALSE; - return 0; - } - - /* Check that it's really possible to send commands */ - if ((self->client.tsap_ctrl == NULL) || - (self->client.state == IRLAN_IDLE)) - { - self->client.tx_busy = FALSE; - dev_kfree_skb(skb); - return -1; - } - pr_debug("%s(), sending ...\n", __func__); - - return irttp_data_request(self->client.tsap_ctrl, skb); -} - -/* - * Function irlan_ctrl_data_request (self, skb) - * - * This function makes sure that commands on the control channel is being - * sent in a command/response fashion - */ -static void irlan_ctrl_data_request(struct irlan_cb *self, struct sk_buff *skb) -{ - /* Queue command */ - skb_queue_tail(&self->client.txq, skb); - - /* Try to send command */ - irlan_run_ctrl_tx_queue(self); -} - -/* - * Function irlan_get_provider_info (self) - * - * Send Get Provider Information command to peer IrLAN layer - * - */ -void irlan_get_provider_info(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER, - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_GET_PROVIDER_INFO; - frame[1] = 0x00; /* Zero parameters */ - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_open_data_channel (self) - * - * Send an Open Data Command to provider - * - */ -void irlan_open_data_channel(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3") + - IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "DIRECT"), - GFP_ATOMIC); - if (!skb) - return; - - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - /* Build frame */ - frame[0] = CMD_OPEN_DATA_CHANNEL; - frame[1] = 0x02; /* Two parameters */ - - irlan_insert_string_param(skb, "MEDIA", "802.3"); - irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT"); - /* irlan_insert_string_param(skb, "MODE", "UNRELIABLE"); */ - -/* self->use_udata = TRUE; */ - - irlan_ctrl_data_request(self, skb); -} - -void irlan_close_data_channel(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Check if the TSAP is still there */ - if (self->client.tsap_ctrl == NULL) - return; - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN"), - GFP_ATOMIC); - if (!skb) - return; - - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - /* Build frame */ - frame[0] = CMD_CLOSE_DATA_CHAN; - frame[1] = 0x01; /* One parameter */ - - irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_open_unicast_addr (self) - * - * Make IrLAN provider accept ethernet frames addressed to the unicast - * address. - * - */ -static void irlan_open_unicast_addr(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + - IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"), - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_FILTER_OPERATION; - frame[1] = 0x03; /* Three parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN" , self->dtsap_sel_data); - irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); - irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_set_broadcast_filter (self, status) - * - * Make IrLAN provider accept ethernet frames addressed to the broadcast - * address. Be careful with the use of this one, since there may be a lot - * of broadcast traffic out there. We can still function without this - * one but then _we_ have to initiate all communication with other - * hosts, since ARP request for this host will not be answered. - */ -void irlan_set_broadcast_filter(struct irlan_cb *self, int status) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") + - /* We may waste one byte here...*/ - IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "FILTER"), - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_FILTER_OPERATION; - frame[1] = 0x03; /* Three parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); - irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST"); - if (status) - irlan_insert_string_param(skb, "FILTER_MODE", "FILTER"); - else - irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_set_multicast_filter (self, status) - * - * Make IrLAN provider accept ethernet frames addressed to the multicast - * address. - * - */ -void irlan_set_multicast_filter(struct irlan_cb *self, int status) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") + - /* We may waste one byte here...*/ - IRLAN_STRING_PARAMETER_LEN("FILTER_MODE", "NONE"), - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_FILTER_OPERATION; - frame[1] = 0x03; /* Three parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); - irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST"); - if (status) - irlan_insert_string_param(skb, "FILTER_MODE", "ALL"); - else - irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_get_unicast_addr (self) - * - * Retrieves the unicast address from the IrLAN provider. This address - * will be inserted into the devices structure, so the ethernet layer - * can construct its packets. - * - */ -static void irlan_get_unicast_addr(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_BYTE_PARAMETER_LEN("DATA_CHAN") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + - IRLAN_STRING_PARAMETER_LEN("FILTER_OPERATION", - "DYNAMIC"), - GFP_ATOMIC); - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - frame[0] = CMD_FILTER_OPERATION; - frame[1] = 0x03; /* Three parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN", self->dtsap_sel_data); - irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); - irlan_insert_string_param(skb, "FILTER_OPERATION", "DYNAMIC"); - - irlan_ctrl_data_request(self, skb); -} - -/* - * Function irlan_get_media_char (self) - * - * - * - */ -void irlan_get_media_char(struct irlan_cb *self) -{ - struct sk_buff *skb; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - IRLAN_STRING_PARAMETER_LEN("MEDIA", "802.3"), - GFP_ATOMIC); - - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->client.max_header_size); - skb_put(skb, 2); - - frame = skb->data; - - /* Build frame */ - frame[0] = CMD_GET_MEDIA_CHAR; - frame[1] = 0x01; /* One parameter */ - - irlan_insert_string_param(skb, "MEDIA", "802.3"); - irlan_ctrl_data_request(self, skb); -} - -/* - * Function insert_byte_param (skb, param, value) - * - * Insert byte parameter into frame - * - */ -int irlan_insert_byte_param(struct sk_buff *skb, char *param, __u8 value) -{ - return __irlan_insert_param(skb, param, IRLAN_BYTE, value, 0, NULL, 0); -} - -int irlan_insert_short_param(struct sk_buff *skb, char *param, __u16 value) -{ - return __irlan_insert_param(skb, param, IRLAN_SHORT, 0, value, NULL, 0); -} - -/* - * Function insert_string (skb, param, value) - * - * Insert string parameter into frame - * - */ -int irlan_insert_string_param(struct sk_buff *skb, char *param, char *string) -{ - int string_len = strlen(string); - - return __irlan_insert_param(skb, param, IRLAN_ARRAY, 0, 0, string, - string_len); -} - -/* - * Function insert_array_param(skb, param, value, len_value) - * - * Insert array parameter into frame - * - */ -int irlan_insert_array_param(struct sk_buff *skb, char *name, __u8 *array, - __u16 array_len) -{ - return __irlan_insert_param(skb, name, IRLAN_ARRAY, 0, 0, array, - array_len); -} - -/* - * Function insert_param (skb, param, value, byte) - * - * Insert parameter at end of buffer, structure of a parameter is: - * - * ----------------------------------------------------------------------- - * | Name Length[1] | Param Name[1..255] | Val Length[2] | Value[0..1016]| - * ----------------------------------------------------------------------- - */ -static int __irlan_insert_param(struct sk_buff *skb, char *param, int type, - __u8 value_byte, __u16 value_short, - __u8 *value_array, __u16 value_len) -{ - __u8 *frame; - __u8 param_len; - __le16 tmp_le; /* Temporary value in little endian format */ - int n=0; - - if (skb == NULL) { - pr_debug("%s(), Got NULL skb\n", __func__); - return 0; - } - - param_len = strlen(param); - switch (type) { - case IRLAN_BYTE: - value_len = 1; - break; - case IRLAN_SHORT: - value_len = 2; - break; - case IRLAN_ARRAY: - IRDA_ASSERT(value_array != NULL, return 0;); - IRDA_ASSERT(value_len > 0, return 0;); - break; - default: - pr_debug("%s(), Unknown parameter type!\n", __func__); - return 0; - } - - /* Insert at end of sk-buffer */ - frame = skb_tail_pointer(skb); - - /* Make space for data */ - if (skb_tailroom(skb) < (param_len+value_len+3)) { - pr_debug("%s(), No more space at end of skb\n", __func__); - return 0; - } - skb_put(skb, param_len+value_len+3); - - /* Insert parameter length */ - frame[n++] = param_len; - - /* Insert parameter */ - memcpy(frame+n, param, param_len); n += param_len; - - /* Insert value length (2 byte little endian format, LSB first) */ - tmp_le = cpu_to_le16(value_len); - memcpy(frame+n, &tmp_le, 2); n += 2; /* To avoid alignment problems */ - - /* Insert value */ - switch (type) { - case IRLAN_BYTE: - frame[n++] = value_byte; - break; - case IRLAN_SHORT: - tmp_le = cpu_to_le16(value_short); - memcpy(frame+n, &tmp_le, 2); n += 2; - break; - case IRLAN_ARRAY: - memcpy(frame+n, value_array, value_len); n+=value_len; - break; - default: - break; - } - IRDA_ASSERT(n == (param_len+value_len+3), return 0;); - - return param_len+value_len+3; -} - -/* - * Function irlan_extract_param (buf, name, value, len) - * - * Extracts a single parameter name/value pair from buffer and updates - * the buffer pointer to point to the next name/value pair. - */ -int irlan_extract_param(__u8 *buf, char *name, char *value, __u16 *len) -{ - __u8 name_len; - __u16 val_len; - int n=0; - - /* get length of parameter name (1 byte) */ - name_len = buf[n++]; - - if (name_len > 254) { - pr_debug("%s(), name_len > 254\n", __func__); - return -RSP_INVALID_COMMAND_FORMAT; - } - - /* get parameter name */ - memcpy(name, buf+n, name_len); - name[name_len] = '\0'; - n+=name_len; - - /* - * Get length of parameter value (2 bytes in little endian - * format) - */ - memcpy(&val_len, buf+n, 2); /* To avoid alignment problems */ - le16_to_cpus(&val_len); n+=2; - - if (val_len >= 1016) { - pr_debug("%s(), parameter length to long\n", __func__); - return -RSP_INVALID_COMMAND_FORMAT; - } - *len = val_len; - - /* get parameter value */ - memcpy(value, buf+n, val_len); - value[val_len] = '\0'; - n+=val_len; - - pr_debug("Parameter: %s ", name); - pr_debug("Value: %s\n", value); - - return n; -} - -#ifdef CONFIG_PROC_FS - -/* - * Start of reading /proc entries. - * Return entry at pos, - * or start_token to indicate print header line - * or NULL if end of file - */ -static void *irlan_seq_start(struct seq_file *seq, loff_t *pos) -{ - rcu_read_lock(); - return seq_list_start_head(&irlans, *pos); -} - -/* Return entry after v, and increment pos */ -static void *irlan_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - return seq_list_next(v, &irlans, pos); -} - -/* End of reading /proc file */ -static void irlan_seq_stop(struct seq_file *seq, void *v) -{ - rcu_read_unlock(); -} - - -/* - * Show one entry in /proc file. - */ -static int irlan_seq_show(struct seq_file *seq, void *v) -{ - if (v == &irlans) - seq_puts(seq, "IrLAN instances:\n"); - else { - struct irlan_cb *self = list_entry(v, struct irlan_cb, dev_list); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - seq_printf(seq,"ifname: %s,\n", - self->dev->name); - seq_printf(seq,"client state: %s, ", - irlan_state[ self->client.state]); - seq_printf(seq,"provider state: %s,\n", - irlan_state[ self->provider.state]); - seq_printf(seq,"saddr: %#08x, ", - self->saddr); - seq_printf(seq,"daddr: %#08x\n", - self->daddr); - seq_printf(seq,"version: %d.%d,\n", - self->version[1], self->version[0]); - seq_printf(seq,"access type: %s\n", - irlan_access[self->client.access_type]); - seq_printf(seq,"media: %s\n", - irlan_media[self->media]); - - seq_printf(seq,"local filter:\n"); - seq_printf(seq,"remote filter: "); - irlan_print_filter(seq, self->client.filter_type); - seq_printf(seq,"tx busy: %s\n", - netif_queue_stopped(self->dev) ? "TRUE" : "FALSE"); - - seq_putc(seq,'\n'); - } - return 0; -} - -static const struct seq_operations irlan_seq_ops = { - .start = irlan_seq_start, - .next = irlan_seq_next, - .stop = irlan_seq_stop, - .show = irlan_seq_show, -}; - -static int irlan_seq_open(struct inode *inode, struct file *file) -{ - return seq_open(file, &irlan_seq_ops); -} -#endif - -MODULE_AUTHOR("Dag Brattli "); -MODULE_DESCRIPTION("The Linux IrDA LAN protocol"); -MODULE_LICENSE("GPL"); - -module_param(eth, bool, 0); -MODULE_PARM_DESC(eth, "Name devices ethX (0) or irlanX (1)"); -module_param(access, int, 0); -MODULE_PARM_DESC(access, "Access type DIRECT=1, PEER=2, HOSTED=3"); - -module_init(irlan_init); -module_exit(irlan_cleanup); - diff --git a/net/irda/irlan/irlan_eth.c b/net/irda/irlan/irlan_eth.c deleted file mode 100644 index 3be852808a9d..000000000000 --- a/net/irda/irlan/irlan_eth.c +++ /dev/null @@ -1,340 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_eth.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Thu Oct 15 08:37:58 1998 - * Modified at: Tue Mar 21 09:06:41 2000 - * Modified by: Dag Brattli - * Sources: skeleton.c by Donald Becker - * slip.c by Laurence Culhane, - * Fred N. van Kempen, - * - * Copyright (c) 1998-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static int irlan_eth_open(struct net_device *dev); -static int irlan_eth_close(struct net_device *dev); -static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb, - struct net_device *dev); -static void irlan_eth_set_multicast_list(struct net_device *dev); - -static const struct net_device_ops irlan_eth_netdev_ops = { - .ndo_open = irlan_eth_open, - .ndo_stop = irlan_eth_close, - .ndo_start_xmit = irlan_eth_xmit, - .ndo_set_rx_mode = irlan_eth_set_multicast_list, - .ndo_validate_addr = eth_validate_addr, -}; - -/* - * Function irlan_eth_setup (dev) - * - * The network device initialization function. - * - */ -static void irlan_eth_setup(struct net_device *dev) -{ - ether_setup(dev); - - dev->netdev_ops = &irlan_eth_netdev_ops; - dev->needs_free_netdev = true; - dev->min_mtu = 0; - dev->max_mtu = ETH_MAX_MTU; - - /* - * Lets do all queueing in IrTTP instead of this device driver. - * Queueing here as well can introduce some strange latency - * problems, which we will avoid by setting the queue size to 0. - */ - /* - * The bugs in IrTTP and IrLAN that created this latency issue - * have now been fixed, and we can propagate flow control properly - * to the network layer. However, this requires a minimal queue of - * packets for the device. - * Without flow control, the Tx Queue is 14 (ttp) + 0 (dev) = 14 - * With flow control, the Tx Queue is 7 (ttp) + 4 (dev) = 11 - * See irlan_eth_flow_indication()... - * Note : this number was randomly selected and would need to - * be adjusted. - * Jean II */ - dev->tx_queue_len = 4; -} - -/* - * Function alloc_irlandev - * - * Allocate network device and control block - * - */ -struct net_device *alloc_irlandev(const char *name) -{ - return alloc_netdev(sizeof(struct irlan_cb), name, NET_NAME_UNKNOWN, - irlan_eth_setup); -} - -/* - * Function irlan_eth_open (dev) - * - * Network device has been opened by user - * - */ -static int irlan_eth_open(struct net_device *dev) -{ - struct irlan_cb *self = netdev_priv(dev); - - /* Ready to play! */ - netif_stop_queue(dev); /* Wait until data link is ready */ - - /* We are now open, so time to do some work */ - self->disconnect_reason = 0; - irlan_client_wakeup(self, self->saddr, self->daddr); - - /* Make sure we have a hardware address before we return, - so DHCP clients gets happy */ - return wait_event_interruptible(self->open_wait, - !self->tsap_data->connected); -} - -/* - * Function irlan_eth_close (dev) - * - * Stop the ether network device, his function will usually be called by - * ifconfig down. We should now disconnect the link, We start the - * close timer, so that the instance will be removed if we are unable - * to discover the remote device after the disconnect. - */ -static int irlan_eth_close(struct net_device *dev) -{ - struct irlan_cb *self = netdev_priv(dev); - - /* Stop device */ - netif_stop_queue(dev); - - irlan_close_data_channel(self); - irlan_close_tsaps(self); - - irlan_do_client_event(self, IRLAN_LMP_DISCONNECT, NULL); - irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); - - /* Remove frames queued on the control channel */ - skb_queue_purge(&self->client.txq); - - self->client.tx_busy = 0; - - return 0; -} - -/* - * Function irlan_eth_tx (skb) - * - * Transmits ethernet frames over IrDA link. - * - */ -static netdev_tx_t irlan_eth_xmit(struct sk_buff *skb, - struct net_device *dev) -{ - struct irlan_cb *self = netdev_priv(dev); - int ret; - unsigned int len; - - /* skb headroom large enough to contain all IrDA-headers? */ - if ((skb_headroom(skb) < self->max_header_size) || (skb_shared(skb))) { - struct sk_buff *new_skb = - skb_realloc_headroom(skb, self->max_header_size); - - /* We have to free the original skb anyway */ - dev_kfree_skb(skb); - - /* Did the realloc succeed? */ - if (new_skb == NULL) - return NETDEV_TX_OK; - - /* Use the new skb instead */ - skb = new_skb; - } - - netif_trans_update(dev); - - len = skb->len; - /* Now queue the packet in the transport layer */ - if (self->use_udata) - ret = irttp_udata_request(self->tsap_data, skb); - else - ret = irttp_data_request(self->tsap_data, skb); - - if (ret < 0) { - /* - * IrTTPs tx queue is full, so we just have to - * drop the frame! You might think that we should - * just return -1 and don't deallocate the frame, - * but that is dangerous since it's possible that - * we have replaced the original skb with a new - * one with larger headroom, and that would really - * confuse do_dev_queue_xmit() in dev.c! I have - * tried :-) DB - */ - /* irttp_data_request already free the packet */ - dev->stats.tx_dropped++; - } else { - dev->stats.tx_packets++; - dev->stats.tx_bytes += len; - } - - return NETDEV_TX_OK; -} - -/* - * Function irlan_eth_receive (handle, skb) - * - * This function gets the data that is received on the data channel - * - */ -int irlan_eth_receive(void *instance, void *sap, struct sk_buff *skb) -{ - struct irlan_cb *self = instance; - struct net_device *dev = self->dev; - - if (skb == NULL) { - dev->stats.rx_dropped++; - return 0; - } - if (skb->len < ETH_HLEN) { - pr_debug("%s() : IrLAN frame too short (%d)\n", - __func__, skb->len); - dev->stats.rx_dropped++; - dev_kfree_skb(skb); - return 0; - } - - /* - * Adopt this frame! Important to set all these fields since they - * might have been previously set by the low level IrDA network - * device driver - */ - skb->protocol = eth_type_trans(skb, dev); /* Remove eth header */ - - dev->stats.rx_packets++; - dev->stats.rx_bytes += skb->len; - - netif_rx(skb); /* Eat it! */ - - return 0; -} - -/* - * Function irlan_eth_flow (status) - * - * Do flow control between IP/Ethernet and IrLAN/IrTTP. This is done by - * controlling the queue stop/start. - * - * The IrDA link layer has the advantage to have flow control, and - * IrTTP now properly handles that. Flow controlling the higher layers - * prevent us to drop Tx packets in here (up to 15% for a TCP socket, - * more for UDP socket). - * Also, this allow us to reduce the overall transmit queue, which means - * less latency in case of mixed traffic. - * Jean II - */ -void irlan_eth_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) -{ - struct irlan_cb *self; - struct net_device *dev; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - dev = self->dev; - - IRDA_ASSERT(dev != NULL, return;); - - pr_debug("%s() : flow %s ; running %d\n", __func__, - flow == FLOW_STOP ? "FLOW_STOP" : "FLOW_START", - netif_running(dev)); - - switch (flow) { - case FLOW_STOP: - /* IrTTP is full, stop higher layers */ - netif_stop_queue(dev); - break; - case FLOW_START: - default: - /* Tell upper layers that its time to transmit frames again */ - /* Schedule network layer */ - netif_wake_queue(dev); - break; - } -} - -/* - * Function set_multicast_list (dev) - * - * Configure the filtering of the device - * - */ -#define HW_MAX_ADDRS 4 /* Must query to get it! */ -static void irlan_eth_set_multicast_list(struct net_device *dev) -{ - struct irlan_cb *self = netdev_priv(dev); - - /* Check if data channel has been connected yet */ - if (self->client.state != IRLAN_DATA) { - pr_debug("%s(), delaying!\n", __func__); - return; - } - - if (dev->flags & IFF_PROMISC) { - /* Enable promiscuous mode */ - net_warn_ratelimited("Promiscuous mode not implemented by IrLAN!\n"); - } else if ((dev->flags & IFF_ALLMULTI) || - netdev_mc_count(dev) > HW_MAX_ADDRS) { - /* Disable promiscuous mode, use normal mode. */ - pr_debug("%s(), Setting multicast filter\n", __func__); - /* hardware_set_filter(NULL); */ - - irlan_set_multicast_filter(self, TRUE); - } else if (!netdev_mc_empty(dev)) { - pr_debug("%s(), Setting multicast filter\n", __func__); - /* Walk the address list, and load the filter */ - /* hardware_set_filter(dev->mc_list); */ - - irlan_set_multicast_filter(self, TRUE); - } else { - pr_debug("%s(), Clearing multicast filter\n", __func__); - irlan_set_multicast_filter(self, FALSE); - } - - if (dev->flags & IFF_BROADCAST) - irlan_set_broadcast_filter(self, TRUE); - else - irlan_set_broadcast_filter(self, FALSE); -} diff --git a/net/irda/irlan/irlan_event.c b/net/irda/irlan/irlan_event.c deleted file mode 100644 index 9a1cc11c16f6..000000000000 --- a/net/irda/irlan/irlan_event.c +++ /dev/null @@ -1,60 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_event.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Oct 20 09:10:16 1998 - * Modified at: Sat Oct 30 12:59:01 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -const char * const irlan_state[] = { - "IRLAN_IDLE", - "IRLAN_QUERY", - "IRLAN_CONN", - "IRLAN_INFO", - "IRLAN_MEDIA", - "IRLAN_OPEN", - "IRLAN_WAIT", - "IRLAN_ARB", - "IRLAN_DATA", - "IRLAN_CLOSE", - "IRLAN_SYNC", -}; - -void irlan_next_client_state(struct irlan_cb *self, IRLAN_STATE state) -{ - pr_debug("%s(), %s\n", __func__ , irlan_state[state]); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - self->client.state = state; -} - -void irlan_next_provider_state(struct irlan_cb *self, IRLAN_STATE state) -{ - pr_debug("%s(), %s\n", __func__ , irlan_state[state]); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - self->provider.state = state; -} - diff --git a/net/irda/irlan/irlan_filter.c b/net/irda/irlan/irlan_filter.c deleted file mode 100644 index e755e90b2f26..000000000000 --- a/net/irda/irlan/irlan_filter.c +++ /dev/null @@ -1,240 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_filter.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Fri Jan 29 11:16:38 1999 - * Modified at: Sat Oct 30 12:58:45 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include - -#include -#include - -/* - * Function irlan_filter_request (self, skb) - * - * Handle filter request from client peer device - * - */ -void irlan_filter_request(struct irlan_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - if ((self->provider.filter_type == IRLAN_DIRECTED) && - (self->provider.filter_operation == DYNAMIC)) - { - pr_debug("Giving peer a dynamic Ethernet address\n"); - self->provider.mac_address[0] = 0x40; - self->provider.mac_address[1] = 0x00; - self->provider.mac_address[2] = 0x00; - self->provider.mac_address[3] = 0x00; - - /* Use arbitration value to generate MAC address */ - if (self->provider.access_type == ACCESS_PEER) { - self->provider.mac_address[4] = - self->provider.send_arb_val & 0xff; - self->provider.mac_address[5] = - (self->provider.send_arb_val >> 8) & 0xff; - } else { - /* Just generate something for now */ - get_random_bytes(self->provider.mac_address+4, 1); - get_random_bytes(self->provider.mac_address+5, 1); - } - - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x03; - irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - irlan_insert_short_param(skb, "MAX_ENTRY", 0x0001); - irlan_insert_array_param(skb, "FILTER_ENTRY", - self->provider.mac_address, 6); - return; - } - - if ((self->provider.filter_type == IRLAN_DIRECTED) && - (self->provider.filter_mode == FILTER)) - { - pr_debug("Directed filter on\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_DIRECTED) && - (self->provider.filter_mode == NONE)) - { - pr_debug("Directed filter off\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - - if ((self->provider.filter_type == IRLAN_BROADCAST) && - (self->provider.filter_mode == FILTER)) - { - pr_debug("Broadcast filter on\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_BROADCAST) && - (self->provider.filter_mode == NONE)) - { - pr_debug("Broadcast filter off\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_MULTICAST) && - (self->provider.filter_mode == FILTER)) - { - pr_debug("Multicast filter on\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_MULTICAST) && - (self->provider.filter_mode == NONE)) - { - pr_debug("Multicast filter off\n"); - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x00; - return; - } - if ((self->provider.filter_type == IRLAN_MULTICAST) && - (self->provider.filter_operation == GET)) - { - pr_debug("Multicast filter get\n"); - skb->data[0] = 0x00; /* Success? */ - skb->data[1] = 0x02; - irlan_insert_string_param(skb, "FILTER_MODE", "NONE"); - irlan_insert_short_param(skb, "MAX_ENTRY", 16); - return; - } - skb->data[0] = 0x00; /* Command not supported */ - skb->data[1] = 0x00; - - pr_debug("Not implemented!\n"); -} - -/* - * Function check_request_param (self, param, value) - * - * Check parameters in request from peer device - * - */ -void irlan_check_command_param(struct irlan_cb *self, char *param, char *value) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - pr_debug("%s, %s\n", param, value); - - /* - * This is experimental!! DB. - */ - if (strcmp(param, "MODE") == 0) { - self->use_udata = TRUE; - return; - } - - /* - * FILTER_TYPE - */ - if (strcmp(param, "FILTER_TYPE") == 0) { - if (strcmp(value, "DIRECTED") == 0) { - self->provider.filter_type = IRLAN_DIRECTED; - return; - } - if (strcmp(value, "MULTICAST") == 0) { - self->provider.filter_type = IRLAN_MULTICAST; - return; - } - if (strcmp(value, "BROADCAST") == 0) { - self->provider.filter_type = IRLAN_BROADCAST; - return; - } - } - /* - * FILTER_MODE - */ - if (strcmp(param, "FILTER_MODE") == 0) { - if (strcmp(value, "ALL") == 0) { - self->provider.filter_mode = ALL; - return; - } - if (strcmp(value, "FILTER") == 0) { - self->provider.filter_mode = FILTER; - return; - } - if (strcmp(value, "NONE") == 0) { - self->provider.filter_mode = FILTER; - return; - } - } - /* - * FILTER_OPERATION - */ - if (strcmp(param, "FILTER_OPERATION") == 0) { - if (strcmp(value, "DYNAMIC") == 0) { - self->provider.filter_operation = DYNAMIC; - return; - } - if (strcmp(value, "GET") == 0) { - self->provider.filter_operation = GET; - return; - } - } -} - -/* - * Function irlan_print_filter (filter_type, buf) - * - * Print status of filter. Used by /proc file system - * - */ -#ifdef CONFIG_PROC_FS -#define MASK2STR(m,s) { .mask = m, .str = s } - -void irlan_print_filter(struct seq_file *seq, int filter_type) -{ - static struct { - int mask; - const char *str; - } filter_mask2str[] = { - MASK2STR(IRLAN_DIRECTED, "DIRECTED"), - MASK2STR(IRLAN_FUNCTIONAL, "FUNCTIONAL"), - MASK2STR(IRLAN_GROUP, "GROUP"), - MASK2STR(IRLAN_MAC_FRAME, "MAC_FRAME"), - MASK2STR(IRLAN_MULTICAST, "MULTICAST"), - MASK2STR(IRLAN_BROADCAST, "BROADCAST"), - MASK2STR(IRLAN_IPX_SOCKET, "IPX_SOCKET"), - MASK2STR(0, NULL) - }, *p; - - for (p = filter_mask2str; p->str; p++) { - if (filter_type & p->mask) - seq_printf(seq, "%s ", p->str); - } - seq_putc(seq, '\n'); -} -#undef MASK2STR -#endif diff --git a/net/irda/irlan/irlan_provider.c b/net/irda/irlan/irlan_provider.c deleted file mode 100644 index 15c292cf2644..000000000000 --- a/net/irda/irlan/irlan_provider.c +++ /dev/null @@ -1,408 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_provider.c - * Version: 0.9 - * Description: IrDA LAN Access Protocol Implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sat Oct 30 12:52:10 1999 - * Modified by: Dag Brattli - * Sources: skeleton.c by Donald Becker - * slip.c by Laurence Culhane, - * Fred N. van Kempen, - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -static void irlan_provider_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb); - -/* - * Function irlan_provider_control_data_indication (handle, skb) - * - * This function gets the data that is received on the control channel - * - */ -static int irlan_provider_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct irlan_cb *self; - __u8 code; - - self = instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - IRDA_ASSERT(skb != NULL, return -1;); - - code = skb->data[0]; - switch(code) { - case CMD_GET_PROVIDER_INFO: - pr_debug("Got GET_PROVIDER_INFO command!\n"); - irlan_do_provider_event(self, IRLAN_GET_INFO_CMD, skb); - break; - - case CMD_GET_MEDIA_CHAR: - pr_debug("Got GET_MEDIA_CHAR command!\n"); - irlan_do_provider_event(self, IRLAN_GET_MEDIA_CMD, skb); - break; - case CMD_OPEN_DATA_CHANNEL: - pr_debug("Got OPEN_DATA_CHANNEL command!\n"); - irlan_do_provider_event(self, IRLAN_OPEN_DATA_CMD, skb); - break; - case CMD_FILTER_OPERATION: - pr_debug("Got FILTER_OPERATION command!\n"); - irlan_do_provider_event(self, IRLAN_FILTER_CONFIG_CMD, skb); - break; - case CMD_RECONNECT_DATA_CHAN: - pr_debug("%s(), Got RECONNECT_DATA_CHAN command\n", __func__); - pr_debug("%s(), NOT IMPLEMENTED\n", __func__); - break; - case CMD_CLOSE_DATA_CHAN: - pr_debug("Got CLOSE_DATA_CHAN command!\n"); - pr_debug("%s(), NOT IMPLEMENTED\n", __func__); - break; - default: - pr_debug("%s(), Unknown command!\n", __func__); - break; - } - return 0; -} - -/* - * Function irlan_provider_connect_indication (handle, skb, priv) - * - * Got connection from peer IrLAN client - * - */ -static void irlan_provider_connect_indication(void *instance, void *sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - IRDA_ASSERT(tsap == self->provider.tsap_ctrl,return;); - IRDA_ASSERT(self->provider.state == IRLAN_IDLE, return;); - - self->provider.max_sdu_size = max_sdu_size; - self->provider.max_header_size = max_header_size; - - irlan_do_provider_event(self, IRLAN_CONNECT_INDICATION, NULL); - - /* - * If we are in peer mode, the client may not have got the discovery - * indication it needs to make progress. If the client is still in - * IDLE state, we must kick it. - */ - if ((self->provider.access_type == ACCESS_PEER) && - (self->client.state == IRLAN_IDLE)) - { - irlan_client_wakeup(self, self->saddr, self->daddr); - } -} - -/* - * Function irlan_provider_connect_response (handle) - * - * Accept incoming connection - * - */ -void irlan_provider_connect_response(struct irlan_cb *self, - struct tsap_cb *tsap) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - - /* Just accept */ - irttp_connect_response(tsap, IRLAN_MTU, NULL); -} - -static void irlan_provider_disconnect_indication(void *instance, void *sap, - LM_REASON reason, - struct sk_buff *userdata) -{ - struct irlan_cb *self; - struct tsap_cb *tsap; - - pr_debug("%s(), reason=%d\n", __func__ , reason); - - self = instance; - tsap = sap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return;); - IRDA_ASSERT(tsap != NULL, return;); - IRDA_ASSERT(tsap->magic == TTP_TSAP_MAGIC, return;); - - IRDA_ASSERT(tsap == self->provider.tsap_ctrl, return;); - - irlan_do_provider_event(self, IRLAN_LMP_DISCONNECT, NULL); -} - -/* - * Function irlan_parse_open_data_cmd (self, skb) - * - * - * - */ -int irlan_parse_open_data_cmd(struct irlan_cb *self, struct sk_buff *skb) -{ - int ret; - - ret = irlan_provider_parse_command(self, CMD_OPEN_DATA_CHANNEL, skb); - - /* Open data channel */ - irlan_open_data_tsap(self); - - return ret; -} - -/* - * Function parse_command (skb) - * - * Extract all parameters from received buffer, then feed them to - * check_params for parsing - * - */ -int irlan_provider_parse_command(struct irlan_cb *self, int cmd, - struct sk_buff *skb) -{ - __u8 *frame; - __u8 *ptr; - int count; - __u16 val_len; - int i; - char *name; - char *value; - int ret = RSP_SUCCESS; - - IRDA_ASSERT(skb != NULL, return -RSP_PROTOCOL_ERROR;); - - pr_debug("%s(), skb->len=%d\n", __func__ , (int)skb->len); - - IRDA_ASSERT(self != NULL, return -RSP_PROTOCOL_ERROR;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -RSP_PROTOCOL_ERROR;); - - if (!skb) - return -RSP_PROTOCOL_ERROR; - - frame = skb->data; - - name = kmalloc(255, GFP_ATOMIC); - if (!name) - return -RSP_INSUFFICIENT_RESOURCES; - value = kmalloc(1016, GFP_ATOMIC); - if (!value) { - kfree(name); - return -RSP_INSUFFICIENT_RESOURCES; - } - - /* How many parameters? */ - count = frame[1]; - - pr_debug("Got %d parameters\n", count); - - ptr = frame+2; - - /* For all parameters */ - for (i=0; imagic == IRLAN_MAGIC, return;); - - skb = alloc_skb(IRLAN_MAX_HEADER + IRLAN_CMD_HEADER + - /* Bigger param length comes from CMD_GET_MEDIA_CHAR */ - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "DIRECTED") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "BROADCAST") + - IRLAN_STRING_PARAMETER_LEN("FILTER_TYPE", "MULTICAST") + - IRLAN_STRING_PARAMETER_LEN("ACCESS_TYPE", "HOSTED"), - GFP_ATOMIC); - - if (!skb) - return; - - /* Reserve space for TTP, LMP, and LAP header */ - skb_reserve(skb, self->provider.max_header_size); - skb_put(skb, 2); - - switch (command) { - case CMD_GET_PROVIDER_INFO: - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x02; /* 2 parameters */ - switch (self->media) { - case MEDIA_802_3: - irlan_insert_string_param(skb, "MEDIA", "802.3"); - break; - case MEDIA_802_5: - irlan_insert_string_param(skb, "MEDIA", "802.5"); - break; - default: - pr_debug("%s(), unknown media type!\n", __func__); - break; - } - irlan_insert_short_param(skb, "IRLAN_VER", 0x0101); - break; - - case CMD_GET_MEDIA_CHAR: - skb->data[0] = 0x00; /* Success */ - skb->data[1] = 0x05; /* 5 parameters */ - irlan_insert_string_param(skb, "FILTER_TYPE", "DIRECTED"); - irlan_insert_string_param(skb, "FILTER_TYPE", "BROADCAST"); - irlan_insert_string_param(skb, "FILTER_TYPE", "MULTICAST"); - - switch (self->provider.access_type) { - case ACCESS_DIRECT: - irlan_insert_string_param(skb, "ACCESS_TYPE", "DIRECT"); - break; - case ACCESS_PEER: - irlan_insert_string_param(skb, "ACCESS_TYPE", "PEER"); - break; - case ACCESS_HOSTED: - irlan_insert_string_param(skb, "ACCESS_TYPE", "HOSTED"); - break; - default: - pr_debug("%s(), Unknown access type\n", __func__); - break; - } - irlan_insert_short_param(skb, "MAX_FRAME", 0x05ee); - break; - case CMD_OPEN_DATA_CHANNEL: - skb->data[0] = 0x00; /* Success */ - if (self->provider.send_arb_val) { - skb->data[1] = 0x03; /* 3 parameters */ - irlan_insert_short_param(skb, "CON_ARB", - self->provider.send_arb_val); - } else - skb->data[1] = 0x02; /* 2 parameters */ - irlan_insert_byte_param(skb, "DATA_CHAN", self->stsap_sel_data); - irlan_insert_string_param(skb, "RECONNECT_KEY", "LINUX RULES!"); - break; - case CMD_FILTER_OPERATION: - irlan_filter_request(self, skb); - break; - default: - pr_debug("%s(), Unknown command!\n", __func__); - break; - } - - irttp_data_request(self->provider.tsap_ctrl, skb); -} - -/* - * Function irlan_provider_register(void) - * - * Register provider support so we can accept incoming connections. - * - */ -int irlan_provider_open_ctrl_tsap(struct irlan_cb *self) -{ - struct tsap_cb *tsap; - notify_t notify; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - /* Check if already open */ - if (self->provider.tsap_ctrl) - return -1; - - /* - * First register well known control TSAP - */ - irda_notify_init(¬ify); - notify.data_indication = irlan_provider_data_indication; - notify.connect_indication = irlan_provider_connect_indication; - notify.disconnect_indication = irlan_provider_disconnect_indication; - notify.instance = self; - strlcpy(notify.name, "IrLAN ctrl (p)", sizeof(notify.name)); - - tsap = irttp_open_tsap(LSAP_ANY, 1, ¬ify); - if (!tsap) { - pr_debug("%s(), Got no tsap!\n", __func__); - return -1; - } - self->provider.tsap_ctrl = tsap; - - /* Register with LM-IAS */ - irlan_ias_register(self, tsap->stsap_sel); - - return 0; -} - diff --git a/net/irda/irlan/irlan_provider_event.c b/net/irda/irlan/irlan_provider_event.c deleted file mode 100644 index 9c4f7f51d6b5..000000000000 --- a/net/irda/irlan/irlan_provider_event.c +++ /dev/null @@ -1,233 +0,0 @@ -/********************************************************************* - * - * Filename: irlan_provider_event.c - * Version: 0.9 - * Description: IrLAN provider state machine) - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:37 1997 - * Modified at: Sat Oct 30 12:52:41 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include -#include - -static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); -static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb); - -static int (*state[])(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) = -{ - irlan_provider_state_idle, - NULL, /* Query */ - NULL, /* Info */ - irlan_provider_state_info, - NULL, /* Media */ - irlan_provider_state_open, - NULL, /* Wait */ - NULL, /* Arb */ - irlan_provider_state_data, - NULL, /* Close */ - NULL, /* Sync */ -}; - -void irlan_do_provider_event(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(*state[ self->provider.state] != NULL, return;); - - (*state[self->provider.state]) (self, event, skb); -} - -/* - * Function irlan_provider_state_idle (event, skb, info) - * - * IDLE, We are waiting for an indication that there is a provider - * available. - */ -static int irlan_provider_state_idle(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_CONNECT_INDICATION: - irlan_provider_connect_response( self, self->provider.tsap_ctrl); - irlan_next_provider_state( self, IRLAN_INFO); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_provider_state_info (self, event, skb, info) - * - * INFO, We have issued a GetInfo command and is awaiting a reply. - */ -static int irlan_provider_state_info(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_GET_INFO_CMD: - /* Be sure to use 802.3 in case of peer mode */ - if (self->provider.access_type == ACCESS_PEER) { - self->media = MEDIA_802_3; - - /* Check if client has started yet */ - if (self->client.state == IRLAN_IDLE) { - /* This should get the client going */ - irlmp_discovery_request(8); - } - } - - irlan_provider_send_reply(self, CMD_GET_PROVIDER_INFO, - RSP_SUCCESS); - /* Keep state */ - break; - case IRLAN_GET_MEDIA_CMD: - irlan_provider_send_reply(self, CMD_GET_MEDIA_CHAR, - RSP_SUCCESS); - /* Keep state */ - break; - case IRLAN_OPEN_DATA_CMD: - ret = irlan_parse_open_data_cmd(self, skb); - if (self->provider.access_type == ACCESS_PEER) { - /* FIXME: make use of random functions! */ - self->provider.send_arb_val = (jiffies & 0xffff); - } - irlan_provider_send_reply(self, CMD_OPEN_DATA_CHANNEL, ret); - - if (ret == RSP_SUCCESS) { - irlan_next_provider_state(self, IRLAN_OPEN); - - /* Signal client that we are now open */ - irlan_do_client_event(self, IRLAN_PROVIDER_SIGNAL, NULL); - } - break; - case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ - case IRLAN_LAP_DISCONNECT: - irlan_next_provider_state(self, IRLAN_IDLE); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_provider_state_open (self, event, skb, info) - * - * OPEN, The client has issued a OpenData command and is awaiting a - * reply - * - */ -static int irlan_provider_state_open(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - - switch(event) { - case IRLAN_FILTER_CONFIG_CMD: - irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb); - irlan_provider_send_reply(self, CMD_FILTER_OPERATION, - RSP_SUCCESS); - /* Keep state */ - break; - case IRLAN_DATA_CONNECT_INDICATION: - irlan_next_provider_state(self, IRLAN_DATA); - irlan_provider_connect_response(self, self->tsap_data); - break; - case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ - case IRLAN_LAP_DISCONNECT: - irlan_next_provider_state(self, IRLAN_IDLE); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irlan_provider_state_data (self, event, skb, info) - * - * DATA, The data channel is connected, allowing data transfers between - * the local and remote machines. - * - */ -static int irlan_provider_state_data(struct irlan_cb *self, IRLAN_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == IRLAN_MAGIC, return -1;); - - switch(event) { - case IRLAN_FILTER_CONFIG_CMD: - irlan_provider_parse_command(self, CMD_FILTER_OPERATION, skb); - irlan_provider_send_reply(self, CMD_FILTER_OPERATION, - RSP_SUCCESS); - break; - case IRLAN_LMP_DISCONNECT: /* FALLTHROUGH */ - case IRLAN_LAP_DISCONNECT: - irlan_next_provider_state(self, IRLAN_IDLE); - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__ , event); - break; - } - if (skb) - dev_kfree_skb(skb); - - return 0; -} - - - - - - - - - - diff --git a/net/irda/irlap.c b/net/irda/irlap.c deleted file mode 100644 index 1cde711bcab5..000000000000 --- a/net/irda/irlap.c +++ /dev/null @@ -1,1207 +0,0 @@ -/********************************************************************* - * - * Filename: irlap.c - * Version: 1.0 - * Description: IrLAP implementation for Linux - * Status: Stable - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Tue Dec 14 09:26:44 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static hashbin_t *irlap = NULL; -int sysctl_slot_timeout = SLOT_TIMEOUT * 1000 / HZ; - -/* This is the delay of missed pf period before generating an event - * to the application. The spec mandate 3 seconds, but in some cases - * it's way too long. - Jean II */ -int sysctl_warn_noreply_time = 3; - -extern void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb); -static void __irlap_close(struct irlap_cb *self); -static void irlap_init_qos_capabilities(struct irlap_cb *self, - struct qos_info *qos_user); - -static const char *const lap_reasons[] __maybe_unused = { - "ERROR, NOT USED", - "LAP_DISC_INDICATION", - "LAP_NO_RESPONSE", - "LAP_RESET_INDICATION", - "LAP_FOUND_NONE", - "LAP_MEDIA_BUSY", - "LAP_PRIMARY_CONFLICT", - "ERROR, NOT USED", -}; - -int __init irlap_init(void) -{ - /* Check if the compiler did its job properly. - * May happen on some ARM configuration, check with Russell King. */ - IRDA_ASSERT(sizeof(struct xid_frame) == 14, ;); - IRDA_ASSERT(sizeof(struct test_frame) == 10, ;); - IRDA_ASSERT(sizeof(struct ua_frame) == 10, ;); - IRDA_ASSERT(sizeof(struct snrm_frame) == 11, ;); - - /* Allocate master array */ - irlap = hashbin_new(HB_LOCK); - if (irlap == NULL) { - net_err_ratelimited("%s: can't allocate irlap hashbin!\n", - __func__); - return -ENOMEM; - } - - return 0; -} - -void irlap_cleanup(void) -{ - IRDA_ASSERT(irlap != NULL, return;); - - hashbin_delete(irlap, (FREE_FUNC) __irlap_close); -} - -/* - * Function irlap_open (driver) - * - * Initialize IrLAP layer - * - */ -struct irlap_cb *irlap_open(struct net_device *dev, struct qos_info *qos, - const char *hw_name) -{ - struct irlap_cb *self; - - /* Initialize the irlap structure. */ - self = kzalloc(sizeof(struct irlap_cb), GFP_KERNEL); - if (self == NULL) - return NULL; - - self->magic = LAP_MAGIC; - - /* Make a binding between the layers */ - self->netdev = dev; - self->qos_dev = qos; - /* Copy hardware name */ - if(hw_name != NULL) { - strlcpy(self->hw_name, hw_name, sizeof(self->hw_name)); - } else { - self->hw_name[0] = '\0'; - } - - /* FIXME: should we get our own field? */ - dev->atalk_ptr = self; - - self->state = LAP_OFFLINE; - - /* Initialize transmit queue */ - skb_queue_head_init(&self->txq); - skb_queue_head_init(&self->txq_ultra); - skb_queue_head_init(&self->wx_list); - - /* My unique IrLAP device address! */ - /* We don't want the broadcast address, neither the NULL address - * (most often used to signify "invalid"), and we don't want an - * address already in use (otherwise connect won't be able - * to select the proper link). - Jean II */ - do { - get_random_bytes(&self->saddr, sizeof(self->saddr)); - } while ((self->saddr == 0x0) || (self->saddr == BROADCAST) || - (hashbin_lock_find(irlap, self->saddr, NULL)) ); - /* Copy to the driver */ - memcpy(dev->dev_addr, &self->saddr, 4); - - init_timer(&self->slot_timer); - init_timer(&self->query_timer); - init_timer(&self->discovery_timer); - init_timer(&self->final_timer); - init_timer(&self->poll_timer); - init_timer(&self->wd_timer); - init_timer(&self->backoff_timer); - init_timer(&self->media_busy_timer); - - irlap_apply_default_connection_parameters(self); - - self->N3 = 3; /* # connections attempts to try before giving up */ - - self->state = LAP_NDM; - - hashbin_insert(irlap, (irda_queue_t *) self, self->saddr, NULL); - - irlmp_register_link(self, self->saddr, &self->notify); - - return self; -} -EXPORT_SYMBOL(irlap_open); - -/* - * Function __irlap_close (self) - * - * Remove IrLAP and all allocated memory. Stop any pending timers. - * - */ -static void __irlap_close(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Stop timers */ - del_timer(&self->slot_timer); - del_timer(&self->query_timer); - del_timer(&self->discovery_timer); - del_timer(&self->final_timer); - del_timer(&self->poll_timer); - del_timer(&self->wd_timer); - del_timer(&self->backoff_timer); - del_timer(&self->media_busy_timer); - - irlap_flush_all_queues(self); - - self->magic = 0; - - kfree(self); -} - -/* - * Function irlap_close (self) - * - * Remove IrLAP instance - * - */ -void irlap_close(struct irlap_cb *self) -{ - struct irlap_cb *lap; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* We used to send a LAP_DISC_INDICATION here, but this was - * racy. This has been move within irlmp_unregister_link() - * itself. Jean II */ - - /* Kill the LAP and all LSAPs on top of it */ - irlmp_unregister_link(self->saddr); - self->notify.instance = NULL; - - /* Be sure that we manage to remove ourself from the hash */ - lap = hashbin_remove(irlap, self->saddr, NULL); - if (!lap) { - pr_debug("%s(), Didn't find myself!\n", __func__); - return; - } - __irlap_close(lap); -} -EXPORT_SYMBOL(irlap_close); - -/* - * Function irlap_connect_indication (self, skb) - * - * Another device is attempting to make a connection - * - */ -void irlap_connect_indication(struct irlap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_init_qos_capabilities(self, NULL); /* No user QoS! */ - - irlmp_link_connect_indication(self->notify.instance, self->saddr, - self->daddr, &self->qos_tx, skb); -} - -/* - * Function irlap_connect_response (self, skb) - * - * Service user has accepted incoming connection - * - */ -void irlap_connect_response(struct irlap_cb *self, struct sk_buff *userdata) -{ - irlap_do_event(self, CONNECT_RESPONSE, userdata, NULL); -} - -/* - * Function irlap_connect_request (self, daddr, qos_user, sniff) - * - * Request connection with another device, sniffing is not implemented - * yet. - * - */ -void irlap_connect_request(struct irlap_cb *self, __u32 daddr, - struct qos_info *qos_user, int sniff) -{ - pr_debug("%s(), daddr=0x%08x\n", __func__, daddr); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - self->daddr = daddr; - - /* - * If the service user specifies QoS values for this connection, - * then use them - */ - irlap_init_qos_capabilities(self, qos_user); - - if ((self->state == LAP_NDM) && !self->media_busy) - irlap_do_event(self, CONNECT_REQUEST, NULL, NULL); - else - self->connect_pending = TRUE; -} - -/* - * Function irlap_connect_confirm (self, skb) - * - * Connection request has been accepted - * - */ -void irlap_connect_confirm(struct irlap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlmp_link_connect_confirm(self->notify.instance, &self->qos_tx, skb); -} - -/* - * Function irlap_data_indication (self, skb) - * - * Received data frames from IR-port, so we just pass them up to - * IrLMP for further processing - * - */ -void irlap_data_indication(struct irlap_cb *self, struct sk_buff *skb, - int unreliable) -{ - /* Hide LAP header from IrLMP layer */ - skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); - - irlmp_link_data_indication(self->notify.instance, skb, unreliable); -} - - -/* - * Function irlap_data_request (self, skb) - * - * Queue data for transmission, must wait until XMIT state - * - */ -void irlap_data_request(struct irlap_cb *self, struct sk_buff *skb, - int unreliable) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), - return;); - skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); - - /* - * Must set frame format now so that the rest of the code knows - * if its dealing with an I or an UI frame - */ - if (unreliable) - skb->data[1] = UI_FRAME; - else - skb->data[1] = I_FRAME; - - /* Don't forget to refcount it - see irlmp_connect_request(). */ - skb_get(skb); - - /* Add at the end of the queue (keep ordering) - Jean II */ - skb_queue_tail(&self->txq, skb); - - /* - * Send event if this frame only if we are in the right state - * FIXME: udata should be sent first! (skb_queue_head?) - */ - if ((self->state == LAP_XMIT_P) || (self->state == LAP_XMIT_S)) { - /* If we are not already processing the Tx queue, trigger - * transmission immediately - Jean II */ - if((skb_queue_len(&self->txq) <= 1) && (!self->local_busy)) - irlap_do_event(self, DATA_REQUEST, skb, NULL); - /* Otherwise, the packets will be sent normally at the - * next pf-poll - Jean II */ - } -} - -/* - * Function irlap_unitdata_request (self, skb) - * - * Send Ultra data. This is data that must be sent outside any connection - * - */ -#ifdef CONFIG_IRDA_ULTRA -void irlap_unitdata_request(struct irlap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - IRDA_ASSERT(skb_headroom(skb) >= (LAP_ADDR_HEADER+LAP_CTRL_HEADER), - return;); - skb_push(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); - - skb->data[0] = CBROADCAST; - skb->data[1] = UI_FRAME; - - /* Don't need to refcount, see irlmp_connless_data_request() */ - - skb_queue_tail(&self->txq_ultra, skb); - - irlap_do_event(self, SEND_UI_FRAME, NULL, NULL); -} -#endif /*CONFIG_IRDA_ULTRA */ - -/* - * Function irlap_udata_indication (self, skb) - * - * Receive Ultra data. This is data that is received outside any connection - * - */ -#ifdef CONFIG_IRDA_ULTRA -void irlap_unitdata_indication(struct irlap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Hide LAP header from IrLMP layer */ - skb_pull(skb, LAP_ADDR_HEADER+LAP_CTRL_HEADER); - - irlmp_link_unitdata_indication(self->notify.instance, skb); -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irlap_disconnect_request (void) - * - * Request to disconnect connection by service user - */ -void irlap_disconnect_request(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Don't disconnect until all data frames are successfully sent */ - if (!skb_queue_empty(&self->txq)) { - self->disconnect_pending = TRUE; - return; - } - - /* Check if we are in the right state for disconnecting */ - switch (self->state) { - case LAP_XMIT_P: /* FALLTHROUGH */ - case LAP_XMIT_S: /* FALLTHROUGH */ - case LAP_CONN: /* FALLTHROUGH */ - case LAP_RESET_WAIT: /* FALLTHROUGH */ - case LAP_RESET_CHECK: - irlap_do_event(self, DISCONNECT_REQUEST, NULL, NULL); - break; - default: - pr_debug("%s(), disconnect pending!\n", __func__); - self->disconnect_pending = TRUE; - break; - } -} - -/* - * Function irlap_disconnect_indication (void) - * - * Disconnect request from other device - * - */ -void irlap_disconnect_indication(struct irlap_cb *self, LAP_REASON reason) -{ - pr_debug("%s(), reason=%s\n", __func__, lap_reasons[reason]); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Flush queues */ - irlap_flush_all_queues(self); - - switch (reason) { - case LAP_RESET_INDICATION: - pr_debug("%s(), Sending reset request!\n", __func__); - irlap_do_event(self, RESET_REQUEST, NULL, NULL); - break; - case LAP_NO_RESPONSE: /* FALLTHROUGH */ - case LAP_DISC_INDICATION: /* FALLTHROUGH */ - case LAP_FOUND_NONE: /* FALLTHROUGH */ - case LAP_MEDIA_BUSY: - irlmp_link_disconnect_indication(self->notify.instance, self, - reason, NULL); - break; - default: - net_err_ratelimited("%s: Unknown reason %d\n", - __func__, reason); - } -} - -/* - * Function irlap_discovery_request (gen_addr_bit) - * - * Start one single discovery operation. - * - */ -void irlap_discovery_request(struct irlap_cb *self, discovery_t *discovery) -{ - struct irlap_info info; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(discovery != NULL, return;); - - pr_debug("%s(), nslots = %d\n", __func__, discovery->nslots); - - IRDA_ASSERT((discovery->nslots == 1) || (discovery->nslots == 6) || - (discovery->nslots == 8) || (discovery->nslots == 16), - return;); - - /* Discovery is only possible in NDM mode */ - if (self->state != LAP_NDM) { - pr_debug("%s(), discovery only possible in NDM mode\n", - __func__); - irlap_discovery_confirm(self, NULL); - /* Note : in theory, if we are not in NDM, we could postpone - * the discovery like we do for connection request. - * In practice, it's not worth it. If the media was busy, - * it's likely next time around it won't be busy. If we are - * in REPLY state, we will get passive discovery info & event. - * Jean II */ - return; - } - - /* Check if last discovery request finished in time, or if - * it was aborted due to the media busy flag. */ - if (self->discovery_log != NULL) { - hashbin_delete(self->discovery_log, (FREE_FUNC) kfree); - self->discovery_log = NULL; - } - - /* All operations will occur at predictable time, no need to lock */ - self->discovery_log = hashbin_new(HB_NOLOCK); - - if (self->discovery_log == NULL) { - net_warn_ratelimited("%s(), Unable to allocate discovery log!\n", - __func__); - return; - } - - info.S = discovery->nslots; /* Number of slots */ - info.s = 0; /* Current slot */ - - self->discovery_cmd = discovery; - info.discovery = discovery; - - /* sysctl_slot_timeout bounds are checked in irsysctl.c - Jean II */ - self->slot_timeout = msecs_to_jiffies(sysctl_slot_timeout); - - irlap_do_event(self, DISCOVERY_REQUEST, NULL, &info); -} - -/* - * Function irlap_discovery_confirm (log) - * - * A device has been discovered in front of this station, we - * report directly to LMP. - */ -void irlap_discovery_confirm(struct irlap_cb *self, hashbin_t *discovery_log) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - IRDA_ASSERT(self->notify.instance != NULL, return;); - - /* - * Check for successful discovery, since we are then allowed to clear - * the media busy condition (IrLAP 6.13.4 - p.94). This should allow - * us to make connection attempts much faster and easier (i.e. no - * collisions). - * Setting media busy to false will also generate an event allowing - * to process pending events in NDM state machine. - * Note : the spec doesn't define what's a successful discovery is. - * If we want Ultra to work, it's successful even if there is - * nobody discovered - Jean II - */ - if (discovery_log) - irda_device_set_media_busy(self->netdev, FALSE); - - /* Inform IrLMP */ - irlmp_link_discovery_confirm(self->notify.instance, discovery_log); -} - -/* - * Function irlap_discovery_indication (log) - * - * Somebody is trying to discover us! - * - */ -void irlap_discovery_indication(struct irlap_cb *self, discovery_t *discovery) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(discovery != NULL, return;); - - IRDA_ASSERT(self->notify.instance != NULL, return;); - - /* A device is very likely to connect immediately after it performs - * a successful discovery. This means that in our case, we are much - * more likely to receive a connection request over the medium. - * So, we backoff to avoid collisions. - * IrLAP spec 6.13.4 suggest 100ms... - * Note : this little trick actually make a *BIG* difference. If I set - * my Linux box with discovery enabled and one Ultra frame sent every - * second, my Palm has no trouble connecting to it every time ! - * Jean II */ - irda_device_set_media_busy(self->netdev, SMALL); - - irlmp_link_discovery_indication(self->notify.instance, discovery); -} - -/* - * Function irlap_status_indication (quality_of_link) - */ -void irlap_status_indication(struct irlap_cb *self, int quality_of_link) -{ - switch (quality_of_link) { - case STATUS_NO_ACTIVITY: - net_info_ratelimited("IrLAP, no activity on link!\n"); - break; - case STATUS_NOISY: - net_info_ratelimited("IrLAP, noisy link!\n"); - break; - default: - break; - } - irlmp_status_indication(self->notify.instance, - quality_of_link, LOCK_NO_CHANGE); -} - -/* - * Function irlap_reset_indication (void) - */ -void irlap_reset_indication(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - if (self->state == LAP_RESET_WAIT) - irlap_do_event(self, RESET_REQUEST, NULL, NULL); - else - irlap_do_event(self, RESET_RESPONSE, NULL, NULL); -} - -/* - * Function irlap_reset_confirm (void) - */ -void irlap_reset_confirm(void) -{ -} - -/* - * Function irlap_generate_rand_time_slot (S, s) - * - * Generate a random time slot between s and S-1 where - * S = Number of slots (0 -> S-1) - * s = Current slot - */ -int irlap_generate_rand_time_slot(int S, int s) -{ - static int rand; - int slot; - - IRDA_ASSERT((S - s) > 0, return 0;); - - rand += jiffies; - rand ^= (rand << 12); - rand ^= (rand >> 20); - - slot = s + rand % (S-s); - - IRDA_ASSERT((slot >= s) || (slot < S), return 0;); - - return slot; -} - -/* - * Function irlap_update_nr_received (nr) - * - * Remove all acknowledged frames in current window queue. This code is - * not intuitive and you should not try to change it. If you think it - * contains bugs, please mail a patch to the author instead. - */ -void irlap_update_nr_received(struct irlap_cb *self, int nr) -{ - struct sk_buff *skb = NULL; - int count = 0; - - /* - * Remove all the ack-ed frames from the window queue. - */ - - /* - * Optimize for the common case. It is most likely that the receiver - * will acknowledge all the frames we have sent! So in that case we - * delete all frames stored in window. - */ - if (nr == self->vs) { - while ((skb = skb_dequeue(&self->wx_list)) != NULL) { - dev_kfree_skb(skb); - } - /* The last acked frame is the next to send minus one */ - self->va = nr - 1; - } else { - /* Remove all acknowledged frames in current window */ - while ((skb_peek(&self->wx_list) != NULL) && - (((self->va+1) % 8) != nr)) - { - skb = skb_dequeue(&self->wx_list); - dev_kfree_skb(skb); - - self->va = (self->va + 1) % 8; - count++; - } - } - - /* Advance window */ - self->window = self->window_size - skb_queue_len(&self->wx_list); -} - -/* - * Function irlap_validate_ns_received (ns) - * - * Validate the next to send (ns) field from received frame. - */ -int irlap_validate_ns_received(struct irlap_cb *self, int ns) -{ - /* ns as expected? */ - if (ns == self->vr) - return NS_EXPECTED; - /* - * Stations are allowed to treat invalid NS as unexpected NS - * IrLAP, Recv ... with-invalid-Ns. p. 84 - */ - return NS_UNEXPECTED; - - /* return NR_INVALID; */ -} -/* - * Function irlap_validate_nr_received (nr) - * - * Validate the next to receive (nr) field from received frame. - * - */ -int irlap_validate_nr_received(struct irlap_cb *self, int nr) -{ - /* nr as expected? */ - if (nr == self->vs) { - pr_debug("%s(), expected!\n", __func__); - return NR_EXPECTED; - } - - /* - * unexpected nr? (but within current window), first we check if the - * ns numbers of the frames in the current window wrap. - */ - if (self->va < self->vs) { - if ((nr >= self->va) && (nr <= self->vs)) - return NR_UNEXPECTED; - } else { - if ((nr >= self->va) || (nr <= self->vs)) - return NR_UNEXPECTED; - } - - /* Invalid nr! */ - return NR_INVALID; -} - -/* - * Function irlap_initiate_connection_state () - * - * Initialize the connection state parameters - * - */ -void irlap_initiate_connection_state(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Next to send and next to receive */ - self->vs = self->vr = 0; - - /* Last frame which got acked (0 - 1) % 8 */ - self->va = 7; - - self->window = 1; - - self->remote_busy = FALSE; - self->retry_count = 0; -} - -/* - * Function irlap_wait_min_turn_around (self, qos) - * - * Wait negotiated minimum turn around time, this function actually sets - * the number of BOS's that must be sent before the next transmitted - * frame in order to delay for the specified amount of time. This is - * done to avoid using timers, and the forbidden udelay! - */ -void irlap_wait_min_turn_around(struct irlap_cb *self, struct qos_info *qos) -{ - __u32 min_turn_time; - __u32 speed; - - /* Get QoS values. */ - speed = qos->baud_rate.value; - min_turn_time = qos->min_turn_time.value; - - /* No need to calculate XBOFs for speeds over 115200 bps */ - if (speed > 115200) { - self->mtt_required = min_turn_time; - return; - } - - /* - * Send additional BOF's for the next frame for the requested - * min turn time, so now we must calculate how many chars (XBOF's) we - * must send for the requested time period (min turn time) - */ - self->xbofs_delay = irlap_min_turn_time_in_bytes(speed, min_turn_time); -} - -/* - * Function irlap_flush_all_queues (void) - * - * Flush all queues - * - */ -void irlap_flush_all_queues(struct irlap_cb *self) -{ - struct sk_buff* skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Free transmission queue */ - while ((skb = skb_dequeue(&self->txq)) != NULL) - dev_kfree_skb(skb); - - while ((skb = skb_dequeue(&self->txq_ultra)) != NULL) - dev_kfree_skb(skb); - - /* Free sliding window buffered packets */ - while ((skb = skb_dequeue(&self->wx_list)) != NULL) - dev_kfree_skb(skb); -} - -/* - * Function irlap_setspeed (self, speed) - * - * Change the speed of the IrDA port - * - */ -static void irlap_change_speed(struct irlap_cb *self, __u32 speed, int now) -{ - struct sk_buff *skb; - - pr_debug("%s(), setting speed to %d\n", __func__, speed); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - self->speed = speed; - - /* Change speed now, or just piggyback speed on frames */ - if (now) { - /* Send down empty frame to trigger speed change */ - skb = alloc_skb(0, GFP_ATOMIC); - if (skb) - irlap_queue_xmit(self, skb); - } -} - -/* - * Function irlap_init_qos_capabilities (self, qos) - * - * Initialize QoS for this IrLAP session, What we do is to compute the - * intersection of the QoS capabilities for the user, driver and for - * IrLAP itself. Normally, IrLAP will not specify any values, but it can - * be used to restrict certain values. - */ -static void irlap_init_qos_capabilities(struct irlap_cb *self, - struct qos_info *qos_user) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(self->netdev != NULL, return;); - - /* Start out with the maximum QoS support possible */ - irda_init_max_qos_capabilies(&self->qos_rx); - - /* Apply drivers QoS capabilities */ - irda_qos_compute_intersection(&self->qos_rx, self->qos_dev); - - /* - * Check for user supplied QoS parameters. The service user is only - * allowed to supply these values. We check each parameter since the - * user may not have set all of them. - */ - if (qos_user) { - pr_debug("%s(), Found user specified QoS!\n", __func__); - - if (qos_user->baud_rate.bits) - self->qos_rx.baud_rate.bits &= qos_user->baud_rate.bits; - - if (qos_user->max_turn_time.bits) - self->qos_rx.max_turn_time.bits &= qos_user->max_turn_time.bits; - if (qos_user->data_size.bits) - self->qos_rx.data_size.bits &= qos_user->data_size.bits; - - if (qos_user->link_disc_time.bits) - self->qos_rx.link_disc_time.bits &= qos_user->link_disc_time.bits; - } - - /* Use 500ms in IrLAP for now */ - self->qos_rx.max_turn_time.bits &= 0x01; - - /* Set data size */ - /*self->qos_rx.data_size.bits &= 0x03;*/ - - irda_qos_bits_to_value(&self->qos_rx); -} - -/* - * Function irlap_apply_default_connection_parameters (void, now) - * - * Use the default connection and transmission parameters - */ -void irlap_apply_default_connection_parameters(struct irlap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* xbofs : Default value in NDM */ - self->next_bofs = 12; - self->bofs_count = 12; - - /* NDM Speed is 9600 */ - irlap_change_speed(self, 9600, TRUE); - - /* Set mbusy when going to NDM state */ - irda_device_set_media_busy(self->netdev, TRUE); - - /* - * Generate random connection address for this session, which must - * be 7 bits wide and different from 0x00 and 0xfe - */ - while ((self->caddr == 0x00) || (self->caddr == 0xfe)) { - get_random_bytes(&self->caddr, sizeof(self->caddr)); - self->caddr &= 0xfe; - } - - /* Use default values until connection has been negitiated */ - self->slot_timeout = sysctl_slot_timeout; - self->final_timeout = FINAL_TIMEOUT; - self->poll_timeout = POLL_TIMEOUT; - self->wd_timeout = WD_TIMEOUT; - - /* Set some default values */ - self->qos_tx.baud_rate.value = 9600; - self->qos_rx.baud_rate.value = 9600; - self->qos_tx.max_turn_time.value = 0; - self->qos_rx.max_turn_time.value = 0; - self->qos_tx.min_turn_time.value = 0; - self->qos_rx.min_turn_time.value = 0; - self->qos_tx.data_size.value = 64; - self->qos_rx.data_size.value = 64; - self->qos_tx.window_size.value = 1; - self->qos_rx.window_size.value = 1; - self->qos_tx.additional_bofs.value = 12; - self->qos_rx.additional_bofs.value = 12; - self->qos_tx.link_disc_time.value = 0; - self->qos_rx.link_disc_time.value = 0; - - irlap_flush_all_queues(self); - - self->disconnect_pending = FALSE; - self->connect_pending = FALSE; -} - -/* - * Function irlap_apply_connection_parameters (qos, now) - * - * Initialize IrLAP with the negotiated QoS values - * - * If 'now' is false, the speed and xbofs will be changed after the next - * frame is sent. - * If 'now' is true, the speed and xbofs is changed immediately - */ -void irlap_apply_connection_parameters(struct irlap_cb *self, int now) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Set the negotiated xbofs value */ - self->next_bofs = self->qos_tx.additional_bofs.value; - if (now) - self->bofs_count = self->next_bofs; - - /* Set the negotiated link speed (may need the new xbofs value) */ - irlap_change_speed(self, self->qos_tx.baud_rate.value, now); - - self->window_size = self->qos_tx.window_size.value; - self->window = self->qos_tx.window_size.value; - -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - /* - * Calculate how many bytes it is possible to transmit before the - * link must be turned around - */ - self->line_capacity = - irlap_max_line_capacity(self->qos_tx.baud_rate.value, - self->qos_tx.max_turn_time.value); - self->bytes_left = self->line_capacity; -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - - - /* - * Initialize timeout values, some of the rules are listed on - * page 92 in IrLAP. - */ - IRDA_ASSERT(self->qos_tx.max_turn_time.value != 0, return;); - IRDA_ASSERT(self->qos_rx.max_turn_time.value != 0, return;); - /* The poll timeout applies only to the primary station. - * It defines the maximum time the primary stay in XMIT mode - * before timeout and turning the link around (sending a RR). - * Or, this is how much we can keep the pf bit in primary mode. - * Therefore, it must be lower or equal than our *OWN* max turn around. - * Jean II */ - self->poll_timeout = msecs_to_jiffies( - self->qos_tx.max_turn_time.value); - /* The Final timeout applies only to the primary station. - * It defines the maximum time the primary wait (mostly in RECV mode) - * for an answer from the secondary station before polling it again. - * Therefore, it must be greater or equal than our *PARTNER* - * max turn around time - Jean II */ - self->final_timeout = msecs_to_jiffies( - self->qos_rx.max_turn_time.value); - /* The Watchdog Bit timeout applies only to the secondary station. - * It defines the maximum time the secondary wait (mostly in RECV mode) - * for poll from the primary station before getting annoyed. - * Therefore, it must be greater or equal than our *PARTNER* - * max turn around time - Jean II */ - self->wd_timeout = self->final_timeout * 2; - - /* - * N1 and N2 are maximum retry count for *both* the final timer - * and the wd timer (with a factor 2) as defined above. - * After N1 retry of a timer, we give a warning to the user. - * After N2 retry, we consider the link dead and disconnect it. - * Jean II - */ - - /* - * Set N1 to 0 if Link Disconnect/Threshold Time = 3 and set it to - * 3 seconds otherwise. See page 71 in IrLAP for more details. - * Actually, it's not always 3 seconds, as we allow to set - * it via sysctl... Max maxtt is 500ms, and N1 need to be multiple - * of 2, so 1 second is minimum we can allow. - Jean II - */ - if (self->qos_tx.link_disc_time.value == sysctl_warn_noreply_time) - /* - * If we set N1 to 0, it will trigger immediately, which is - * not what we want. What we really want is to disable it, - * Jean II - */ - self->N1 = -2; /* Disable - Need to be multiple of 2*/ - else - self->N1 = sysctl_warn_noreply_time * 1000 / - self->qos_rx.max_turn_time.value; - - pr_debug("Setting N1 = %d\n", self->N1); - - /* Set N2 to match our own disconnect time */ - self->N2 = self->qos_tx.link_disc_time.value * 1000 / - self->qos_rx.max_turn_time.value; - pr_debug("Setting N2 = %d\n", self->N2); -} - -#ifdef CONFIG_PROC_FS -struct irlap_iter_state { - int id; -}; - -static void *irlap_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct irlap_iter_state *iter = seq->private; - struct irlap_cb *self; - - /* Protect our access to the tsap list */ - spin_lock_irq(&irlap->hb_spinlock); - iter->id = 0; - - for (self = (struct irlap_cb *) hashbin_get_first(irlap); - self; self = (struct irlap_cb *) hashbin_get_next(irlap)) { - if (iter->id == *pos) - break; - ++iter->id; - } - - return self; -} - -static void *irlap_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct irlap_iter_state *iter = seq->private; - - ++*pos; - ++iter->id; - return (void *) hashbin_get_next(irlap); -} - -static void irlap_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&irlap->hb_spinlock); -} - -static int irlap_seq_show(struct seq_file *seq, void *v) -{ - const struct irlap_iter_state *iter = seq->private; - const struct irlap_cb *self = v; - - IRDA_ASSERT(self->magic == LAP_MAGIC, return -EINVAL;); - - seq_printf(seq, "irlap%d ", iter->id); - seq_printf(seq, "state: %s\n", - irlap_state[self->state]); - - seq_printf(seq, " device name: %s, ", - (self->netdev) ? self->netdev->name : "bug"); - seq_printf(seq, "hardware name: %s\n", self->hw_name); - - seq_printf(seq, " caddr: %#02x, ", self->caddr); - seq_printf(seq, "saddr: %#08x, ", self->saddr); - seq_printf(seq, "daddr: %#08x\n", self->daddr); - - seq_printf(seq, " win size: %d, ", - self->window_size); - seq_printf(seq, "win: %d, ", self->window); -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - seq_printf(seq, "line capacity: %d, ", - self->line_capacity); - seq_printf(seq, "bytes left: %d\n", self->bytes_left); -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - seq_printf(seq, " tx queue len: %d ", - skb_queue_len(&self->txq)); - seq_printf(seq, "win queue len: %d ", - skb_queue_len(&self->wx_list)); - seq_printf(seq, "rbusy: %s", self->remote_busy ? - "TRUE" : "FALSE"); - seq_printf(seq, " mbusy: %s\n", self->media_busy ? - "TRUE" : "FALSE"); - - seq_printf(seq, " retrans: %d ", self->retry_count); - seq_printf(seq, "vs: %d ", self->vs); - seq_printf(seq, "vr: %d ", self->vr); - seq_printf(seq, "va: %d\n", self->va); - - seq_printf(seq, " qos\tbps\tmaxtt\tdsize\twinsize\taddbofs\tmintt\tldisc\tcomp\n"); - - seq_printf(seq, " tx\t%d\t", - self->qos_tx.baud_rate.value); - seq_printf(seq, "%d\t", - self->qos_tx.max_turn_time.value); - seq_printf(seq, "%d\t", - self->qos_tx.data_size.value); - seq_printf(seq, "%d\t", - self->qos_tx.window_size.value); - seq_printf(seq, "%d\t", - self->qos_tx.additional_bofs.value); - seq_printf(seq, "%d\t", - self->qos_tx.min_turn_time.value); - seq_printf(seq, "%d\t", - self->qos_tx.link_disc_time.value); - seq_printf(seq, "\n"); - - seq_printf(seq, " rx\t%d\t", - self->qos_rx.baud_rate.value); - seq_printf(seq, "%d\t", - self->qos_rx.max_turn_time.value); - seq_printf(seq, "%d\t", - self->qos_rx.data_size.value); - seq_printf(seq, "%d\t", - self->qos_rx.window_size.value); - seq_printf(seq, "%d\t", - self->qos_rx.additional_bofs.value); - seq_printf(seq, "%d\t", - self->qos_rx.min_turn_time.value); - seq_printf(seq, "%d\n", - self->qos_rx.link_disc_time.value); - - return 0; -} - -static const struct seq_operations irlap_seq_ops = { - .start = irlap_seq_start, - .next = irlap_seq_next, - .stop = irlap_seq_stop, - .show = irlap_seq_show, -}; - -static int irlap_seq_open(struct inode *inode, struct file *file) -{ - if (irlap == NULL) - return -EINVAL; - - return seq_open_private(file, &irlap_seq_ops, - sizeof(struct irlap_iter_state)); -} - -const struct file_operations irlap_seq_fops = { - .owner = THIS_MODULE, - .open = irlap_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -#endif /* CONFIG_PROC_FS */ diff --git a/net/irda/irlap_event.c b/net/irda/irlap_event.c deleted file mode 100644 index 0e1b4d79f745..000000000000 --- a/net/irda/irlap_event.c +++ /dev/null @@ -1,2316 +0,0 @@ -/********************************************************************* - * - * Filename: irlap_event.c - * Version: 0.9 - * Description: IrLAP state machine implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Aug 16 00:59:29 1997 - * Modified at: Sat Dec 25 21:07:57 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * Copyright (c) 1998 Thomas Davis - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include -#include /* irlmp_flow_indication(), ... */ - -#include - -#ifdef CONFIG_IRDA_FAST_RR -int sysctl_fast_poll_increase = 50; -#endif - -static int irlap_state_ndm (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_query (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_reply (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_conn (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_setup (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_xmit_p (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_pclose (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_nrm_p (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_reset (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_nrm_s (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_xmit_s (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_sclose (struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info); -static int irlap_state_reset_check(struct irlap_cb *, IRLAP_EVENT event, - struct sk_buff *, struct irlap_info *); - -static const char *const irlap_event[] __maybe_unused = { - "DISCOVERY_REQUEST", - "CONNECT_REQUEST", - "CONNECT_RESPONSE", - "DISCONNECT_REQUEST", - "DATA_REQUEST", - "RESET_REQUEST", - "RESET_RESPONSE", - "SEND_I_CMD", - "SEND_UI_FRAME", - "RECV_DISCOVERY_XID_CMD", - "RECV_DISCOVERY_XID_RSP", - "RECV_SNRM_CMD", - "RECV_TEST_CMD", - "RECV_TEST_RSP", - "RECV_UA_RSP", - "RECV_DM_RSP", - "RECV_RD_RSP", - "RECV_I_CMD", - "RECV_I_RSP", - "RECV_UI_FRAME", - "RECV_FRMR_RSP", - "RECV_RR_CMD", - "RECV_RR_RSP", - "RECV_RNR_CMD", - "RECV_RNR_RSP", - "RECV_REJ_CMD", - "RECV_REJ_RSP", - "RECV_SREJ_CMD", - "RECV_SREJ_RSP", - "RECV_DISC_CMD", - "SLOT_TIMER_EXPIRED", - "QUERY_TIMER_EXPIRED", - "FINAL_TIMER_EXPIRED", - "POLL_TIMER_EXPIRED", - "DISCOVERY_TIMER_EXPIRED", - "WD_TIMER_EXPIRED", - "BACKOFF_TIMER_EXPIRED", - "MEDIA_BUSY_TIMER_EXPIRED", -}; - -const char *const irlap_state[] = { - "LAP_NDM", - "LAP_QUERY", - "LAP_REPLY", - "LAP_CONN", - "LAP_SETUP", - "LAP_OFFLINE", - "LAP_XMIT_P", - "LAP_PCLOSE", - "LAP_NRM_P", - "LAP_RESET_WAIT", - "LAP_RESET", - "LAP_NRM_S", - "LAP_XMIT_S", - "LAP_SCLOSE", - "LAP_RESET_CHECK", -}; - -static int (*state[])(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) = -{ - irlap_state_ndm, - irlap_state_query, - irlap_state_reply, - irlap_state_conn, - irlap_state_setup, - irlap_state_offline, - irlap_state_xmit_p, - irlap_state_pclose, - irlap_state_nrm_p, - irlap_state_reset_wait, - irlap_state_reset, - irlap_state_nrm_s, - irlap_state_xmit_s, - irlap_state_sclose, - irlap_state_reset_check, -}; - -/* - * Function irda_poll_timer_expired (data) - * - * Poll timer has expired. Normally we must now send a RR frame to the - * remote device - */ -static void irlap_poll_timer_expired(void *data) -{ - struct irlap_cb *self = (struct irlap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Calculate and set time before we will have to send back the pf bit - * to the peer. Use in primary. - * Make sure that state is XMIT_P/XMIT_S when calling this function - * (and that nobody messed up with the state). - Jean II - */ -static void irlap_start_poll_timer(struct irlap_cb *self, int timeout) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - -#ifdef CONFIG_IRDA_FAST_RR - /* - * Send out the RR frames faster if our own transmit queue is empty, or - * if the peer is busy. The effect is a much faster conversation - */ - if (skb_queue_empty(&self->txq) || self->remote_busy) { - if (self->fast_RR == TRUE) { - /* - * Assert that the fast poll timer has not reached the - * normal poll timer yet - */ - if (self->fast_RR_timeout < timeout) { - /* - * FIXME: this should be a more configurable - * function - */ - self->fast_RR_timeout += - (sysctl_fast_poll_increase * HZ/1000); - - /* Use this fast(er) timeout instead */ - timeout = self->fast_RR_timeout; - } - } else { - self->fast_RR = TRUE; - - /* Start with just 0 ms */ - self->fast_RR_timeout = 0; - timeout = 0; - } - } else - self->fast_RR = FALSE; - - pr_debug("%s(), timeout=%d (%ld)\n", __func__, timeout, jiffies); -#endif /* CONFIG_IRDA_FAST_RR */ - - if (timeout == 0) - irlap_do_event(self, POLL_TIMER_EXPIRED, NULL, NULL); - else - irda_start_timer(&self->poll_timer, timeout, self, - irlap_poll_timer_expired); -} - -/* - * Function irlap_do_event (event, skb, info) - * - * Rushes through the state machine without any delay. If state == XMIT - * then send queued data frames. - */ -void irlap_do_event(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret; - - if (!self || self->magic != LAP_MAGIC) - return; - - pr_debug("%s(), event = %s, state = %s\n", __func__, - irlap_event[event], irlap_state[self->state]); - - ret = (*state[self->state])(self, event, skb, info); - - /* - * Check if there are any pending events that needs to be executed - */ - switch (self->state) { - case LAP_XMIT_P: /* FALLTHROUGH */ - case LAP_XMIT_S: - /* - * We just received the pf bit and are at the beginning - * of a new LAP transmit window. - * Check if there are any queued data frames, and do not - * try to disconnect link if we send any data frames, since - * that will change the state away form XMIT - */ - pr_debug("%s() : queue len = %d\n", __func__, - skb_queue_len(&self->txq)); - - if (!skb_queue_empty(&self->txq)) { - /* Prevent race conditions with irlap_data_request() */ - self->local_busy = TRUE; - - /* Theory of operation. - * We send frames up to when we fill the window or - * reach line capacity. Those frames will queue up - * in the device queue, and the driver will slowly - * send them. - * After each frame that we send, we poll the higher - * layer for more data. It's the right time to do - * that because the link layer need to perform the mtt - * and then send the first frame, so we can afford - * to send a bit of time in kernel space. - * The explicit flow indication allow to minimise - * buffers (== lower latency), to avoid higher layer - * polling via timers (== less context switches) and - * to implement a crude scheduler - Jean II */ - - /* Try to send away all queued data frames */ - while ((skb = skb_dequeue(&self->txq)) != NULL) { - /* Send one frame */ - ret = (*state[self->state])(self, SEND_I_CMD, - skb, NULL); - /* Drop reference count. - * It will be increase as needed in - * irlap_send_data_xxx() */ - kfree_skb(skb); - - /* Poll the higher layers for one more frame */ - irlmp_flow_indication(self->notify.instance, - FLOW_START); - - if (ret == -EPROTO) - break; /* Try again later! */ - } - /* Finished transmitting */ - self->local_busy = FALSE; - } else if (self->disconnect_pending) { - self->disconnect_pending = FALSE; - - ret = (*state[self->state])(self, DISCONNECT_REQUEST, - NULL, NULL); - } - break; -/* case LAP_NDM: */ -/* case LAP_CONN: */ -/* case LAP_RESET_WAIT: */ -/* case LAP_RESET_CHECK: */ - default: - break; - } -} - -/* - * Function irlap_state_ndm (event, skb, frame) - * - * NDM (Normal Disconnected Mode) state - * - */ -static int irlap_state_ndm(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - discovery_t *discovery_rsp; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case CONNECT_REQUEST: - IRDA_ASSERT(self->netdev != NULL, return -1;); - - if (self->media_busy) { - /* Note : this will never happen, because we test - * media busy in irlap_connect_request() and - * postpone the event... - Jean II */ - pr_debug("%s(), CONNECT_REQUEST: media busy!\n", - __func__); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_MEDIA_BUSY); - } else { - irlap_send_snrm_frame(self, &self->qos_rx); - - /* Start Final-bit timer */ - irlap_start_final_timer(self, self->final_timeout); - - self->retry_count = 0; - irlap_next_state(self, LAP_SETUP); - } - break; - case RECV_SNRM_CMD: - /* Check if the frame contains and I field */ - if (info) { - self->daddr = info->daddr; - self->caddr = info->caddr; - - irlap_next_state(self, LAP_CONN); - - irlap_connect_indication(self, skb); - } else { - pr_debug("%s(), SNRM frame does not contain an I field!\n", - __func__); - } - break; - case DISCOVERY_REQUEST: - IRDA_ASSERT(info != NULL, return -1;); - - if (self->media_busy) { - pr_debug("%s(), DISCOVERY_REQUEST: media busy!\n", - __func__); - /* irlap->log.condition = MEDIA_BUSY; */ - - /* This will make IrLMP try again */ - irlap_discovery_confirm(self, NULL); - /* Note : the discovery log is not cleaned up here, - * it will be done in irlap_discovery_request() - * Jean II */ - return 0; - } - - self->S = info->S; - self->s = info->s; - irlap_send_discovery_xid_frame(self, info->S, info->s, TRUE, - info->discovery); - self->frame_sent = FALSE; - self->s++; - - irlap_start_slot_timer(self, self->slot_timeout); - irlap_next_state(self, LAP_QUERY); - break; - case RECV_DISCOVERY_XID_CMD: - IRDA_ASSERT(info != NULL, return -1;); - - /* Assert that this is not the final slot */ - if (info->s <= info->S) { - self->slot = irlap_generate_rand_time_slot(info->S, - info->s); - if (self->slot == info->s) { - discovery_rsp = irlmp_get_discovery_response(); - discovery_rsp->data.daddr = info->daddr; - - irlap_send_discovery_xid_frame(self, info->S, - self->slot, - FALSE, - discovery_rsp); - self->frame_sent = TRUE; - } else - self->frame_sent = FALSE; - - /* - * Go to reply state until end of discovery to - * inhibit our own transmissions. Set the timer - * to not stay forever there... Jean II - */ - irlap_start_query_timer(self, info->S, info->s); - irlap_next_state(self, LAP_REPLY); - } else { - /* This is the final slot. How is it possible ? - * This would happen is both discoveries are just slightly - * offset (if they are in sync, all packets are lost). - * Most often, all the discovery requests will be received - * in QUERY state (see my comment there), except for the - * last frame that will come here. - * The big trouble when it happen is that active discovery - * doesn't happen, because nobody answer the discoveries - * frame of the other guy, so the log shows up empty. - * What should we do ? - * Not much. It's too late to answer those discovery frames, - * so we just pass the info to IrLMP who will put it in the - * log (and post an event). - * Another cause would be devices that do discovery much - * slower than us, however the latest fixes should minimise - * those cases... - * Jean II - */ - pr_debug("%s(), Receiving final discovery request, missed the discovery slots :-(\n", - __func__); - - /* Last discovery request -> in the log */ - irlap_discovery_indication(self, info->discovery); - } - break; - case MEDIA_BUSY_TIMER_EXPIRED: - /* A bunch of events may be postponed because the media is - * busy (usually immediately after we close a connection), - * or while we are doing discovery (state query/reply). - * In all those cases, the media busy flag will be cleared - * when it's OK for us to process those postponed events. - * This event is not mentioned in the state machines in the - * IrLAP spec. It's because they didn't consider Ultra and - * postponing connection request is optional. - * Jean II */ -#ifdef CONFIG_IRDA_ULTRA - /* Send any pending Ultra frames if any */ - if (!skb_queue_empty(&self->txq_ultra)) { - /* We don't send the frame, just post an event. - * Also, previously this code was in timer.c... - * Jean II */ - ret = (*state[self->state])(self, SEND_UI_FRAME, - NULL, NULL); - } -#endif /* CONFIG_IRDA_ULTRA */ - /* Check if we should try to connect. - * This code was previously in irlap_do_event() */ - if (self->connect_pending) { - self->connect_pending = FALSE; - - /* This one *should* not pend in this state, except - * if a socket try to connect and immediately - * disconnect. - clear - Jean II */ - if (self->disconnect_pending) - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - else - ret = (*state[self->state])(self, - CONNECT_REQUEST, - NULL, NULL); - self->disconnect_pending = FALSE; - } - /* Note : one way to test if this code works well (including - * media busy and small busy) is to create a user space - * application generating an Ultra packet every 3.05 sec (or - * 2.95 sec) and to see how it interact with discovery. - * It's fairly easy to check that no packet is lost, that the - * packets are postponed during discovery and that after - * discovery indication you have a 100ms "gap". - * As connection request and Ultra are now processed the same - * way, this avoid the tedious job of trying IrLAP connection - * in all those cases... - * Jean II */ - break; -#ifdef CONFIG_IRDA_ULTRA - case SEND_UI_FRAME: - { - int i; - /* Only allowed to repeat an operation twice */ - for (i=0; ((i<2) && (self->media_busy == FALSE)); i++) { - skb = skb_dequeue(&self->txq_ultra); - if (skb) - irlap_send_ui_frame(self, skb, CBROADCAST, - CMD_FRAME); - else - break; - /* irlap_send_ui_frame() won't increase skb reference - * count, so no dev_kfree_skb() - Jean II */ - } - if (i == 2) { - /* Force us to listen 500 ms again */ - irda_device_set_media_busy(self->netdev, TRUE); - } - break; - } - case RECV_UI_FRAME: - /* Only accept broadcast frames in NDM mode */ - if (info->caddr != CBROADCAST) { - pr_debug("%s(), not a broadcast frame!\n", - __func__); - } else - irlap_unitdata_indication(self, skb); - break; -#endif /* CONFIG_IRDA_ULTRA */ - case RECV_TEST_CMD: - /* Remove test frame header */ - skb_pull(skb, sizeof(struct test_frame)); - - /* - * Send response. This skb will not be sent out again, and - * will only be used to send out the same info as the cmd - */ - irlap_send_test_frame(self, CBROADCAST, info->daddr, skb); - break; - case RECV_TEST_RSP: - pr_debug("%s() not implemented!\n", __func__); - break; - default: - pr_debug("%s(), Unknown event %s\n", __func__, - irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_query (event, skb, info) - * - * QUERY state - * - */ -static int irlap_state_query(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RECV_DISCOVERY_XID_RSP: - IRDA_ASSERT(info != NULL, return -1;); - IRDA_ASSERT(info->discovery != NULL, return -1;); - - pr_debug("%s(), daddr=%08x\n", __func__, - info->discovery->data.daddr); - - if (!self->discovery_log) { - net_warn_ratelimited("%s: discovery log is gone! maybe the discovery timeout has been set too short?\n", - __func__); - break; - } - hashbin_insert(self->discovery_log, - (irda_queue_t *) info->discovery, - info->discovery->data.daddr, NULL); - - /* Keep state */ - /* irlap_next_state(self, LAP_QUERY); */ - - break; - case RECV_DISCOVERY_XID_CMD: - /* Yes, it is possible to receive those frames in this mode. - * Note that most often the last discovery request won't - * occur here but in NDM state (see my comment there). - * What should we do ? - * Not much. We are currently performing our own discovery, - * therefore we can't answer those frames. We don't want - * to change state either. We just pass the info to - * IrLMP who will put it in the log (and post an event). - * Jean II - */ - - IRDA_ASSERT(info != NULL, return -1;); - - pr_debug("%s(), Receiving discovery request (s = %d) while performing discovery :-(\n", - __func__, info->s); - - /* Last discovery request ? */ - if (info->s == 0xff) - irlap_discovery_indication(self, info->discovery); - break; - case SLOT_TIMER_EXPIRED: - /* - * Wait a little longer if we detect an incoming frame. This - * is not mentioned in the spec, but is a good thing to do, - * since we want to work even with devices that violate the - * timing requirements. - */ - if (irda_device_is_receiving(self->netdev) && !self->add_wait) { - pr_debug("%s(), device is slow to answer, waiting some more!\n", - __func__); - irlap_start_slot_timer(self, msecs_to_jiffies(10)); - self->add_wait = TRUE; - return ret; - } - self->add_wait = FALSE; - - if (self->s < self->S) { - irlap_send_discovery_xid_frame(self, self->S, - self->s, TRUE, - self->discovery_cmd); - self->s++; - irlap_start_slot_timer(self, self->slot_timeout); - - /* Keep state */ - irlap_next_state(self, LAP_QUERY); - } else { - /* This is the final slot! */ - irlap_send_discovery_xid_frame(self, self->S, 0xff, - TRUE, - self->discovery_cmd); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - /* - * We are now finished with the discovery procedure, - * so now we must return the results - */ - irlap_discovery_confirm(self, self->discovery_log); - - /* IrLMP should now have taken care of the log */ - self->discovery_log = NULL; - } - break; - default: - pr_debug("%s(), Unknown event %s\n", __func__, - irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_reply (self, event, skb, info) - * - * REPLY, we have received a XID discovery frame from a device and we - * are waiting for the right time slot to send a response XID frame - * - */ -static int irlap_state_reply(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - discovery_t *discovery_rsp; - int ret=0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case QUERY_TIMER_EXPIRED: - pr_debug("%s(), QUERY_TIMER_EXPIRED <%ld>\n", - __func__, jiffies); - irlap_next_state(self, LAP_NDM); - break; - case RECV_DISCOVERY_XID_CMD: - IRDA_ASSERT(info != NULL, return -1;); - /* Last frame? */ - if (info->s == 0xff) { - del_timer(&self->query_timer); - - /* info->log.condition = REMOTE; */ - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_discovery_indication(self, info->discovery); - } else { - /* If it's our slot, send our reply */ - if ((info->s >= self->slot) && (!self->frame_sent)) { - discovery_rsp = irlmp_get_discovery_response(); - discovery_rsp->data.daddr = info->daddr; - - irlap_send_discovery_xid_frame(self, info->S, - self->slot, - FALSE, - discovery_rsp); - - self->frame_sent = TRUE; - } - /* Readjust our timer to accommodate devices - * doing faster or slower discovery than us... - * Jean II */ - irlap_start_query_timer(self, info->S, info->s); - - /* Keep state */ - //irlap_next_state(self, LAP_REPLY); - } - break; - default: - pr_debug("%s(), Unknown event %d, %s\n", __func__, - event, irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_conn (event, skb, info) - * - * CONN, we have received a SNRM command and is waiting for the upper - * layer to accept or refuse connection - * - */ -static int irlap_state_conn(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case CONNECT_RESPONSE: - skb_pull(skb, sizeof(struct snrm_frame)); - - IRDA_ASSERT(self->netdev != NULL, return -1;); - - irlap_qos_negotiate(self, skb); - - irlap_initiate_connection_state(self); - - /* - * Applying the parameters now will make sure we change speed - * *after* we have sent the next frame - */ - irlap_apply_connection_parameters(self, FALSE); - - /* - * Sending this frame will force a speed change after it has - * been sent (i.e. the frame will be sent at 9600). - */ - irlap_send_ua_response_frame(self, &self->qos_rx); - -#if 0 - /* - * We are allowed to send two frames, but this may increase - * the connect latency, so lets not do it for now. - */ - /* This is full of good intentions, but doesn't work in - * practice. - * After sending the first UA response, we switch the - * dongle to the negotiated speed, which is usually - * different than 9600 kb/s. - * From there, there is two solutions : - * 1) The other end has received the first UA response : - * it will set up the connection, move to state LAP_NRM_P, - * and will ignore and drop the second UA response. - * Actually, it's even worse : the other side will almost - * immediately send a RR that will likely collide with the - * UA response (depending on negotiated turnaround). - * 2) The other end has not received the first UA response, - * will stay at 9600 and will never see the second UA response. - * Jean II */ - irlap_send_ua_response_frame(self, &self->qos_rx); -#endif - - /* - * The WD-timer could be set to the duration of the P-timer - * for this case, but it is recommended to use twice the - * value (note 3 IrLAP p. 60). - */ - irlap_start_wd_timer(self, self->wd_timeout); - irlap_next_state(self, LAP_NRM_S); - - break; - case RECV_DISCOVERY_XID_CMD: - pr_debug("%s(), event RECV_DISCOVER_XID_CMD!\n", - __func__); - irlap_next_state(self, LAP_NDM); - - break; - case DISCONNECT_REQUEST: - pr_debug("%s(), Disconnect request!\n", __func__); - irlap_send_dm_frame(self); - irlap_next_state( self, LAP_NDM); - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - default: - pr_debug("%s(), Unknown event %d, %s\n", __func__, - event, irlap_event[event]); - - ret = -1; - break; - } - - return ret; -} - -/* - * Function irlap_state_setup (event, skb, frame) - * - * SETUP state, The local layer has transmitted a SNRM command frame to - * a remote peer layer and is awaiting a reply . - * - */ -static int irlap_state_setup(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case FINAL_TIMER_EXPIRED: - if (self->retry_count < self->N3) { -/* - * Perform random backoff, Wait a random number of time units, minimum - * duration half the time taken to transmitt a SNRM frame, maximum duration - * 1.5 times the time taken to transmit a SNRM frame. So this time should - * between 15 msecs and 45 msecs. - */ - irlap_start_backoff_timer(self, msecs_to_jiffies(20 + - (jiffies % 30))); - } else { - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_FOUND_NONE); - } - break; - case BACKOFF_TIMER_EXPIRED: - irlap_send_snrm_frame(self, &self->qos_rx); - irlap_start_final_timer(self, self->final_timeout); - self->retry_count++; - break; - case RECV_SNRM_CMD: - pr_debug("%s(), SNRM battle!\n", __func__); - - IRDA_ASSERT(skb != NULL, return 0;); - IRDA_ASSERT(info != NULL, return 0;); - - /* - * The device with the largest device address wins the battle - * (both have sent a SNRM command!) - */ - if (info &&(info->daddr > self->saddr)) { - del_timer(&self->final_timer); - irlap_initiate_connection_state(self); - - IRDA_ASSERT(self->netdev != NULL, return -1;); - - skb_pull(skb, sizeof(struct snrm_frame)); - - irlap_qos_negotiate(self, skb); - - /* Send UA frame and then change link settings */ - irlap_apply_connection_parameters(self, FALSE); - irlap_send_ua_response_frame(self, &self->qos_rx); - - irlap_next_state(self, LAP_NRM_S); - irlap_connect_confirm(self, skb); - - /* - * The WD-timer could be set to the duration of the - * P-timer for this case, but it is recommended - * to use twice the value (note 3 IrLAP p. 60). - */ - irlap_start_wd_timer(self, self->wd_timeout); - } else { - /* We just ignore the other device! */ - irlap_next_state(self, LAP_SETUP); - } - break; - case RECV_UA_RSP: - /* Stop F-timer */ - del_timer(&self->final_timer); - - /* Initiate connection state */ - irlap_initiate_connection_state(self); - - /* Negotiate connection parameters */ - IRDA_ASSERT(skb->len > 10, return -1;); - - skb_pull(skb, sizeof(struct ua_frame)); - - IRDA_ASSERT(self->netdev != NULL, return -1;); - - irlap_qos_negotiate(self, skb); - - /* Set the new link setting *now* (before the rr frame) */ - irlap_apply_connection_parameters(self, TRUE); - self->retry_count = 0; - - /* Wait for turnaround time to give a chance to the other - * device to be ready to receive us. - * Note : the time to switch speed is typically larger - * than the turnaround time, but as we don't have the other - * side speed switch time, that's our best guess... - * Jean II */ - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* This frame will actually be sent at the new speed */ - irlap_send_rr_frame(self, CMD_FRAME); - - /* The timer is set to half the normal timer to quickly - * detect a failure to negotiate the new connection - * parameters. IrLAP 6.11.3.2, note 3. - * Note that currently we don't process this failure - * properly, as we should do a quick disconnect. - * Jean II */ - irlap_start_final_timer(self, self->final_timeout/2); - irlap_next_state(self, LAP_NRM_P); - - irlap_connect_confirm(self, skb); - break; - case RECV_DM_RSP: /* FALLTHROUGH */ - case RECV_DISC_CMD: - del_timer(&self->final_timer); - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - default: - pr_debug("%s(), Unknown event %d, %s\n", __func__, - event, irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_offline (self, event, skb, info) - * - * OFFLINE state, not used for now! - * - */ -static int irlap_state_offline(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - pr_debug("%s(), Unknown event\n", __func__); - - return -1; -} - -/* - * Function irlap_state_xmit_p (self, event, skb, info) - * - * XMIT, Only the primary station has right to transmit, and we - * therefore do not expect to receive any transmissions from other - * stations. - * - */ -static int irlap_state_xmit_p(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - switch (event) { - case SEND_I_CMD: - /* - * Only send frame if send-window > 0. - */ - if ((self->window > 0) && (!self->remote_busy)) { - int nextfit; -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - struct sk_buff *skb_next; - - /* With DYNAMIC_WINDOW, we keep the window size - * maximum, and adapt on the packets we are sending. - * At 115k, we can send only 2 packets of 2048 bytes - * in a 500 ms turnaround. Without this option, we - * would always limit the window to 2. With this - * option, if we send smaller packets, we can send - * up to 7 of them (always depending on QoS). - * Jean II */ - - /* Look at the next skb. This is safe, as we are - * the only consumer of the Tx queue (if we are not, - * we have other problems) - Jean II */ - skb_next = skb_peek(&self->txq); - - /* Check if a subsequent skb exist and would fit in - * the current window (with respect to turnaround - * time). - * This allow us to properly mark the current packet - * with the pf bit, to avoid falling back on the - * second test below, and avoid waiting the - * end of the window and sending a extra RR. - * Note : (skb_next != NULL) <=> (skb_queue_len() > 0) - * Jean II */ - nextfit = ((skb_next != NULL) && - ((skb_next->len + skb->len) <= - self->bytes_left)); - - /* - * The current packet may not fit ! Because of test - * above, this should not happen any more !!! - * Test if we have transmitted more bytes over the - * link than its possible to do with the current - * speed and turn-around-time. - */ - if((!nextfit) && (skb->len > self->bytes_left)) { - pr_debug("%s(), Not allowed to transmit more bytes!\n", - __func__); - /* Requeue the skb */ - skb_queue_head(&self->txq, skb_get(skb)); - /* - * We should switch state to LAP_NRM_P, but - * that is not possible since we must be sure - * that we poll the other side. Since we have - * used up our time, the poll timer should - * trigger anyway now, so we just wait for it - * DB - */ - /* - * Sorry, but that's not totally true. If - * we send 2000B packets, we may wait another - * 1000B until our turnaround expire. That's - * why we need to be proactive in avoiding - * coming here. - Jean II - */ - return -EPROTO; - } - - /* Subtract space used by this skb */ - self->bytes_left -= skb->len; -#else /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* Window has been adjusted for the max packet - * size, so much simpler... - Jean II */ - nextfit = !skb_queue_empty(&self->txq); -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* - * Send data with poll bit cleared only if window > 1 - * and there is more frames after this one to be sent - */ - if ((self->window > 1) && (nextfit)) { - /* More packet to send in current window */ - irlap_send_data_primary(self, skb); - irlap_next_state(self, LAP_XMIT_P); - } else { - /* Final packet of window */ - irlap_send_data_primary_poll(self, skb); - - /* - * Make sure state machine does not try to send - * any more frames - */ - ret = -EPROTO; - } -#ifdef CONFIG_IRDA_FAST_RR - /* Peer may want to reply immediately */ - self->fast_RR = FALSE; -#endif /* CONFIG_IRDA_FAST_RR */ - } else { - pr_debug("%s(), Unable to send! remote busy?\n", - __func__); - skb_queue_head(&self->txq, skb_get(skb)); - - /* - * The next ret is important, because it tells - * irlap_next_state _not_ to deliver more frames - */ - ret = -EPROTO; - } - break; - case POLL_TIMER_EXPIRED: - pr_debug("%s(), POLL_TIMER_EXPIRED <%ld>\n", - __func__, jiffies); - irlap_send_rr_frame(self, CMD_FRAME); - /* Return to NRM properly - Jean II */ - self->window = self->window_size; -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - /* Allowed to transmit a maximum number of bytes again. */ - self->bytes_left = self->line_capacity; -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_NRM_P); - break; - case DISCONNECT_REQUEST: - del_timer(&self->poll_timer); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_disc_frame(self); - irlap_flush_all_queues(self); - irlap_start_final_timer(self, self->final_timeout); - self->retry_count = 0; - irlap_next_state(self, LAP_PCLOSE); - break; - case DATA_REQUEST: - /* Nothing to do, irlap_do_event() will send the packet - * when we return... - Jean II */ - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlap_event[event]); - - ret = -EINVAL; - break; - } - return ret; -} - -/* - * Function irlap_state_pclose (event, skb, info) - * - * PCLOSE state - */ -static int irlap_state_pclose(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RECV_UA_RSP: /* FALLTHROUGH */ - case RECV_DM_RSP: - del_timer(&self->final_timer); - - /* Set new link parameters */ - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - case FINAL_TIMER_EXPIRED: - if (self->retry_count < self->N3) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_disc_frame(self); - irlap_start_final_timer(self, self->final_timeout); - self->retry_count++; - /* Keep state */ - } else { - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - } - break; - default: - pr_debug("%s(), Unknown event %d\n", __func__, event); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_nrm_p (self, event, skb, info) - * - * NRM_P (Normal Response Mode as Primary), The primary station has given - * permissions to a secondary station to transmit IrLAP resonse frames - * (by sending a frame with the P bit set). The primary station will not - * transmit any frames and is expecting to receive frames only from the - * secondary to which transmission permissions has been given. - */ -static int irlap_state_nrm_p(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - int ns_status; - int nr_status; - - switch (event) { - case RECV_I_RSP: /* Optimize for the common case */ - if (unlikely(skb->len <= LAP_ADDR_HEADER + LAP_CTRL_HEADER)) { - /* - * Input validation check: a stir4200/mcp2150 - * combination sometimes results in an empty i:rsp. - * This makes no sense; we can just ignore the frame - * and send an rr:cmd immediately. This happens before - * changing nr or ns so triggers a retransmit - */ - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - /* Keep state */ - break; - } - /* FIXME: must check for remote_busy below */ -#ifdef CONFIG_IRDA_FAST_RR - /* - * Reset the fast_RR so we can use the fast RR code with - * full speed the next time since peer may have more frames - * to transmitt - */ - self->fast_RR = FALSE; -#endif /* CONFIG_IRDA_FAST_RR */ - IRDA_ASSERT( info != NULL, return -1;); - - ns_status = irlap_validate_ns_received(self, info->ns); - nr_status = irlap_validate_nr_received(self, info->nr); - - /* - * Check for expected I(nformation) frame - */ - if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) { - - /* Update Vr (next frame for us to receive) */ - self->vr = (self->vr + 1) % 8; - - /* Update Nr received, cleanup our retry queue */ - irlap_update_nr_received(self, info->nr); - - /* - * Got expected NR, so reset the - * retry_count. This is not done by IrLAP spec, - * which is strange! - */ - self->retry_count = 0; - self->ack_required = TRUE; - - /* poll bit cleared? */ - if (!info->pf) { - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_P); - - irlap_data_indication(self, skb, FALSE); - } else { - /* No longer waiting for pf */ - del_timer(&self->final_timer); - - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* Call higher layer *before* changing state - * to give them a chance to send data in the - * next LAP frame. - * Jean II */ - irlap_data_indication(self, skb, FALSE); - - /* XMIT states are the most dangerous state - * to be in, because user requests are - * processed directly and may change state. - * On the other hand, in NDM_P, those - * requests are queued and we will process - * them when we return to irlap_do_event(). - * Jean II - */ - irlap_next_state(self, LAP_XMIT_P); - - /* This is the last frame. - * Make sure it's always called in XMIT state. - * - Jean II */ - irlap_start_poll_timer(self, self->poll_timeout); - } - break; - - } - /* Unexpected next to send (Ns) */ - if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED)) - { - if (!info->pf) { - irlap_update_nr_received(self, info->nr); - - /* - * Wait until the last frame before doing - * anything - */ - - /* Keep state */ - irlap_next_state(self, LAP_NRM_P); - } else { - pr_debug("%s(), missing or duplicate frame!\n", - __func__); - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - - self->ack_required = FALSE; - - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_NRM_P); - } - break; - } - /* - * Unexpected next to receive (Nr) - */ - if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED)) - { - if (info->pf) { - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* Resend rejected frames */ - irlap_resend_rejected_frames(self, CMD_FRAME); - - self->ack_required = FALSE; - - /* Make sure we account for the time - * to transmit our frames. See comemnts - * in irlap_send_data_primary_poll(). - * Jean II */ - irlap_start_final_timer(self, 2 * self->final_timeout); - - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_P); - - irlap_data_indication(self, skb, FALSE); - } else { - /* - * Do not resend frames until the last - * frame has arrived from the other - * device. This is not documented in - * IrLAP!! - */ - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - self->ack_required = FALSE; - - /* Keep state, do not move this line!*/ - irlap_next_state(self, LAP_NRM_P); - - irlap_data_indication(self, skb, FALSE); - } - break; - } - /* - * Unexpected next to send (Ns) and next to receive (Nr) - * Not documented by IrLAP! - */ - if ((ns_status == NS_UNEXPECTED) && - (nr_status == NR_UNEXPECTED)) - { - pr_debug("%s(), unexpected nr and ns!\n", - __func__); - if (info->pf) { - /* Resend rejected frames */ - irlap_resend_rejected_frames(self, CMD_FRAME); - - /* Give peer some time to retransmit! - * But account for our own Tx. */ - irlap_start_final_timer(self, 2 * self->final_timeout); - - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_P); - } else { - /* Update Nr received */ - /* irlap_update_nr_received( info->nr); */ - - self->ack_required = FALSE; - } - break; - } - - /* - * Invalid NR or NS - */ - if ((nr_status == NR_INVALID) || (ns_status == NS_INVALID)) { - if (info->pf) { - del_timer(&self->final_timer); - - irlap_next_state(self, LAP_RESET_WAIT); - - irlap_disconnect_indication(self, LAP_RESET_INDICATION); - self->xmitflag = TRUE; - } else { - del_timer(&self->final_timer); - - irlap_disconnect_indication(self, LAP_RESET_INDICATION); - - self->xmitflag = FALSE; - } - break; - } - pr_debug("%s(), Not implemented!\n", __func__); - pr_debug("%s(), event=%s, ns_status=%d, nr_status=%d\n", - __func__, irlap_event[event], ns_status, nr_status); - break; - case RECV_UI_FRAME: - /* Poll bit cleared? */ - if (!info->pf) { - irlap_data_indication(self, skb, TRUE); - irlap_next_state(self, LAP_NRM_P); - } else { - del_timer(&self->final_timer); - irlap_data_indication(self, skb, TRUE); - irlap_next_state(self, LAP_XMIT_P); - pr_debug("%s: RECV_UI_FRAME: next state %s\n", - __func__, irlap_state[self->state]); - irlap_start_poll_timer(self, self->poll_timeout); - } - break; - case RECV_RR_RSP: - /* - * If you get a RR, the remote isn't busy anymore, - * no matter what the NR - */ - self->remote_busy = FALSE; - - /* Stop final timer */ - del_timer(&self->final_timer); - - /* - * Nr as expected? - */ - ret = irlap_validate_nr_received(self, info->nr); - if (ret == NR_EXPECTED) { - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* - * Got expected NR, so reset the retry_count. This - * is not done by the IrLAP standard , which is - * strange! DB. - */ - self->retry_count = 0; - irlap_wait_min_turn_around(self, &self->qos_tx); - - irlap_next_state(self, LAP_XMIT_P); - - /* Start poll timer */ - irlap_start_poll_timer(self, self->poll_timeout); - } else if (ret == NR_UNEXPECTED) { - IRDA_ASSERT(info != NULL, return -1;); - /* - * Unexpected nr! - */ - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - pr_debug("RECV_RR_FRAME: Retrans:%d, nr=%d, va=%d, vs=%d, vr=%d\n", - self->retry_count, info->nr, self->va, - self->vs, self->vr); - - /* Resend rejected frames */ - irlap_resend_rejected_frames(self, CMD_FRAME); - irlap_start_final_timer(self, self->final_timeout * 2); - - irlap_next_state(self, LAP_NRM_P); - } else if (ret == NR_INVALID) { - pr_debug("%s(), Received RR with invalid nr !\n", - __func__); - - irlap_next_state(self, LAP_RESET_WAIT); - - irlap_disconnect_indication(self, LAP_RESET_INDICATION); - self->xmitflag = TRUE; - } - break; - case RECV_RNR_RSP: - IRDA_ASSERT(info != NULL, return -1;); - - /* Stop final timer */ - del_timer(&self->final_timer); - self->remote_busy = TRUE; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - irlap_next_state(self, LAP_XMIT_P); - - /* Start poll timer */ - irlap_start_poll_timer(self, self->poll_timeout); - break; - case RECV_FRMR_RSP: - del_timer(&self->final_timer); - self->xmitflag = TRUE; - irlap_next_state(self, LAP_RESET_WAIT); - irlap_reset_indication(self); - break; - case FINAL_TIMER_EXPIRED: - /* - * We are allowed to wait for additional 300 ms if - * final timer expires when we are in the middle - * of receiving a frame (page 45, IrLAP). Check that - * we only do this once for each frame. - */ - if (irda_device_is_receiving(self->netdev) && !self->add_wait) { - pr_debug("FINAL_TIMER_EXPIRED when receiving a frame! Waiting a little bit more!\n"); - irlap_start_final_timer(self, msecs_to_jiffies(300)); - - /* - * Don't allow this to happen one more time in a row, - * or else we can get a pretty tight loop here if - * if we only receive half a frame. DB. - */ - self->add_wait = TRUE; - break; - } - self->add_wait = FALSE; - - /* N2 is the disconnect timer. Until we reach it, we retry */ - if (self->retry_count < self->N2) { - if (skb_peek(&self->wx_list) == NULL) { - /* Retry sending the pf bit to the secondary */ - pr_debug("nrm_p: resending rr"); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - } else { - pr_debug("nrm_p: resend frames"); - irlap_resend_rejected_frames(self, CMD_FRAME); - } - - irlap_start_final_timer(self, self->final_timeout); - self->retry_count++; - pr_debug("irlap_state_nrm_p: FINAL_TIMER_EXPIRED: retry_count=%d\n", - self->retry_count); - - /* Early warning event. I'm using a pretty liberal - * interpretation of the spec and generate an event - * every time the timer is multiple of N1 (and not - * only the first time). This allow application - * to know precisely if connectivity restart... - * Jean II */ - if((self->retry_count % self->N1) == 0) - irlap_status_indication(self, - STATUS_NO_ACTIVITY); - - /* Keep state */ - } else { - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - } - break; - case RECV_REJ_RSP: - irlap_update_nr_received(self, info->nr); - if (self->remote_busy) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - } else - irlap_resend_rejected_frames(self, CMD_FRAME); - irlap_start_final_timer(self, 2 * self->final_timeout); - break; - case RECV_SREJ_RSP: - irlap_update_nr_received(self, info->nr); - if (self->remote_busy) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, CMD_FRAME); - } else - irlap_resend_rejected_frame(self, CMD_FRAME); - irlap_start_final_timer(self, 2 * self->final_timeout); - break; - case RECV_RD_RSP: - pr_debug("%s(), RECV_RD_RSP\n", __func__); - - irlap_flush_all_queues(self); - irlap_next_state(self, LAP_XMIT_P); - /* Call back the LAP state machine to do a proper disconnect */ - irlap_disconnect_request(self); - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_reset_wait (event, skb, info) - * - * We have informed the service user of a reset condition, and is - * awaiting reset of disconnect request. - * - */ -static int irlap_state_reset_wait(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event = %s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RESET_REQUEST: - if (self->xmitflag) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_snrm_frame(self, NULL); - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_RESET); - } else { - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_RESET); - } - break; - case DISCONNECT_REQUEST: - irlap_wait_min_turn_around( self, &self->qos_tx); - irlap_send_disc_frame( self); - irlap_flush_all_queues( self); - irlap_start_final_timer( self, self->final_timeout); - self->retry_count = 0; - irlap_next_state( self, LAP_PCLOSE); - break; - default: - pr_debug("%s(), Unknown event %s\n", __func__, - irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_reset (self, event, skb, info) - * - * We have sent a SNRM reset command to the peer layer, and is awaiting - * reply. - * - */ -static int irlap_state_reset(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event = %s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RECV_DISC_CMD: - del_timer(&self->final_timer); - - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - - break; - case RECV_UA_RSP: - del_timer(&self->final_timer); - - /* Initiate connection state */ - irlap_initiate_connection_state(self); - - irlap_reset_confirm(); - - self->remote_busy = FALSE; - - irlap_next_state(self, LAP_XMIT_P); - - irlap_start_poll_timer(self, self->poll_timeout); - - break; - case FINAL_TIMER_EXPIRED: - if (self->retry_count < 3) { - irlap_wait_min_turn_around(self, &self->qos_tx); - - IRDA_ASSERT(self->netdev != NULL, return -1;); - irlap_send_snrm_frame(self, self->qos_dev); - - self->retry_count++; /* Experimental!! */ - - irlap_start_final_timer(self, self->final_timeout); - irlap_next_state(self, LAP_RESET); - } else if (self->retry_count >= self->N3) { - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - } - break; - case RECV_SNRM_CMD: - /* - * SNRM frame is not allowed to contain an I-field in this - * state - */ - if (!info) { - pr_debug("%s(), RECV_SNRM_CMD\n", __func__); - irlap_initiate_connection_state(self); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_ua_response_frame(self, &self->qos_rx); - irlap_reset_confirm(); - irlap_start_wd_timer(self, self->wd_timeout); - irlap_next_state(self, LAP_NDM); - } else { - pr_debug("%s(), SNRM frame contained an I field!\n", - __func__); - } - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlap_event[event]); - - ret = -1; - break; - } - return ret; -} - -/* - * Function irlap_state_xmit_s (event, skb, info) - * - * XMIT_S, The secondary station has been given the right to transmit, - * and we therefore do not expect to receive any transmissions from other - * stations. - */ -static int irlap_state_xmit_s(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -ENODEV;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); - - switch (event) { - case SEND_I_CMD: - /* - * Send frame only if send window > 0 - */ - if ((self->window > 0) && (!self->remote_busy)) { - int nextfit; -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - struct sk_buff *skb_next; - - /* - * Same deal as in irlap_state_xmit_p(), so see - * the comments at that point. - * We are the secondary, so there are only subtle - * differences. - Jean II - */ - - /* Check if a subsequent skb exist and would fit in - * the current window (with respect to turnaround - * time). - Jean II */ - skb_next = skb_peek(&self->txq); - nextfit = ((skb_next != NULL) && - ((skb_next->len + skb->len) <= - self->bytes_left)); - - /* - * Test if we have transmitted more bytes over the - * link than its possible to do with the current - * speed and turn-around-time. - */ - if((!nextfit) && (skb->len > self->bytes_left)) { - pr_debug("%s(), Not allowed to transmit more bytes!\n", - __func__); - /* Requeue the skb */ - skb_queue_head(&self->txq, skb_get(skb)); - - /* - * Switch to NRM_S, this is only possible - * when we are in secondary mode, since we - * must be sure that we don't miss any RR - * frames - */ - self->window = self->window_size; - self->bytes_left = self->line_capacity; - irlap_start_wd_timer(self, self->wd_timeout); - - irlap_next_state(self, LAP_NRM_S); - /* Slight difference with primary : - * here we would wait for the other side to - * expire the turnaround. - Jean II */ - - return -EPROTO; /* Try again later */ - } - /* Subtract space used by this skb */ - self->bytes_left -= skb->len; -#else /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* Window has been adjusted for the max packet - * size, so much simpler... - Jean II */ - nextfit = !skb_queue_empty(&self->txq); -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* - * Send data with final bit cleared only if window > 1 - * and there is more frames to be sent - */ - if ((self->window > 1) && (nextfit)) { - irlap_send_data_secondary(self, skb); - irlap_next_state(self, LAP_XMIT_S); - } else { - irlap_send_data_secondary_final(self, skb); - irlap_next_state(self, LAP_NRM_S); - - /* - * Make sure state machine does not try to send - * any more frames - */ - ret = -EPROTO; - } - } else { - pr_debug("%s(), Unable to send!\n", __func__); - skb_queue_head(&self->txq, skb_get(skb)); - ret = -EPROTO; - } - break; - case DISCONNECT_REQUEST: - irlap_send_rd_frame(self); - irlap_flush_all_queues(self); - irlap_start_wd_timer(self, self->wd_timeout); - irlap_next_state(self, LAP_SCLOSE); - break; - case DATA_REQUEST: - /* Nothing to do, irlap_do_event() will send the packet - * when we return... - Jean II */ - break; - default: - pr_debug("%s(), Unknown event %s\n", __func__, - irlap_event[event]); - - ret = -EINVAL; - break; - } - return ret; -} - -/* - * Function irlap_state_nrm_s (event, skb, info) - * - * NRM_S (Normal Response Mode as Secondary) state, in this state we are - * expecting to receive frames from the primary station - * - */ -static int irlap_state_nrm_s(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - int ns_status; - int nr_status; - int ret = 0; - - pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - switch (event) { - case RECV_I_CMD: /* Optimize for the common case */ - /* FIXME: must check for remote_busy below */ - pr_debug("%s(), event=%s nr=%d, vs=%d, ns=%d, vr=%d, pf=%d\n", - __func__, irlap_event[event], info->nr, - self->vs, info->ns, self->vr, info->pf); - - self->retry_count = 0; - - ns_status = irlap_validate_ns_received(self, info->ns); - nr_status = irlap_validate_nr_received(self, info->nr); - /* - * Check for expected I(nformation) frame - */ - if ((ns_status == NS_EXPECTED) && (nr_status == NR_EXPECTED)) { - - /* Update Vr (next frame for us to receive) */ - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* - * poll bit cleared? - */ - if (!info->pf) { - - self->ack_required = TRUE; - - /* - * Starting WD-timer here is optional, but - * not recommended. Note 6 IrLAP p. 83 - */ -#if 0 - irda_start_timer(WD_TIMER, self->wd_timeout); -#endif - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_S); - - irlap_data_indication(self, skb, FALSE); - break; - } else { - /* - * We should wait before sending RR, and - * also before changing to XMIT_S - * state. (note 1, IrLAP p. 82) - */ - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* - * Give higher layers a chance to - * immediately reply with some data before - * we decide if we should send a RR frame - * or not - */ - irlap_data_indication(self, skb, FALSE); - - /* Any pending data requests? */ - if (!skb_queue_empty(&self->txq) && - (self->window > 0)) - { - self->ack_required = TRUE; - - del_timer(&self->wd_timer); - - irlap_next_state(self, LAP_XMIT_S); - } else { - irlap_send_rr_frame(self, RSP_FRAME); - irlap_start_wd_timer(self, - self->wd_timeout); - - /* Keep the state */ - irlap_next_state(self, LAP_NRM_S); - } - break; - } - } - /* - * Check for Unexpected next to send (Ns) - */ - if ((ns_status == NS_UNEXPECTED) && (nr_status == NR_EXPECTED)) - { - /* Unexpected next to send, with final bit cleared */ - if (!info->pf) { - irlap_update_nr_received(self, info->nr); - - irlap_start_wd_timer(self, self->wd_timeout); - } else { - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, RSP_FRAME); - - irlap_start_wd_timer(self, self->wd_timeout); - } - break; - } - - /* - * Unexpected Next to Receive(NR) ? - */ - if ((ns_status == NS_EXPECTED) && (nr_status == NR_UNEXPECTED)) - { - if (info->pf) { - pr_debug("RECV_I_RSP: frame(s) lost\n"); - - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* Resend rejected frames */ - irlap_resend_rejected_frames(self, RSP_FRAME); - - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_S); - - irlap_data_indication(self, skb, FALSE); - irlap_start_wd_timer(self, self->wd_timeout); - break; - } - /* - * This is not documented in IrLAP!! Unexpected NR - * with poll bit cleared - */ - if (!info->pf) { - self->vr = (self->vr + 1) % 8; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - - /* Keep state, do not move this line */ - irlap_next_state(self, LAP_NRM_S); - - irlap_data_indication(self, skb, FALSE); - irlap_start_wd_timer(self, self->wd_timeout); - } - break; - } - - if (ret == NR_INVALID) { - pr_debug("NRM_S, NR_INVALID not implemented!\n"); - } - if (ret == NS_INVALID) { - pr_debug("NRM_S, NS_INVALID not implemented!\n"); - } - break; - case RECV_UI_FRAME: - /* - * poll bit cleared? - */ - if (!info->pf) { - irlap_data_indication(self, skb, TRUE); - irlap_next_state(self, LAP_NRM_S); /* Keep state */ - } else { - /* - * Any pending data requests? - */ - if (!skb_queue_empty(&self->txq) && - (self->window > 0) && !self->remote_busy) - { - irlap_data_indication(self, skb, TRUE); - - del_timer(&self->wd_timer); - - irlap_next_state(self, LAP_XMIT_S); - } else { - irlap_data_indication(self, skb, TRUE); - - irlap_wait_min_turn_around(self, &self->qos_tx); - - irlap_send_rr_frame(self, RSP_FRAME); - self->ack_required = FALSE; - - irlap_start_wd_timer(self, self->wd_timeout); - - /* Keep the state */ - irlap_next_state(self, LAP_NRM_S); - } - } - break; - case RECV_RR_CMD: - self->retry_count = 0; - - /* - * Nr as expected? - */ - nr_status = irlap_validate_nr_received(self, info->nr); - if (nr_status == NR_EXPECTED) { - if (!skb_queue_empty(&self->txq) && - (self->window > 0)) { - self->remote_busy = FALSE; - - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - del_timer(&self->wd_timer); - - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_next_state(self, LAP_XMIT_S); - } else { - self->remote_busy = FALSE; - /* Update Nr received */ - irlap_update_nr_received(self, info->nr); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_start_wd_timer(self, self->wd_timeout); - - /* Note : if the link is idle (this case), - * we never go in XMIT_S, so we never get a - * chance to process any DISCONNECT_REQUEST. - * Do it now ! - Jean II */ - if (self->disconnect_pending) { - /* Disconnect */ - irlap_send_rd_frame(self); - irlap_flush_all_queues(self); - - irlap_next_state(self, LAP_SCLOSE); - } else { - /* Just send back pf bit */ - irlap_send_rr_frame(self, RSP_FRAME); - - irlap_next_state(self, LAP_NRM_S); - } - } - } else if (nr_status == NR_UNEXPECTED) { - self->remote_busy = FALSE; - irlap_update_nr_received(self, info->nr); - irlap_resend_rejected_frames(self, RSP_FRAME); - - irlap_start_wd_timer(self, self->wd_timeout); - - /* Keep state */ - irlap_next_state(self, LAP_NRM_S); - } else { - pr_debug("%s(), invalid nr not implemented!\n", - __func__); - } - break; - case RECV_SNRM_CMD: - /* SNRM frame is not allowed to contain an I-field */ - if (!info) { - del_timer(&self->wd_timer); - pr_debug("%s(), received SNRM cmd\n", __func__); - irlap_next_state(self, LAP_RESET_CHECK); - - irlap_reset_indication(self); - } else { - pr_debug("%s(), SNRM frame contained an I-field!\n", - __func__); - - } - break; - case RECV_REJ_CMD: - irlap_update_nr_received(self, info->nr); - if (self->remote_busy) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, RSP_FRAME); - } else - irlap_resend_rejected_frames(self, RSP_FRAME); - irlap_start_wd_timer(self, self->wd_timeout); - break; - case RECV_SREJ_CMD: - irlap_update_nr_received(self, info->nr); - if (self->remote_busy) { - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, RSP_FRAME); - } else - irlap_resend_rejected_frame(self, RSP_FRAME); - irlap_start_wd_timer(self, self->wd_timeout); - break; - case WD_TIMER_EXPIRED: - /* - * Wait until retry_count * n matches negotiated threshold/ - * disconnect time (note 2 in IrLAP p. 82) - * - * Similar to irlap_state_nrm_p() -> FINAL_TIMER_EXPIRED - * Note : self->wd_timeout = (self->final_timeout * 2), - * which explain why we use (self->N2 / 2) here !!! - * Jean II - */ - pr_debug("%s(), retry_count = %d\n", __func__, - self->retry_count); - - if (self->retry_count < (self->N2 / 2)) { - /* No retry, just wait for primary */ - irlap_start_wd_timer(self, self->wd_timeout); - self->retry_count++; - - if((self->retry_count % (self->N1 / 2)) == 0) - irlap_status_indication(self, - STATUS_NO_ACTIVITY); - } else { - irlap_apply_default_connection_parameters(self); - - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - irlap_disconnect_indication(self, LAP_NO_RESPONSE); - } - break; - case RECV_DISC_CMD: - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - /* Send disconnect response */ - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_ua_response_frame(self, NULL); - - del_timer(&self->wd_timer); - irlap_flush_all_queues(self); - /* Set default link parameters */ - irlap_apply_default_connection_parameters(self); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - case RECV_DISCOVERY_XID_CMD: - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rr_frame(self, RSP_FRAME); - self->ack_required = TRUE; - irlap_start_wd_timer(self, self->wd_timeout); - irlap_next_state(self, LAP_NRM_S); - - break; - case RECV_TEST_CMD: - /* Remove test frame header (only LAP header in NRM) */ - skb_pull(skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER); - - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_start_wd_timer(self, self->wd_timeout); - - /* Send response (info will be copied) */ - irlap_send_test_frame(self, self->caddr, info->daddr, skb); - break; - default: - pr_debug("%s(), Unknown event %d, (%s)\n", __func__, - event, irlap_event[event]); - - ret = -EINVAL; - break; - } - return ret; -} - -/* - * Function irlap_state_sclose (self, event, skb, info) - */ -static int irlap_state_sclose(struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, struct irlap_info *info) -{ - IRDA_ASSERT(self != NULL, return -ENODEV;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); - - switch (event) { - case RECV_DISC_CMD: - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - /* Send disconnect response */ - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_ua_response_frame(self, NULL); - - del_timer(&self->wd_timer); - /* Set default link parameters */ - irlap_apply_default_connection_parameters(self); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - case RECV_DM_RSP: - /* IrLAP-1.1 p.82: in SCLOSE, S and I type RSP frames - * shall take us down into default NDM state, like DM_RSP - */ - case RECV_RR_RSP: - case RECV_RNR_RSP: - case RECV_REJ_RSP: - case RECV_SREJ_RSP: - case RECV_I_RSP: - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - del_timer(&self->wd_timer); - irlap_apply_default_connection_parameters(self); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - case WD_TIMER_EXPIRED: - /* Always switch state before calling upper layers */ - irlap_next_state(self, LAP_NDM); - - irlap_apply_default_connection_parameters(self); - - irlap_disconnect_indication(self, LAP_DISC_INDICATION); - break; - default: - /* IrLAP-1.1 p.82: in SCLOSE, basically any received frame - * with pf=1 shall restart the wd-timer and resend the rd:rsp - */ - if (info != NULL && info->pf) { - del_timer(&self->wd_timer); - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rd_frame(self); - irlap_start_wd_timer(self, self->wd_timeout); - break; /* stay in SCLOSE */ - } - - pr_debug("%s(), Unknown event %d, (%s)\n", __func__, - event, irlap_event[event]); - - break; - } - - return -1; -} - -static int irlap_state_reset_check( struct irlap_cb *self, IRLAP_EVENT event, - struct sk_buff *skb, - struct irlap_info *info) -{ - int ret = 0; - - pr_debug("%s(), event=%s\n", __func__, irlap_event[event]); - - IRDA_ASSERT(self != NULL, return -ENODEV;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -EBADR;); - - switch (event) { - case RESET_RESPONSE: - irlap_send_ua_response_frame(self, &self->qos_rx); - irlap_initiate_connection_state(self); - irlap_start_wd_timer(self, WD_TIMEOUT); - irlap_flush_all_queues(self); - - irlap_next_state(self, LAP_NRM_S); - break; - case DISCONNECT_REQUEST: - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_send_rd_frame(self); - irlap_start_wd_timer(self, WD_TIMEOUT); - irlap_next_state(self, LAP_SCLOSE); - break; - default: - pr_debug("%s(), Unknown event %d, (%s)\n", __func__, - event, irlap_event[event]); - - ret = -EINVAL; - break; - } - return ret; -} diff --git a/net/irda/irlap_frame.c b/net/irda/irlap_frame.c deleted file mode 100644 index debda3de4726..000000000000 --- a/net/irda/irlap_frame.c +++ /dev/null @@ -1,1407 +0,0 @@ -/********************************************************************* - * - * Filename: irlap_frame.c - * Version: 1.0 - * Description: Build and transmit IrLAP frames - * Status: Stable - * Author: Dag Brattli - * Created at: Tue Aug 19 10:27:26 1997 - * Modified at: Wed Jan 5 08:59:04 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb, - int command); - -/* - * Function irlap_insert_info (self, skb) - * - * Insert minimum turnaround time and speed information into the skb. We - * need to do this since it's per packet relevant information. Safe to - * have this function inlined since it's only called from one place - */ -static inline void irlap_insert_info(struct irlap_cb *self, - struct sk_buff *skb) -{ - struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; - - /* - * Insert MTT (min. turn time) and speed into skb, so that the - * device driver knows which settings to use - */ - cb->magic = LAP_MAGIC; - cb->mtt = self->mtt_required; - cb->next_speed = self->speed; - - /* Reset */ - self->mtt_required = 0; - - /* - * Delay equals negotiated BOFs count, plus the number of BOFs to - * force the negotiated minimum turnaround time - */ - cb->xbofs = self->bofs_count; - cb->next_xbofs = self->next_bofs; - cb->xbofs_delay = self->xbofs_delay; - - /* Reset XBOF's delay (used only for getting min turn time) */ - self->xbofs_delay = 0; - /* Put the correct xbofs value for the next packet */ - self->bofs_count = self->next_bofs; -} - -/* - * Function irlap_queue_xmit (self, skb) - * - * A little wrapper for dev_queue_xmit, so we can insert some common - * code into it. - */ -void irlap_queue_xmit(struct irlap_cb *self, struct sk_buff *skb) -{ - /* Some common init stuff */ - skb->dev = self->netdev; - skb_reset_mac_header(skb); - skb_reset_network_header(skb); - skb_reset_transport_header(skb); - skb->protocol = htons(ETH_P_IRDA); - skb->priority = TC_PRIO_BESTEFFORT; - - irlap_insert_info(self, skb); - - if (unlikely(self->mode & IRDA_MODE_MONITOR)) { - pr_debug("%s(): %s is in monitor mode\n", __func__, - self->netdev->name); - dev_kfree_skb(skb); - return; - } - - dev_queue_xmit(skb); -} - -/* - * Function irlap_send_snrm_cmd (void) - * - * Transmits a connect SNRM command frame - */ -void irlap_send_snrm_frame(struct irlap_cb *self, struct qos_info *qos) -{ - struct sk_buff *tx_skb; - struct snrm_frame *frame; - int ret; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Allocate frame */ - tx_skb = alloc_skb(sizeof(struct snrm_frame) + - IRLAP_NEGOCIATION_PARAMS_LEN, - GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - /* Insert connection address field */ - if (qos) - frame->caddr = CMD_FRAME | CBROADCAST; - else - frame->caddr = CMD_FRAME | self->caddr; - - /* Insert control field */ - frame->control = SNRM_CMD | PF_BIT; - - /* - * If we are establishing a connection then insert QoS parameters - */ - if (qos) { - skb_put(tx_skb, 9); /* 25 left */ - frame->saddr = cpu_to_le32(self->saddr); - frame->daddr = cpu_to_le32(self->daddr); - - frame->ncaddr = self->caddr; - - ret = irlap_insert_qos_negotiation_params(self, tx_skb); - if (ret < 0) { - dev_kfree_skb(tx_skb); - return; - } - } - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_recv_snrm_cmd (skb, info) - * - * Received SNRM (Set Normal Response Mode) command frame - * - */ -static void irlap_recv_snrm_cmd(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info) -{ - struct snrm_frame *frame; - - if (pskb_may_pull(skb,sizeof(struct snrm_frame))) { - frame = (struct snrm_frame *) skb->data; - - /* Copy the new connection address ignoring the C/R bit */ - info->caddr = frame->ncaddr & 0xFE; - - /* Check if the new connection address is valid */ - if ((info->caddr == 0x00) || (info->caddr == 0xfe)) { - pr_debug("%s(), invalid connection address!\n", - __func__); - return; - } - - /* Copy peer device address */ - info->daddr = le32_to_cpu(frame->saddr); - info->saddr = le32_to_cpu(frame->daddr); - - /* Only accept if addressed directly to us */ - if (info->saddr != self->saddr) { - pr_debug("%s(), not addressed to us!\n", - __func__); - return; - } - irlap_do_event(self, RECV_SNRM_CMD, skb, info); - } else { - /* Signal that this SNRM frame does not contain and I-field */ - irlap_do_event(self, RECV_SNRM_CMD, skb, NULL); - } -} - -/* - * Function irlap_send_ua_response_frame (qos) - * - * Send UA (Unnumbered Acknowledgement) frame - * - */ -void irlap_send_ua_response_frame(struct irlap_cb *self, struct qos_info *qos) -{ - struct sk_buff *tx_skb; - struct ua_frame *frame; - int ret; - - pr_debug("%s() <%ld>\n", __func__, jiffies); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Allocate frame */ - tx_skb = alloc_skb(sizeof(struct ua_frame) + - IRLAP_NEGOCIATION_PARAMS_LEN, - GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 10); - - /* Build UA response */ - frame->caddr = self->caddr; - frame->control = UA_RSP | PF_BIT; - - frame->saddr = cpu_to_le32(self->saddr); - frame->daddr = cpu_to_le32(self->daddr); - - /* Should we send QoS negotiation parameters? */ - if (qos) { - ret = irlap_insert_qos_negotiation_params(self, tx_skb); - if (ret < 0) { - dev_kfree_skb(tx_skb); - return; - } - } - - irlap_queue_xmit(self, tx_skb); -} - - -/* - * Function irlap_send_dm_frame (void) - * - * Send disconnected mode (DM) frame - * - */ -void irlap_send_dm_frame( struct irlap_cb *self) -{ - struct sk_buff *tx_skb = NULL; - struct dm_frame *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - tx_skb = alloc_skb(sizeof(struct dm_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - if (self->state == LAP_NDM) - frame->caddr = CBROADCAST; - else - frame->caddr = self->caddr; - - frame->control = DM_RSP | PF_BIT; - - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_send_disc_frame (void) - * - * Send disconnect (DISC) frame - * - */ -void irlap_send_disc_frame(struct irlap_cb *self) -{ - struct sk_buff *tx_skb = NULL; - struct disc_frame *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - tx_skb = alloc_skb(sizeof(struct disc_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - frame->caddr = self->caddr | CMD_FRAME; - frame->control = DISC_CMD | PF_BIT; - - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_send_discovery_xid_frame (S, s, command) - * - * Build and transmit a XID (eXchange station IDentifier) discovery - * frame. - */ -void irlap_send_discovery_xid_frame(struct irlap_cb *self, int S, __u8 s, - __u8 command, discovery_t *discovery) -{ - struct sk_buff *tx_skb = NULL; - struct xid_frame *frame; - __u32 bcast = BROADCAST; - __u8 *info; - - pr_debug("%s(), s=%d, S=%d, command=%d\n", __func__, - s, S, command); - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(discovery != NULL, return;); - - tx_skb = alloc_skb(sizeof(struct xid_frame) + IRLAP_DISCOVERY_INFO_LEN, - GFP_ATOMIC); - if (!tx_skb) - return; - - skb_put(tx_skb, 14); - frame = (struct xid_frame *) tx_skb->data; - - if (command) { - frame->caddr = CBROADCAST | CMD_FRAME; - frame->control = XID_CMD | PF_BIT; - } else { - frame->caddr = CBROADCAST; - frame->control = XID_RSP | PF_BIT; - } - frame->ident = XID_FORMAT; - - frame->saddr = cpu_to_le32(self->saddr); - - if (command) - frame->daddr = cpu_to_le32(bcast); - else - frame->daddr = cpu_to_le32(discovery->data.daddr); - - switch (S) { - case 1: - frame->flags = 0x00; - break; - case 6: - frame->flags = 0x01; - break; - case 8: - frame->flags = 0x02; - break; - case 16: - frame->flags = 0x03; - break; - default: - frame->flags = 0x02; - break; - } - - frame->slotnr = s; - frame->version = 0x00; - - /* - * Provide info for final slot only in commands, and for all - * responses. Send the second byte of the hint only if the - * EXTENSION bit is set in the first byte. - */ - if (!command || (frame->slotnr == 0xff)) { - int len; - - if (discovery->data.hints[0] & HINT_EXTENSION) { - info = skb_put(tx_skb, 2); - info[0] = discovery->data.hints[0]; - info[1] = discovery->data.hints[1]; - } else { - info = skb_put(tx_skb, 1); - info[0] = discovery->data.hints[0]; - } - info = skb_put(tx_skb, 1); - info[0] = discovery->data.charset; - - len = IRDA_MIN(discovery->name_len, skb_tailroom(tx_skb)); - skb_put_data(tx_skb, discovery->data.info, len); - } - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_recv_discovery_xid_rsp (skb, info) - * - * Received a XID discovery response - * - */ -static void irlap_recv_discovery_xid_rsp(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info) -{ - struct xid_frame *xid; - discovery_t *discovery = NULL; - __u8 *discovery_info; - char *text; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { - net_err_ratelimited("%s: frame too short!\n", __func__); - return; - } - - xid = (struct xid_frame *) skb->data; - - info->daddr = le32_to_cpu(xid->saddr); - info->saddr = le32_to_cpu(xid->daddr); - - /* Make sure frame is addressed to us */ - if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { - pr_debug("%s(), frame is not addressed to us!\n", - __func__); - return; - } - - if ((discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC)) == NULL) { - net_warn_ratelimited("%s: kmalloc failed!\n", __func__); - return; - } - - discovery->data.daddr = info->daddr; - discovery->data.saddr = self->saddr; - discovery->timestamp = jiffies; - - pr_debug("%s(), daddr=%08x\n", __func__, - discovery->data.daddr); - - discovery_info = skb_pull(skb, sizeof(struct xid_frame)); - - /* Get info returned from peer */ - discovery->data.hints[0] = discovery_info[0]; - if (discovery_info[0] & HINT_EXTENSION) { - pr_debug("EXTENSION\n"); - discovery->data.hints[1] = discovery_info[1]; - discovery->data.charset = discovery_info[2]; - text = (char *) &discovery_info[3]; - } else { - discovery->data.hints[1] = 0; - discovery->data.charset = discovery_info[1]; - text = (char *) &discovery_info[2]; - } - /* - * Terminate info string, should be safe since this is where the - * FCS bytes resides. - */ - skb->data[skb->len] = '\0'; - strncpy(discovery->data.info, text, NICKNAME_MAX_LEN); - discovery->name_len = strlen(discovery->data.info); - - info->discovery = discovery; - - irlap_do_event(self, RECV_DISCOVERY_XID_RSP, skb, info); -} - -/* - * Function irlap_recv_discovery_xid_cmd (skb, info) - * - * Received a XID discovery command - * - */ -static void irlap_recv_discovery_xid_cmd(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info) -{ - struct xid_frame *xid; - discovery_t *discovery = NULL; - __u8 *discovery_info; - char *text; - - if (!pskb_may_pull(skb, sizeof(struct xid_frame))) { - net_err_ratelimited("%s: frame too short!\n", __func__); - return; - } - - xid = (struct xid_frame *) skb->data; - - info->daddr = le32_to_cpu(xid->saddr); - info->saddr = le32_to_cpu(xid->daddr); - - /* Make sure frame is addressed to us */ - if ((info->saddr != self->saddr) && (info->saddr != BROADCAST)) { - pr_debug("%s(), frame is not addressed to us!\n", - __func__); - return; - } - - switch (xid->flags & 0x03) { - case 0x00: - info->S = 1; - break; - case 0x01: - info->S = 6; - break; - case 0x02: - info->S = 8; - break; - case 0x03: - info->S = 16; - break; - default: - /* Error!! */ - return; - } - info->s = xid->slotnr; - - discovery_info = skb_pull(skb, sizeof(struct xid_frame)); - - /* - * Check if last frame - */ - if (info->s == 0xff) { - /* Check if things are sane at this point... */ - if((discovery_info == NULL) || - !pskb_may_pull(skb, 3)) { - net_err_ratelimited("%s: discovery frame too short!\n", - __func__); - return; - } - - /* - * We now have some discovery info to deliver! - */ - discovery = kzalloc(sizeof(discovery_t), GFP_ATOMIC); - if (!discovery) - return; - - discovery->data.daddr = info->daddr; - discovery->data.saddr = self->saddr; - discovery->timestamp = jiffies; - - discovery->data.hints[0] = discovery_info[0]; - if (discovery_info[0] & HINT_EXTENSION) { - discovery->data.hints[1] = discovery_info[1]; - discovery->data.charset = discovery_info[2]; - text = (char *) &discovery_info[3]; - } else { - discovery->data.hints[1] = 0; - discovery->data.charset = discovery_info[1]; - text = (char *) &discovery_info[2]; - } - /* - * Terminate string, should be safe since this is where the - * FCS bytes resides. - */ - skb->data[skb->len] = '\0'; - strncpy(discovery->data.info, text, NICKNAME_MAX_LEN); - discovery->name_len = strlen(discovery->data.info); - - info->discovery = discovery; - } else - info->discovery = NULL; - - irlap_do_event(self, RECV_DISCOVERY_XID_CMD, skb, info); -} - -/* - * Function irlap_send_rr_frame (self, command) - * - * Build and transmit RR (Receive Ready) frame. Notice that it is currently - * only possible to send RR frames with the poll bit set. - */ -void irlap_send_rr_frame(struct irlap_cb *self, int command) -{ - struct sk_buff *tx_skb; - struct rr_frame *frame; - - tx_skb = alloc_skb(sizeof(struct rr_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - frame->caddr = self->caddr; - frame->caddr |= (command) ? CMD_FRAME : 0; - - frame->control = RR | PF_BIT | (self->vr << 5); - - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_send_rd_frame (self) - * - * Request disconnect. Used by a secondary station to request the - * disconnection of the link. - */ -void irlap_send_rd_frame(struct irlap_cb *self) -{ - struct sk_buff *tx_skb; - struct rd_frame *frame; - - tx_skb = alloc_skb(sizeof(struct rd_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - frame = skb_put(tx_skb, 2); - - frame->caddr = self->caddr; - frame->control = RD_RSP | PF_BIT; - - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_recv_rr_frame (skb, info) - * - * Received RR (Receive Ready) frame from peer station, no harm in - * making it inline since its called only from one single place - * (irlap_driver_rcv). - */ -static inline void irlap_recv_rr_frame(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; - - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_RR_CMD, skb, info); - else - irlap_do_event(self, RECV_RR_RSP, skb, info); -} - -/* - * Function irlap_recv_rnr_frame (self, skb, info) - * - * Received RNR (Receive Not Ready) frame from peer station - * - */ -static void irlap_recv_rnr_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; - - pr_debug("%s(), nr=%d, %ld\n", __func__, info->nr, jiffies); - - if (command) - irlap_do_event(self, RECV_RNR_CMD, skb, info); - else - irlap_do_event(self, RECV_RNR_RSP, skb, info); -} - -static void irlap_recv_rej_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; - - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_REJ_CMD, skb, info); - else - irlap_do_event(self, RECV_REJ_RSP, skb, info); -} - -static void irlap_recv_srej_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; - - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_SREJ_CMD, skb, info); - else - irlap_do_event(self, RECV_SREJ_RSP, skb, info); -} - -static void irlap_recv_disc_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_DISC_CMD, skb, info); - else - irlap_do_event(self, RECV_RD_RSP, skb, info); -} - -/* - * Function irlap_recv_ua_frame (skb, frame) - * - * Received UA (Unnumbered Acknowledgement) frame - * - */ -static inline void irlap_recv_ua_frame(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info) -{ - irlap_do_event(self, RECV_UA_RSP, skb, info); -} - -/* - * Function irlap_send_data_primary(self, skb) - * - * Send I-frames as the primary station but without the poll bit set - * - */ -void irlap_send_data_primary(struct irlap_cb *self, struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - - if (skb->data[1] == I_FRAME) { - - /* - * Insert frame sequence number (Vs) in control field before - * inserting into transmit window queue. - */ - skb->data[1] = I_FRAME | (self->vs << 1); - - /* - * Insert frame in store, in case of retransmissions - * Increase skb reference count, see irlap_do_event() - */ - skb_get(skb); - skb_queue_tail(&self->wx_list, skb); - - /* Copy buffer */ - tx_skb = skb_clone(skb, GFP_ATOMIC); - if (tx_skb == NULL) { - return; - } - - self->vs = (self->vs + 1) % 8; - self->ack_required = FALSE; - self->window -= 1; - - irlap_send_i_frame( self, tx_skb, CMD_FRAME); - } else { - pr_debug("%s(), sending unreliable frame\n", __func__); - irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); - self->window -= 1; - } -} -/* - * Function irlap_send_data_primary_poll (self, skb) - * - * Send I(nformation) frame as primary with poll bit set - */ -void irlap_send_data_primary_poll(struct irlap_cb *self, struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - int transmission_time; - - /* Stop P timer */ - del_timer(&self->poll_timer); - - /* Is this reliable or unreliable data? */ - if (skb->data[1] == I_FRAME) { - - /* - * Insert frame sequence number (Vs) in control field before - * inserting into transmit window queue. - */ - skb->data[1] = I_FRAME | (self->vs << 1); - - /* - * Insert frame in store, in case of retransmissions - * Increase skb reference count, see irlap_do_event() - */ - skb_get(skb); - skb_queue_tail(&self->wx_list, skb); - - /* Copy buffer */ - tx_skb = skb_clone(skb, GFP_ATOMIC); - if (tx_skb == NULL) { - return; - } - - /* - * Set poll bit if necessary. We do this to the copied - * skb, since retransmitted need to set or clear the poll - * bit depending on when they are sent. - */ - tx_skb->data[1] |= PF_BIT; - - self->vs = (self->vs + 1) % 8; - self->ack_required = FALSE; - - irlap_next_state(self, LAP_NRM_P); - irlap_send_i_frame(self, tx_skb, CMD_FRAME); - } else { - pr_debug("%s(), sending unreliable frame\n", __func__); - - if (self->ack_required) { - irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); - irlap_next_state(self, LAP_NRM_P); - irlap_send_rr_frame(self, CMD_FRAME); - self->ack_required = FALSE; - } else { - skb->data[1] |= PF_BIT; - irlap_next_state(self, LAP_NRM_P); - irlap_send_ui_frame(self, skb_get(skb), self->caddr, CMD_FRAME); - } - } - - /* How much time we took for transmission of all frames. - * We don't know, so let assume we used the full window. Jean II */ - transmission_time = self->final_timeout; - - /* Reset parameter so that we can fill next window */ - self->window = self->window_size; - -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - /* Remove what we have not used. Just do a prorata of the - * bytes left in window to window capacity. - * See max_line_capacities[][] in qos.c for details. Jean II */ - transmission_time -= (self->final_timeout * self->bytes_left - / self->line_capacity); - pr_debug("%s() adjusting transmission_time : ft=%d, bl=%d, lc=%d -> tt=%d\n", - __func__, self->final_timeout, self->bytes_left, - self->line_capacity, transmission_time); - - /* We are allowed to transmit a maximum number of bytes again. */ - self->bytes_left = self->line_capacity; -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - - /* - * The network layer has a intermediate buffer between IrLAP - * and the IrDA driver which can contain 8 frames. So, even - * though IrLAP is currently sending the *last* frame of the - * tx-window, the driver most likely has only just started - * sending the *first* frame of the same tx-window. - * I.e. we are always at the very beginning of or Tx window. - * Now, we are supposed to set the final timer from the end - * of our tx-window to let the other peer reply. So, we need - * to add extra time to compensate for the fact that we - * are really at the start of tx-window, otherwise the final timer - * might expire before he can answer... - * Jean II - */ - irlap_start_final_timer(self, self->final_timeout + transmission_time); - - /* - * The clever amongst you might ask why we do this adjustement - * only here, and not in all the other cases in irlap_event.c. - * In all those other case, we only send a very short management - * frame (few bytes), so the adjustement would be lost in the - * noise... - * The exception of course is irlap_resend_rejected_frame(). - * Jean II */ -} - -/* - * Function irlap_send_data_secondary_final (self, skb) - * - * Send I(nformation) frame as secondary with final bit set - * - */ -void irlap_send_data_secondary_final(struct irlap_cb *self, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb = NULL; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Is this reliable or unreliable data? */ - if (skb->data[1] == I_FRAME) { - - /* - * Insert frame sequence number (Vs) in control field before - * inserting into transmit window queue. - */ - skb->data[1] = I_FRAME | (self->vs << 1); - - /* - * Insert frame in store, in case of retransmissions - * Increase skb reference count, see irlap_do_event() - */ - skb_get(skb); - skb_queue_tail(&self->wx_list, skb); - - tx_skb = skb_clone(skb, GFP_ATOMIC); - if (tx_skb == NULL) { - return; - } - - tx_skb->data[1] |= PF_BIT; - - self->vs = (self->vs + 1) % 8; - self->ack_required = FALSE; - - irlap_send_i_frame(self, tx_skb, RSP_FRAME); - } else { - if (self->ack_required) { - irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); - irlap_send_rr_frame(self, RSP_FRAME); - self->ack_required = FALSE; - } else { - skb->data[1] |= PF_BIT; - irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); - } - } - - self->window = self->window_size; -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - /* We are allowed to transmit a maximum number of bytes again. */ - self->bytes_left = self->line_capacity; -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - - irlap_start_wd_timer(self, self->wd_timeout); -} - -/* - * Function irlap_send_data_secondary (self, skb) - * - * Send I(nformation) frame as secondary without final bit set - * - */ -void irlap_send_data_secondary(struct irlap_cb *self, struct sk_buff *skb) -{ - struct sk_buff *tx_skb = NULL; - - /* Is this reliable or unreliable data? */ - if (skb->data[1] == I_FRAME) { - - /* - * Insert frame sequence number (Vs) in control field before - * inserting into transmit window queue. - */ - skb->data[1] = I_FRAME | (self->vs << 1); - - /* - * Insert frame in store, in case of retransmissions - * Increase skb reference count, see irlap_do_event() - */ - skb_get(skb); - skb_queue_tail(&self->wx_list, skb); - - tx_skb = skb_clone(skb, GFP_ATOMIC); - if (tx_skb == NULL) { - return; - } - - self->vs = (self->vs + 1) % 8; - self->ack_required = FALSE; - self->window -= 1; - - irlap_send_i_frame(self, tx_skb, RSP_FRAME); - } else { - irlap_send_ui_frame(self, skb_get(skb), self->caddr, RSP_FRAME); - self->window -= 1; - } -} - -/* - * Function irlap_resend_rejected_frames (nr) - * - * Resend frames which has not been acknowledged. Should be safe to - * traverse the list without locking it since this function will only be - * called from interrupt context (BH) - */ -void irlap_resend_rejected_frames(struct irlap_cb *self, int command) -{ - struct sk_buff *tx_skb; - struct sk_buff *skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Resend unacknowledged frame(s) */ - skb_queue_walk(&self->wx_list, skb) { - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* We copy the skb to be retransmitted since we will have to - * modify it. Cloning will confuse packet sniffers - */ - /* tx_skb = skb_clone( skb, GFP_ATOMIC); */ - tx_skb = skb_copy(skb, GFP_ATOMIC); - if (!tx_skb) { - pr_debug("%s(), unable to copy\n", __func__); - return; - } - - /* Clear old Nr field + poll bit */ - tx_skb->data[1] &= 0x0f; - - /* - * Set poll bit on the last frame retransmitted - */ - if (skb_queue_is_last(&self->wx_list, skb)) - tx_skb->data[1] |= PF_BIT; /* Set p/f bit */ - else - tx_skb->data[1] &= ~PF_BIT; /* Clear p/f bit */ - - irlap_send_i_frame(self, tx_skb, command); - } -#if 0 /* Not yet */ - /* - * We can now fill the window with additional data frames - */ - while (!skb_queue_empty(&self->txq)) { - - pr_debug("%s(), sending additional frames!\n", __func__); - if (self->window > 0) { - skb = skb_dequeue( &self->txq); - IRDA_ASSERT(skb != NULL, return;); - - /* - * If send window > 1 then send frame with pf - * bit cleared - */ - if ((self->window > 1) && - !skb_queue_empty(&self->txq)) { - irlap_send_data_primary(self, skb); - } else { - irlap_send_data_primary_poll(self, skb); - } - kfree_skb(skb); - } - } -#endif -} - -void irlap_resend_rejected_frame(struct irlap_cb *self, int command) -{ - struct sk_buff *tx_skb; - struct sk_buff *skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - /* Resend unacknowledged frame(s) */ - skb = skb_peek(&self->wx_list); - if (skb != NULL) { - irlap_wait_min_turn_around(self, &self->qos_tx); - - /* We copy the skb to be retransmitted since we will have to - * modify it. Cloning will confuse packet sniffers - */ - /* tx_skb = skb_clone( skb, GFP_ATOMIC); */ - tx_skb = skb_copy(skb, GFP_ATOMIC); - if (!tx_skb) { - pr_debug("%s(), unable to copy\n", __func__); - return; - } - - /* Clear old Nr field + poll bit */ - tx_skb->data[1] &= 0x0f; - - /* Set poll/final bit */ - tx_skb->data[1] |= PF_BIT; /* Set p/f bit */ - - irlap_send_i_frame(self, tx_skb, command); - } -} - -/* - * Function irlap_send_ui_frame (self, skb, command) - * - * Contruct and transmit an Unnumbered Information (UI) frame - * - */ -void irlap_send_ui_frame(struct irlap_cb *self, struct sk_buff *skb, - __u8 caddr, int command) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Insert connection address */ - skb->data[0] = caddr | ((command) ? CMD_FRAME : 0); - - irlap_queue_xmit(self, skb); -} - -/* - * Function irlap_send_i_frame (skb) - * - * Contruct and transmit Information (I) frame - */ -static void irlap_send_i_frame(struct irlap_cb *self, struct sk_buff *skb, - int command) -{ - /* Insert connection address */ - skb->data[0] = self->caddr; - skb->data[0] |= (command) ? CMD_FRAME : 0; - - /* Insert next to receive (Vr) */ - skb->data[1] |= (self->vr << 5); /* insert nr */ - - irlap_queue_xmit(self, skb); -} - -/* - * Function irlap_recv_i_frame (skb, frame) - * - * Receive and parse an I (Information) frame, no harm in making it inline - * since it's called only from one single place (irlap_driver_rcv). - */ -static inline void irlap_recv_i_frame(struct irlap_cb *self, - struct sk_buff *skb, - struct irlap_info *info, int command) -{ - info->nr = skb->data[1] >> 5; /* Next to receive */ - info->pf = skb->data[1] & PF_BIT; /* Final bit */ - info->ns = (skb->data[1] >> 1) & 0x07; /* Next to send */ - - /* Check if this is a command or a response frame */ - if (command) - irlap_do_event(self, RECV_I_CMD, skb, info); - else - irlap_do_event(self, RECV_I_RSP, skb, info); -} - -/* - * Function irlap_recv_ui_frame (self, skb, info) - * - * Receive and parse an Unnumbered Information (UI) frame - * - */ -static void irlap_recv_ui_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info) -{ - info->pf = skb->data[1] & PF_BIT; /* Final bit */ - - irlap_do_event(self, RECV_UI_FRAME, skb, info); -} - -/* - * Function irlap_recv_frmr_frame (skb, frame) - * - * Received Frame Reject response. - * - */ -static void irlap_recv_frmr_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info) -{ - __u8 *frame; - int w, x, y, z; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(info != NULL, return;); - - if (!pskb_may_pull(skb, 4)) { - net_err_ratelimited("%s: frame too short!\n", __func__); - return; - } - - frame = skb->data; - - info->nr = frame[2] >> 5; /* Next to receive */ - info->pf = frame[2] & PF_BIT; /* Final bit */ - info->ns = (frame[2] >> 1) & 0x07; /* Next to send */ - - w = frame[3] & 0x01; - x = frame[3] & 0x02; - y = frame[3] & 0x04; - z = frame[3] & 0x08; - - if (w) { - pr_debug("Rejected control field is undefined or not implemented\n"); - } - if (x) { - pr_debug("Rejected control field was invalid because it contained a non permitted I field\n"); - } - if (y) { - pr_debug("Received I field exceeded the maximum negotiated for the existing connection or exceeded the maximum this station supports if no connection exists\n"); - } - if (z) { - pr_debug("Rejected control field control field contained an invalid Nr count\n"); - } - irlap_do_event(self, RECV_FRMR_RSP, skb, info); -} - -/* - * Function irlap_send_test_frame (self, daddr) - * - * Send a test frame response - * - */ -void irlap_send_test_frame(struct irlap_cb *self, __u8 caddr, __u32 daddr, - struct sk_buff *cmd) -{ - struct sk_buff *tx_skb; - struct test_frame *frame; - - tx_skb = alloc_skb(cmd->len + sizeof(struct test_frame), GFP_ATOMIC); - if (!tx_skb) - return; - - /* Broadcast frames must include saddr and daddr fields */ - if (caddr == CBROADCAST) { - frame = skb_put(tx_skb, sizeof(struct test_frame)); - - /* Insert the swapped addresses */ - frame->saddr = cpu_to_le32(self->saddr); - frame->daddr = cpu_to_le32(daddr); - } else - frame = skb_put(tx_skb, LAP_ADDR_HEADER + LAP_CTRL_HEADER); - - frame->caddr = caddr; - frame->control = TEST_RSP | PF_BIT; - - /* Copy info */ - skb_put_data(tx_skb, cmd->data, cmd->len); - - /* Return to sender */ - irlap_wait_min_turn_around(self, &self->qos_tx); - irlap_queue_xmit(self, tx_skb); -} - -/* - * Function irlap_recv_test_frame (self, skb) - * - * Receive a test frame - * - */ -static void irlap_recv_test_frame(struct irlap_cb *self, struct sk_buff *skb, - struct irlap_info *info, int command) -{ - struct test_frame *frame; - - if (!pskb_may_pull(skb, sizeof(*frame))) { - net_err_ratelimited("%s: frame too short!\n", __func__); - return; - } - frame = (struct test_frame *) skb->data; - - /* Broadcast frames must carry saddr and daddr fields */ - if (info->caddr == CBROADCAST) { - if (skb->len < sizeof(struct test_frame)) { - pr_debug("%s() test frame too short!\n", - __func__); - return; - } - - /* Read and swap addresses */ - info->daddr = le32_to_cpu(frame->saddr); - info->saddr = le32_to_cpu(frame->daddr); - - /* Make sure frame is addressed to us */ - if ((info->saddr != self->saddr) && - (info->saddr != BROADCAST)) { - return; - } - } - - if (command) - irlap_do_event(self, RECV_TEST_CMD, skb, info); - else - irlap_do_event(self, RECV_TEST_RSP, skb, info); -} - -/* - * Function irlap_driver_rcv (skb, netdev, ptype) - * - * Called when a frame is received. Dispatches the right receive function - * for processing of the frame. - * - * Note on skb management : - * After calling the higher layers of the IrDA stack, we always - * kfree() the skb, which drop the reference count (and potentially - * destroy it). - * If a higher layer of the stack want to keep the skb around (to put - * in a queue or pass it to the higher layer), it will need to use - * skb_get() to keep a reference on it. This is usually done at the - * LMP level in irlmp.c. - * Jean II - */ -int irlap_driver_rcv(struct sk_buff *skb, struct net_device *dev, - struct packet_type *ptype, struct net_device *orig_dev) -{ - struct irlap_info info; - struct irlap_cb *self; - int command; - __u8 control; - int ret = -1; - - if (!net_eq(dev_net(dev), &init_net)) - goto out; - - /* FIXME: should we get our own field? */ - self = (struct irlap_cb *) dev->atalk_ptr; - - /* If the net device is down, then IrLAP is gone! */ - if (!self || self->magic != LAP_MAGIC) - goto err; - - /* We are no longer an "old" protocol, so we need to handle - * share and non linear skbs. This should never happen, so - * we don't need to be clever about it. Jean II */ - if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) { - net_err_ratelimited("%s: can't clone shared skb!\n", __func__); - goto err; - } - - /* Check if frame is large enough for parsing */ - if (!pskb_may_pull(skb, 2)) { - net_err_ratelimited("%s: frame too short!\n", __func__); - goto err; - } - - command = skb->data[0] & CMD_FRAME; - info.caddr = skb->data[0] & CBROADCAST; - - info.pf = skb->data[1] & PF_BIT; - info.control = skb->data[1] & ~PF_BIT; /* Mask away poll/final bit */ - - control = info.control; - - /* First we check if this frame has a valid connection address */ - if ((info.caddr != self->caddr) && (info.caddr != CBROADCAST)) { - pr_debug("%s(), wrong connection address!\n", - __func__); - goto out; - } - /* - * Optimize for the common case and check if the frame is an - * I(nformation) frame. Only I-frames have bit 0 set to 0 - */ - if (~control & 0x01) { - irlap_recv_i_frame(self, skb, &info, command); - goto out; - } - /* - * We now check is the frame is an S(upervisory) frame. Only - * S-frames have bit 0 set to 1 and bit 1 set to 0 - */ - if (~control & 0x02) { - /* - * Received S(upervisory) frame, check which frame type it is - * only the first nibble is of interest - */ - switch (control & 0x0f) { - case RR: - irlap_recv_rr_frame(self, skb, &info, command); - break; - case RNR: - irlap_recv_rnr_frame(self, skb, &info, command); - break; - case REJ: - irlap_recv_rej_frame(self, skb, &info, command); - break; - case SREJ: - irlap_recv_srej_frame(self, skb, &info, command); - break; - default: - net_warn_ratelimited("%s: Unknown S-frame %02x received!\n", - __func__, info.control); - break; - } - goto out; - } - /* - * This must be a C(ontrol) frame - */ - switch (control) { - case XID_RSP: - irlap_recv_discovery_xid_rsp(self, skb, &info); - break; - case XID_CMD: - irlap_recv_discovery_xid_cmd(self, skb, &info); - break; - case SNRM_CMD: - irlap_recv_snrm_cmd(self, skb, &info); - break; - case DM_RSP: - irlap_do_event(self, RECV_DM_RSP, skb, &info); - break; - case DISC_CMD: /* And RD_RSP since they have the same value */ - irlap_recv_disc_frame(self, skb, &info, command); - break; - case TEST_CMD: - irlap_recv_test_frame(self, skb, &info, command); - break; - case UA_RSP: - irlap_recv_ua_frame(self, skb, &info); - break; - case FRMR_RSP: - irlap_recv_frmr_frame(self, skb, &info); - break; - case UI_FRAME: - irlap_recv_ui_frame(self, skb, &info); - break; - default: - net_warn_ratelimited("%s: Unknown frame %02x received!\n", - __func__, info.control); - break; - } -out: - ret = 0; -err: - /* Always drop our reference on the skb */ - dev_kfree_skb(skb); - return ret; -} diff --git a/net/irda/irlmp.c b/net/irda/irlmp.c deleted file mode 100644 index 43964594aa12..000000000000 --- a/net/irda/irlmp.c +++ /dev/null @@ -1,1996 +0,0 @@ -/********************************************************************* - * - * Filename: irlmp.c - * Version: 1.0 - * Description: IrDA Link Management Protocol (LMP) layer - * Status: Stable. - * Author: Dag Brattli - * Created at: Sun Aug 17 20:54:32 1997 - * Modified at: Wed Jan 5 11:26:03 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -static __u8 irlmp_find_free_slsap(void); -static int irlmp_slsap_inuse(__u8 slsap_sel); - -/* Master structure */ -struct irlmp_cb *irlmp = NULL; - -/* These can be altered by the sysctl interface */ -int sysctl_discovery = 0; -int sysctl_discovery_timeout = 3; /* 3 seconds by default */ -int sysctl_discovery_slots = 6; /* 6 slots by default */ -int sysctl_lap_keepalive_time = LM_IDLE_TIMEOUT * 1000 / HZ; -char sysctl_devname[65]; - -static const char *irlmp_reasons[] = { - "ERROR, NOT USED", - "LM_USER_REQUEST", - "LM_LAP_DISCONNECT", - "LM_CONNECT_FAILURE", - "LM_LAP_RESET", - "LM_INIT_DISCONNECT", - "ERROR, NOT USED", - "UNKNOWN", -}; - -const char *irlmp_reason_str(LM_REASON reason) -{ - reason = min_t(size_t, reason, ARRAY_SIZE(irlmp_reasons) - 1); - return irlmp_reasons[reason]; -} - -/* - * Function irlmp_init (void) - * - * Create (allocate) the main IrLMP structure - * - */ -int __init irlmp_init(void) -{ - /* Initialize the irlmp structure. */ - irlmp = kzalloc( sizeof(struct irlmp_cb), GFP_KERNEL); - if (irlmp == NULL) - return -ENOMEM; - - irlmp->magic = LMP_MAGIC; - - irlmp->clients = hashbin_new(HB_LOCK); - irlmp->services = hashbin_new(HB_LOCK); - irlmp->links = hashbin_new(HB_LOCK); - irlmp->unconnected_lsaps = hashbin_new(HB_LOCK); - irlmp->cachelog = hashbin_new(HB_NOLOCK); - - if ((irlmp->clients == NULL) || - (irlmp->services == NULL) || - (irlmp->links == NULL) || - (irlmp->unconnected_lsaps == NULL) || - (irlmp->cachelog == NULL)) { - return -ENOMEM; - } - - spin_lock_init(&irlmp->cachelog->hb_spinlock); - - irlmp->last_lsap_sel = 0x0f; /* Reserved 0x00-0x0f */ - strcpy(sysctl_devname, "Linux"); - - init_timer(&irlmp->discovery_timer); - - /* Do discovery every 3 seconds, conditionally */ - if (sysctl_discovery) - irlmp_start_discovery_timer(irlmp, - sysctl_discovery_timeout*HZ); - - return 0; -} - -/* - * Function irlmp_cleanup (void) - * - * Remove IrLMP layer - * - */ -void irlmp_cleanup(void) -{ - /* Check for main structure */ - IRDA_ASSERT(irlmp != NULL, return;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;); - - del_timer(&irlmp->discovery_timer); - - hashbin_delete(irlmp->links, (FREE_FUNC) kfree); - hashbin_delete(irlmp->unconnected_lsaps, (FREE_FUNC) kfree); - hashbin_delete(irlmp->clients, (FREE_FUNC) kfree); - hashbin_delete(irlmp->services, (FREE_FUNC) kfree); - hashbin_delete(irlmp->cachelog, (FREE_FUNC) kfree); - - /* De-allocate main structure */ - kfree(irlmp); - irlmp = NULL; -} - -/* - * Function irlmp_open_lsap (slsap, notify) - * - * Register with IrLMP and create a local LSAP, - * returns handle to LSAP. - */ -struct lsap_cb *irlmp_open_lsap(__u8 slsap_sel, notify_t *notify, __u8 pid) -{ - struct lsap_cb *self; - - IRDA_ASSERT(notify != NULL, return NULL;); - IRDA_ASSERT(irlmp != NULL, return NULL;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return NULL;); - IRDA_ASSERT(notify->instance != NULL, return NULL;); - - /* Does the client care which Source LSAP selector it gets? */ - if (slsap_sel == LSAP_ANY) { - slsap_sel = irlmp_find_free_slsap(); - if (!slsap_sel) - return NULL; - } else if (irlmp_slsap_inuse(slsap_sel)) - return NULL; - - /* Allocate new instance of a LSAP connection */ - self = kzalloc(sizeof(struct lsap_cb), GFP_ATOMIC); - if (self == NULL) - return NULL; - - self->magic = LMP_LSAP_MAGIC; - self->slsap_sel = slsap_sel; - - /* Fix connectionless LSAP's */ - if (slsap_sel == LSAP_CONNLESS) { -#ifdef CONFIG_IRDA_ULTRA - self->dlsap_sel = LSAP_CONNLESS; - self->pid = pid; -#endif /* CONFIG_IRDA_ULTRA */ - } else - self->dlsap_sel = LSAP_ANY; - /* self->connected = FALSE; -> already NULL via memset() */ - - init_timer(&self->watchdog_timer); - - self->notify = *notify; - - self->lsap_state = LSAP_DISCONNECTED; - - /* Insert into queue of unconnected LSAPs */ - hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, - (long) self, NULL); - - return self; -} -EXPORT_SYMBOL(irlmp_open_lsap); - -/* - * Function __irlmp_close_lsap (self) - * - * Remove an instance of LSAP - */ -static void __irlmp_close_lsap(struct lsap_cb *self) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - - /* - * Set some of the variables to preset values - */ - self->magic = 0; - del_timer(&self->watchdog_timer); /* Important! */ - - if (self->conn_skb) - dev_kfree_skb(self->conn_skb); - - kfree(self); -} - -/* - * Function irlmp_close_lsap (self) - * - * Close and remove LSAP - * - */ -void irlmp_close_lsap(struct lsap_cb *self) -{ - struct lap_cb *lap; - struct lsap_cb *lsap = NULL; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - - /* - * Find out if we should remove this LSAP from a link or from the - * list of unconnected lsaps (not associated with a link) - */ - lap = self->lap; - if (lap) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); - /* We might close a LSAP before it has completed the - * connection setup. In those case, higher layers won't - * send a proper disconnect request. Harmless, except - * that we will forget to close LAP... - Jean II */ - if(self->lsap_state != LSAP_DISCONNECTED) { - self->lsap_state = LSAP_DISCONNECTED; - irlmp_do_lap_event(self->lap, - LM_LAP_DISCONNECT_REQUEST, NULL); - } - /* Now, remove from the link */ - lsap = hashbin_remove(lap->lsaps, (long) self, NULL); -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - lap->cache.valid = FALSE; -#endif - } - self->lap = NULL; - /* Check if we found the LSAP! If not then try the unconnected lsaps */ - if (!lsap) { - lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, - NULL); - } - if (!lsap) { - pr_debug("%s(), Looks like somebody has removed me already!\n", - __func__); - return; - } - __irlmp_close_lsap(self); -} -EXPORT_SYMBOL(irlmp_close_lsap); - -/* - * Function irlmp_register_irlap (saddr, notify) - * - * Register IrLAP layer with IrLMP. There is possible to have multiple - * instances of the IrLAP layer, each connected to different IrDA ports - * - */ -void irlmp_register_link(struct irlap_cb *irlap, __u32 saddr, notify_t *notify) -{ - struct lap_cb *lap; - - IRDA_ASSERT(irlmp != NULL, return;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return;); - IRDA_ASSERT(notify != NULL, return;); - - /* - * Allocate new instance of a LSAP connection - */ - lap = kzalloc(sizeof(struct lap_cb), GFP_KERNEL); - if (lap == NULL) - return; - - lap->irlap = irlap; - lap->magic = LMP_LAP_MAGIC; - lap->saddr = saddr; - lap->daddr = DEV_ADDR_ANY; -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - lap->cache.valid = FALSE; -#endif - lap->lsaps = hashbin_new(HB_LOCK); - if (lap->lsaps == NULL) { - net_warn_ratelimited("%s(), unable to kmalloc lsaps\n", - __func__); - kfree(lap); - return; - } - - lap->lap_state = LAP_STANDBY; - - init_timer(&lap->idle_timer); - - /* - * Insert into queue of LMP links - */ - hashbin_insert(irlmp->links, (irda_queue_t *) lap, lap->saddr, NULL); - - /* - * We set only this variable so IrLAP can tell us on which link the - * different events happened on - */ - irda_notify_init(notify); - notify->instance = lap; -} - -/* - * Function irlmp_unregister_irlap (saddr) - * - * IrLAP layer has been removed! - * - */ -void irlmp_unregister_link(__u32 saddr) -{ - struct lap_cb *link; - - /* We must remove ourselves from the hashbin *first*. This ensure - * that no more LSAPs will be open on this link and no discovery - * will be triggered anymore. Jean II */ - link = hashbin_remove(irlmp->links, saddr, NULL); - if (link) { - IRDA_ASSERT(link->magic == LMP_LAP_MAGIC, return;); - - /* Kill all the LSAPs on this link. Jean II */ - link->reason = LAP_DISC_INDICATION; - link->daddr = DEV_ADDR_ANY; - irlmp_do_lap_event(link, LM_LAP_DISCONNECT_INDICATION, NULL); - - /* Remove all discoveries discovered at this link */ - irlmp_expire_discoveries(irlmp->cachelog, link->saddr, TRUE); - - /* Final cleanup */ - del_timer(&link->idle_timer); - link->magic = 0; - hashbin_delete(link->lsaps, (FREE_FUNC) __irlmp_close_lsap); - kfree(link); - } -} - -/* - * Function irlmp_connect_request (handle, dlsap, userdata) - * - * Connect with a peer LSAP - * - */ -int irlmp_connect_request(struct lsap_cb *self, __u8 dlsap_sel, - __u32 saddr, __u32 daddr, - struct qos_info *qos, struct sk_buff *userdata) -{ - struct sk_buff *tx_skb = userdata; - struct lap_cb *lap; - struct lsap_cb *lsap; - int ret; - - IRDA_ASSERT(self != NULL, return -EBADR;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EBADR;); - - pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x, saddr=%08x, daddr=%08x\n", - __func__, self->slsap_sel, dlsap_sel, saddr, daddr); - - if (test_bit(0, &self->connected)) { - ret = -EISCONN; - goto err; - } - - /* Client must supply destination device address */ - if (!daddr) { - ret = -EINVAL; - goto err; - } - - /* Any userdata? */ - if (tx_skb == NULL) { - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - skb_reserve(tx_skb, LMP_MAX_HEADER); - } - - /* Make room for MUX control header (3 bytes) */ - IRDA_ASSERT(skb_headroom(tx_skb) >= LMP_CONTROL_HEADER, return -1;); - skb_push(tx_skb, LMP_CONTROL_HEADER); - - self->dlsap_sel = dlsap_sel; - - /* - * Find the link to where we should try to connect since there may - * be more than one IrDA port on this machine. If the client has - * passed us the saddr (and already knows which link to use), then - * we use that to find the link, if not then we have to look in the - * discovery log and check if any of the links has discovered a - * device with the given daddr - */ - if ((!saddr) || (saddr == DEV_ADDR_ANY)) { - discovery_t *discovery; - unsigned long flags; - - spin_lock_irqsave(&irlmp->cachelog->hb_spinlock, flags); - if (daddr != DEV_ADDR_ANY) - discovery = hashbin_find(irlmp->cachelog, daddr, NULL); - else { - pr_debug("%s(), no daddr\n", __func__); - discovery = (discovery_t *) - hashbin_get_first(irlmp->cachelog); - } - - if (discovery) { - saddr = discovery->data.saddr; - daddr = discovery->data.daddr; - } - spin_unlock_irqrestore(&irlmp->cachelog->hb_spinlock, flags); - } - lap = hashbin_lock_find(irlmp->links, saddr, NULL); - if (lap == NULL) { - pr_debug("%s(), Unable to find a usable link!\n", __func__); - ret = -EHOSTUNREACH; - goto err; - } - - /* Check if LAP is disconnected or already connected */ - if (lap->daddr == DEV_ADDR_ANY) - lap->daddr = daddr; - else if (lap->daddr != daddr) { - /* Check if some LSAPs are active on this LAP */ - if (HASHBIN_GET_SIZE(lap->lsaps) == 0) { - /* No active connection, but LAP hasn't been - * disconnected yet (waiting for timeout in LAP). - * Maybe we could give LAP a bit of help in this case. - */ - pr_debug("%s(), sorry, but I'm waiting for LAP to timeout!\n", - __func__); - ret = -EAGAIN; - goto err; - } - - /* LAP is already connected to a different node, and LAP - * can only talk to one node at a time */ - pr_debug("%s(), sorry, but link is busy!\n", __func__); - ret = -EBUSY; - goto err; - } - - self->lap = lap; - - /* - * Remove LSAP from list of unconnected LSAPs and insert it into the - * list of connected LSAPs for the particular link - */ - lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, NULL); - - IRDA_ASSERT(lsap != NULL, return -1;); - IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(lsap->lap != NULL, return -1;); - IRDA_ASSERT(lsap->lap->magic == LMP_LAP_MAGIC, return -1;); - - hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, (long) self, - NULL); - - set_bit(0, &self->connected); /* TRUE */ - - /* - * User supplied qos specifications? - */ - if (qos) - self->qos = *qos; - - irlmp_do_lsap_event(self, LM_CONNECT_REQUEST, tx_skb); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(tx_skb); - - return 0; - -err: - /* Cleanup */ - if(tx_skb) - dev_kfree_skb(tx_skb); - return ret; -} -EXPORT_SYMBOL(irlmp_connect_request); - -/* - * Function irlmp_connect_indication (self) - * - * Incoming connection - * - */ -void irlmp_connect_indication(struct lsap_cb *self, struct sk_buff *skb) -{ - int max_seg_size; - int lap_header_size; - int max_header_size; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(self->lap != NULL, return;); - - pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", - __func__, self->slsap_sel, self->dlsap_sel); - - /* Note : self->lap is set in irlmp_link_data_indication(), - * (case CONNECT_CMD:) because we have no way to set it here. - * Similarly, self->dlsap_sel is usually set in irlmp_find_lsap(). - * Jean II */ - - self->qos = *self->lap->qos; - - max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; - lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap); - max_header_size = LMP_HEADER + lap_header_size; - - /* Hide LMP_CONTROL_HEADER header from layer above */ - skb_pull(skb, LMP_CONTROL_HEADER); - - if (self->notify.connect_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - skb_get(skb); - self->notify.connect_indication(self->notify.instance, self, - &self->qos, max_seg_size, - max_header_size, skb); - } -} - -/* - * Function irlmp_connect_response (handle, userdata) - * - * Service user is accepting connection - * - */ -int irlmp_connect_response(struct lsap_cb *self, struct sk_buff *userdata) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(userdata != NULL, return -1;); - - /* We set the connected bit and move the lsap to the connected list - * in the state machine itself. Jean II */ - - pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", - __func__, self->slsap_sel, self->dlsap_sel); - - /* Make room for MUX control header (3 bytes) */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_CONTROL_HEADER, return -1;); - skb_push(userdata, LMP_CONTROL_HEADER); - - irlmp_do_lsap_event(self, LM_CONNECT_RESPONSE, userdata); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(userdata); - - return 0; -} -EXPORT_SYMBOL(irlmp_connect_response); - -/* - * Function irlmp_connect_confirm (handle, skb) - * - * LSAP connection confirmed peer device! - */ -void irlmp_connect_confirm(struct lsap_cb *self, struct sk_buff *skb) -{ - int max_header_size; - int lap_header_size; - int max_seg_size; - - IRDA_ASSERT(skb != NULL, return;); - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - IRDA_ASSERT(self->lap != NULL, return;); - - self->qos = *self->lap->qos; - - max_seg_size = self->lap->qos->data_size.value-LMP_HEADER; - lap_header_size = IRLAP_GET_HEADER_SIZE(self->lap->irlap); - max_header_size = LMP_HEADER + lap_header_size; - - pr_debug("%s(), max_header_size=%d\n", - __func__, max_header_size); - - /* Hide LMP_CONTROL_HEADER header from layer above */ - skb_pull(skb, LMP_CONTROL_HEADER); - - if (self->notify.connect_confirm) { - /* Don't forget to refcount it - see irlap_driver_rcv() */ - skb_get(skb); - self->notify.connect_confirm(self->notify.instance, self, - &self->qos, max_seg_size, - max_header_size, skb); - } -} - -/* - * Function irlmp_dup (orig, instance) - * - * Duplicate LSAP, can be used by servers to confirm a connection on a - * new LSAP so it can keep listening on the old one. - * - */ -struct lsap_cb *irlmp_dup(struct lsap_cb *orig, void *instance) -{ - struct lsap_cb *new; - unsigned long flags; - - spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - /* Only allowed to duplicate unconnected LSAP's, and only LSAPs - * that have received a connect indication. Jean II */ - if ((!hashbin_find(irlmp->unconnected_lsaps, (long) orig, NULL)) || - (orig->lap == NULL)) { - pr_debug("%s(), invalid LSAP (wrong state)\n", - __func__); - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, - flags); - return NULL; - } - - /* Allocate a new instance */ - new = kmemdup(orig, sizeof(*new), GFP_ATOMIC); - if (!new) { - pr_debug("%s(), unable to kmalloc\n", __func__); - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, - flags); - return NULL; - } - /* new->lap = orig->lap; => done in the memcpy() */ - /* new->slsap_sel = orig->slsap_sel; => done in the memcpy() */ - new->conn_skb = NULL; - - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - /* Not everything is the same */ - new->notify.instance = instance; - - init_timer(&new->watchdog_timer); - - hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) new, - (long) new, NULL); - -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - /* Make sure that we invalidate the LSAP cache */ - new->lap->cache.valid = FALSE; -#endif /* CONFIG_IRDA_CACHE_LAST_LSAP */ - - return new; -} - -/* - * Function irlmp_disconnect_request (handle, userdata) - * - * The service user is requesting disconnection, this will not remove the - * LSAP, but only mark it as disconnected - */ -int irlmp_disconnect_request(struct lsap_cb *self, struct sk_buff *userdata) -{ - struct lsap_cb *lsap; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(userdata != NULL, return -1;); - - /* Already disconnected ? - * There is a race condition between irlmp_disconnect_indication() - * and us that might mess up the hashbins below. This fixes it. - * Jean II */ - if (! test_and_clear_bit(0, &self->connected)) { - pr_debug("%s(), already disconnected!\n", __func__); - dev_kfree_skb(userdata); - return -1; - } - - skb_push(userdata, LMP_CONTROL_HEADER); - - /* - * Do the event before the other stuff since we must know - * which lap layer that the frame should be transmitted on - */ - irlmp_do_lsap_event(self, LM_DISCONNECT_REQUEST, userdata); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(userdata); - - /* - * Remove LSAP from list of connected LSAPs for the particular link - * and insert it into the list of unconnected LSAPs - */ - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); - IRDA_ASSERT(self->lap->lsaps != NULL, return -1;); - - lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL); -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - self->lap->cache.valid = FALSE; -#endif - - IRDA_ASSERT(lsap != NULL, return -1;); - IRDA_ASSERT(lsap->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(lsap == self, return -1;); - - hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) self, - (long) self, NULL); - - /* Reset some values */ - self->dlsap_sel = LSAP_ANY; - self->lap = NULL; - - return 0; -} -EXPORT_SYMBOL(irlmp_disconnect_request); - -/* - * Function irlmp_disconnect_indication (reason, userdata) - * - * LSAP is being closed! - */ -void irlmp_disconnect_indication(struct lsap_cb *self, LM_REASON reason, - struct sk_buff *skb) -{ - struct lsap_cb *lsap; - - pr_debug("%s(), reason=%s [%d]\n", __func__, - irlmp_reason_str(reason), reason); - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - - pr_debug("%s(), slsap_sel=%02x, dlsap_sel=%02x\n", - __func__, self->slsap_sel, self->dlsap_sel); - - /* Already disconnected ? - * There is a race condition between irlmp_disconnect_request() - * and us that might mess up the hashbins below. This fixes it. - * Jean II */ - if (! test_and_clear_bit(0, &self->connected)) { - pr_debug("%s(), already disconnected!\n", __func__); - return; - } - - /* - * Remove association between this LSAP and the link it used - */ - IRDA_ASSERT(self->lap != NULL, return;); - IRDA_ASSERT(self->lap->lsaps != NULL, return;); - - lsap = hashbin_remove(self->lap->lsaps, (long) self, NULL); -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - self->lap->cache.valid = FALSE; -#endif - - IRDA_ASSERT(lsap != NULL, return;); - IRDA_ASSERT(lsap == self, return;); - hashbin_insert(irlmp->unconnected_lsaps, (irda_queue_t *) lsap, - (long) lsap, NULL); - - self->dlsap_sel = LSAP_ANY; - self->lap = NULL; - - /* - * Inform service user - */ - if (self->notify.disconnect_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - if(skb) - skb_get(skb); - self->notify.disconnect_indication(self->notify.instance, - self, reason, skb); - } else { - pr_debug("%s(), no handler\n", __func__); - } -} - -/* - * Function irlmp_do_expiry (void) - * - * Do a cleanup of the discovery log (remove old entries) - * - * Note : separate from irlmp_do_discovery() so that we can handle - * passive discovery properly. - */ -void irlmp_do_expiry(void) -{ - struct lap_cb *lap; - - /* - * Expire discovery on all links which are *not* connected. - * On links which are connected, we can't do discovery - * anymore and can't refresh the log, so we freeze the - * discovery log to keep info about the device we are - * connected to. - * This info is mandatory if we want irlmp_connect_request() - * to work properly. - Jean II - */ - lap = (struct lap_cb *) hashbin_get_first(irlmp->links); - while (lap != NULL) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); - - if (lap->lap_state == LAP_STANDBY) { - /* Expire discoveries discovered on this link */ - irlmp_expire_discoveries(irlmp->cachelog, lap->saddr, - FALSE); - } - lap = (struct lap_cb *) hashbin_get_next(irlmp->links); - } -} - -/* - * Function irlmp_do_discovery (nslots) - * - * Do some discovery on all links - * - * Note : log expiry is done above. - */ -void irlmp_do_discovery(int nslots) -{ - struct lap_cb *lap; - __u16 *data_hintsp; - - /* Make sure the value is sane */ - if ((nslots != 1) && (nslots != 6) && (nslots != 8) && (nslots != 16)){ - net_warn_ratelimited("%s: invalid value for number of slots!\n", - __func__); - nslots = sysctl_discovery_slots = 8; - } - - /* Construct new discovery info to be used by IrLAP, */ - data_hintsp = (__u16 *) irlmp->discovery_cmd.data.hints; - put_unaligned(irlmp->hints.word, data_hintsp); - - /* - * Set character set for device name (we use ASCII), and - * copy device name. Remember to make room for a \0 at the - * end - */ - irlmp->discovery_cmd.data.charset = CS_ASCII; - strncpy(irlmp->discovery_cmd.data.info, sysctl_devname, - NICKNAME_MAX_LEN); - irlmp->discovery_cmd.name_len = strlen(irlmp->discovery_cmd.data.info); - irlmp->discovery_cmd.nslots = nslots; - - /* - * Try to send discovery packets on all links - */ - lap = (struct lap_cb *) hashbin_get_first(irlmp->links); - while (lap != NULL) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); - - if (lap->lap_state == LAP_STANDBY) { - /* Try to discover */ - irlmp_do_lap_event(lap, LM_LAP_DISCOVERY_REQUEST, - NULL); - } - lap = (struct lap_cb *) hashbin_get_next(irlmp->links); - } -} - -/* - * Function irlmp_discovery_request (nslots) - * - * Do a discovery of devices in front of the computer - * - * If the caller has registered a client discovery callback, this - * allow him to receive the full content of the discovery log through - * this callback (as normally he will receive only new discoveries). - */ -void irlmp_discovery_request(int nslots) -{ - /* Return current cached discovery log (in full) */ - irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_LOG); - - /* - * Start a single discovery operation if discovery is not already - * running - */ - if (!sysctl_discovery) { - /* Check if user wants to override the default */ - if (nslots == DISCOVERY_DEFAULT_SLOTS) - nslots = sysctl_discovery_slots; - - irlmp_do_discovery(nslots); - /* Note : we never do expiry here. Expiry will run on the - * discovery timer regardless of the state of sysctl_discovery - * Jean II */ - } -} -EXPORT_SYMBOL(irlmp_discovery_request); - -/* - * Function irlmp_get_discoveries (pn, mask, slots) - * - * Return the current discovery log - * - * If discovery is not enabled, you should call this function again - * after 1 or 2 seconds (i.e. after discovery has been done). - */ -struct irda_device_info *irlmp_get_discoveries(int *pn, __u16 mask, int nslots) -{ - /* If discovery is not enabled, it's likely that the discovery log - * will be empty. So, we trigger a single discovery, so that next - * time the user call us there might be some results in the log. - * Jean II - */ - if (!sysctl_discovery) { - /* Check if user wants to override the default */ - if (nslots == DISCOVERY_DEFAULT_SLOTS) - nslots = sysctl_discovery_slots; - - /* Start discovery - will complete sometime later */ - irlmp_do_discovery(nslots); - /* Note : we never do expiry here. Expiry will run on the - * discovery timer regardless of the state of sysctl_discovery - * Jean II */ - } - - /* Return current cached discovery log */ - return irlmp_copy_discoveries(irlmp->cachelog, pn, mask, TRUE); -} -EXPORT_SYMBOL(irlmp_get_discoveries); - -/* - * Function irlmp_notify_client (log) - * - * Notify all about discovered devices - * - * Clients registered with IrLMP are : - * o IrComm - * o IrLAN - * o Any socket (in any state - ouch, that may be a lot !) - * The client may have defined a callback to be notified in case of - * partial/selective discovery based on the hints that it passed to IrLMP. - */ -static inline void -irlmp_notify_client(irlmp_client_t *client, - hashbin_t *log, DISCOVERY_MODE mode) -{ - discinfo_t *discoveries; /* Copy of the discovery log */ - int number; /* Number of nodes in the log */ - int i; - - /* Check if client wants or not partial/selective log (optimisation) */ - if (!client->disco_callback) - return; - - /* - * Locking notes : - * the old code was manipulating the log directly, which was - * very racy. Now, we use copy_discoveries, that protects - * itself while dumping the log for us. - * The overhead of the copy is compensated by the fact that - * we only pass new discoveries in normal mode and don't - * pass the same old entry every 3s to the caller as we used - * to do (virtual function calling is expensive). - * Jean II - */ - - /* - * Now, check all discovered devices (if any), and notify client - * only about the services that the client is interested in - * We also notify only about the new devices unless the caller - * explicitly request a dump of the log. Jean II - */ - discoveries = irlmp_copy_discoveries(log, &number, - client->hint_mask.word, - (mode == DISCOVERY_LOG)); - /* Check if the we got some results */ - if (discoveries == NULL) - return; /* No nodes discovered */ - - /* Pass all entries to the listener */ - for(i = 0; i < number; i++) - client->disco_callback(&(discoveries[i]), mode, client->priv); - - /* Free up our buffer */ - kfree(discoveries); -} - -/* - * Function irlmp_discovery_confirm ( self, log) - * - * Some device(s) answered to our discovery request! Check to see which - * device it is, and give indication to the client(s) - * - */ -void irlmp_discovery_confirm(hashbin_t *log, DISCOVERY_MODE mode) -{ - irlmp_client_t *client; - irlmp_client_t *client_next; - - IRDA_ASSERT(log != NULL, return;); - - if (!(HASHBIN_GET_SIZE(log))) - return; - - /* For each client - notify callback may touch client list */ - client = (irlmp_client_t *) hashbin_get_first(irlmp->clients); - while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL, - (void *) &client_next) ) { - /* Check if we should notify client */ - irlmp_notify_client(client, log, mode); - - client = client_next; - } -} - -/* - * Function irlmp_discovery_expiry (expiry) - * - * This device is no longer been discovered, and therefore it is being - * purged from the discovery log. Inform all clients who have - * registered for this event... - * - * Note : called exclusively from discovery.c - * Note : this is no longer called under discovery spinlock, so the - * client can do whatever he wants in the callback. - */ -void irlmp_discovery_expiry(discinfo_t *expiries, int number) -{ - irlmp_client_t *client; - irlmp_client_t *client_next; - int i; - - IRDA_ASSERT(expiries != NULL, return;); - - /* For each client - notify callback may touch client list */ - client = (irlmp_client_t *) hashbin_get_first(irlmp->clients); - while (NULL != hashbin_find_next(irlmp->clients, (long) client, NULL, - (void *) &client_next) ) { - - /* Pass all entries to the listener */ - for(i = 0; i < number; i++) { - /* Check if we should notify client */ - if ((client->expir_callback) && - (client->hint_mask.word & - get_unaligned((__u16 *)expiries[i].hints) - & 0x7f7f) ) - client->expir_callback(&(expiries[i]), - EXPIRY_TIMEOUT, - client->priv); - } - - /* Next client */ - client = client_next; - } -} - -/* - * Function irlmp_get_discovery_response () - * - * Used by IrLAP to get the discovery info it needs when answering - * discovery requests by other devices. - */ -discovery_t *irlmp_get_discovery_response(void) -{ - IRDA_ASSERT(irlmp != NULL, return NULL;); - - put_unaligned(irlmp->hints.word, (__u16 *)irlmp->discovery_rsp.data.hints); - - /* - * Set character set for device name (we use ASCII), and - * copy device name. Remember to make room for a \0 at the - * end - */ - irlmp->discovery_rsp.data.charset = CS_ASCII; - - strncpy(irlmp->discovery_rsp.data.info, sysctl_devname, - NICKNAME_MAX_LEN); - irlmp->discovery_rsp.name_len = strlen(irlmp->discovery_rsp.data.info); - - return &irlmp->discovery_rsp; -} - -/* - * Function irlmp_data_request (self, skb) - * - * Send some data to peer device - * - * Note on skb management : - * After calling the lower layers of the IrDA stack, we always - * kfree() the skb, which drop the reference count (and potentially - * destroy it). - * IrLMP and IrLAP may queue the packet, and in those cases will need - * to use skb_get() to keep it around. - * Jean II - */ -int irlmp_data_request(struct lsap_cb *self, struct sk_buff *userdata) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - /* Make room for MUX header */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;); - skb_push(userdata, LMP_HEADER); - - ret = irlmp_do_lsap_event(self, LM_DATA_REQUEST, userdata); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(userdata); - - return ret; -} -EXPORT_SYMBOL(irlmp_data_request); - -/* - * Function irlmp_data_indication (handle, skb) - * - * Got data from LAP layer so pass it up to upper layer - * - */ -void irlmp_data_indication(struct lsap_cb *self, struct sk_buff *skb) -{ - /* Hide LMP header from layer above */ - skb_pull(skb, LMP_HEADER); - - if (self->notify.data_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - skb_get(skb); - self->notify.data_indication(self->notify.instance, self, skb); - } -} - -/* - * Function irlmp_udata_request (self, skb) - */ -int irlmp_udata_request(struct lsap_cb *self, struct sk_buff *userdata) -{ - int ret; - - IRDA_ASSERT(userdata != NULL, return -1;); - - /* Make room for MUX header */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER, return -1;); - skb_push(userdata, LMP_HEADER); - - ret = irlmp_do_lsap_event(self, LM_UDATA_REQUEST, userdata); - - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(userdata); - - return ret; -} - -/* - * Function irlmp_udata_indication (self, skb) - * - * Send unreliable data (but still within the connection) - * - */ -void irlmp_udata_indication(struct lsap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Hide LMP header from layer above */ - skb_pull(skb, LMP_HEADER); - - if (self->notify.udata_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - skb_get(skb); - self->notify.udata_indication(self->notify.instance, self, - skb); - } -} - -/* - * Function irlmp_connless_data_request (self, skb) - */ -#ifdef CONFIG_IRDA_ULTRA -int irlmp_connless_data_request(struct lsap_cb *self, struct sk_buff *userdata, - __u8 pid) -{ - struct sk_buff *clone_skb; - struct lap_cb *lap; - - IRDA_ASSERT(userdata != NULL, return -1;); - - /* Make room for MUX and PID header */ - IRDA_ASSERT(skb_headroom(userdata) >= LMP_HEADER+LMP_PID_HEADER, - return -1;); - - /* Insert protocol identifier */ - skb_push(userdata, LMP_PID_HEADER); - if(self != NULL) - userdata->data[0] = self->pid; - else - userdata->data[0] = pid; - - /* Connectionless sockets must use 0x70 */ - skb_push(userdata, LMP_HEADER); - userdata->data[0] = userdata->data[1] = LSAP_CONNLESS; - - /* Try to send Connectionless packets out on all links */ - lap = (struct lap_cb *) hashbin_get_first(irlmp->links); - while (lap != NULL) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return -1;); - - clone_skb = skb_clone(userdata, GFP_ATOMIC); - if (!clone_skb) { - dev_kfree_skb(userdata); - return -ENOMEM; - } - - irlap_unitdata_request(lap->irlap, clone_skb); - /* irlap_unitdata_request() don't increase refcount, - * so no dev_kfree_skb() - Jean II */ - - lap = (struct lap_cb *) hashbin_get_next(irlmp->links); - } - dev_kfree_skb(userdata); - - return 0; -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irlmp_connless_data_indication (self, skb) - * - * Receive unreliable data outside any connection. Mostly used by Ultra - * - */ -#ifdef CONFIG_IRDA_ULTRA -void irlmp_connless_data_indication(struct lsap_cb *self, struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* Hide LMP and PID header from layer above */ - skb_pull(skb, LMP_HEADER+LMP_PID_HEADER); - - if (self->notify.udata_indication) { - /* Don't forget to refcount it - see irlap_driver_rcv(). */ - skb_get(skb); - self->notify.udata_indication(self->notify.instance, self, - skb); - } -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Propagate status indication from LAP to LSAPs (via LMP) - * This don't trigger any change of state in lap_cb, lmp_cb or lsap_cb, - * and the event is stateless, therefore we can bypass both state machines - * and send the event direct to the LSAP user. - * Jean II - */ -void irlmp_status_indication(struct lap_cb *self, - LINK_STATUS link, LOCK_STATUS lock) -{ - struct lsap_cb *next; - struct lsap_cb *curr; - - /* Send status_indication to all LSAPs using this link */ - curr = (struct lsap_cb *) hashbin_get_first( self->lsaps); - while (NULL != hashbin_find_next(self->lsaps, (long) curr, NULL, - (void *) &next) ) { - IRDA_ASSERT(curr->magic == LMP_LSAP_MAGIC, return;); - /* - * Inform service user if he has requested it - */ - if (curr->notify.status_indication != NULL) - curr->notify.status_indication(curr->notify.instance, - link, lock); - else - pr_debug("%s(), no handler\n", __func__); - - curr = next; - } -} - -/* - * Receive flow control indication from LAP. - * LAP want us to send it one more frame. We implement a simple round - * robin scheduler between the active sockets so that we get a bit of - * fairness. Note that the round robin is far from perfect, but it's - * better than nothing. - * We then poll the selected socket so that we can do synchronous - * refilling of IrLAP (which allow to minimise the number of buffers). - * Jean II - */ -void irlmp_flow_indication(struct lap_cb *self, LOCAL_FLOW flow) -{ - struct lsap_cb *next; - struct lsap_cb *curr; - int lsap_todo; - - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(flow == FLOW_START, return;); - - /* Get the number of lsap. That's the only safe way to know - * that we have looped around... - Jean II */ - lsap_todo = HASHBIN_GET_SIZE(self->lsaps); - pr_debug("%s() : %d lsaps to scan\n", __func__, lsap_todo); - - /* Poll lsap in order until the queue is full or until we - * tried them all. - * Most often, the current LSAP will have something to send, - * so we will go through this loop only once. - Jean II */ - while((lsap_todo--) && - (IRLAP_GET_TX_QUEUE_LEN(self->irlap) < LAP_HIGH_THRESHOLD)) { - /* Try to find the next lsap we should poll. */ - next = self->flow_next; - /* If we have no lsap, restart from first one */ - if(next == NULL) - next = (struct lsap_cb *) hashbin_get_first(self->lsaps); - /* Verify current one and find the next one */ - curr = hashbin_find_next(self->lsaps, (long) next, NULL, - (void *) &self->flow_next); - /* Uh-oh... Paranoia */ - if(curr == NULL) - break; - pr_debug("%s() : curr is %p, next was %p and is now %p, still %d to go - queue len = %d\n", - __func__, curr, next, self->flow_next, lsap_todo, - IRLAP_GET_TX_QUEUE_LEN(self->irlap)); - - /* Inform lsap user that it can send one more packet. */ - if (curr->notify.flow_indication != NULL) - curr->notify.flow_indication(curr->notify.instance, - curr, flow); - else - pr_debug("%s(), no handler\n", __func__); - } -} - -#if 0 -/* - * Function irlmp_hint_to_service (hint) - * - * Returns a list of all servics contained in the given hint bits. This - * function assumes that the hint bits have the size of two bytes only - */ -__u8 *irlmp_hint_to_service(__u8 *hint) -{ - __u8 *service; - int i = 0; - - /* - * Allocate array to store services in. 16 entries should be safe - * since we currently only support 2 hint bytes - */ - service = kmalloc(16, GFP_ATOMIC); - if (!service) - return NULL; - - if (!hint[0]) { - pr_debug("\n"); - kfree(service); - return NULL; - } - if (hint[0] & HINT_PNP) - pr_debug("PnP Compatible "); - if (hint[0] & HINT_PDA) - pr_debug("PDA/Palmtop "); - if (hint[0] & HINT_COMPUTER) - pr_debug("Computer "); - if (hint[0] & HINT_PRINTER) { - pr_debug("Printer "); - service[i++] = S_PRINTER; - } - if (hint[0] & HINT_MODEM) - pr_debug("Modem "); - if (hint[0] & HINT_FAX) - pr_debug("Fax "); - if (hint[0] & HINT_LAN) { - pr_debug("LAN Access "); - service[i++] = S_LAN; - } - /* - * Test if extension byte exists. This byte will usually be - * there, but this is not really required by the standard. - * (IrLMP p. 29) - */ - if (hint[0] & HINT_EXTENSION) { - if (hint[1] & HINT_TELEPHONY) { - pr_debug("Telephony "); - service[i++] = S_TELEPHONY; - } - if (hint[1] & HINT_FILE_SERVER) - pr_debug("File Server "); - - if (hint[1] & HINT_COMM) { - pr_debug("IrCOMM "); - service[i++] = S_COMM; - } - if (hint[1] & HINT_OBEX) { - pr_debug("IrOBEX "); - service[i++] = S_OBEX; - } - } - pr_debug("\n"); - - /* So that client can be notified about any discovery */ - service[i++] = S_ANY; - - service[i] = S_END; - - return service; -} -#endif - -static const __u16 service_hint_mapping[S_END][2] = { - { HINT_PNP, 0 }, /* S_PNP */ - { HINT_PDA, 0 }, /* S_PDA */ - { HINT_COMPUTER, 0 }, /* S_COMPUTER */ - { HINT_PRINTER, 0 }, /* S_PRINTER */ - { HINT_MODEM, 0 }, /* S_MODEM */ - { HINT_FAX, 0 }, /* S_FAX */ - { HINT_LAN, 0 }, /* S_LAN */ - { HINT_EXTENSION, HINT_TELEPHONY }, /* S_TELEPHONY */ - { HINT_EXTENSION, HINT_COMM }, /* S_COMM */ - { HINT_EXTENSION, HINT_OBEX }, /* S_OBEX */ - { 0xFF, 0xFF }, /* S_ANY */ -}; - -/* - * Function irlmp_service_to_hint (service) - * - * Converts a service type, to a hint bit - * - * Returns: a 16 bit hint value, with the service bit set - */ -__u16 irlmp_service_to_hint(int service) -{ - __u16_host_order hint; - - hint.byte[0] = service_hint_mapping[service][0]; - hint.byte[1] = service_hint_mapping[service][1]; - - return hint.word; -} -EXPORT_SYMBOL(irlmp_service_to_hint); - -/* - * Function irlmp_register_service (service) - * - * Register local service with IrLMP - * - */ -void *irlmp_register_service(__u16 hints) -{ - irlmp_service_t *service; - - pr_debug("%s(), hints = %04x\n", __func__, hints); - - /* Make a new registration */ - service = kmalloc(sizeof(irlmp_service_t), GFP_ATOMIC); - if (!service) - return NULL; - - service->hints.word = hints; - hashbin_insert(irlmp->services, (irda_queue_t *) service, - (long) service, NULL); - - irlmp->hints.word |= hints; - - return (void *)service; -} -EXPORT_SYMBOL(irlmp_register_service); - -/* - * Function irlmp_unregister_service (handle) - * - * Unregister service with IrLMP. - * - * Returns: 0 on success, -1 on error - */ -int irlmp_unregister_service(void *handle) -{ - irlmp_service_t *service; - unsigned long flags; - - if (!handle) - return -1; - - /* Caller may call with invalid handle (it's legal) - Jean II */ - service = hashbin_lock_find(irlmp->services, (long) handle, NULL); - if (!service) { - pr_debug("%s(), Unknown service!\n", __func__); - return -1; - } - - hashbin_remove_this(irlmp->services, (irda_queue_t *) service); - kfree(service); - - /* Remove old hint bits */ - irlmp->hints.word = 0; - - /* Refresh current hint bits */ - spin_lock_irqsave(&irlmp->services->hb_spinlock, flags); - service = (irlmp_service_t *) hashbin_get_first(irlmp->services); - while (service) { - irlmp->hints.word |= service->hints.word; - - service = (irlmp_service_t *)hashbin_get_next(irlmp->services); - } - spin_unlock_irqrestore(&irlmp->services->hb_spinlock, flags); - return 0; -} -EXPORT_SYMBOL(irlmp_unregister_service); - -/* - * Function irlmp_register_client (hint_mask, callback1, callback2) - * - * Register a local client with IrLMP - * First callback is selective discovery (based on hints) - * Second callback is for selective discovery expiries - * - * Returns: handle > 0 on success, 0 on error - */ -void *irlmp_register_client(__u16 hint_mask, DISCOVERY_CALLBACK1 disco_clb, - DISCOVERY_CALLBACK2 expir_clb, void *priv) -{ - irlmp_client_t *client; - - IRDA_ASSERT(irlmp != NULL, return NULL;); - - /* Make a new registration */ - client = kmalloc(sizeof(irlmp_client_t), GFP_ATOMIC); - if (!client) - return NULL; - - /* Register the details */ - client->hint_mask.word = hint_mask; - client->disco_callback = disco_clb; - client->expir_callback = expir_clb; - client->priv = priv; - - hashbin_insert(irlmp->clients, (irda_queue_t *) client, - (long) client, NULL); - - return (void *) client; -} -EXPORT_SYMBOL(irlmp_register_client); - -/* - * Function irlmp_update_client (handle, hint_mask, callback1, callback2) - * - * Updates specified client (handle) with possibly new hint_mask and - * callback - * - * Returns: 0 on success, -1 on error - */ -int irlmp_update_client(void *handle, __u16 hint_mask, - DISCOVERY_CALLBACK1 disco_clb, - DISCOVERY_CALLBACK2 expir_clb, void *priv) -{ - irlmp_client_t *client; - - if (!handle) - return -1; - - client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); - if (!client) { - pr_debug("%s(), Unknown client!\n", __func__); - return -1; - } - - client->hint_mask.word = hint_mask; - client->disco_callback = disco_clb; - client->expir_callback = expir_clb; - client->priv = priv; - - return 0; -} -EXPORT_SYMBOL(irlmp_update_client); - -/* - * Function irlmp_unregister_client (handle) - * - * Returns: 0 on success, -1 on error - * - */ -int irlmp_unregister_client(void *handle) -{ - struct irlmp_client *client; - - if (!handle) - return -1; - - /* Caller may call with invalid handle (it's legal) - Jean II */ - client = hashbin_lock_find(irlmp->clients, (long) handle, NULL); - if (!client) { - pr_debug("%s(), Unknown client!\n", __func__); - return -1; - } - - pr_debug("%s(), removing client!\n", __func__); - hashbin_remove_this(irlmp->clients, (irda_queue_t *) client); - kfree(client); - - return 0; -} -EXPORT_SYMBOL(irlmp_unregister_client); - -/* - * Function irlmp_slsap_inuse (slsap) - * - * Check if the given source LSAP selector is in use - * - * This function is clearly not very efficient. On the mitigating side, the - * stack make sure that in 99% of the cases, we are called only once - * for each socket allocation. We could probably keep a bitmap - * of the allocated LSAP, but I'm not sure the complexity is worth it. - * Jean II - */ -static int irlmp_slsap_inuse(__u8 slsap_sel) -{ - struct lsap_cb *self; - struct lap_cb *lap; - unsigned long flags; - - IRDA_ASSERT(irlmp != NULL, return TRUE;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return TRUE;); - IRDA_ASSERT(slsap_sel != LSAP_ANY, return TRUE;); - -#ifdef CONFIG_IRDA_ULTRA - /* Accept all bindings to the connectionless LSAP */ - if (slsap_sel == LSAP_CONNLESS) - return FALSE; -#endif /* CONFIG_IRDA_ULTRA */ - - /* Valid values are between 0 and 127 (0x0-0x6F) */ - if (slsap_sel > LSAP_MAX) - return TRUE; - - /* - * Check if slsap is already in use. To do this we have to loop over - * every IrLAP connection and check every LSAP associated with each - * the connection. - */ - spin_lock_irqsave_nested(&irlmp->links->hb_spinlock, flags, - SINGLE_DEPTH_NESTING); - lap = (struct lap_cb *) hashbin_get_first(irlmp->links); - while (lap != NULL) { - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, goto errlap;); - - /* Careful for priority inversions here ! - * irlmp->links is never taken while another IrDA - * spinlock is held, so we are safe. Jean II */ - spin_lock(&lap->lsaps->hb_spinlock); - - /* For this IrLAP, check all the LSAPs */ - self = (struct lsap_cb *) hashbin_get_first(lap->lsaps); - while (self != NULL) { - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, - goto errlsap;); - - if ((self->slsap_sel == slsap_sel)) { - pr_debug("Source LSAP selector=%02x in use\n", - self->slsap_sel); - goto errlsap; - } - self = (struct lsap_cb*) hashbin_get_next(lap->lsaps); - } - spin_unlock(&lap->lsaps->hb_spinlock); - - /* Next LAP */ - lap = (struct lap_cb *) hashbin_get_next(irlmp->links); - } - spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags); - - /* - * Server sockets are typically waiting for connections and - * therefore reside in the unconnected list. We don't want - * to give out their LSAPs for obvious reasons... - * Jean II - */ - spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - self = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps); - while (self != NULL) { - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, goto erruncon;); - if ((self->slsap_sel == slsap_sel)) { - pr_debug("Source LSAP selector=%02x in use (unconnected)\n", - self->slsap_sel); - goto erruncon; - } - self = (struct lsap_cb*) hashbin_get_next(irlmp->unconnected_lsaps); - } - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - return FALSE; - - /* Error exit from within one of the two nested loops. - * Make sure we release the right spinlock in the righ order. - * Jean II */ -errlsap: - spin_unlock(&lap->lsaps->hb_spinlock); -IRDA_ASSERT_LABEL(errlap:) - spin_unlock_irqrestore(&irlmp->links->hb_spinlock, flags); - return TRUE; - - /* Error exit from within the unconnected loop. - * Just one spinlock to release... Jean II */ -erruncon: - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); - return TRUE; -} - -/* - * Function irlmp_find_free_slsap () - * - * Find a free source LSAP to use. This function is called if the service - * user has requested a source LSAP equal to LM_ANY - */ -static __u8 irlmp_find_free_slsap(void) -{ - __u8 lsap_sel; - int wrapped = 0; - - IRDA_ASSERT(irlmp != NULL, return -1;); - IRDA_ASSERT(irlmp->magic == LMP_MAGIC, return -1;); - - /* Most users don't really care which LSAPs they are given, - * and therefore we automatically give them a free LSAP. - * This function try to find a suitable LSAP, i.e. which is - * not in use and is within the acceptable range. Jean II */ - - do { - /* Always increment to LSAP number before using it. - * In theory, we could reuse the last LSAP number, as long - * as it is no longer in use. Some IrDA stack do that. - * However, the previous socket may be half closed, i.e. - * we closed it, we think it's no longer in use, but the - * other side did not receive our close and think it's - * active and still send data on it. - * This is similar to what is done with PIDs and TCP ports. - * Also, this reduce the number of calls to irlmp_slsap_inuse() - * which is an expensive function to call. - * Jean II */ - irlmp->last_lsap_sel++; - - /* Check if we need to wraparound (0x70-0x7f are reserved) */ - if (irlmp->last_lsap_sel > LSAP_MAX) { - /* 0x00-0x10 are also reserved for well know ports */ - irlmp->last_lsap_sel = 0x10; - - /* Make sure we terminate the loop */ - if (wrapped++) { - net_err_ratelimited("%s: no more free LSAPs !\n", - __func__); - return 0; - } - } - - /* If the LSAP is in use, try the next one. - * Despite the autoincrement, we need to check if the lsap - * is really in use or not, first because LSAP may be - * directly allocated in irlmp_open_lsap(), and also because - * we may wraparound on old sockets. Jean II */ - } while (irlmp_slsap_inuse(irlmp->last_lsap_sel)); - - /* Got it ! */ - lsap_sel = irlmp->last_lsap_sel; - pr_debug("%s(), found free lsap_sel=%02x\n", - __func__, lsap_sel); - - return lsap_sel; -} - -/* - * Function irlmp_convert_lap_reason (lap_reason) - * - * Converts IrLAP disconnect reason codes to IrLMP disconnect reason - * codes - * - */ -LM_REASON irlmp_convert_lap_reason( LAP_REASON lap_reason) -{ - int reason = LM_LAP_DISCONNECT; - - switch (lap_reason) { - case LAP_DISC_INDICATION: /* Received a disconnect request from peer */ - pr_debug("%s(), LAP_DISC_INDICATION\n", __func__); - reason = LM_USER_REQUEST; - break; - case LAP_NO_RESPONSE: /* To many retransmits without response */ - pr_debug("%s(), LAP_NO_RESPONSE\n", __func__); - reason = LM_LAP_DISCONNECT; - break; - case LAP_RESET_INDICATION: - pr_debug("%s(), LAP_RESET_INDICATION\n", __func__); - reason = LM_LAP_RESET; - break; - case LAP_FOUND_NONE: - case LAP_MEDIA_BUSY: - case LAP_PRIMARY_CONFLICT: - pr_debug("%s(), LAP_FOUND_NONE, LAP_MEDIA_BUSY or LAP_PRIMARY_CONFLICT\n", - __func__); - reason = LM_CONNECT_FAILURE; - break; - default: - pr_debug("%s(), Unknown IrLAP disconnect reason %d!\n", - __func__, lap_reason); - reason = LM_LAP_DISCONNECT; - break; - } - - return reason; -} - -#ifdef CONFIG_PROC_FS - -struct irlmp_iter_state { - hashbin_t *hashbin; -}; - -#define LSAP_START_TOKEN ((void *)1) -#define LINK_START_TOKEN ((void *)2) - -static void *irlmp_seq_hb_idx(struct irlmp_iter_state *iter, loff_t *off) -{ - void *element; - - spin_lock_irq(&iter->hashbin->hb_spinlock); - for (element = hashbin_get_first(iter->hashbin); - element != NULL; - element = hashbin_get_next(iter->hashbin)) { - if (!off || (*off)-- == 0) { - /* NB: hashbin left locked */ - return element; - } - } - spin_unlock_irq(&iter->hashbin->hb_spinlock); - iter->hashbin = NULL; - return NULL; -} - - -static void *irlmp_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct irlmp_iter_state *iter = seq->private; - void *v; - loff_t off = *pos; - - iter->hashbin = NULL; - if (off-- == 0) - return LSAP_START_TOKEN; - - iter->hashbin = irlmp->unconnected_lsaps; - v = irlmp_seq_hb_idx(iter, &off); - if (v) - return v; - - if (off-- == 0) - return LINK_START_TOKEN; - - iter->hashbin = irlmp->links; - return irlmp_seq_hb_idx(iter, &off); -} - -static void *irlmp_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct irlmp_iter_state *iter = seq->private; - - ++*pos; - - if (v == LSAP_START_TOKEN) { /* start of list of lsaps */ - iter->hashbin = irlmp->unconnected_lsaps; - v = irlmp_seq_hb_idx(iter, NULL); - return v ? v : LINK_START_TOKEN; - } - - if (v == LINK_START_TOKEN) { /* start of list of links */ - iter->hashbin = irlmp->links; - return irlmp_seq_hb_idx(iter, NULL); - } - - v = hashbin_get_next(iter->hashbin); - - if (v == NULL) { /* no more in this hash bin */ - spin_unlock_irq(&iter->hashbin->hb_spinlock); - - if (iter->hashbin == irlmp->unconnected_lsaps) - v = LINK_START_TOKEN; - - iter->hashbin = NULL; - } - return v; -} - -static void irlmp_seq_stop(struct seq_file *seq, void *v) -{ - struct irlmp_iter_state *iter = seq->private; - - if (iter->hashbin) - spin_unlock_irq(&iter->hashbin->hb_spinlock); -} - -static int irlmp_seq_show(struct seq_file *seq, void *v) -{ - const struct irlmp_iter_state *iter = seq->private; - struct lsap_cb *self = v; - - if (v == LSAP_START_TOKEN) - seq_puts(seq, "Unconnected LSAPs:\n"); - else if (v == LINK_START_TOKEN) - seq_puts(seq, "\nRegistered Link Layers:\n"); - else if (iter->hashbin == irlmp->unconnected_lsaps) { - self = v; - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -EINVAL; ); - seq_printf(seq, "lsap state: %s, ", - irlsap_state[ self->lsap_state]); - seq_printf(seq, - "slsap_sel: %#02x, dlsap_sel: %#02x, ", - self->slsap_sel, self->dlsap_sel); - seq_printf(seq, "(%s)", self->notify.name); - seq_printf(seq, "\n"); - } else if (iter->hashbin == irlmp->links) { - struct lap_cb *lap = v; - - seq_printf(seq, "lap state: %s, ", - irlmp_state[lap->lap_state]); - - seq_printf(seq, "saddr: %#08x, daddr: %#08x, ", - lap->saddr, lap->daddr); - seq_printf(seq, "num lsaps: %d", - HASHBIN_GET_SIZE(lap->lsaps)); - seq_printf(seq, "\n"); - - /* Careful for priority inversions here ! - * All other uses of attrib spinlock are independent of - * the object spinlock, so we are safe. Jean II */ - spin_lock(&lap->lsaps->hb_spinlock); - - seq_printf(seq, "\n Connected LSAPs:\n"); - for (self = (struct lsap_cb *) hashbin_get_first(lap->lsaps); - self != NULL; - self = (struct lsap_cb *)hashbin_get_next(lap->lsaps)) { - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, - goto outloop;); - seq_printf(seq, " lsap state: %s, ", - irlsap_state[ self->lsap_state]); - seq_printf(seq, - "slsap_sel: %#02x, dlsap_sel: %#02x, ", - self->slsap_sel, self->dlsap_sel); - seq_printf(seq, "(%s)", self->notify.name); - seq_putc(seq, '\n'); - - } - IRDA_ASSERT_LABEL(outloop:) - spin_unlock(&lap->lsaps->hb_spinlock); - seq_putc(seq, '\n'); - } else - return -EINVAL; - - return 0; -} - -static const struct seq_operations irlmp_seq_ops = { - .start = irlmp_seq_start, - .next = irlmp_seq_next, - .stop = irlmp_seq_stop, - .show = irlmp_seq_show, -}; - -static int irlmp_seq_open(struct inode *inode, struct file *file) -{ - IRDA_ASSERT(irlmp != NULL, return -EINVAL;); - - return seq_open_private(file, &irlmp_seq_ops, - sizeof(struct irlmp_iter_state)); -} - -const struct file_operations irlmp_seq_fops = { - .owner = THIS_MODULE, - .open = irlmp_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -#endif /* PROC_FS */ diff --git a/net/irda/irlmp_event.c b/net/irda/irlmp_event.c deleted file mode 100644 index e306cf2c1e04..000000000000 --- a/net/irda/irlmp_event.c +++ /dev/null @@ -1,886 +0,0 @@ -/********************************************************************* - * - * Filename: irlmp_event.c - * Version: 0.8 - * Description: An IrDA LMP event driver for Linux - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Tue Dec 14 23:04:16 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include - -const char *const irlmp_state[] = { - "LAP_STANDBY", - "LAP_U_CONNECT", - "LAP_ACTIVE", -}; - -const char *const irlsap_state[] = { - "LSAP_DISCONNECTED", - "LSAP_CONNECT", - "LSAP_CONNECT_PEND", - "LSAP_DATA_TRANSFER_READY", - "LSAP_SETUP", - "LSAP_SETUP_PEND", -}; - -static const char *const irlmp_event[] __maybe_unused = { - "LM_CONNECT_REQUEST", - "LM_CONNECT_CONFIRM", - "LM_CONNECT_RESPONSE", - "LM_CONNECT_INDICATION", - - "LM_DISCONNECT_INDICATION", - "LM_DISCONNECT_REQUEST", - - "LM_DATA_REQUEST", - "LM_UDATA_REQUEST", - "LM_DATA_INDICATION", - "LM_UDATA_INDICATION", - - "LM_WATCHDOG_TIMEOUT", - - /* IrLAP events */ - "LM_LAP_CONNECT_REQUEST", - "LM_LAP_CONNECT_INDICATION", - "LM_LAP_CONNECT_CONFIRM", - "LM_LAP_DISCONNECT_INDICATION", - "LM_LAP_DISCONNECT_REQUEST", - "LM_LAP_DISCOVERY_REQUEST", - "LM_LAP_DISCOVERY_CONFIRM", - "LM_LAP_IDLE_TIMEOUT", -}; - -/* LAP Connection control proto declarations */ -static void irlmp_state_standby (struct lap_cb *, IRLMP_EVENT, - struct sk_buff *); -static void irlmp_state_u_connect(struct lap_cb *, IRLMP_EVENT, - struct sk_buff *); -static void irlmp_state_active (struct lap_cb *, IRLMP_EVENT, - struct sk_buff *); - -/* LSAP Connection control proto declarations */ -static int irlmp_state_disconnected(struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_connect (struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_connect_pend(struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_dtr (struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_setup (struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); -static int irlmp_state_setup_pend (struct lsap_cb *, IRLMP_EVENT, - struct sk_buff *); - -static void (*lap_state[]) (struct lap_cb *, IRLMP_EVENT, struct sk_buff *) = -{ - irlmp_state_standby, - irlmp_state_u_connect, - irlmp_state_active, -}; - -static int (*lsap_state[])( struct lsap_cb *, IRLMP_EVENT, struct sk_buff *) = -{ - irlmp_state_disconnected, - irlmp_state_connect, - irlmp_state_connect_pend, - irlmp_state_dtr, - irlmp_state_setup, - irlmp_state_setup_pend -}; - -static inline void irlmp_next_lap_state(struct lap_cb *self, - IRLMP_STATE state) -{ - /* - pr_debug("%s(), LMP LAP = %s\n", __func__, irlmp_state[state]); - */ - self->lap_state = state; -} - -static inline void irlmp_next_lsap_state(struct lsap_cb *self, - LSAP_STATE state) -{ - /* - IRDA_ASSERT(self != NULL, return;); - pr_debug("%s(), LMP LSAP = %s\n", __func__, irlsap_state[state]); - */ - self->lsap_state = state; -} - -/* Do connection control events */ -int irlmp_do_lsap_event(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - pr_debug("%s(), EVENT = %s, STATE = %s\n", - __func__, irlmp_event[event], irlsap_state[self->lsap_state]); - - return (*lsap_state[self->lsap_state]) (self, event, skb); -} - -/* - * Function do_lap_event (event, skb, info) - * - * Do IrLAP control events - * - */ -void irlmp_do_lap_event(struct lap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - - pr_debug("%s(), EVENT = %s, STATE = %s\n", __func__, - irlmp_event[event], - irlmp_state[self->lap_state]); - - (*lap_state[self->lap_state]) (self, event, skb); -} - -void irlmp_discovery_timer_expired(void *data) -{ - /* We always cleanup the log (active & passive discovery) */ - irlmp_do_expiry(); - - irlmp_do_discovery(sysctl_discovery_slots); - - /* Restart timer */ - irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout * HZ); -} - -void irlmp_watchdog_timer_expired(void *data) -{ - struct lsap_cb *self = (struct lsap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return;); - - irlmp_do_lsap_event(self, LM_WATCHDOG_TIMEOUT, NULL); -} - -void irlmp_idle_timer_expired(void *data) -{ - struct lap_cb *self = (struct lap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - - irlmp_do_lap_event(self, LM_LAP_IDLE_TIMEOUT, NULL); -} - -/* - * Send an event on all LSAPs attached to this LAP. - */ -static inline void -irlmp_do_all_lsap_event(hashbin_t * lsap_hashbin, - IRLMP_EVENT event) -{ - struct lsap_cb *lsap; - struct lsap_cb *lsap_next; - - /* Note : this function use the new hashbin_find_next() - * function, instead of the old hashbin_get_next(). - * This make sure that we are always pointing one lsap - * ahead, so that if the current lsap is removed as the - * result of sending the event, we don't care. - * Also, as we store the context ourselves, if an enumeration - * of the same lsap hashbin happens as the result of sending the - * event, we don't care. - * The only problem is if the next lsap is removed. In that case, - * hashbin_find_next() will return NULL and we will abort the - * enumeration. - Jean II */ - - /* Also : we don't accept any skb in input. We can *NOT* pass - * the same skb to multiple clients safely, we would need to - * skb_clone() it. - Jean II */ - - lsap = (struct lsap_cb *) hashbin_get_first(lsap_hashbin); - - while (NULL != hashbin_find_next(lsap_hashbin, - (long) lsap, - NULL, - (void *) &lsap_next) ) { - irlmp_do_lsap_event(lsap, event, NULL); - lsap = lsap_next; - } -} - -/********************************************************************* - * - * LAP connection control states - * - ********************************************************************/ - -/* - * Function irlmp_state_standby (event, skb, info) - * - * STANDBY, The IrLAP connection does not exist. - * - */ -static void irlmp_state_standby(struct lap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - IRDA_ASSERT(self->irlap != NULL, return;); - - switch (event) { - case LM_LAP_DISCOVERY_REQUEST: - /* irlmp_next_station_state( LMP_DISCOVER); */ - - irlap_discovery_request(self->irlap, &irlmp->discovery_cmd); - break; - case LM_LAP_CONNECT_INDICATION: - /* It's important to switch state first, to avoid IrLMP to - * think that the link is free since IrLMP may then start - * discovery before the connection is properly set up. DB. - */ - irlmp_next_lap_state(self, LAP_ACTIVE); - - /* Just accept connection TODO, this should be fixed */ - irlap_connect_response(self->irlap, skb); - break; - case LM_LAP_CONNECT_REQUEST: - pr_debug("%s() LS_CONNECT_REQUEST\n", __func__); - - irlmp_next_lap_state(self, LAP_U_CONNECT); - - /* FIXME: need to set users requested QoS */ - irlap_connect_request(self->irlap, self->daddr, NULL, 0); - break; - case LM_LAP_DISCONNECT_INDICATION: - pr_debug("%s(), Error LM_LAP_DISCONNECT_INDICATION\n", - __func__); - - irlmp_next_lap_state(self, LAP_STANDBY); - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlmp_event[event]); - break; - } -} - -/* - * Function irlmp_state_u_connect (event, skb, info) - * - * U_CONNECT, The layer above has tried to open an LSAP connection but - * since the IrLAP connection does not exist, we must first start an - * IrLAP connection. We are now waiting response from IrLAP. - * */ -static void irlmp_state_u_connect(struct lap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - pr_debug("%s(), event=%s\n", __func__, irlmp_event[event]); - - switch (event) { - case LM_LAP_CONNECT_INDICATION: - /* It's important to switch state first, to avoid IrLMP to - * think that the link is free since IrLMP may then start - * discovery before the connection is properly set up. DB. - */ - irlmp_next_lap_state(self, LAP_ACTIVE); - - /* Just accept connection TODO, this should be fixed */ - irlap_connect_response(self->irlap, skb); - - /* Tell LSAPs that they can start sending data */ - irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); - - /* Note : by the time we get there (LAP retries and co), - * the lsaps may already have gone. This avoid getting stuck - * forever in LAP_ACTIVE state - Jean II */ - if (HASHBIN_GET_SIZE(self->lsaps) == 0) { - pr_debug("%s() NO LSAPs !\n", __func__); - irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); - } - break; - case LM_LAP_CONNECT_REQUEST: - /* Already trying to connect */ - break; - case LM_LAP_CONNECT_CONFIRM: - /* For all lsap_ce E Associated do LS_Connect_confirm */ - irlmp_next_lap_state(self, LAP_ACTIVE); - - /* Tell LSAPs that they can start sending data */ - irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); - - /* Note : by the time we get there (LAP retries and co), - * the lsaps may already have gone. This avoid getting stuck - * forever in LAP_ACTIVE state - Jean II */ - if (HASHBIN_GET_SIZE(self->lsaps) == 0) { - pr_debug("%s() NO LSAPs !\n", __func__); - irlmp_start_idle_timer(self, LM_IDLE_TIMEOUT); - } - break; - case LM_LAP_DISCONNECT_INDICATION: - pr_debug("%s(), LM_LAP_DISCONNECT_INDICATION\n", __func__); - irlmp_next_lap_state(self, LAP_STANDBY); - - /* Send disconnect event to all LSAPs using this link */ - irlmp_do_all_lsap_event(self->lsaps, - LM_LAP_DISCONNECT_INDICATION); - break; - case LM_LAP_DISCONNECT_REQUEST: - pr_debug("%s(), LM_LAP_DISCONNECT_REQUEST\n", __func__); - - /* One of the LSAP did timeout or was closed, if it was - * the last one, try to get out of here - Jean II */ - if (HASHBIN_GET_SIZE(self->lsaps) <= 1) { - irlap_disconnect_request(self->irlap); - } - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlmp_event[event]); - break; - } -} - -/* - * Function irlmp_state_active (event, skb, info) - * - * ACTIVE, IrLAP connection is active - * - */ -static void irlmp_state_active(struct lap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - switch (event) { - case LM_LAP_CONNECT_REQUEST: - pr_debug("%s(), LS_CONNECT_REQUEST\n", __func__); - - /* - * IrLAP may have a pending disconnect. We tried to close - * IrLAP, but it was postponed because the link was - * busy or we were still sending packets. As we now - * need it, make sure it stays on. Jean II - */ - irlap_clear_disconnect(self->irlap); - - /* - * LAP connection already active, just bounce back! Since we - * don't know which LSAP that tried to do this, we have to - * notify all LSAPs using this LAP, but that should be safe to - * do anyway. - */ - irlmp_do_all_lsap_event(self->lsaps, LM_LAP_CONNECT_CONFIRM); - - /* Needed by connect indication */ - irlmp_do_all_lsap_event(irlmp->unconnected_lsaps, - LM_LAP_CONNECT_CONFIRM); - /* Keep state */ - break; - case LM_LAP_DISCONNECT_REQUEST: - /* - * Need to find out if we should close IrLAP or not. If there - * is only one LSAP connection left on this link, that LSAP - * must be the one that tries to close IrLAP. It will be - * removed later and moved to the list of unconnected LSAPs - */ - if (HASHBIN_GET_SIZE(self->lsaps) > 0) { - /* Timer value is checked in irsysctl - Jean II */ - irlmp_start_idle_timer(self, sysctl_lap_keepalive_time * HZ / 1000); - } else { - /* No more connections, so close IrLAP */ - - /* We don't want to change state just yet, because - * we want to reflect accurately the real state of - * the LAP, not the state we wish it was in, - * so that we don't lose LM_LAP_CONNECT_REQUEST. - * In some cases, IrLAP won't close the LAP - * immediately. For example, it might still be - * retrying packets or waiting for the pf bit. - * As the LAP always send a DISCONNECT_INDICATION - * in PCLOSE or SCLOSE, just change state on that. - * Jean II */ - irlap_disconnect_request(self->irlap); - } - break; - case LM_LAP_IDLE_TIMEOUT: - if (HASHBIN_GET_SIZE(self->lsaps) == 0) { - /* Same reasoning as above - keep state */ - irlap_disconnect_request(self->irlap); - } - break; - case LM_LAP_DISCONNECT_INDICATION: - irlmp_next_lap_state(self, LAP_STANDBY); - - /* In some case, at this point our side has already closed - * all lsaps, and we are waiting for the idle_timer to - * expire. If another device reconnect immediately, the - * idle timer will expire in the midle of the connection - * initialisation, screwing up things a lot... - * Therefore, we must stop the timer... */ - irlmp_stop_idle_timer(self); - - /* - * Inform all connected LSAP's using this link - */ - irlmp_do_all_lsap_event(self->lsaps, - LM_LAP_DISCONNECT_INDICATION); - - /* Force an expiry of the discovery log. - * Now that the LAP is free, the system may attempt to - * connect to another device. Unfortunately, our entries - * are stale. There is a small window (<3s) before the - * normal discovery will run and where irlmp_connect_request() - * can get the wrong info, so make sure things get - * cleaned *NOW* ;-) - Jean II */ - irlmp_do_expiry(); - break; - default: - pr_debug("%s(), Unknown event %s\n", - __func__, irlmp_event[event]); - break; - } -} - -/********************************************************************* - * - * LSAP connection control states - * - ********************************************************************/ - -/* - * Function irlmp_state_disconnected (event, skb, info) - * - * DISCONNECTED - * - */ -static int irlmp_state_disconnected(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - switch (event) { -#ifdef CONFIG_IRDA_ULTRA - case LM_UDATA_INDICATION: - /* This is most bizarre. Those packets are aka unreliable - * connected, aka IrLPT or SOCK_DGRAM/IRDAPROTO_UNITDATA. - * Why do we pass them as Ultra ??? Jean II */ - irlmp_connless_data_indication(self, skb); - break; -#endif /* CONFIG_IRDA_ULTRA */ - case LM_CONNECT_REQUEST: - pr_debug("%s(), LM_CONNECT_REQUEST\n", __func__); - - if (self->conn_skb) { - net_warn_ratelimited("%s: busy with another request!\n", - __func__); - return -EBUSY; - } - /* Don't forget to refcount it (see irlmp_connect_request()) */ - skb_get(skb); - self->conn_skb = skb; - - irlmp_next_lsap_state(self, LSAP_SETUP_PEND); - - /* Start watchdog timer (5 secs for now) */ - irlmp_start_watchdog_timer(self, 5*HZ); - - irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL); - break; - case LM_CONNECT_INDICATION: - if (self->conn_skb) { - net_warn_ratelimited("%s: busy with another request!\n", - __func__); - return -EBUSY; - } - /* Don't forget to refcount it (see irlap_driver_rcv()) */ - skb_get(skb); - self->conn_skb = skb; - - irlmp_next_lsap_state(self, LSAP_CONNECT_PEND); - - /* Start watchdog timer - * This is not mentionned in the spec, but there is a rare - * race condition that can get the socket stuck. - * If we receive this event while our LAP is closing down, - * the LM_LAP_CONNECT_REQUEST get lost and we get stuck in - * CONNECT_PEND state forever. - * The other cause of getting stuck down there is if the - * higher layer never reply to the CONNECT_INDICATION. - * Anyway, it make sense to make sure that we always have - * a backup plan. 1 second is plenty (should be immediate). - * Jean II */ - irlmp_start_watchdog_timer(self, 1*HZ); - - irlmp_do_lap_event(self->lap, LM_LAP_CONNECT_REQUEST, NULL); - break; - default: - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_connect (self, event, skb) - * - * CONNECT - * - */ -static int irlmp_state_connect(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - struct lsap_cb *lsap; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - switch (event) { - case LM_CONNECT_RESPONSE: - /* - * Bind this LSAP to the IrLAP link where the connect was - * received - */ - lsap = hashbin_remove(irlmp->unconnected_lsaps, (long) self, - NULL); - - IRDA_ASSERT(lsap == self, return -1;); - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->lsaps != NULL, return -1;); - - hashbin_insert(self->lap->lsaps, (irda_queue_t *) self, - (long) self, NULL); - - set_bit(0, &self->connected); /* TRUE */ - - irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, - self->slsap_sel, CONNECT_CNF, skb); - - del_timer(&self->watchdog_timer); - - irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY); - break; - case LM_WATCHDOG_TIMEOUT: - /* May happen, who knows... - * Jean II */ - pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); - - /* Disconnect, get out... - Jean II */ - self->lap = NULL; - self->dlsap_sel = LSAP_ANY; - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - break; - default: - /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we - * are *not* yet bound to the IrLAP link. Jean II */ - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_connect_pend (event, skb, info) - * - * CONNECT_PEND - * - */ -static int irlmp_state_connect_pend(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - switch (event) { - case LM_CONNECT_REQUEST: - /* Keep state */ - break; - case LM_CONNECT_RESPONSE: - pr_debug("%s(), LM_CONNECT_RESPONSE, no indication issued yet\n", - __func__); - /* Keep state */ - break; - case LM_DISCONNECT_REQUEST: - pr_debug("%s(), LM_DISCONNECT_REQUEST, not yet bound to IrLAP connection\n", - __func__); - /* Keep state */ - break; - case LM_LAP_CONNECT_CONFIRM: - pr_debug("%s(), LS_CONNECT_CONFIRM\n", __func__); - irlmp_next_lsap_state(self, LSAP_CONNECT); - - tx_skb = self->conn_skb; - self->conn_skb = NULL; - - irlmp_connect_indication(self, tx_skb); - /* Drop reference count - see irlmp_connect_indication(). */ - dev_kfree_skb(tx_skb); - break; - case LM_WATCHDOG_TIMEOUT: - /* Will happen in some rare cases because of a race condition. - * Just make sure we don't stay there forever... - * Jean II */ - pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); - - /* Go back to disconnected mode, keep the socket waiting */ - self->lap = NULL; - self->dlsap_sel = LSAP_ANY; - if(self->conn_skb) - dev_kfree_skb(self->conn_skb); - self->conn_skb = NULL; - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - break; - default: - /* LM_LAP_DISCONNECT_INDICATION : Should never happen, we - * are *not* yet bound to the IrLAP link. Jean II */ - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_dtr (self, event, skb) - * - * DATA_TRANSFER_READY - * - */ -static int irlmp_state_dtr(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - LM_REASON reason; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - IRDA_ASSERT(self->lap != NULL, return -1;); - - switch (event) { - case LM_DATA_REQUEST: /* Optimize for the common case */ - irlmp_send_data_pdu(self->lap, self->dlsap_sel, - self->slsap_sel, FALSE, skb); - break; - case LM_DATA_INDICATION: /* Optimize for the common case */ - irlmp_data_indication(self, skb); - break; - case LM_UDATA_REQUEST: - IRDA_ASSERT(skb != NULL, return -1;); - irlmp_send_data_pdu(self->lap, self->dlsap_sel, - self->slsap_sel, TRUE, skb); - break; - case LM_UDATA_INDICATION: - irlmp_udata_indication(self, skb); - break; - case LM_CONNECT_REQUEST: - pr_debug("%s(), LM_CONNECT_REQUEST, error, LSAP already connected\n", - __func__); - /* Keep state */ - break; - case LM_CONNECT_RESPONSE: - pr_debug("%s(), LM_CONNECT_RESPONSE, error, LSAP already connected\n", - __func__); - /* Keep state */ - break; - case LM_DISCONNECT_REQUEST: - irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, self->slsap_sel, - DISCONNECT, skb); - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - /* Called only from irlmp_disconnect_request(), will - * unbind from LAP over there. Jean II */ - - /* Try to close the LAP connection if its still there */ - if (self->lap) { - pr_debug("%s(), trying to close IrLAP\n", - __func__); - irlmp_do_lap_event(self->lap, - LM_LAP_DISCONNECT_REQUEST, - NULL); - } - break; - case LM_LAP_DISCONNECT_INDICATION: - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - reason = irlmp_convert_lap_reason(self->lap->reason); - - irlmp_disconnect_indication(self, reason, NULL); - break; - case LM_DISCONNECT_INDICATION: - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); - - IRDA_ASSERT(skb != NULL, return -1;); - IRDA_ASSERT(skb->len > 3, return -1;); - reason = skb->data[3]; - - /* Try to close the LAP connection */ - pr_debug("%s(), trying to close IrLAP\n", __func__); - irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); - - irlmp_disconnect_indication(self, reason, skb); - break; - default: - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_setup (event, skb, info) - * - * SETUP, Station Control has set up the underlying IrLAP connection. - * An LSAP connection request has been transmitted to the peer - * LSAP-Connection Control FSM and we are awaiting reply. - */ -static int irlmp_state_setup(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - LM_REASON reason; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LMP_LSAP_MAGIC, return -1;); - - switch (event) { - case LM_CONNECT_CONFIRM: - irlmp_next_lsap_state(self, LSAP_DATA_TRANSFER_READY); - - del_timer(&self->watchdog_timer); - - irlmp_connect_confirm(self, skb); - break; - case LM_DISCONNECT_INDICATION: - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); - - IRDA_ASSERT(skb != NULL, return -1;); - IRDA_ASSERT(skb->len > 3, return -1;); - reason = skb->data[3]; - - /* Try to close the LAP connection */ - pr_debug("%s(), trying to close IrLAP\n", __func__); - irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); - - irlmp_disconnect_indication(self, reason, skb); - break; - case LM_LAP_DISCONNECT_INDICATION: - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - del_timer(&self->watchdog_timer); - - IRDA_ASSERT(self->lap != NULL, return -1;); - IRDA_ASSERT(self->lap->magic == LMP_LAP_MAGIC, return -1;); - - reason = irlmp_convert_lap_reason(self->lap->reason); - - irlmp_disconnect_indication(self, reason, skb); - break; - case LM_WATCHDOG_TIMEOUT: - pr_debug("%s() WATCHDOG_TIMEOUT!\n", __func__); - - IRDA_ASSERT(self->lap != NULL, return -1;); - irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL); - break; - default: - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} - -/* - * Function irlmp_state_setup_pend (event, skb, info) - * - * SETUP_PEND, An LM_CONNECT_REQUEST has been received from the service - * user to set up an LSAP connection. A request has been sent to the - * LAP FSM to set up the underlying IrLAP connection, and we - * are awaiting confirm. - */ -static int irlmp_state_setup_pend(struct lsap_cb *self, IRLMP_EVENT event, - struct sk_buff *skb) -{ - struct sk_buff *tx_skb; - LM_REASON reason; - int ret = 0; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(irlmp != NULL, return -1;); - - switch (event) { - case LM_LAP_CONNECT_CONFIRM: - IRDA_ASSERT(self->conn_skb != NULL, return -1;); - - tx_skb = self->conn_skb; - self->conn_skb = NULL; - - irlmp_send_lcf_pdu(self->lap, self->dlsap_sel, - self->slsap_sel, CONNECT_CMD, tx_skb); - /* Drop reference count - see irlap_data_request(). */ - dev_kfree_skb(tx_skb); - - irlmp_next_lsap_state(self, LSAP_SETUP); - break; - case LM_WATCHDOG_TIMEOUT: - pr_debug("%s() : WATCHDOG_TIMEOUT !\n", __func__); - - IRDA_ASSERT(self->lap != NULL, return -1;); - irlmp_do_lap_event(self->lap, LM_LAP_DISCONNECT_REQUEST, NULL); - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - irlmp_disconnect_indication(self, LM_CONNECT_FAILURE, NULL); - break; - case LM_LAP_DISCONNECT_INDICATION: /* LS_Disconnect.indication */ - del_timer( &self->watchdog_timer); - - irlmp_next_lsap_state(self, LSAP_DISCONNECTED); - - reason = irlmp_convert_lap_reason(self->lap->reason); - - irlmp_disconnect_indication(self, reason, NULL); - break; - default: - pr_debug("%s(), Unknown event %s on LSAP %#02x\n", - __func__, irlmp_event[event], self->slsap_sel); - break; - } - return ret; -} diff --git a/net/irda/irlmp_frame.c b/net/irda/irlmp_frame.c deleted file mode 100644 index 38b0f994bc7b..000000000000 --- a/net/irda/irlmp_frame.c +++ /dev/null @@ -1,476 +0,0 @@ -/********************************************************************* - * - * Filename: irlmp_frame.c - * Version: 0.9 - * Description: IrLMP frame implementation - * Status: Experimental. - * Author: Dag Brattli - * Created at: Tue Aug 19 02:09:59 1997 - * Modified at: Mon Dec 13 13:41:12 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999 Dag Brattli - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include - -#include -#include -#include -#include -#include -#include - -static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap, - __u8 slsap, int status, hashbin_t *); - -inline void irlmp_send_data_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, - int expedited, struct sk_buff *skb) -{ - skb->data[0] = dlsap; - skb->data[1] = slsap; - - if (expedited) { - pr_debug("%s(), sending expedited data\n", __func__); - irlap_data_request(self->irlap, skb, TRUE); - } else - irlap_data_request(self->irlap, skb, FALSE); -} - -/* - * Function irlmp_send_lcf_pdu (dlsap, slsap, opcode,skb) - * - * Send Link Control Frame to IrLAP - */ -void irlmp_send_lcf_pdu(struct lap_cb *self, __u8 dlsap, __u8 slsap, - __u8 opcode, struct sk_buff *skb) -{ - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - frame = skb->data; - - frame[0] = dlsap | CONTROL_BIT; - frame[1] = slsap; - - frame[2] = opcode; - - if (opcode == DISCONNECT) - frame[3] = 0x01; /* Service user request */ - else - frame[3] = 0x00; /* rsvd */ - - irlap_data_request(self->irlap, skb, FALSE); -} - -/* - * Function irlmp_input (skb) - * - * Used by IrLAP to pass received data frames to IrLMP layer - * - */ -void irlmp_link_data_indication(struct lap_cb *self, struct sk_buff *skb, - int unreliable) -{ - struct lsap_cb *lsap; - __u8 slsap_sel; /* Source (this) LSAP address */ - __u8 dlsap_sel; /* Destination LSAP address */ - __u8 *fp; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(skb->len > 2, return;); - - fp = skb->data; - - /* - * The next statements may be confusing, but we do this so that - * destination LSAP of received frame is source LSAP in our view - */ - slsap_sel = fp[0] & LSAP_MASK; - dlsap_sel = fp[1]; - - /* - * Check if this is an incoming connection, since we must deal with - * it in a different way than other established connections. - */ - if ((fp[0] & CONTROL_BIT) && (fp[2] == CONNECT_CMD)) { - pr_debug("%s(), incoming connection, source LSAP=%d, dest LSAP=%d\n", - __func__, slsap_sel, dlsap_sel); - - /* Try to find LSAP among the unconnected LSAPs */ - lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, CONNECT_CMD, - irlmp->unconnected_lsaps); - - /* Maybe LSAP was already connected, so try one more time */ - if (!lsap) { - pr_debug("%s(), incoming connection for LSAP already connected\n", - __func__); - lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, - self->lsaps); - } - } else - lsap = irlmp_find_lsap(self, dlsap_sel, slsap_sel, 0, - self->lsaps); - - if (lsap == NULL) { - pr_debug("IrLMP, Sorry, no LSAP for received frame!\n"); - pr_debug("%s(), slsap_sel = %02x, dlsap_sel = %02x\n", - __func__, slsap_sel, dlsap_sel); - if (fp[0] & CONTROL_BIT) { - pr_debug("%s(), received control frame %02x\n", - __func__, fp[2]); - } else { - pr_debug("%s(), received data frame\n", __func__); - } - return; - } - - /* - * Check if we received a control frame? - */ - if (fp[0] & CONTROL_BIT) { - switch (fp[2]) { - case CONNECT_CMD: - lsap->lap = self; - irlmp_do_lsap_event(lsap, LM_CONNECT_INDICATION, skb); - break; - case CONNECT_CNF: - irlmp_do_lsap_event(lsap, LM_CONNECT_CONFIRM, skb); - break; - case DISCONNECT: - pr_debug("%s(), Disconnect indication!\n", - __func__); - irlmp_do_lsap_event(lsap, LM_DISCONNECT_INDICATION, - skb); - break; - case ACCESSMODE_CMD: - pr_debug("Access mode cmd not implemented!\n"); - break; - case ACCESSMODE_CNF: - pr_debug("Access mode cnf not implemented!\n"); - break; - default: - pr_debug("%s(), Unknown control frame %02x\n", - __func__, fp[2]); - break; - } - } else if (unreliable) { - /* Optimize and bypass the state machine if possible */ - if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY) - irlmp_udata_indication(lsap, skb); - else - irlmp_do_lsap_event(lsap, LM_UDATA_INDICATION, skb); - } else { - /* Optimize and bypass the state machine if possible */ - if (lsap->lsap_state == LSAP_DATA_TRANSFER_READY) - irlmp_data_indication(lsap, skb); - else - irlmp_do_lsap_event(lsap, LM_DATA_INDICATION, skb); - } -} - -/* - * Function irlmp_link_unitdata_indication (self, skb) - * - * - * - */ -#ifdef CONFIG_IRDA_ULTRA -void irlmp_link_unitdata_indication(struct lap_cb *self, struct sk_buff *skb) -{ - struct lsap_cb *lsap; - __u8 slsap_sel; /* Source (this) LSAP address */ - __u8 dlsap_sel; /* Destination LSAP address */ - __u8 pid; /* Protocol identifier */ - __u8 *fp; - unsigned long flags; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(skb->len > 2, return;); - - fp = skb->data; - - /* - * The next statements may be confusing, but we do this so that - * destination LSAP of received frame is source LSAP in our view - */ - slsap_sel = fp[0] & LSAP_MASK; - dlsap_sel = fp[1]; - pid = fp[2]; - - if (pid & 0x80) { - pr_debug("%s(), extension in PID not supp!\n", - __func__); - return; - } - - /* Check if frame is addressed to the connectionless LSAP */ - if ((slsap_sel != LSAP_CONNLESS) || (dlsap_sel != LSAP_CONNLESS)) { - pr_debug("%s(), dropping frame!\n", __func__); - return; - } - - /* Search the connectionless LSAP */ - spin_lock_irqsave(&irlmp->unconnected_lsaps->hb_spinlock, flags); - lsap = (struct lsap_cb *) hashbin_get_first(irlmp->unconnected_lsaps); - while (lsap != NULL) { - /* - * Check if source LSAP and dest LSAP selectors and PID match. - */ - if ((lsap->slsap_sel == slsap_sel) && - (lsap->dlsap_sel == dlsap_sel) && - (lsap->pid == pid)) - { - break; - } - lsap = (struct lsap_cb *) hashbin_get_next(irlmp->unconnected_lsaps); - } - spin_unlock_irqrestore(&irlmp->unconnected_lsaps->hb_spinlock, flags); - - if (lsap) - irlmp_connless_data_indication(lsap, skb); - else { - pr_debug("%s(), found no matching LSAP!\n", __func__); - } -} -#endif /* CONFIG_IRDA_ULTRA */ - -/* - * Function irlmp_link_disconnect_indication (reason, userdata) - * - * IrLAP has disconnected - * - */ -void irlmp_link_disconnect_indication(struct lap_cb *lap, - struct irlap_cb *irlap, - LAP_REASON reason, - struct sk_buff *skb) -{ - IRDA_ASSERT(lap != NULL, return;); - IRDA_ASSERT(lap->magic == LMP_LAP_MAGIC, return;); - - lap->reason = reason; - lap->daddr = DEV_ADDR_ANY; - - /* FIXME: must do something with the skb if any */ - - /* - * Inform station state machine - */ - irlmp_do_lap_event(lap, LM_LAP_DISCONNECT_INDICATION, NULL); -} - -/* - * Function irlmp_link_connect_indication (qos) - * - * Incoming LAP connection! - * - */ -void irlmp_link_connect_indication(struct lap_cb *self, __u32 saddr, - __u32 daddr, struct qos_info *qos, - struct sk_buff *skb) -{ - /* Copy QoS settings for this session */ - self->qos = qos; - - /* Update destination device address */ - self->daddr = daddr; - IRDA_ASSERT(self->saddr == saddr, return;); - - irlmp_do_lap_event(self, LM_LAP_CONNECT_INDICATION, skb); -} - -/* - * Function irlmp_link_connect_confirm (qos) - * - * LAP connection confirmed! - * - */ -void irlmp_link_connect_confirm(struct lap_cb *self, struct qos_info *qos, - struct sk_buff *skb) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - IRDA_ASSERT(qos != NULL, return;); - - /* Don't need use the skb for now */ - - /* Copy QoS settings for this session */ - self->qos = qos; - - irlmp_do_lap_event(self, LM_LAP_CONNECT_CONFIRM, NULL); -} - -/* - * Function irlmp_link_discovery_indication (self, log) - * - * Device is discovering us - * - * It's not an answer to our own discoveries, just another device trying - * to perform discovery, but we don't want to miss the opportunity - * to exploit this information, because : - * o We may not actively perform discovery (just passive discovery) - * o This type of discovery is much more reliable. In some cases, it - * seem that less than 50% of our discoveries get an answer, while - * we always get ~100% of these. - * o Make faster discovery, statistically divide time of discovery - * events by 2 (important for the latency aspect and user feel) - * o Even is we do active discovery, the other node might not - * answer our discoveries (ex: Palm). The Palm will just perform - * one active discovery and connect directly to us. - * - * However, when both devices discover each other, they might attempt to - * connect to each other following the discovery event, and it would create - * collisions on the medium (SNRM battle). - * The "fix" for that is to disable all connection requests in IrLAP - * for 100ms after a discovery indication by setting the media_busy flag. - * Previously, we used to postpone the event which was quite ugly. Now - * that IrLAP takes care of this problem, just pass the event up... - * - * Jean II - */ -void irlmp_link_discovery_indication(struct lap_cb *self, - discovery_t *discovery) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - - /* Add to main log, cleanup */ - irlmp_add_discovery(irlmp->cachelog, discovery); - - /* Just handle it the same way as a discovery confirm, - * bypass the LM_LAP state machine (see below) */ - irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_PASSIVE); -} - -/* - * Function irlmp_link_discovery_confirm (self, log) - * - * Called by IrLAP with a list of discoveries after the discovery - * request has been carried out. A NULL log is received if IrLAP - * was unable to carry out the discovery request - * - */ -void irlmp_link_discovery_confirm(struct lap_cb *self, hashbin_t *log) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LMP_LAP_MAGIC, return;); - - /* Add to main log, cleanup */ - irlmp_add_discovery_log(irlmp->cachelog, log); - - /* Propagate event to various LSAPs registered for it. - * We bypass the LM_LAP state machine because - * 1) We do it regardless of the LM_LAP state - * 2) It doesn't affect the LM_LAP state - * 3) Faster, slimer, simpler, ... - * Jean II */ - irlmp_discovery_confirm(irlmp->cachelog, DISCOVERY_ACTIVE); -} - -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP -static inline void irlmp_update_cache(struct lap_cb *lap, - struct lsap_cb *lsap) -{ - /* Prevent concurrent read to get garbage */ - lap->cache.valid = FALSE; - /* Update cache entry */ - lap->cache.dlsap_sel = lsap->dlsap_sel; - lap->cache.slsap_sel = lsap->slsap_sel; - lap->cache.lsap = lsap; - lap->cache.valid = TRUE; -} -#endif - -/* - * Function irlmp_find_handle (self, dlsap_sel, slsap_sel, status, queue) - * - * Find handle associated with destination and source LSAP - * - * Any IrDA connection (LSAP/TSAP) is uniquely identified by - * 3 parameters, the local lsap, the remote lsap and the remote address. - * We may initiate multiple connections to the same remote service - * (they will have different local lsap), a remote device may initiate - * multiple connections to the same local service (they will have - * different remote lsap), or multiple devices may connect to the same - * service and may use the same remote lsap (and they will have - * different remote address). - * So, where is the remote address ? Each LAP connection is made with - * a single remote device, so imply a specific remote address. - * Jean II - */ -static struct lsap_cb *irlmp_find_lsap(struct lap_cb *self, __u8 dlsap_sel, - __u8 slsap_sel, int status, - hashbin_t *queue) -{ - struct lsap_cb *lsap; - unsigned long flags; - - /* - * Optimize for the common case. We assume that the last frame - * received is in the same connection as the last one, so check in - * cache first to avoid the linear search - */ -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - if ((self->cache.valid) && - (self->cache.slsap_sel == slsap_sel) && - (self->cache.dlsap_sel == dlsap_sel)) - { - return self->cache.lsap; - } -#endif - - spin_lock_irqsave(&queue->hb_spinlock, flags); - - lsap = (struct lsap_cb *) hashbin_get_first(queue); - while (lsap != NULL) { - /* - * If this is an incoming connection, then the destination - * LSAP selector may have been specified as LM_ANY so that - * any client can connect. In that case we only need to check - * if the source LSAP (in our view!) match! - */ - if ((status == CONNECT_CMD) && - (lsap->slsap_sel == slsap_sel) && - (lsap->dlsap_sel == LSAP_ANY)) { - /* This is where the dest lsap sel is set on incoming - * lsaps */ - lsap->dlsap_sel = dlsap_sel; - break; - } - /* - * Check if source LSAP and dest LSAP selectors match. - */ - if ((lsap->slsap_sel == slsap_sel) && - (lsap->dlsap_sel == dlsap_sel)) - break; - - lsap = (struct lsap_cb *) hashbin_get_next(queue); - } -#ifdef CONFIG_IRDA_CACHE_LAST_LSAP - if(lsap) - irlmp_update_cache(self, lsap); -#endif - spin_unlock_irqrestore(&queue->hb_spinlock, flags); - - /* Return what we've found or NULL */ - return lsap; -} diff --git a/net/irda/irmod.c b/net/irda/irmod.c deleted file mode 100644 index c5e35b85c477..000000000000 --- a/net/irda/irmod.c +++ /dev/null @@ -1,199 +0,0 @@ -/********************************************************************* - * - * Filename: irmod.c - * Version: 0.9 - * Description: IrDA stack main entry points - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Dec 15 13:55:39 1997 - * Modified at: Wed Jan 5 15:12:41 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999-2000 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2004 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -/* - * This file contains the main entry points of the IrDA stack. - * They are in this file and not af_irda.c because some developpers - * are using the IrDA stack without the socket API (compiling out - * af_irda.c). - * Jean II - */ - -#include -#include - -#include -#include /* notify_t */ -#include /* irlap_init */ -#include /* irlmp_init */ -#include /* iriap_init */ -#include /* irttp_init */ -#include /* irda_device_init */ - -/* Packet type handler. - * Tell the kernel how IrDA packets should be handled. - */ -static struct packet_type irda_packet_type __read_mostly = { - .type = cpu_to_be16(ETH_P_IRDA), - .func = irlap_driver_rcv, /* Packet type handler irlap_frame.c */ -}; - -/* - * Function irda_notify_init (notify) - * - * Used for initializing the notify structure - * - */ -void irda_notify_init(notify_t *notify) -{ - notify->data_indication = NULL; - notify->udata_indication = NULL; - notify->connect_confirm = NULL; - notify->connect_indication = NULL; - notify->disconnect_indication = NULL; - notify->flow_indication = NULL; - notify->status_indication = NULL; - notify->instance = NULL; - strlcpy(notify->name, "Unknown", sizeof(notify->name)); -} -EXPORT_SYMBOL(irda_notify_init); - -/* - * Function irda_init (void) - * - * Protocol stack initialisation entry point. - * Initialise the various components of the IrDA stack - */ -static int __init irda_init(void) -{ - int ret = 0; - - /* Lower layer of the stack */ - irlmp_init(); - irlap_init(); - - /* Driver/dongle support */ - irda_device_init(); - - /* Higher layers of the stack */ - iriap_init(); - irttp_init(); - ret = irsock_init(); - if (ret < 0) - goto out_err_1; - - /* Add IrDA packet type (Start receiving packets) */ - dev_add_pack(&irda_packet_type); - - /* External APIs */ -#ifdef CONFIG_PROC_FS - irda_proc_register(); -#endif -#ifdef CONFIG_SYSCTL - ret = irda_sysctl_register(); - if (ret < 0) - goto out_err_2; -#endif - - ret = irda_nl_register(); - if (ret < 0) - goto out_err_3; - - return 0; - - out_err_3: -#ifdef CONFIG_SYSCTL - irda_sysctl_unregister(); - out_err_2: -#endif -#ifdef CONFIG_PROC_FS - irda_proc_unregister(); -#endif - - /* Remove IrDA packet type (stop receiving packets) */ - dev_remove_pack(&irda_packet_type); - - /* Remove higher layers */ - irsock_cleanup(); - out_err_1: - irttp_cleanup(); - iriap_cleanup(); - - /* Remove lower layers */ - irda_device_cleanup(); - irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */ - - /* Remove middle layer */ - irlmp_cleanup(); - - - return ret; -} - -/* - * Function irda_cleanup (void) - * - * Protocol stack cleanup/removal entry point. - * Cleanup the various components of the IrDA stack - */ -static void __exit irda_cleanup(void) -{ - /* Remove External APIs */ - irda_nl_unregister(); - -#ifdef CONFIG_SYSCTL - irda_sysctl_unregister(); -#endif -#ifdef CONFIG_PROC_FS - irda_proc_unregister(); -#endif - - /* Remove IrDA packet type (stop receiving packets) */ - dev_remove_pack(&irda_packet_type); - - /* Remove higher layers */ - irsock_cleanup(); - irttp_cleanup(); - iriap_cleanup(); - - /* Remove lower layers */ - irda_device_cleanup(); - irlap_cleanup(); /* Must be done before irlmp_cleanup()! DB */ - - /* Remove middle layer */ - irlmp_cleanup(); -} - -/* - * The IrDA stack must be initialised *before* drivers get initialised, - * and *before* higher protocols (IrLAN/IrCOMM/IrNET) get initialised, - * otherwise bad things will happen (hashbins will be NULL for example). - * Those modules are at module_init()/device_initcall() level. - * - * On the other hand, it needs to be initialised *after* the basic - * networking, the /proc/net filesystem and sysctl module. Those are - * currently initialised in .../init/main.c (before initcalls). - * Also, IrDA drivers needs to be initialised *after* the random number - * generator (main stack and higher layer init don't need it anymore). - * - * Jean II - */ -subsys_initcall(irda_init); -module_exit(irda_cleanup); - -MODULE_AUTHOR("Dag Brattli & Jean Tourrilhes "); -MODULE_DESCRIPTION("The Linux IrDA Protocol Stack"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_NETPROTO(PF_IRDA); diff --git a/net/irda/irnet/Kconfig b/net/irda/irnet/Kconfig deleted file mode 100644 index 28c557f0fdd2..000000000000 --- a/net/irda/irnet/Kconfig +++ /dev/null @@ -1,13 +0,0 @@ -config IRNET - tristate "IrNET protocol" - depends on IRDA && PPP - help - Say Y here if you want to build support for the IrNET protocol. - To compile it as a module, choose M here: the module will be - called irnet. IrNET is a PPP driver, so you will also need a - working PPP subsystem (driver, daemon and config)... - - IrNET is an alternate way to transfer TCP/IP traffic over IrDA. It - uses synchronous PPP over a set of point to point IrDA sockets. You - can use it between Linux machine or with W2k. - diff --git a/net/irda/irnet/Makefile b/net/irda/irnet/Makefile deleted file mode 100644 index 61c365c8a2a0..000000000000 --- a/net/irda/irnet/Makefile +++ /dev/null @@ -1,7 +0,0 @@ -# -# Makefile for the Linux IrDA IrNET protocol layer. -# - -obj-$(CONFIG_IRNET) += irnet.o - -irnet-y := irnet_ppp.o irnet_irda.o diff --git a/net/irda/irnet/irnet.h b/net/irda/irnet/irnet.h deleted file mode 100644 index 9d451f8ed47a..000000000000 --- a/net/irda/irnet/irnet.h +++ /dev/null @@ -1,522 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file contains definitions and declarations global to the IrNET module, - * all grouped in one place... - * This file is a *private* header, so other modules don't want to know - * what's in there... - * - * Note : as most part of the Linux kernel, this module is available - * under the GNU General Public License (GPL). - */ - -#ifndef IRNET_H -#define IRNET_H - -/************************** DOCUMENTATION ***************************/ -/* - * What is IrNET - * ------------- - * IrNET is a protocol allowing to carry TCP/IP traffic between two - * IrDA peers in an efficient fashion. It is a thin layer, passing PPP - * packets to IrTTP and vice versa. It uses PPP in synchronous mode, - * because IrTTP offer a reliable sequenced packet service (as opposed - * to a byte stream). In fact, you could see IrNET as carrying TCP/IP - * in a IrDA socket, using PPP to provide the glue. - * - * The main difference with traditional PPP over IrCOMM is that we - * avoid the framing and serial emulation which are a performance - * bottleneck. It also allows multipoint communications in a sensible - * fashion. - * - * The main difference with IrLAN is that we use PPP for the link - * management, which is more standard, interoperable and flexible than - * the IrLAN protocol. For example, PPP adds authentication, - * encryption, compression, header compression and automated routing - * setup. And, as IrNET let PPP do the hard work, the implementation - * is much simpler than IrLAN. - * - * The Linux implementation - * ------------------------ - * IrNET is written on top of the Linux-IrDA stack, and interface with - * the generic Linux PPP driver. Because IrNET depend on recent - * changes of the PPP driver interface, IrNET will work only with very - * recent kernel (2.3.99-pre6 and up). - * - * The present implementation offer the following features : - * o simple user interface using pppd - * o efficient implementation (interface directly to PPP and IrTTP) - * o addressing (you can specify the name of the IrNET recipient) - * o multipoint operation (limited by IrLAP specification) - * o information in /proc/net/irda/irnet - * o IrNET events on /dev/irnet (for user space daemon) - * o IrNET daemon (irnetd) to automatically handle incoming requests - * o Windows 2000 compatibility (tested, but need more work) - * Currently missing : - * o Lot's of testing (that's your job) - * o Connection retries (may be too hard to do) - * o Check pppd persist mode - * o User space daemon (to automatically handle incoming requests) - * - * The setup is not currently the most easy, but this should get much - * better when everything will get integrated... - * - * Acknowledgements - * ---------------- - * This module is based on : - * o The PPP driver (ppp_synctty/ppp_generic) by Paul Mackerras - * o The IrLAN protocol (irlan_common/XXX) by Dag Brattli - * o The IrSock interface (af_irda) by Dag Brattli - * o Some other bits from the kernel and my drivers... - * Infinite thanks to those brave souls for providing the infrastructure - * upon which IrNET is built. - * - * Thanks to all my colleagues in HP for helping me. In particular, - * thanks to Salil Pradhan and Bill Serra for W2k testing... - * Thanks to Luiz Magalhaes for irnetd and much testing... - * - * Thanks to Alan Cox for answering lot's of my stupid questions, and - * to Paul Mackerras answering my questions on how to best integrate - * IrNET and pppd. - * - * Jean II - * - * Note on some implementations choices... - * ------------------------------------ - * 1) Direct interface vs tty/socket - * I could have used a tty interface to hook to ppp and use the full - * socket API to connect to IrDA. The code would have been easier to - * maintain, and maybe the code would have been smaller... - * Instead, we hook directly to ppp_generic and to IrTTP, which make - * things more complicated... - * - * The first reason is flexibility : this allow us to create IrNET - * instances on demand (no /dev/ircommX crap) and to allow linkname - * specification on pppd command line... - * - * Second reason is speed optimisation. If you look closely at the - * transmit and receive paths, you will notice that they are "super lean" - * (that's why they look ugly), with no function calls and as little data - * copy and modification as I could... - * - * 2) irnetd in user space - * irnetd is implemented in user space, which is necessary to call pppd. - * This also give maximum benefits in term of flexibility and customability, - * and allow to offer the event channel, useful for other stuff like debug. - * - * On the other hand, this require a loose coordination between the - * present module and irnetd. One critical area is how incoming request - * are handled. - * When irnet receive an incoming request, it send an event to irnetd and - * drop the incoming IrNET socket. - * irnetd start a pppd instance, which create a new IrNET socket. This new - * socket is then connected in the originating node to the pppd instance. - * At this point, in the originating node, the first socket is closed. - * - * I admit, this is a bit messy and waste some resources. The alternative - * is caching incoming socket, and that's also quite messy and waste - * resources. - * We also make connection time slower. For example, on a 115 kb/s link it - * adds 60ms to the connection time (770 ms). However, this is slower than - * the time it takes to fire up pppd on my P133... - * - * - * History : - * ------- - * - * v1 - 15.5.00 - Jean II - * o Basic IrNET (hook to ppp_generic & IrTTP - incl. multipoint) - * o control channel on /dev/irnet (set name/address) - * o event channel on /dev/irnet (for user space daemon) - * - * v2 - 5.6.00 - Jean II - * o Enable DROP_NOT_READY to avoid PPP timeouts & other weirdness... - * o Add DISCONNECT_TO event and rename DISCONNECT_FROM. - * o Set official device number alloaction on /dev/irnet - * - * v3 - 30.8.00 - Jean II - * o Update to latest Linux-IrDA changes : - * - queue_t => irda_queue_t - * o Update to ppp-2.4.0 : - * - move irda_irnet_connect from PPPIOCATTACH to TIOCSETD - * o Add EXPIRE event (depend on new IrDA-Linux patch) - * o Switch from `hashbin_remove' to `hashbin_remove_this' to fix - * a multilink bug... (depend on new IrDA-Linux patch) - * o fix a self->daddr to self->raddr in irda_irnet_connect to fix - * another multilink bug (darn !) - * o Remove LINKNAME_IOCTL cruft - * - * v3b - 31.8.00 - Jean II - * o Dump discovery log at event channel startup - * - * v4 - 28.9.00 - Jean II - * o Fix interaction between poll/select and dump discovery log - * o Add IRNET_BLOCKED_LINK event (depend on new IrDA-Linux patch) - * o Add IRNET_NOANSWER_FROM event (mostly to help support) - * o Release flow control in disconnect_indication - * o Block packets while connecting (speed up connections) - * - * v5 - 11.01.01 - Jean II - * o Init self->max_header_size, just in case... - * o Set up ap->chan.hdrlen, to get zero copy on tx side working. - * o avoid tx->ttp->flow->ppp->tx->... loop, by checking flow state - * Thanks to Christian Gennerat for finding this bug ! - * --- - * o Declare the proper MTU/MRU that we can support - * (but PPP doesn't read the MTU value :-() - * o Declare hashbin HB_NOLOCK instead of HB_LOCAL to avoid - * disabling and enabling irq twice - * - * v6 - 31.05.01 - Jean II - * o Print source address in Found, Discovery, Expiry & Request events - * o Print requested source address in /proc/net/irnet - * o Change control channel input. Allow multiple commands in one line. - * o Add saddr command to change ap->rsaddr (and use that in IrDA) - * --- - * o Make the IrDA connection procedure totally asynchronous. - * Heavy rewrite of the IAS query code and the whole connection - * procedure. Now, irnet_connect() no longer need to be called from - * a process context... - * o Enable IrDA connect retries in ppp_irnet_send(). The good thing - * is that IrDA connect retries are directly driven by PPP LCP - * retries (we retry for each LCP packet), so that everything - * is transparently controlled from pppd lcp-max-configure. - * o Add ttp_connect flag to prevent rentry on the connect procedure - * o Test and fixups to eliminate side effects of retries - * - * v7 - 22.08.01 - Jean II - * o Cleanup : Change "saddr = 0x0" to "saddr = DEV_ADDR_ANY" - * o Fix bug in BLOCK_WHEN_CONNECT introduced in v6 : due to the - * asynchronous IAS query, self->tsap is NULL when PPP send the - * first packet. This was preventing "connect-delay 0" to work. - * Change the test in ppp_irnet_send() to self->ttp_connect. - * - * v8 - 1.11.01 - Jean II - * o Tighten the use of self->ttp_connect and self->ttp_open to - * prevent various race conditions. - * o Avoid leaking discovery log and skb - * o Replace "self" with "server" in irnet_connect_indication() to - * better detect cut'n'paste error ;-) - * - * v9 - 29.11.01 - Jean II - * o Fix event generation in disconnect indication that I broke in v8 - * It was always generation "No-Answer" because I was testing ttp_open - * just after clearing it. *blush*. - * o Use newly created irttp_listen() to fix potential crash when LAP - * destroyed before irnet module removed. - * - * v10 - 4.3.2 - Jean II - * o When receiving a disconnect indication, don't reenable the - * PPP Tx queue, this will trigger a reconnect. Instead, close - * the channel, which will kill pppd... - * - * v11 - 20.3.02 - Jean II - * o Oops ! v10 fix disabled IrNET retries and passive behaviour. - * Better fix in irnet_disconnect_indication() : - * - if connected, kill pppd via hangup. - * - if not connected, reenable ppp Tx, which trigger IrNET retry. - * - * v12 - 10.4.02 - Jean II - * o Fix race condition in irnet_connect_indication(). - * If the socket was already trying to connect, drop old connection - * and use new one only if acting as primary. See comments. - * - * v13 - 30.5.02 - Jean II - * o Update module init code - * - * v14 - 20.2.03 - Jean II - * o Add discovery hint bits in the control channel. - * o Remove obsolete MOD_INC/DEC_USE_COUNT in favor of .owner - * - * v15 - 7.4.03 - Jean II - * o Replace spin_lock_irqsave() with spin_lock_bh() so that we can - * use ppp_unit_number(). It's probably also better overall... - * o Disable call to ppp_unregister_channel(), because we can't do it. - */ - -/***************************** INCLUDES *****************************/ - -#include - -#include -#include -#include -#include -#include -#include -#include -#include /* isspace() */ -#include /* skip_spaces() */ -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/***************************** OPTIONS *****************************/ -/* - * Define or undefine to compile or not some optional part of the - * IrNET driver... - * Note : the present defaults make sense, play with that at your - * own risk... - */ -/* IrDA side of the business... */ -#define DISCOVERY_NOMASK /* To enable W2k compatibility... */ -#define ADVERTISE_HINT /* Advertise IrLAN hint bit */ -#define ALLOW_SIMULT_CONNECT /* This seem to work, cross fingers... */ -#define DISCOVERY_EVENTS /* Query the discovery log to post events */ -#define INITIAL_DISCOVERY /* Dump current discovery log as events */ -#undef STREAM_COMPAT /* Not needed - potentially messy */ -#undef CONNECT_INDIC_KICK /* Might mess IrDA, not needed */ -#undef FAIL_SEND_DISCONNECT /* Might mess IrDA, not needed */ -#undef PASS_CONNECT_PACKETS /* Not needed ? Safe */ -#undef MISSING_PPP_API /* Stuff I wish I could do */ - -/* PPP side of the business */ -#define BLOCK_WHEN_CONNECT /* Block packets when connecting */ -#define CONNECT_IN_SEND /* Retry IrDA connection procedure */ -#undef FLUSH_TO_PPP /* Not sure about this one, let's play safe */ -#undef SECURE_DEVIRNET /* Bah... */ - -/****************************** DEBUG ******************************/ - -/* - * This set of flags enable and disable all the various warning, - * error and debug message of this driver. - * Each section can be enabled and disabled independently - */ -/* In the PPP part */ -#define DEBUG_CTRL_TRACE 0 /* Control channel */ -#define DEBUG_CTRL_INFO 0 /* various info */ -#define DEBUG_CTRL_ERROR 1 /* problems */ -#define DEBUG_FS_TRACE 0 /* filesystem callbacks */ -#define DEBUG_FS_INFO 0 /* various info */ -#define DEBUG_FS_ERROR 1 /* problems */ -#define DEBUG_PPP_TRACE 0 /* PPP related functions */ -#define DEBUG_PPP_INFO 0 /* various info */ -#define DEBUG_PPP_ERROR 1 /* problems */ -#define DEBUG_MODULE_TRACE 0 /* module insertion/removal */ -#define DEBUG_MODULE_ERROR 1 /* problems */ - -/* In the IrDA part */ -#define DEBUG_IRDA_SR_TRACE 0 /* IRDA subroutines */ -#define DEBUG_IRDA_SR_INFO 0 /* various info */ -#define DEBUG_IRDA_SR_ERROR 1 /* problems */ -#define DEBUG_IRDA_SOCK_TRACE 0 /* IRDA main socket functions */ -#define DEBUG_IRDA_SOCK_INFO 0 /* various info */ -#define DEBUG_IRDA_SOCK_ERROR 1 /* problems */ -#define DEBUG_IRDA_SERV_TRACE 0 /* The IrNET server */ -#define DEBUG_IRDA_SERV_INFO 0 /* various info */ -#define DEBUG_IRDA_SERV_ERROR 1 /* problems */ -#define DEBUG_IRDA_TCB_TRACE 0 /* IRDA IrTTP callbacks */ -#define DEBUG_IRDA_CB_INFO 0 /* various info */ -#define DEBUG_IRDA_CB_ERROR 1 /* problems */ -#define DEBUG_IRDA_OCB_TRACE 0 /* IRDA other callbacks */ -#define DEBUG_IRDA_OCB_INFO 0 /* various info */ -#define DEBUG_IRDA_OCB_ERROR 1 /* problems */ - -#define DEBUG_ASSERT 0 /* Verify all assertions */ - -/* - * These are the macros we are using to actually print the debug - * statements. Don't look at it, it's ugly... - * - * One of the trick is that, as the DEBUG_XXX are constant, the - * compiler will optimise away the if() in all cases. - */ -/* All error messages (will show up in the normal logs) */ -#define DERROR(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_INFO "irnet: %s(): " format, __func__ , ##args);} - -/* Normal debug message (will show up in /var/log/debug) */ -#define DEBUG(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_DEBUG "irnet: %s(): " format, __func__ , ##args);} - -/* Entering a function (trace) */ -#define DENTER(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_DEBUG "irnet: -> %s" format, __func__ , ##args);} - -/* Entering and exiting a function in one go (trace) */ -#define DPASS(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_DEBUG "irnet: <>%s" format, __func__ , ##args);} - -/* Exiting a function (trace) */ -#define DEXIT(dbg, format, args...) \ - {if(DEBUG_##dbg) \ - printk(KERN_DEBUG "irnet: <-%s()" format, __func__ , ##args);} - -/* Exit a function with debug */ -#define DRETURN(ret, dbg, args...) \ - {DEXIT(dbg, ": " args);\ - return ret; } - -/* Exit a function on failed condition */ -#define DABORT(cond, ret, dbg, args...) \ - {if(cond) {\ - DERROR(dbg, args);\ - return ret; }} - -/* Invalid assertion, print out an error and exit... */ -#define DASSERT(cond, ret, dbg, args...) \ - {if((DEBUG_ASSERT) && !(cond)) {\ - DERROR(dbg, "Invalid assertion: " args);\ - return ret; }} - -/************************ CONSTANTS & MACROS ************************/ - -/* Paranoia */ -#define IRNET_MAGIC 0xB00754 - -/* Number of control events in the control channel buffer... */ -#define IRNET_MAX_EVENTS 8 /* Should be more than enough... */ - -/****************************** TYPES ******************************/ - -/* - * This is the main structure where we store all the data pertaining to - * one instance of irnet. - * Note : in irnet functions, a pointer this structure is usually called - * "ap" or "self". If the code is borrowed from the IrDA stack, it tend - * to be called "self", and if it is borrowed from the PPP driver it is - * "ap". Apart from that, it's exactly the same structure ;-) - */ -typedef struct irnet_socket -{ - /* ------------------- Instance management ------------------- */ - /* We manage a linked list of IrNET socket instances */ - irda_queue_t q; /* Must be first - for hasbin */ - int magic; /* Paranoia */ - - /* --------------------- FileSystem part --------------------- */ - /* "pppd" interact directly with us on a /dev/ file */ - struct file * file; /* File descriptor of this instance */ - /* TTY stuff - to keep "pppd" happy */ - struct ktermios termios; /* Various tty flags */ - /* Stuff for the control channel */ - int event_index; /* Last read in the event log */ - - /* ------------------------- PPP part ------------------------- */ - /* We interface directly to the ppp_generic driver in the kernel */ - int ppp_open; /* registered with ppp_generic */ - struct ppp_channel chan; /* Interface to generic ppp layer */ - - int mru; /* Max size of PPP payload */ - u32 xaccm[8]; /* Asynchronous character map (just */ - u32 raccm; /* to please pppd - dummy) */ - unsigned int flags; /* PPP flags (compression, ...) */ - unsigned int rbits; /* Unused receive flags ??? */ - struct work_struct disconnect_work; /* Process context disconnection */ - /* ------------------------ IrTTP part ------------------------ */ - /* We create a pseudo "socket" over the IrDA tranport */ - unsigned long ttp_open; /* Set when IrTTP is ready */ - unsigned long ttp_connect; /* Set when IrTTP is connecting */ - struct tsap_cb * tsap; /* IrTTP instance (the connection) */ - - char rname[NICKNAME_MAX_LEN + 1]; - /* IrDA nickname of destination */ - __u32 rdaddr; /* Requested peer IrDA address */ - __u32 rsaddr; /* Requested local IrDA address */ - __u32 daddr; /* actual peer IrDA address */ - __u32 saddr; /* my local IrDA address */ - __u8 dtsap_sel; /* Remote TSAP selector */ - __u8 stsap_sel; /* Local TSAP selector */ - - __u32 max_sdu_size_rx;/* Socket parameters used for IrTTP */ - __u32 max_sdu_size_tx; - __u32 max_data_size; - __u8 max_header_size; - LOCAL_FLOW tx_flow; /* State of the Tx path in IrTTP */ - - /* ------------------- IrLMP and IrIAS part ------------------- */ - /* Used for IrDA Discovery and socket name resolution */ - void * ckey; /* IrLMP client handle */ - __u16 mask; /* Hint bits mask (filter discov.)*/ - int nslots; /* Number of slots for discovery */ - - struct iriap_cb * iriap; /* Used to query remote IAS */ - int errno; /* status of the IAS query */ - - /* -------------------- Discovery log part -------------------- */ - /* Used by initial discovery on the control channel - * and by irnet_discover_daddr_and_lsap_sel() */ - struct irda_device_info *discoveries; /* Copy of the discovery log */ - int disco_index; /* Last read in the discovery log */ - int disco_number; /* Size of the discovery log */ - - struct mutex lock; - -} irnet_socket; - -/* - * This is the various event that we will generate on the control channel - */ -typedef enum irnet_event -{ - IRNET_DISCOVER, /* New IrNET node discovered */ - IRNET_EXPIRE, /* IrNET node expired */ - IRNET_CONNECT_TO, /* IrNET socket has connected to other node */ - IRNET_CONNECT_FROM, /* Other node has connected to IrNET socket */ - IRNET_REQUEST_FROM, /* Non satisfied connection request */ - IRNET_NOANSWER_FROM, /* Failed connection request */ - IRNET_BLOCKED_LINK, /* Link (IrLAP) is blocked for > 3s */ - IRNET_DISCONNECT_FROM, /* IrNET socket has disconnected */ - IRNET_DISCONNECT_TO /* Closing IrNET socket */ -} irnet_event; - -/* - * This is the storage for an event and its arguments - */ -typedef struct irnet_log -{ - irnet_event event; - int unit; - __u32 saddr; - __u32 daddr; - char name[NICKNAME_MAX_LEN + 1]; /* 21 + 1 */ - __u16_host_order hints; /* Discovery hint bits */ -} irnet_log; - -/* - * This is the storage for all events and related stuff... - */ -typedef struct irnet_ctrl_channel -{ - irnet_log log[IRNET_MAX_EVENTS]; /* Event log */ - int index; /* Current index in log */ - spinlock_t spinlock; /* Serialize access to the event log */ - wait_queue_head_t rwait; /* processes blocked on read (or poll) */ -} irnet_ctrl_channel; - -/**************************** PROTOTYPES ****************************/ -/* - * Global functions of the IrNET module - * Note : we list here also functions called from one file to the other. - */ - -/* -------------------------- IRDA PART -------------------------- */ -int irda_irnet_create(irnet_socket *); /* Initialise an IrNET socket */ -int irda_irnet_connect(irnet_socket *); /* Try to connect over IrDA */ -void irda_irnet_destroy(irnet_socket *); /* Teardown an IrNET socket */ -int irda_irnet_init(void); /* Initialise IrDA part of IrNET */ -void irda_irnet_cleanup(void); /* Teardown IrDA part of IrNET */ - -/**************************** VARIABLES ****************************/ - -/* Control channel stuff - allocated in irnet_irda.h */ -extern struct irnet_ctrl_channel irnet_events; - -#endif /* IRNET_H */ diff --git a/net/irda/irnet/irnet_irda.c b/net/irda/irnet/irnet_irda.c deleted file mode 100644 index e390bceeb2f8..000000000000 --- a/net/irda/irnet/irnet_irda.c +++ /dev/null @@ -1,1885 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file implement the IRDA interface of IrNET. - * Basically, we sit on top of IrTTP. We set up IrTTP, IrIAS properly, - * and exchange frames with IrTTP. - */ - -#include "irnet_irda.h" /* Private header */ -#include -#include -#include -#include - -/* - * PPP disconnect work: we need to make sure we're in - * process context when calling ppp_unregister_channel(). - */ -static void irnet_ppp_disconnect(struct work_struct *work) -{ - irnet_socket * self = - container_of(work, irnet_socket, disconnect_work); - - if (self == NULL) - return; - /* - * If we were connected, cleanup & close the PPP - * channel, which will kill pppd (hangup) and the rest. - */ - if (self->ppp_open && !self->ttp_open && !self->ttp_connect) { - ppp_unregister_channel(&self->chan); - self->ppp_open = 0; - } -} - -/************************* CONTROL CHANNEL *************************/ -/* - * When ppp is not active, /dev/irnet act as a control channel. - * Writing allow to set up the IrDA destination of the IrNET channel, - * and any application may be read events happening on IrNET... - */ - -/*------------------------------------------------------------------*/ -/* - * Post an event to the control channel... - * Put the event in the log, and then wait all process blocked on read - * so they can read the log... - */ -static void -irnet_post_event(irnet_socket * ap, - irnet_event event, - __u32 saddr, - __u32 daddr, - char * name, - __u16 hints) -{ - int index; /* In the log */ - - DENTER(CTRL_TRACE, "(ap=0x%p, event=%d, daddr=%08x, name=``%s'')\n", - ap, event, daddr, name); - - /* Protect this section via spinlock. - * Note : as we are the only event producer, we only need to exclude - * ourself when touching the log, which is nice and easy. - */ - spin_lock_bh(&irnet_events.spinlock); - - /* Copy the event in the log */ - index = irnet_events.index; - irnet_events.log[index].event = event; - irnet_events.log[index].daddr = daddr; - irnet_events.log[index].saddr = saddr; - /* Try to copy IrDA nickname */ - if(name) - strcpy(irnet_events.log[index].name, name); - else - irnet_events.log[index].name[0] = '\0'; - /* Copy hints */ - irnet_events.log[index].hints.word = hints; - /* Try to get ppp unit number */ - if((ap != (irnet_socket *) NULL) && (ap->ppp_open)) - irnet_events.log[index].unit = ppp_unit_number(&ap->chan); - else - irnet_events.log[index].unit = -1; - - /* Increment the index - * Note that we increment the index only after the event is written, - * to make sure that the readers don't get garbage... */ - irnet_events.index = (index + 1) % IRNET_MAX_EVENTS; - - DEBUG(CTRL_INFO, "New event index is %d\n", irnet_events.index); - - /* Spin lock end */ - spin_unlock_bh(&irnet_events.spinlock); - - /* Now : wake up everybody waiting for events... */ - wake_up_interruptible_all(&irnet_events.rwait); - - DEXIT(CTRL_TRACE, "\n"); -} - -/************************* IRDA SUBROUTINES *************************/ -/* - * These are a bunch of subroutines called from other functions - * down there, mostly common code or to improve readability... - * - * Note : we duplicate quite heavily some routines of af_irda.c, - * because our input structure (self) is quite different - * (struct irnet instead of struct irda_sock), which make sharing - * the same code impossible (at least, without templates). - */ - -/*------------------------------------------------------------------*/ -/* - * Function irda_open_tsap (self) - * - * Open local Transport Service Access Point (TSAP) - * - * Create a IrTTP instance for us and set all the IrTTP callbacks. - */ -static inline int -irnet_open_tsap(irnet_socket * self) -{ - notify_t notify; /* Callback structure */ - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - DABORT(self->tsap != NULL, -EBUSY, IRDA_SR_ERROR, "Already busy !\n"); - - /* Initialize IrTTP callbacks to be used by the IrDA stack */ - irda_notify_init(¬ify); - notify.connect_confirm = irnet_connect_confirm; - notify.connect_indication = irnet_connect_indication; - notify.disconnect_indication = irnet_disconnect_indication; - notify.data_indication = irnet_data_indication; - /*notify.udata_indication = NULL;*/ - notify.flow_indication = irnet_flow_indication; - notify.status_indication = irnet_status_indication; - notify.instance = self; - strlcpy(notify.name, IRNET_NOTIFY_NAME, sizeof(notify.name)); - - /* Open an IrTTP instance */ - self->tsap = irttp_open_tsap(LSAP_ANY, DEFAULT_INITIAL_CREDIT, - ¬ify); - DABORT(self->tsap == NULL, -ENOMEM, - IRDA_SR_ERROR, "Unable to allocate TSAP !\n"); - - /* Remember which TSAP selector we actually got */ - self->stsap_sel = self->tsap->stsap_sel; - - DEXIT(IRDA_SR_TRACE, " - tsap=0x%p, sel=0x%X\n", - self->tsap, self->stsap_sel); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_ias_to_tsap (self, result, value) - * - * Examine an IAS object and extract TSAP - * - * We do an IAP query to find the TSAP associated with the IrNET service. - * When IrIAP pass us the result of the query, this function look at - * the return values to check for failures and extract the TSAP if - * possible. - * Also deallocate value - * The failure is in self->errno - * Return TSAP or -1 - */ -static inline __u8 -irnet_ias_to_tsap(irnet_socket * self, - int result, - struct ias_value * value) -{ - __u8 dtsap_sel = 0; /* TSAP we are looking for */ - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* By default, no error */ - self->errno = 0; - - /* Check if request succeeded */ - switch(result) - { - /* Standard errors : service not available */ - case IAS_CLASS_UNKNOWN: - case IAS_ATTRIB_UNKNOWN: - DEBUG(IRDA_SR_INFO, "IAS object doesn't exist ! (%d)\n", result); - self->errno = -EADDRNOTAVAIL; - break; - - /* Other errors, most likely IrDA stack failure */ - default : - DEBUG(IRDA_SR_INFO, "IAS query failed ! (%d)\n", result); - self->errno = -EHOSTUNREACH; - break; - - /* Success : we got what we wanted */ - case IAS_SUCCESS: - break; - } - - /* Check what was returned to us */ - if(value != NULL) - { - /* What type of argument have we got ? */ - switch(value->type) - { - case IAS_INTEGER: - DEBUG(IRDA_SR_INFO, "result=%d\n", value->t.integer); - if(value->t.integer != -1) - /* Get the remote TSAP selector */ - dtsap_sel = value->t.integer; - else - self->errno = -EADDRNOTAVAIL; - break; - default: - self->errno = -EADDRNOTAVAIL; - DERROR(IRDA_SR_ERROR, "bad type ! (0x%X)\n", value->type); - break; - } - - /* Cleanup */ - irias_delete_value(value); - } - else /* value == NULL */ - { - /* Nothing returned to us - usually result != SUCCESS */ - if(!(self->errno)) - { - DERROR(IRDA_SR_ERROR, - "IrDA bug : result == SUCCESS && value == NULL\n"); - self->errno = -EHOSTUNREACH; - } - } - DEXIT(IRDA_SR_TRACE, "\n"); - - /* Return the TSAP */ - return dtsap_sel; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_find_lsap_sel (self) - * - * Try to lookup LSAP selector in remote LM-IAS - * - * Basically, we start a IAP query, and then go to sleep. When the query - * return, irnet_getvalue_confirm will wake us up, and we can examine the - * result of the query... - * Note that in some case, the query fail even before we go to sleep, - * creating some races... - */ -static inline int -irnet_find_lsap_sel(irnet_socket * self) -{ - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* This should not happen */ - DABORT(self->iriap, -EBUSY, IRDA_SR_ERROR, "busy with a previous query.\n"); - - /* Create an IAP instance, will be closed in irnet_getvalue_confirm() */ - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irnet_getvalue_confirm); - - /* Treat unexpected signals as disconnect */ - self->errno = -EHOSTUNREACH; - - /* Query remote LM-IAS */ - iriap_getvaluebyclass_request(self->iriap, self->rsaddr, self->daddr, - IRNET_SERVICE_NAME, IRNET_IAS_VALUE); - - /* The above request is non-blocking. - * After a while, IrDA will call us back in irnet_getvalue_confirm() - * We will then call irnet_ias_to_tsap() and finish the - * connection procedure */ - - DEXIT(IRDA_SR_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_connect_tsap (self) - * - * Initialise the TTP socket and initiate TTP connection - * - */ -static inline int -irnet_connect_tsap(irnet_socket * self) -{ - int err; - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* Open a local TSAP (an IrTTP instance) */ - err = irnet_open_tsap(self); - if(err != 0) - { - clear_bit(0, &self->ttp_connect); - DERROR(IRDA_SR_ERROR, "connect aborted!\n"); - return err; - } - - /* Connect to remote device */ - err = irttp_connect_request(self->tsap, self->dtsap_sel, - self->rsaddr, self->daddr, NULL, - self->max_sdu_size_rx, NULL); - if(err != 0) - { - clear_bit(0, &self->ttp_connect); - DERROR(IRDA_SR_ERROR, "connect aborted!\n"); - return err; - } - - /* The above call is non-blocking. - * After a while, the IrDA stack will either call us back in - * irnet_connect_confirm() or irnet_disconnect_indication() - * See you there ;-) */ - - DEXIT(IRDA_SR_TRACE, "\n"); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_discover_next_daddr (self) - * - * Query the IrNET TSAP of the next device in the log. - * - * Used in the TSAP discovery procedure. - */ -static inline int -irnet_discover_next_daddr(irnet_socket * self) -{ - /* Close the last instance of IrIAP, and open a new one. - * We can't reuse the IrIAP instance in the IrIAP callback */ - if(self->iriap) - { - iriap_close(self->iriap); - self->iriap = NULL; - } - /* Create a new IAP instance */ - self->iriap = iriap_open(LSAP_ANY, IAS_CLIENT, self, - irnet_discovervalue_confirm); - if(self->iriap == NULL) - return -ENOMEM; - - /* Next discovery - before the call to avoid races */ - self->disco_index++; - - /* Check if we have one more address to try */ - if(self->disco_index < self->disco_number) - { - /* Query remote LM-IAS */ - iriap_getvaluebyclass_request(self->iriap, - self->discoveries[self->disco_index].saddr, - self->discoveries[self->disco_index].daddr, - IRNET_SERVICE_NAME, IRNET_IAS_VALUE); - /* The above request is non-blocking. - * After a while, IrDA will call us back in irnet_discovervalue_confirm() - * We will then call irnet_ias_to_tsap() and come back here again... */ - return 0; - } - else - return 1; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_discover_daddr_and_lsap_sel (self) - * - * This try to find a device with the requested service. - * - * Initiate a TSAP discovery procedure. - * It basically look into the discovery log. For each address in the list, - * it queries the LM-IAS of the device to find if this device offer - * the requested service. - * If there is more than one node supporting the service, we complain - * to the user (it should move devices around). - * If we find one node which have the requested TSAP, we connect to it. - * - * This function just start the whole procedure. It request the discovery - * log and submit the first IAS query. - * The bulk of the job is handled in irnet_discovervalue_confirm() - * - * Note : this procedure fails if there is more than one device in range - * on the same dongle, because IrLMP doesn't disconnect the LAP when the - * last LSAP is closed. Moreover, we would need to wait the LAP - * disconnection... - */ -static inline int -irnet_discover_daddr_and_lsap_sel(irnet_socket * self) -{ - int ret; - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* Ask lmp for the current discovery log */ - self->discoveries = irlmp_get_discoveries(&self->disco_number, self->mask, - DISCOVERY_DEFAULT_SLOTS); - - /* Check if the we got some results */ - if(self->discoveries == NULL) - { - self->disco_number = -1; - clear_bit(0, &self->ttp_connect); - DRETURN(-ENETUNREACH, IRDA_SR_INFO, "No Cachelog...\n"); - } - DEBUG(IRDA_SR_INFO, "Got the log (0x%p), size is %d\n", - self->discoveries, self->disco_number); - - /* Start with the first discovery */ - self->disco_index = -1; - self->daddr = DEV_ADDR_ANY; - - /* This will fail if the log is empty - this is non-blocking */ - ret = irnet_discover_next_daddr(self); - if(ret) - { - /* Close IAP */ - if(self->iriap) - iriap_close(self->iriap); - self->iriap = NULL; - - /* Cleanup our copy of the discovery log */ - kfree(self->discoveries); - self->discoveries = NULL; - - clear_bit(0, &self->ttp_connect); - DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n"); - } - - /* Follow me in irnet_discovervalue_confirm() */ - - DEXIT(IRDA_SR_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_dname_to_daddr (self) - * - * Convert an IrDA nickname to a valid IrDA address - * - * It basically look into the discovery log until there is a match. - */ -static inline int -irnet_dname_to_daddr(irnet_socket * self) -{ - struct irda_device_info *discoveries; /* Copy of the discovery log */ - int number; /* Number of nodes in the log */ - int i; - - DENTER(IRDA_SR_TRACE, "(self=0x%p)\n", self); - - /* Ask lmp for the current discovery log */ - discoveries = irlmp_get_discoveries(&number, 0xffff, - DISCOVERY_DEFAULT_SLOTS); - /* Check if the we got some results */ - if(discoveries == NULL) - DRETURN(-ENETUNREACH, IRDA_SR_INFO, "Cachelog empty...\n"); - - /* - * Now, check all discovered devices (if any), and connect - * client only about the services that the client is - * interested in... - */ - for(i = 0; i < number; i++) - { - /* Does the name match ? */ - if(!strncmp(discoveries[i].info, self->rname, NICKNAME_MAX_LEN)) - { - /* Yes !!! Get it.. */ - self->daddr = discoveries[i].daddr; - DEBUG(IRDA_SR_INFO, "discovered device ``%s'' at address 0x%08x.\n", - self->rname, self->daddr); - kfree(discoveries); - DEXIT(IRDA_SR_TRACE, "\n"); - return 0; - } - } - /* No luck ! */ - DEBUG(IRDA_SR_INFO, "cannot discover device ``%s'' !!!\n", self->rname); - kfree(discoveries); - return -EADDRNOTAVAIL; -} - - -/************************* SOCKET ROUTINES *************************/ -/* - * This are the main operations on IrNET sockets, basically to create - * and destroy IrNET sockets. These are called from the PPP part... - */ - -/*------------------------------------------------------------------*/ -/* - * Create a IrNET instance : just initialise some parameters... - */ -int -irda_irnet_create(irnet_socket * self) -{ - DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self); - - self->magic = IRNET_MAGIC; /* Paranoia */ - - self->ttp_open = 0; /* Prevent higher layer from accessing IrTTP */ - self->ttp_connect = 0; /* Not connecting yet */ - self->rname[0] = '\0'; /* May be set via control channel */ - self->rdaddr = DEV_ADDR_ANY; /* May be set via control channel */ - self->rsaddr = DEV_ADDR_ANY; /* May be set via control channel */ - self->daddr = DEV_ADDR_ANY; /* Until we get connected */ - self->saddr = DEV_ADDR_ANY; /* Until we get connected */ - self->max_sdu_size_rx = TTP_SAR_UNBOUND; - - /* Register as a client with IrLMP */ - self->ckey = irlmp_register_client(0, NULL, NULL, NULL); -#ifdef DISCOVERY_NOMASK - self->mask = 0xffff; /* For W2k compatibility */ -#else /* DISCOVERY_NOMASK */ - self->mask = irlmp_service_to_hint(S_LAN); -#endif /* DISCOVERY_NOMASK */ - self->tx_flow = FLOW_START; /* Flow control from IrTTP */ - - INIT_WORK(&self->disconnect_work, irnet_ppp_disconnect); - - DEXIT(IRDA_SOCK_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Connect to the other side : - * o convert device name to an address - * o find the socket number (dlsap) - * o Establish the connection - * - * Note : We no longer mimic af_irda. The IAS query for finding the TSAP - * is done asynchronously, like the TTP connection. This allow us to - * call this function from any context (not only process). - * The downside is that following what's happening in there is tricky - * because it involve various functions all over the place... - */ -int -irda_irnet_connect(irnet_socket * self) -{ - int err; - - DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self); - - /* Check if we are already trying to connect. - * Because irda_irnet_connect() can be called directly by pppd plus - * packet retries in ppp_generic and connect may take time, plus we may - * race with irnet_connect_indication(), we need to be careful there... */ - if(test_and_set_bit(0, &self->ttp_connect)) - DRETURN(-EBUSY, IRDA_SOCK_INFO, "Already connecting...\n"); - if((self->iriap != NULL) || (self->tsap != NULL)) - DERROR(IRDA_SOCK_ERROR, "Socket not cleaned up...\n"); - - /* Insert ourselves in the hashbin so that the IrNET server can find us. - * Notes : 4th arg is string of 32 char max and must be null terminated - * When 4th arg is used (string), 3rd arg isn't (int) - * Can't re-insert (MUST remove first) so check for that... */ - if((irnet_server.running) && (self->q.q_next == NULL)) - { - spin_lock_bh(&irnet_server.spinlock); - hashbin_insert(irnet_server.list, (irda_queue_t *) self, 0, self->rname); - spin_unlock_bh(&irnet_server.spinlock); - DEBUG(IRDA_SOCK_INFO, "Inserted ``%s'' in hashbin...\n", self->rname); - } - - /* If we don't have anything (no address, no name) */ - if((self->rdaddr == DEV_ADDR_ANY) && (self->rname[0] == '\0')) - { - /* Try to find a suitable address */ - if((err = irnet_discover_daddr_and_lsap_sel(self)) != 0) - DRETURN(err, IRDA_SOCK_INFO, "auto-connect failed!\n"); - /* In most cases, the call above is non-blocking */ - } - else - { - /* If we have only the name (no address), try to get an address */ - if(self->rdaddr == DEV_ADDR_ANY) - { - if((err = irnet_dname_to_daddr(self)) != 0) - DRETURN(err, IRDA_SOCK_INFO, "name connect failed!\n"); - } - else - /* Use the requested destination address */ - self->daddr = self->rdaddr; - - /* Query remote LM-IAS to find LSAP selector */ - irnet_find_lsap_sel(self); - /* The above call is non blocking */ - } - - /* At this point, we are waiting for the IrDA stack to call us back, - * or we have already failed. - * We will finish the connection procedure in irnet_connect_tsap(). - */ - DEXIT(IRDA_SOCK_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_irnet_destroy(self) - * - * Destroy irnet instance - * - * Note : this need to be called from a process context. - */ -void -irda_irnet_destroy(irnet_socket * self) -{ - DENTER(IRDA_SOCK_TRACE, "(self=0x%p)\n", self); - if(self == NULL) - return; - - /* Remove ourselves from hashbin (if we are queued in hashbin) - * Note : `irnet_server.running' protect us from calls in hashbin_delete() */ - if((irnet_server.running) && (self->q.q_next != NULL)) - { - struct irnet_socket * entry; - DEBUG(IRDA_SOCK_INFO, "Removing from hash..\n"); - spin_lock_bh(&irnet_server.spinlock); - entry = hashbin_remove_this(irnet_server.list, (irda_queue_t *) self); - self->q.q_next = NULL; - spin_unlock_bh(&irnet_server.spinlock); - DASSERT(entry == self, , IRDA_SOCK_ERROR, "Can't remove from hash.\n"); - } - - /* If we were connected, post a message */ - if(test_bit(0, &self->ttp_open)) - { - /* Note : as the disconnect comes from ppp_generic, the unit number - * doesn't exist anymore when we post the event, so we need to pass - * NULL as the first arg... */ - irnet_post_event(NULL, IRNET_DISCONNECT_TO, - self->saddr, self->daddr, self->rname, 0); - } - - /* Prevent various IrDA callbacks from messing up things - * Need to be first */ - clear_bit(0, &self->ttp_connect); - - /* Prevent higher layer from accessing IrTTP */ - clear_bit(0, &self->ttp_open); - - /* Unregister with IrLMP */ - irlmp_unregister_client(self->ckey); - - /* Unregister with LM-IAS */ - if(self->iriap) - { - iriap_close(self->iriap); - self->iriap = NULL; - } - - /* Cleanup eventual discoveries from connection attempt or control channel */ - if(self->discoveries != NULL) - { - /* Cleanup our copy of the discovery log */ - kfree(self->discoveries); - self->discoveries = NULL; - } - - /* Close our IrTTP connection */ - if(self->tsap) - { - DEBUG(IRDA_SOCK_INFO, "Closing our TTP connection.\n"); - irttp_disconnect_request(self->tsap, NULL, P_NORMAL); - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - self->stsap_sel = 0; - - DEXIT(IRDA_SOCK_TRACE, "\n"); -} - - -/************************** SERVER SOCKET **************************/ -/* - * The IrNET service is composed of one server socket and a variable - * number of regular IrNET sockets. The server socket is supposed to - * handle incoming connections and redirect them to one IrNET sockets. - * It's a superset of the regular IrNET socket, but has a very distinct - * behaviour... - */ - -/*------------------------------------------------------------------*/ -/* - * Function irnet_daddr_to_dname (self) - * - * Convert an IrDA address to a IrDA nickname - * - * It basically look into the discovery log until there is a match. - */ -static inline int -irnet_daddr_to_dname(irnet_socket * self) -{ - struct irda_device_info *discoveries; /* Copy of the discovery log */ - int number; /* Number of nodes in the log */ - int i; - - DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self); - - /* Ask lmp for the current discovery log */ - discoveries = irlmp_get_discoveries(&number, 0xffff, - DISCOVERY_DEFAULT_SLOTS); - /* Check if the we got some results */ - if (discoveries == NULL) - DRETURN(-ENETUNREACH, IRDA_SERV_INFO, "Cachelog empty...\n"); - - /* Now, check all discovered devices (if any) */ - for(i = 0; i < number; i++) - { - /* Does the name match ? */ - if(discoveries[i].daddr == self->daddr) - { - /* Yes !!! Get it.. */ - strlcpy(self->rname, discoveries[i].info, sizeof(self->rname)); - self->rname[sizeof(self->rname) - 1] = '\0'; - DEBUG(IRDA_SERV_INFO, "Device 0x%08x is in fact ``%s''.\n", - self->daddr, self->rname); - kfree(discoveries); - DEXIT(IRDA_SERV_TRACE, "\n"); - return 0; - } - } - /* No luck ! */ - DEXIT(IRDA_SERV_INFO, ": cannot discover device 0x%08x !!!\n", self->daddr); - kfree(discoveries); - return -EADDRNOTAVAIL; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_find_socket (self) - * - * Find the correct IrNET socket - * - * Look into the list of IrNET sockets and finds one with the right - * properties... - */ -static inline irnet_socket * -irnet_find_socket(irnet_socket * self) -{ - irnet_socket * new = (irnet_socket *) NULL; - int err; - - DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self); - - /* Get the addresses of the requester */ - self->daddr = irttp_get_daddr(self->tsap); - self->saddr = irttp_get_saddr(self->tsap); - - /* Try to get the IrDA nickname of the requester */ - err = irnet_daddr_to_dname(self); - - /* Protect access to the instance list */ - spin_lock_bh(&irnet_server.spinlock); - - /* So now, try to get an socket having specifically - * requested that nickname */ - if(err == 0) - { - new = (irnet_socket *) hashbin_find(irnet_server.list, - 0, self->rname); - if(new) - DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches rname ``%s''.\n", - new, new->rname); - } - - /* If no name matches, try to find an socket by the destination address */ - /* It can be either the requested destination address (set via the - * control channel), or the current destination address if the - * socket is in the middle of a connection request */ - if(new == (irnet_socket *) NULL) - { - new = (irnet_socket *) hashbin_get_first(irnet_server.list); - while(new !=(irnet_socket *) NULL) - { - /* Does it have the same address ? */ - if((new->rdaddr == self->daddr) || (new->daddr == self->daddr)) - { - /* Yes !!! Get it.. */ - DEBUG(IRDA_SERV_INFO, "Socket 0x%p matches daddr %#08x.\n", - new, self->daddr); - break; - } - new = (irnet_socket *) hashbin_get_next(irnet_server.list); - } - } - - /* If we don't have any socket, get the first unconnected socket */ - if(new == (irnet_socket *) NULL) - { - new = (irnet_socket *) hashbin_get_first(irnet_server.list); - while(new !=(irnet_socket *) NULL) - { - /* Is it available ? */ - if(!(test_bit(0, &new->ttp_open)) && (new->rdaddr == DEV_ADDR_ANY) && - (new->rname[0] == '\0') && (new->ppp_open)) - { - /* Yes !!! Get it.. */ - DEBUG(IRDA_SERV_INFO, "Socket 0x%p is free.\n", - new); - break; - } - new = (irnet_socket *) hashbin_get_next(irnet_server.list); - } - } - - /* Spin lock end */ - spin_unlock_bh(&irnet_server.spinlock); - - DEXIT(IRDA_SERV_TRACE, " - new = 0x%p\n", new); - return new; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_connect_socket (self) - * - * Connect an incoming connection to the socket - * - */ -static inline int -irnet_connect_socket(irnet_socket * server, - irnet_socket * new, - struct qos_info * qos, - __u32 max_sdu_size, - __u8 max_header_size) -{ - DENTER(IRDA_SERV_TRACE, "(server=0x%p, new=0x%p)\n", - server, new); - - /* Now attach up the new socket */ - new->tsap = irttp_dup(server->tsap, new); - DABORT(new->tsap == NULL, -1, IRDA_SERV_ERROR, "dup failed!\n"); - - /* Set up all the relevant parameters on the new socket */ - new->stsap_sel = new->tsap->stsap_sel; - new->dtsap_sel = new->tsap->dtsap_sel; - new->saddr = irttp_get_saddr(new->tsap); - new->daddr = irttp_get_daddr(new->tsap); - - new->max_header_size = max_header_size; - new->max_sdu_size_tx = max_sdu_size; - new->max_data_size = max_sdu_size; -#ifdef STREAM_COMPAT - /* If we want to receive "stream sockets" */ - if(max_sdu_size == 0) - new->max_data_size = irttp_get_max_seg_size(new->tsap); -#endif /* STREAM_COMPAT */ - - /* Clean up the original one to keep it in listen state */ - irttp_listen(server->tsap); - - /* Send a connection response on the new socket */ - irttp_connect_response(new->tsap, new->max_sdu_size_rx, NULL); - - /* Allow PPP to send its junk over the new socket... */ - set_bit(0, &new->ttp_open); - - /* Not connecting anymore, and clean up last possible remains - * of connection attempts on the socket */ - clear_bit(0, &new->ttp_connect); - if(new->iriap) - { - iriap_close(new->iriap); - new->iriap = NULL; - } - if(new->discoveries != NULL) - { - kfree(new->discoveries); - new->discoveries = NULL; - } - -#ifdef CONNECT_INDIC_KICK - /* As currently we don't block packets in ppp_irnet_send() while passive, - * this is not really needed... - * Also, not doing it give IrDA a chance to finish the setup properly - * before being swamped with packets... */ - ppp_output_wakeup(&new->chan); -#endif /* CONNECT_INDIC_KICK */ - - /* Notify the control channel */ - irnet_post_event(new, IRNET_CONNECT_FROM, - new->saddr, new->daddr, server->rname, 0); - - DEXIT(IRDA_SERV_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_disconnect_server (self) - * - * Cleanup the server socket when the incoming connection abort - * - */ -static inline void -irnet_disconnect_server(irnet_socket * self, - struct sk_buff *skb) -{ - DENTER(IRDA_SERV_TRACE, "(self=0x%p)\n", self); - - /* Put the received packet in the black hole */ - kfree_skb(skb); - -#ifdef FAIL_SEND_DISCONNECT - /* Tell the other party we don't want to be connected */ - /* Hum... Is it the right thing to do ? And do we need to send - * a connect response before ? It looks ok without this... */ - irttp_disconnect_request(self->tsap, NULL, P_NORMAL); -#endif /* FAIL_SEND_DISCONNECT */ - - /* Notify the control channel (see irnet_find_socket()) */ - irnet_post_event(NULL, IRNET_REQUEST_FROM, - self->saddr, self->daddr, self->rname, 0); - - /* Clean up the server to keep it in listen state */ - irttp_listen(self->tsap); - - DEXIT(IRDA_SERV_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_setup_server (self) - * - * Create a IrTTP server and set it up... - * - * Register the IrLAN hint bit, create a IrTTP instance for us, - * set all the IrTTP callbacks and create an IrIAS entry... - */ -static inline int -irnet_setup_server(void) -{ - __u16 hints; - - DENTER(IRDA_SERV_TRACE, "()\n"); - - /* Initialise the regular socket part of the server */ - irda_irnet_create(&irnet_server.s); - - /* Open a local TSAP (an IrTTP instance) for the server */ - irnet_open_tsap(&irnet_server.s); - - /* PPP part setup */ - irnet_server.s.ppp_open = 0; - irnet_server.s.chan.private = NULL; - irnet_server.s.file = NULL; - - /* Get the hint bit corresponding to IrLAN */ - /* Note : we overload the IrLAN hint bit. As it is only a "hint", and as - * we provide roughly the same functionality as IrLAN, this is ok. - * In fact, the situation is similar as JetSend overloading the Obex hint - */ - hints = irlmp_service_to_hint(S_LAN); - -#ifdef ADVERTISE_HINT - /* Register with IrLMP as a service (advertise our hint bit) */ - irnet_server.skey = irlmp_register_service(hints); -#endif /* ADVERTISE_HINT */ - - /* Register with LM-IAS (so that people can connect to us) */ - irnet_server.ias_obj = irias_new_object(IRNET_SERVICE_NAME, jiffies); - irias_add_integer_attrib(irnet_server.ias_obj, IRNET_IAS_VALUE, - irnet_server.s.stsap_sel, IAS_KERNEL_ATTR); - irias_insert_object(irnet_server.ias_obj); - -#ifdef DISCOVERY_EVENTS - /* Tell IrLMP we want to be notified of newly discovered nodes */ - irlmp_update_client(irnet_server.s.ckey, hints, - irnet_discovery_indication, irnet_expiry_indication, - (void *) &irnet_server.s); -#endif - - DEXIT(IRDA_SERV_TRACE, " - self=0x%p\n", &irnet_server.s); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Function irda_destroy_server (self) - * - * Destroy the IrTTP server... - * - * Reverse of the previous function... - */ -static inline void -irnet_destroy_server(void) -{ - DENTER(IRDA_SERV_TRACE, "()\n"); - -#ifdef ADVERTISE_HINT - /* Unregister with IrLMP */ - irlmp_unregister_service(irnet_server.skey); -#endif /* ADVERTISE_HINT */ - - /* Unregister with LM-IAS */ - if(irnet_server.ias_obj) - irias_delete_object(irnet_server.ias_obj); - - /* Cleanup the socket part */ - irda_irnet_destroy(&irnet_server.s); - - DEXIT(IRDA_SERV_TRACE, "\n"); -} - - -/************************ IRDA-TTP CALLBACKS ************************/ -/* - * When we create a IrTTP instance, we pass to it a set of callbacks - * that IrTTP will call in case of various events. - * We take care of those events here. - */ - -/*------------------------------------------------------------------*/ -/* - * Function irnet_data_indication (instance, sap, skb) - * - * Received some data from TinyTP. Just queue it on the receive queue - * - */ -static int -irnet_data_indication(void * instance, - void * sap, - struct sk_buff *skb) -{ - irnet_socket * ap = (irnet_socket *) instance; - unsigned char * p; - int code = 0; - - DENTER(IRDA_TCB_TRACE, "(self/ap=0x%p, skb=0x%p)\n", - ap, skb); - DASSERT(skb != NULL, 0, IRDA_CB_ERROR, "skb is NULL !!!\n"); - - /* Check is ppp is ready to receive our packet */ - if(!ap->ppp_open) - { - DERROR(IRDA_CB_ERROR, "PPP not ready, dropping packet...\n"); - /* When we return error, TTP will need to requeue the skb and - * will stop the sender. IrTTP will stall until we send it a - * flow control request... */ - return -ENOMEM; - } - - /* strip address/control field if present */ - p = skb->data; - if((p[0] == PPP_ALLSTATIONS) && (p[1] == PPP_UI)) - { - /* chop off address/control */ - if(skb->len < 3) - goto err_exit; - p = skb_pull(skb, 2); - } - - /* decompress protocol field if compressed */ - if(p[0] & 1) - { - /* protocol is compressed */ - *(u8 *)skb_push(skb, 1) = 0; - } - else - if(skb->len < 2) - goto err_exit; - - /* pass to generic ppp layer */ - /* Note : how do I know if ppp can accept or not the packet ? This is - * essential if I want to manage flow control smoothly... */ - ppp_input(&ap->chan, skb); - - DEXIT(IRDA_TCB_TRACE, "\n"); - return 0; - - err_exit: - DERROR(IRDA_CB_ERROR, "Packet too small, dropping...\n"); - kfree_skb(skb); - ppp_input_error(&ap->chan, code); - return 0; /* Don't return an error code, only for flow control... */ -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_disconnect_indication (instance, sap, reason, skb) - * - * Connection has been closed. Chech reason to find out why - * - * Note : there are many cases where we come here : - * o attempted to connect, timeout - * o connected, link is broken, LAP has timeout - * o connected, other side close the link - * o connection request on the server not handled - */ -static void -irnet_disconnect_indication(void * instance, - void * sap, - LM_REASON reason, - struct sk_buff *skb) -{ - irnet_socket * self = (irnet_socket *) instance; - int test_open; - int test_connect; - - DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self); - DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); - - /* Don't care about it, but let's not leak it */ - if(skb) - dev_kfree_skb(skb); - - /* Prevent higher layer from accessing IrTTP */ - test_open = test_and_clear_bit(0, &self->ttp_open); - /* Not connecting anymore... - * (note : TSAP is open, so IAP callbacks are no longer pending...) */ - test_connect = test_and_clear_bit(0, &self->ttp_connect); - - /* If both self->ttp_open and self->ttp_connect are NULL, it mean that we - * have a race condition with irda_irnet_destroy() or - * irnet_connect_indication(), so don't mess up tsap... - */ - if(!(test_open || test_connect)) - { - DERROR(IRDA_CB_ERROR, "Race condition detected...\n"); - return; - } - - /* If we were active, notify the control channel */ - if(test_open) - irnet_post_event(self, IRNET_DISCONNECT_FROM, - self->saddr, self->daddr, self->rname, 0); - else - /* If we were trying to connect, notify the control channel */ - if((self->tsap) && (self != &irnet_server.s)) - irnet_post_event(self, IRNET_NOANSWER_FROM, - self->saddr, self->daddr, self->rname, 0); - - /* Close our IrTTP connection, cleanup tsap */ - if((self->tsap) && (self != &irnet_server.s)) - { - DEBUG(IRDA_CB_INFO, "Closing our TTP connection.\n"); - irttp_close_tsap(self->tsap); - self->tsap = NULL; - } - /* Cleanup the socket in case we want to reconnect in ppp_output_wakeup() */ - self->stsap_sel = 0; - self->daddr = DEV_ADDR_ANY; - self->tx_flow = FLOW_START; - - /* Deal with the ppp instance if it's still alive */ - if(self->ppp_open) - { - if(test_open) - { - /* ppp_unregister_channel() wants a user context. */ - schedule_work(&self->disconnect_work); - } - else - { - /* If we were trying to connect, flush (drain) ppp_generic - * Tx queue (most often we have blocked it), which will - * trigger an other attempt to connect. If we are passive, - * this will empty the Tx queue after last try. */ - ppp_output_wakeup(&self->chan); - } - } - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_connect_confirm (instance, sap, qos, max_sdu_size, skb) - * - * Connections has been confirmed by the remote device - * - */ -static void -irnet_connect_confirm(void * instance, - void * sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - irnet_socket * self = (irnet_socket *) instance; - - DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self); - - /* Check if socket is closing down (via irda_irnet_destroy()) */ - if(! test_bit(0, &self->ttp_connect)) - { - DERROR(IRDA_CB_ERROR, "Socket no longer connecting. Ouch !\n"); - return; - } - - /* How much header space do we need to reserve */ - self->max_header_size = max_header_size; - - /* IrTTP max SDU size in transmit direction */ - self->max_sdu_size_tx = max_sdu_size; - self->max_data_size = max_sdu_size; -#ifdef STREAM_COMPAT - if(max_sdu_size == 0) - self->max_data_size = irttp_get_max_seg_size(self->tsap); -#endif /* STREAM_COMPAT */ - - /* At this point, IrLMP has assigned our source address */ - self->saddr = irttp_get_saddr(self->tsap); - - /* Allow higher layer to access IrTTP */ - set_bit(0, &self->ttp_open); - clear_bit(0, &self->ttp_connect); /* Not racy, IrDA traffic is serial */ - /* Give a kick in the ass of ppp_generic so that he sends us some data */ - ppp_output_wakeup(&self->chan); - - /* Check size of received packet */ - if(skb->len > 0) - { -#ifdef PASS_CONNECT_PACKETS - DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n"); - /* Try to pass it to PPP */ - irnet_data_indication(instance, sap, skb); -#else /* PASS_CONNECT_PACKETS */ - DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n"); - kfree_skb(skb); /* Note : will be optimised with other kfree... */ -#endif /* PASS_CONNECT_PACKETS */ - } - else - kfree_skb(skb); - - /* Notify the control channel */ - irnet_post_event(self, IRNET_CONNECT_TO, - self->saddr, self->daddr, self->rname, 0); - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_flow_indication (instance, sap, flow) - * - * Used by TinyTP to tell us if it can accept more data or not - * - */ -static void -irnet_flow_indication(void * instance, - void * sap, - LOCAL_FLOW flow) -{ - irnet_socket * self = (irnet_socket *) instance; - LOCAL_FLOW oldflow = self->tx_flow; - - DENTER(IRDA_TCB_TRACE, "(self=0x%p, flow=%d)\n", self, flow); - - /* Update our state */ - self->tx_flow = flow; - - /* Check what IrTTP want us to do... */ - switch(flow) - { - case FLOW_START: - DEBUG(IRDA_CB_INFO, "IrTTP wants us to start again\n"); - /* Check if we really need to wake up PPP */ - if(oldflow == FLOW_STOP) - ppp_output_wakeup(&self->chan); - else - DEBUG(IRDA_CB_INFO, "But we were already transmitting !!!\n"); - break; - case FLOW_STOP: - DEBUG(IRDA_CB_INFO, "IrTTP wants us to slow down\n"); - break; - default: - DEBUG(IRDA_CB_INFO, "Unknown flow command!\n"); - break; - } - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_status_indication (instance, sap, reason, skb) - * - * Link (IrLAP) status report. - * - */ -static void -irnet_status_indication(void * instance, - LINK_STATUS link, - LOCK_STATUS lock) -{ - irnet_socket * self = (irnet_socket *) instance; - - DENTER(IRDA_TCB_TRACE, "(self=0x%p)\n", self); - DASSERT(self != NULL, , IRDA_CB_ERROR, "Self is NULL !!!\n"); - - /* We can only get this event if we are connected */ - switch(link) - { - case STATUS_NO_ACTIVITY: - irnet_post_event(self, IRNET_BLOCKED_LINK, - self->saddr, self->daddr, self->rname, 0); - break; - default: - DEBUG(IRDA_CB_INFO, "Unknown status...\n"); - } - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_connect_indication(instance, sap, qos, max_sdu_size, userdata) - * - * Incoming connection - * - * In theory, this function is called only on the server socket. - * Some other node is attempting to connect to the IrNET service, and has - * sent a connection request on our server socket. - * We just redirect the connection to the relevant IrNET socket. - * - * Note : we also make sure that between 2 irnet nodes, there can - * exist only one irnet connection. - */ -static void -irnet_connect_indication(void * instance, - void * sap, - struct qos_info *qos, - __u32 max_sdu_size, - __u8 max_header_size, - struct sk_buff *skb) -{ - irnet_socket * server = &irnet_server.s; - irnet_socket * new = (irnet_socket *) NULL; - - DENTER(IRDA_TCB_TRACE, "(server=0x%p)\n", server); - DASSERT(instance == &irnet_server, , IRDA_CB_ERROR, - "Invalid instance (0x%p) !!!\n", instance); - DASSERT(sap == irnet_server.s.tsap, , IRDA_CB_ERROR, "Invalid sap !!!\n"); - - /* Try to find the most appropriate IrNET socket */ - new = irnet_find_socket(server); - - /* After all this hard work, do we have an socket ? */ - if(new == (irnet_socket *) NULL) - { - DEXIT(IRDA_CB_INFO, ": No socket waiting for this connection.\n"); - irnet_disconnect_server(server, skb); - return; - } - - /* Is the socket already busy ? */ - if(test_bit(0, &new->ttp_open)) - { - DEXIT(IRDA_CB_INFO, ": Socket already connected.\n"); - irnet_disconnect_server(server, skb); - return; - } - - /* The following code is a bit tricky, so need comments ;-) - */ - /* If ttp_connect is set, the socket is trying to connect to the other - * end and may have sent a IrTTP connection request and is waiting for - * a connection response (that may never come). - * Now, the pain is that the socket may have opened a tsap and is - * waiting on it, while the other end is trying to connect to it on - * another tsap. - * Because IrNET can be peer to peer, we need to workaround this. - * Furthermore, the way the irnetd script is implemented, the - * target will create a second IrNET connection back to the - * originator and expect the originator to bind this new connection - * to the original PPPD instance. - * And of course, if we don't use irnetd, we can have a race when - * both side try to connect simultaneously, which could leave both - * connections half closed (yuck). - * Conclusions : - * 1) The "originator" must accept the new connection and get rid - * of the old one so that irnetd works - * 2) One side must deny the new connection to avoid races, - * but both side must agree on which side it is... - * Most often, the originator is primary at the LAP layer. - * Jean II - */ - /* Now, let's look at the way I wrote the test... - * We need to clear up the ttp_connect flag atomically to prevent - * irnet_disconnect_indication() to mess up the tsap we are going to close. - * We want to clear the ttp_connect flag only if we close the tsap, - * otherwise we will never close it, so we need to check for primary - * *before* doing the test on the flag. - * And of course, ALLOW_SIMULT_CONNECT can disable this entirely... - * Jean II - */ - - /* Socket already connecting ? On primary ? */ - if(0 -#ifdef ALLOW_SIMULT_CONNECT - || ((irttp_is_primary(server->tsap) == 1) && /* primary */ - (test_and_clear_bit(0, &new->ttp_connect))) -#endif /* ALLOW_SIMULT_CONNECT */ - ) - { - DERROR(IRDA_CB_ERROR, "Socket already connecting, but going to reuse it !\n"); - - /* Cleanup the old TSAP if necessary - IrIAP will be cleaned up later */ - if(new->tsap != NULL) - { - /* Close the old connection the new socket was attempting, - * so that we can hook it up to the new connection. - * It's now safe to do it... */ - irttp_close_tsap(new->tsap); - new->tsap = NULL; - } - } - else - { - /* Three options : - * 1) socket was not connecting or connected : ttp_connect should be 0. - * 2) we don't want to connect the socket because we are secondary or - * ALLOW_SIMULT_CONNECT is undefined. ttp_connect should be 1. - * 3) we are half way in irnet_disconnect_indication(), and it's a - * nice race condition... Fortunately, we can detect that by checking - * if tsap is still alive. On the other hand, we can't be in - * irda_irnet_destroy() otherwise we would not have found this - * socket in the hashbin. - * Jean II */ - if((test_bit(0, &new->ttp_connect)) || (new->tsap != NULL)) - { - /* Don't mess this socket, somebody else in in charge... */ - DERROR(IRDA_CB_ERROR, "Race condition detected, socket in use, abort connect...\n"); - irnet_disconnect_server(server, skb); - return; - } - } - - /* So : at this point, we have a socket, and it is idle. Good ! */ - irnet_connect_socket(server, new, qos, max_sdu_size, max_header_size); - - /* Check size of received packet */ - if(skb->len > 0) - { -#ifdef PASS_CONNECT_PACKETS - DEBUG(IRDA_CB_INFO, "Passing connect packet to PPP.\n"); - /* Try to pass it to PPP */ - irnet_data_indication(new, new->tsap, skb); -#else /* PASS_CONNECT_PACKETS */ - DERROR(IRDA_CB_ERROR, "Dropping non empty packet.\n"); - kfree_skb(skb); /* Note : will be optimised with other kfree... */ -#endif /* PASS_CONNECT_PACKETS */ - } - else - kfree_skb(skb); - - DEXIT(IRDA_TCB_TRACE, "\n"); -} - - -/********************** IRDA-IAS/LMP CALLBACKS **********************/ -/* - * These are the callbacks called by other layers of the IrDA stack, - * mainly LMP for discovery and IAS for name queries. - */ - -/*------------------------------------------------------------------*/ -/* - * Function irnet_getvalue_confirm (result, obj_id, value, priv) - * - * Got answer from remote LM-IAS, just connect - * - * This is the reply to a IAS query we were doing to find the TSAP of - * the device we want to connect to. - * If we have found a valid TSAP, just initiate the TTP connection - * on this TSAP. - */ -static void -irnet_getvalue_confirm(int result, - __u16 obj_id, - struct ias_value *value, - void * priv) -{ - irnet_socket * self = (irnet_socket *) priv; - - DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); - DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n"); - - /* Check if already connected (via irnet_connect_socket()) - * or socket is closing down (via irda_irnet_destroy()) */ - if(! test_bit(0, &self->ttp_connect)) - { - DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n"); - return; - } - - /* We probably don't need to make any more queries */ - iriap_close(self->iriap); - self->iriap = NULL; - - /* Post process the IAS reply */ - self->dtsap_sel = irnet_ias_to_tsap(self, result, value); - - /* If error, just go out */ - if(self->errno) - { - clear_bit(0, &self->ttp_connect); - DERROR(IRDA_OCB_ERROR, "IAS connect failed ! (0x%X)\n", self->errno); - return; - } - - DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n", - self->daddr, self->dtsap_sel); - - /* Start up TTP - non blocking */ - irnet_connect_tsap(self); - - DEXIT(IRDA_OCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_discovervalue_confirm (result, obj_id, value, priv) - * - * Handle the TSAP discovery procedure state machine. - * Got answer from remote LM-IAS, try next device - * - * We are doing a TSAP discovery procedure, and we got an answer to - * a IAS query we were doing to find the TSAP on one of the address - * in the discovery log. - * - * If we have found a valid TSAP for the first time, save it. If it's - * not the first time we found one, complain. - * - * If we have more addresses in the log, just initiate a new query. - * Note that those query may fail (see irnet_discover_daddr_and_lsap_sel()) - * - * Otherwise, wrap up the procedure (cleanup), check if we have found - * any device and connect to it. - */ -static void -irnet_discovervalue_confirm(int result, - __u16 obj_id, - struct ias_value *value, - void * priv) -{ - irnet_socket * self = (irnet_socket *) priv; - __u8 dtsap_sel; /* TSAP we are looking for */ - - DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); - DASSERT(self != NULL, , IRDA_OCB_ERROR, "Self is NULL !!!\n"); - - /* Check if already connected (via irnet_connect_socket()) - * or socket is closing down (via irda_irnet_destroy()) */ - if(! test_bit(0, &self->ttp_connect)) - { - DERROR(IRDA_OCB_ERROR, "Socket no longer connecting. Ouch !\n"); - return; - } - - /* Post process the IAS reply */ - dtsap_sel = irnet_ias_to_tsap(self, result, value); - - /* Have we got something ? */ - if(self->errno == 0) - { - /* We found the requested service */ - if(self->daddr != DEV_ADDR_ANY) - { - DERROR(IRDA_OCB_ERROR, "More than one device in range supports IrNET...\n"); - } - else - { - /* First time we found that one, save it ! */ - self->daddr = self->discoveries[self->disco_index].daddr; - self->dtsap_sel = dtsap_sel; - } - } - - /* If no failure */ - if((self->errno == -EADDRNOTAVAIL) || (self->errno == 0)) - { - int ret; - - /* Search the next node */ - ret = irnet_discover_next_daddr(self); - if(!ret) - { - /* In this case, the above request was non-blocking. - * We will return here after a while... */ - return; - } - /* In this case, we have processed the last discovery item */ - } - - /* No more queries to be done (failure or last one) */ - - /* We probably don't need to make any more queries */ - iriap_close(self->iriap); - self->iriap = NULL; - - /* No more items : remove the log and signal termination */ - DEBUG(IRDA_OCB_INFO, "Cleaning up log (0x%p)\n", - self->discoveries); - if(self->discoveries != NULL) - { - /* Cleanup our copy of the discovery log */ - kfree(self->discoveries); - self->discoveries = NULL; - } - self->disco_number = -1; - - /* Check out what we found */ - if(self->daddr == DEV_ADDR_ANY) - { - self->daddr = DEV_ADDR_ANY; - clear_bit(0, &self->ttp_connect); - DEXIT(IRDA_OCB_TRACE, ": cannot discover IrNET in any device !!!\n"); - return; - } - - /* We have a valid address - just connect */ - - DEBUG(IRDA_OCB_INFO, "daddr = %08x, lsap = %d, starting IrTTP connection\n", - self->daddr, self->dtsap_sel); - - /* Start up TTP - non blocking */ - irnet_connect_tsap(self); - - DEXIT(IRDA_OCB_TRACE, "\n"); -} - -#ifdef DISCOVERY_EVENTS -/*------------------------------------------------------------------*/ -/* - * Function irnet_discovery_indication (discovery) - * - * Got a discovery indication from IrLMP, post an event - * - * Note : IrLMP take care of matching the hint mask for us, and also - * check if it is a "new" node for us... - * - * As IrLMP filter on the IrLAN hint bit, we get both IrLAN and IrNET - * nodes, so it's only at connection time that we will know if the - * node support IrNET, IrLAN or both. The other solution is to check - * in IAS the PNP ids and service name. - * Note : even if a node support IrNET (or IrLAN), it's no guarantee - * that we will be able to connect to it, the node might already be - * busy... - * - * One last thing : in some case, this function will trigger duplicate - * discovery events. On the other hand, we should catch all - * discoveries properly (i.e. not miss one). Filtering duplicate here - * is to messy, so we leave that to user space... - */ -static void -irnet_discovery_indication(discinfo_t * discovery, - DISCOVERY_MODE mode, - void * priv) -{ - irnet_socket * self = &irnet_server.s; - - DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); - DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR, - "Invalid instance (0x%p) !!!\n", priv); - - DEBUG(IRDA_OCB_INFO, "Discovered new IrNET/IrLAN node %s...\n", - discovery->info); - - /* Notify the control channel */ - irnet_post_event(NULL, IRNET_DISCOVER, - discovery->saddr, discovery->daddr, discovery->info, - get_unaligned((__u16 *)discovery->hints)); - - DEXIT(IRDA_OCB_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_expiry_indication (expiry) - * - * Got a expiry indication from IrLMP, post an event - * - * Note : IrLMP take care of matching the hint mask for us, we only - * check if it is a "new" node... - */ -static void -irnet_expiry_indication(discinfo_t * expiry, - DISCOVERY_MODE mode, - void * priv) -{ - irnet_socket * self = &irnet_server.s; - - DENTER(IRDA_OCB_TRACE, "(self=0x%p)\n", self); - DASSERT(priv == &irnet_server, , IRDA_OCB_ERROR, - "Invalid instance (0x%p) !!!\n", priv); - - DEBUG(IRDA_OCB_INFO, "IrNET/IrLAN node %s expired...\n", - expiry->info); - - /* Notify the control channel */ - irnet_post_event(NULL, IRNET_EXPIRE, - expiry->saddr, expiry->daddr, expiry->info, - get_unaligned((__u16 *)expiry->hints)); - - DEXIT(IRDA_OCB_TRACE, "\n"); -} -#endif /* DISCOVERY_EVENTS */ - - -/*********************** PROC ENTRY CALLBACKS ***********************/ -/* - * We create a instance in the /proc filesystem, and here we take care - * of that... - */ - -#ifdef CONFIG_PROC_FS -static int -irnet_proc_show(struct seq_file *m, void *v) -{ - irnet_socket * self; - char * state; - int i = 0; - - /* Get the IrNET server information... */ - seq_printf(m, "IrNET server - "); - seq_printf(m, "IrDA state: %s, ", - (irnet_server.running ? "running" : "dead")); - seq_printf(m, "stsap_sel: %02x, ", irnet_server.s.stsap_sel); - seq_printf(m, "dtsap_sel: %02x\n", irnet_server.s.dtsap_sel); - - /* Do we need to continue ? */ - if(!irnet_server.running) - return 0; - - /* Protect access to the instance list */ - spin_lock_bh(&irnet_server.spinlock); - - /* Get the sockets one by one... */ - self = (irnet_socket *) hashbin_get_first(irnet_server.list); - while(self != NULL) - { - /* Start printing info about the socket. */ - seq_printf(m, "\nIrNET socket %d - ", i++); - - /* First, get the requested configuration */ - seq_printf(m, "Requested IrDA name: \"%s\", ", self->rname); - seq_printf(m, "daddr: %08x, ", self->rdaddr); - seq_printf(m, "saddr: %08x\n", self->rsaddr); - - /* Second, get all the PPP info */ - seq_printf(m, " PPP state: %s", - (self->ppp_open ? "registered" : "unregistered")); - if(self->ppp_open) - { - seq_printf(m, ", unit: ppp%d", - ppp_unit_number(&self->chan)); - seq_printf(m, ", channel: %d", - ppp_channel_index(&self->chan)); - seq_printf(m, ", mru: %d", - self->mru); - /* Maybe add self->flags ? Later... */ - } - - /* Then, get all the IrDA specific info... */ - if(self->ttp_open) - state = "connected"; - else - if(self->tsap != NULL) - state = "connecting"; - else - if(self->iriap != NULL) - state = "searching"; - else - if(self->ttp_connect) - state = "weird"; - else - state = "idle"; - seq_printf(m, "\n IrDA state: %s, ", state); - seq_printf(m, "daddr: %08x, ", self->daddr); - seq_printf(m, "stsap_sel: %02x, ", self->stsap_sel); - seq_printf(m, "dtsap_sel: %02x\n", self->dtsap_sel); - - /* Next socket, please... */ - self = (irnet_socket *) hashbin_get_next(irnet_server.list); - } - - /* Spin lock end */ - spin_unlock_bh(&irnet_server.spinlock); - - return 0; -} - -static int irnet_proc_open(struct inode *inode, struct file *file) -{ - return single_open(file, irnet_proc_show, NULL); -} - -static const struct file_operations irnet_proc_fops = { - .owner = THIS_MODULE, - .open = irnet_proc_open, - .read = seq_read, - .llseek = seq_lseek, - .release = single_release, -}; -#endif /* PROC_FS */ - - -/********************** CONFIGURATION/CLEANUP **********************/ -/* - * Initialisation and teardown of the IrDA part, called at module - * insertion and removal... - */ - -/*------------------------------------------------------------------*/ -/* - * Prepare the IrNET layer for operation... - */ -int __init -irda_irnet_init(void) -{ - int err = 0; - - DENTER(MODULE_TRACE, "()\n"); - - /* Pure paranoia - should be redundant */ - memset(&irnet_server, 0, sizeof(struct irnet_root)); - - /* Setup start of irnet instance list */ - irnet_server.list = hashbin_new(HB_NOLOCK); - DABORT(irnet_server.list == NULL, -ENOMEM, - MODULE_ERROR, "Can't allocate hashbin!\n"); - /* Init spinlock for instance list */ - spin_lock_init(&irnet_server.spinlock); - - /* Initialise control channel */ - init_waitqueue_head(&irnet_events.rwait); - irnet_events.index = 0; - /* Init spinlock for event logging */ - spin_lock_init(&irnet_events.spinlock); - -#ifdef CONFIG_PROC_FS - /* Add a /proc file for irnet infos */ - proc_create("irnet", 0, proc_irda, &irnet_proc_fops); -#endif /* CONFIG_PROC_FS */ - - /* Setup the IrNET server */ - err = irnet_setup_server(); - - if(!err) - /* We are no longer functional... */ - irnet_server.running = 1; - - DEXIT(MODULE_TRACE, "\n"); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Cleanup at exit... - */ -void __exit -irda_irnet_cleanup(void) -{ - DENTER(MODULE_TRACE, "()\n"); - - /* We are no longer there... */ - irnet_server.running = 0; - -#ifdef CONFIG_PROC_FS - /* Remove our /proc file */ - remove_proc_entry("irnet", proc_irda); -#endif /* CONFIG_PROC_FS */ - - /* Remove our IrNET server from existence */ - irnet_destroy_server(); - - /* Remove all instances of IrNET socket still present */ - hashbin_delete(irnet_server.list, (FREE_FUNC) irda_irnet_destroy); - - DEXIT(MODULE_TRACE, "\n"); -} diff --git a/net/irda/irnet/irnet_irda.h b/net/irda/irnet/irnet_irda.h deleted file mode 100644 index 3e408952a3f1..000000000000 --- a/net/irda/irnet/irnet_irda.h +++ /dev/null @@ -1,178 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file contains all definitions and declarations necessary for the - * IRDA part of the IrNET module (dealing with IrTTP, IrIAS and co). - * This file is a private header, so other modules don't want to know - * what's in there... - */ - -#ifndef IRNET_IRDA_H -#define IRNET_IRDA_H - -/***************************** INCLUDES *****************************/ -/* Please add other headers in irnet.h */ - -#include "irnet.h" /* Module global include */ - -/************************ CONSTANTS & MACROS ************************/ - -/* - * Name of the service (socket name) used by IrNET - */ -/* IAS object name (or part of it) */ -#define IRNET_SERVICE_NAME "IrNetv1" -/* IAS attribute */ -#define IRNET_IAS_VALUE "IrDA:TinyTP:LsapSel" -/* LMP notify name for client (only for /proc/net/irda/irlmp) */ -#define IRNET_NOTIFY_NAME "IrNET socket" -/* LMP notify name for server (only for /proc/net/irda/irlmp) */ -#define IRNET_NOTIFY_NAME_SERV "IrNET server" - -/****************************** TYPES ******************************/ - -/* - * This is the main structure where we store all the data pertaining to - * the IrNET server (listen for connection requests) and the root - * of the IrNET socket list - */ -typedef struct irnet_root -{ - irnet_socket s; /* To pretend we are a client... */ - - /* Generic stuff */ - int magic; /* Paranoia */ - int running; /* Are we operational ? */ - - /* Link list of all IrNET instances opened */ - hashbin_t * list; - spinlock_t spinlock; /* Serialize access to the list */ - /* Note : the way hashbin has been designed is absolutely not - * reentrant, beware... So, we blindly protect all with spinlock */ - - /* Handle for the hint bit advertised in IrLMP */ - void * skey; - - /* Server socket part */ - struct ias_object * ias_obj; /* Our service name + lsap in IAS */ - -} irnet_root; - - -/**************************** PROTOTYPES ****************************/ - -/* ----------------------- CONTROL CHANNEL ----------------------- */ -static void - irnet_post_event(irnet_socket *, - irnet_event, - __u32, - __u32, - char *, - __u16); -/* ----------------------- IRDA SUBROUTINES ----------------------- */ -static inline int - irnet_open_tsap(irnet_socket *); -static inline __u8 - irnet_ias_to_tsap(irnet_socket *, - int, - struct ias_value *); -static inline int - irnet_find_lsap_sel(irnet_socket *); -static inline int - irnet_connect_tsap(irnet_socket *); -static inline int - irnet_discover_next_daddr(irnet_socket *); -static inline int - irnet_discover_daddr_and_lsap_sel(irnet_socket *); -static inline int - irnet_dname_to_daddr(irnet_socket *); -/* ------------------------ SERVER SOCKET ------------------------ */ -static inline int - irnet_daddr_to_dname(irnet_socket *); -static inline irnet_socket * - irnet_find_socket(irnet_socket *); -static inline int - irnet_connect_socket(irnet_socket *, - irnet_socket *, - struct qos_info *, - __u32, - __u8); -static inline void - irnet_disconnect_server(irnet_socket *, - struct sk_buff *); -static inline int - irnet_setup_server(void); -static inline void - irnet_destroy_server(void); -/* ---------------------- IRDA-TTP CALLBACKS ---------------------- */ -static int - irnet_data_indication(void *, /* instance */ - void *, /* sap */ - struct sk_buff *); -static void - irnet_disconnect_indication(void *, - void *, - LM_REASON, - struct sk_buff *); -static void - irnet_connect_confirm(void *, - void *, - struct qos_info *, - __u32, - __u8, - struct sk_buff *); -static void - irnet_flow_indication(void *, - void *, - LOCAL_FLOW); -static void - irnet_status_indication(void *, - LINK_STATUS, - LOCK_STATUS); -static void - irnet_connect_indication(void *, - void *, - struct qos_info *, - __u32, - __u8, - struct sk_buff *); -/* -------------------- IRDA-IAS/LMP CALLBACKS -------------------- */ -static void - irnet_getvalue_confirm(int, - __u16, - struct ias_value *, - void *); -static void - irnet_discovervalue_confirm(int, - __u16, - struct ias_value *, - void *); -#ifdef DISCOVERY_EVENTS -static void - irnet_discovery_indication(discinfo_t *, - DISCOVERY_MODE, - void *); -static void - irnet_expiry_indication(discinfo_t *, - DISCOVERY_MODE, - void *); -#endif - -/**************************** VARIABLES ****************************/ - -/* - * The IrNET server. Listen to connection requests and co... - */ -static struct irnet_root irnet_server; - -/* Control channel stuff (note : extern) */ -struct irnet_ctrl_channel irnet_events; - -/* The /proc/net/irda directory, defined elsewhere... */ -#ifdef CONFIG_PROC_FS -extern struct proc_dir_entry *proc_irda; -#endif /* CONFIG_PROC_FS */ - -#endif /* IRNET_IRDA_H */ diff --git a/net/irda/irnet/irnet_ppp.c b/net/irda/irnet/irnet_ppp.c deleted file mode 100644 index 7025dcb853d0..000000000000 --- a/net/irda/irnet/irnet_ppp.c +++ /dev/null @@ -1,1189 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file implement the PPP interface and /dev/irnet character device. - * The PPP interface hook to the ppp_generic module, handle all our - * relationship to the PPP code in the kernel (and by extension to pppd), - * and exchange PPP frames with this module (send/receive). - * The /dev/irnet device is used primarily for 2 functions : - * 1) as a stub for pppd (the ppp daemon), so that we can appropriately - * generate PPP sessions (we pretend we are a tty). - * 2) as a control channel (write commands, read events) - */ - -#include -#include - -#include "irnet_ppp.h" /* Private header */ -/* Please put other headers in irnet.h - Thanks */ - -/* Generic PPP callbacks (to call us) */ -static const struct ppp_channel_ops irnet_ppp_ops = { - .start_xmit = ppp_irnet_send, - .ioctl = ppp_irnet_ioctl -}; - -/************************* CONTROL CHANNEL *************************/ -/* - * When a pppd instance is not active on /dev/irnet, it acts as a control - * channel. - * Writing allow to set up the IrDA destination of the IrNET channel, - * and any application may be read events happening in IrNET... - */ - -/*------------------------------------------------------------------*/ -/* - * Write is used to send a command to configure a IrNET channel - * before it is open by pppd. The syntax is : "command argument" - * Currently there is only two defined commands : - * o name : set the requested IrDA nickname of the IrNET peer. - * o addr : set the requested IrDA address of the IrNET peer. - * Note : the code is crude, but effective... - */ -static inline ssize_t -irnet_ctrl_write(irnet_socket * ap, - const char __user *buf, - size_t count) -{ - char command[IRNET_MAX_COMMAND]; - char * start; /* Current command being processed */ - char * next; /* Next command to process */ - int length; /* Length of current command */ - - DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count); - - /* Check for overflow... */ - DABORT(count >= IRNET_MAX_COMMAND, -ENOMEM, - CTRL_ERROR, "Too much data !!!\n"); - - /* Get the data in the driver */ - if(copy_from_user(command, buf, count)) - { - DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); - return -EFAULT; - } - - /* Safe terminate the string */ - command[count] = '\0'; - DEBUG(CTRL_INFO, "Command line received is ``%s'' (%zd).\n", - command, count); - - /* Check every commands in the command line */ - next = command; - while(next != NULL) - { - /* Look at the next command */ - start = next; - - /* Scrap whitespaces before the command */ - start = skip_spaces(start); - - /* ',' is our command separator */ - next = strchr(start, ','); - if(next) - { - *next = '\0'; /* Terminate command */ - length = next - start; /* Length */ - next++; /* Skip the '\0' */ - } - else - length = strlen(start); - - DEBUG(CTRL_INFO, "Found command ``%s'' (%d).\n", start, length); - - /* Check if we recognised one of the known command - * We can't use "switch" with strings, so hack with "continue" */ - - /* First command : name -> Requested IrDA nickname */ - if(!strncmp(start, "name", 4)) - { - /* Copy the name only if is included and not "any" */ - if((length > 5) && (strcmp(start + 5, "any"))) - { - /* Strip out trailing whitespaces */ - while(isspace(start[length - 1])) - length--; - - DABORT(length < 5 || length > NICKNAME_MAX_LEN + 5, - -EINVAL, CTRL_ERROR, "Invalid nickname.\n"); - - /* Copy the name for later reuse */ - memcpy(ap->rname, start + 5, length - 5); - ap->rname[length - 5] = '\0'; - } - else - ap->rname[0] = '\0'; - DEBUG(CTRL_INFO, "Got rname = ``%s''\n", ap->rname); - - /* Restart the loop */ - continue; - } - - /* Second command : addr, daddr -> Requested IrDA destination address - * Also process : saddr -> Requested IrDA source address */ - if((!strncmp(start, "addr", 4)) || - (!strncmp(start, "daddr", 5)) || - (!strncmp(start, "saddr", 5))) - { - __u32 addr = DEV_ADDR_ANY; - - /* Copy the address only if is included and not "any" */ - if((length > 5) && (strcmp(start + 5, "any"))) - { - char * begp = start + 5; - char * endp; - - /* Scrap whitespaces before the command */ - begp = skip_spaces(begp); - - /* Convert argument to a number (last arg is the base) */ - addr = simple_strtoul(begp, &endp, 16); - /* Has it worked ? (endp should be start + length) */ - DABORT(endp <= (start + 5), -EINVAL, - CTRL_ERROR, "Invalid address.\n"); - } - /* Which type of address ? */ - if(start[0] == 's') - { - /* Save it */ - ap->rsaddr = addr; - DEBUG(CTRL_INFO, "Got rsaddr = %08x\n", ap->rsaddr); - } - else - { - /* Save it */ - ap->rdaddr = addr; - DEBUG(CTRL_INFO, "Got rdaddr = %08x\n", ap->rdaddr); - } - - /* Restart the loop */ - continue; - } - - /* Other possible command : connect N (number of retries) */ - - /* No command matched -> Failed... */ - DABORT(1, -EINVAL, CTRL_ERROR, "Not a recognised IrNET command.\n"); - } - - /* Success : we have parsed all commands successfully */ - return count; -} - -#ifdef INITIAL_DISCOVERY -/*------------------------------------------------------------------*/ -/* - * Function irnet_get_discovery_log (self) - * - * Query the content on the discovery log if not done - * - * This function query the current content of the discovery log - * at the startup of the event channel and save it in the internal struct. - */ -static void -irnet_get_discovery_log(irnet_socket * ap) -{ - __u16 mask = irlmp_service_to_hint(S_LAN); - - /* Ask IrLMP for the current discovery log */ - ap->discoveries = irlmp_get_discoveries(&ap->disco_number, mask, - DISCOVERY_DEFAULT_SLOTS); - - /* Check if the we got some results */ - if(ap->discoveries == NULL) - ap->disco_number = -1; - - DEBUG(CTRL_INFO, "Got the log (0x%p), size is %d\n", - ap->discoveries, ap->disco_number); -} - -/*------------------------------------------------------------------*/ -/* - * Function irnet_read_discovery_log (self, event) - * - * Read the content on the discovery log - * - * This function dump the current content of the discovery log - * at the startup of the event channel. - * Return 1 if wrote an event on the control channel... - * - * State of the ap->disco_XXX variables : - * Socket creation : discoveries = NULL ; disco_index = 0 ; disco_number = 0 - * While reading : discoveries = ptr ; disco_index = X ; disco_number = Y - * After reading : discoveries = NULL ; disco_index = Y ; disco_number = -1 - */ -static inline int -irnet_read_discovery_log(irnet_socket *ap, char *event, int buf_size) -{ - int done_event = 0; - - DENTER(CTRL_TRACE, "(ap=0x%p, event=0x%p)\n", - ap, event); - - /* Test if we have some work to do or we have already finished */ - if(ap->disco_number == -1) - { - DEBUG(CTRL_INFO, "Already done\n"); - return 0; - } - - /* Test if it's the first time and therefore we need to get the log */ - if(ap->discoveries == NULL) - irnet_get_discovery_log(ap); - - /* Check if we have more item to dump */ - if(ap->disco_index < ap->disco_number) - { - /* Write an event */ - snprintf(event, buf_size, - "Found %08x (%s) behind %08x {hints %02X-%02X}\n", - ap->discoveries[ap->disco_index].daddr, - ap->discoveries[ap->disco_index].info, - ap->discoveries[ap->disco_index].saddr, - ap->discoveries[ap->disco_index].hints[0], - ap->discoveries[ap->disco_index].hints[1]); - DEBUG(CTRL_INFO, "Writing discovery %d : %s\n", - ap->disco_index, ap->discoveries[ap->disco_index].info); - - /* We have an event */ - done_event = 1; - /* Next discovery */ - ap->disco_index++; - } - - /* Check if we have done the last item */ - if(ap->disco_index >= ap->disco_number) - { - /* No more items : remove the log and signal termination */ - DEBUG(CTRL_INFO, "Cleaning up log (0x%p)\n", - ap->discoveries); - if(ap->discoveries != NULL) - { - /* Cleanup our copy of the discovery log */ - kfree(ap->discoveries); - ap->discoveries = NULL; - } - ap->disco_number = -1; - } - - return done_event; -} -#endif /* INITIAL_DISCOVERY */ - -/*------------------------------------------------------------------*/ -/* - * Read is used to get IrNET events - */ -static inline ssize_t -irnet_ctrl_read(irnet_socket * ap, - struct file * file, - char __user * buf, - size_t count) -{ - DECLARE_WAITQUEUE(wait, current); - char event[75]; - ssize_t ret = 0; - - DENTER(CTRL_TRACE, "(ap=0x%p, count=%zd)\n", ap, count); - -#ifdef INITIAL_DISCOVERY - /* Check if we have read the log */ - if (irnet_read_discovery_log(ap, event, sizeof(event))) - { - count = min(strlen(event), count); - if (copy_to_user(buf, event, count)) - { - DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); - return -EFAULT; - } - - DEXIT(CTRL_TRACE, "\n"); - return count; - } -#endif /* INITIAL_DISCOVERY */ - - /* Put ourselves on the wait queue to be woken up */ - add_wait_queue(&irnet_events.rwait, &wait); - set_current_state(TASK_INTERRUPTIBLE); - for(;;) - { - /* If there is unread events */ - ret = 0; - if(ap->event_index != irnet_events.index) - break; - ret = -EAGAIN; - if(file->f_flags & O_NONBLOCK) - break; - ret = -ERESTARTSYS; - if(signal_pending(current)) - break; - /* Yield and wait to be woken up */ - schedule(); - } - __set_current_state(TASK_RUNNING); - remove_wait_queue(&irnet_events.rwait, &wait); - - /* Did we got it ? */ - if(ret != 0) - { - /* No, return the error code */ - DEXIT(CTRL_TRACE, " - ret %zd\n", ret); - return ret; - } - - /* Which event is it ? */ - switch(irnet_events.log[ap->event_index].event) - { - case IRNET_DISCOVER: - snprintf(event, sizeof(event), - "Discovered %08x (%s) behind %08x {hints %02X-%02X}\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].saddr, - irnet_events.log[ap->event_index].hints.byte[0], - irnet_events.log[ap->event_index].hints.byte[1]); - break; - case IRNET_EXPIRE: - snprintf(event, sizeof(event), - "Expired %08x (%s) behind %08x {hints %02X-%02X}\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].saddr, - irnet_events.log[ap->event_index].hints.byte[0], - irnet_events.log[ap->event_index].hints.byte[1]); - break; - case IRNET_CONNECT_TO: - snprintf(event, sizeof(event), "Connected to %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_CONNECT_FROM: - snprintf(event, sizeof(event), "Connection from %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_REQUEST_FROM: - snprintf(event, sizeof(event), "Request from %08x (%s) behind %08x\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].saddr); - break; - case IRNET_NOANSWER_FROM: - snprintf(event, sizeof(event), "No-answer from %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_BLOCKED_LINK: - snprintf(event, sizeof(event), "Blocked link with %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_DISCONNECT_FROM: - snprintf(event, sizeof(event), "Disconnection from %08x (%s) on ppp%d\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name, - irnet_events.log[ap->event_index].unit); - break; - case IRNET_DISCONNECT_TO: - snprintf(event, sizeof(event), "Disconnected to %08x (%s)\n", - irnet_events.log[ap->event_index].daddr, - irnet_events.log[ap->event_index].name); - break; - default: - snprintf(event, sizeof(event), "Bug\n"); - } - /* Increment our event index */ - ap->event_index = (ap->event_index + 1) % IRNET_MAX_EVENTS; - - DEBUG(CTRL_INFO, "Event is :%s", event); - - count = min(strlen(event), count); - if (copy_to_user(buf, event, count)) - { - DERROR(CTRL_ERROR, "Invalid user space pointer.\n"); - return -EFAULT; - } - - DEXIT(CTRL_TRACE, "\n"); - return count; -} - -/*------------------------------------------------------------------*/ -/* - * Poll : called when someone do a select on /dev/irnet. - * Just check if there are new events... - */ -static inline unsigned int -irnet_ctrl_poll(irnet_socket * ap, - struct file * file, - poll_table * wait) -{ - unsigned int mask; - - DENTER(CTRL_TRACE, "(ap=0x%p)\n", ap); - - poll_wait(file, &irnet_events.rwait, wait); - mask = POLLOUT | POLLWRNORM; - /* If there is unread events */ - if(ap->event_index != irnet_events.index) - mask |= POLLIN | POLLRDNORM; -#ifdef INITIAL_DISCOVERY - if(ap->disco_number != -1) - { - /* Test if it's the first time and therefore we need to get the log */ - if(ap->discoveries == NULL) - irnet_get_discovery_log(ap); - /* Recheck */ - if(ap->disco_number != -1) - mask |= POLLIN | POLLRDNORM; - } -#endif /* INITIAL_DISCOVERY */ - - DEXIT(CTRL_TRACE, " - mask=0x%X\n", mask); - return mask; -} - - -/*********************** FILESYSTEM CALLBACKS ***********************/ -/* - * Implement the usual open, read, write functions that will be called - * by the file system when some action is performed on /dev/irnet. - * Most of those actions will in fact be performed by "pppd" or - * the control channel, we just act as a redirector... - */ - -/*------------------------------------------------------------------*/ -/* - * Open : when somebody open /dev/irnet - * We basically create a new instance of irnet and initialise it. - */ -static int -dev_irnet_open(struct inode * inode, - struct file * file) -{ - struct irnet_socket * ap; - int err; - - DENTER(FS_TRACE, "(file=0x%p)\n", file); - -#ifdef SECURE_DEVIRNET - /* This could (should?) be enforced by the permissions on /dev/irnet. */ - if(!capable(CAP_NET_ADMIN)) - return -EPERM; -#endif /* SECURE_DEVIRNET */ - - /* Allocate a private structure for this IrNET instance */ - ap = kzalloc(sizeof(*ap), GFP_KERNEL); - DABORT(ap == NULL, -ENOMEM, FS_ERROR, "Can't allocate struct irnet...\n"); - - /* initialize the irnet structure */ - ap->file = file; - - /* PPP channel setup */ - ap->ppp_open = 0; - ap->chan.private = ap; - ap->chan.ops = &irnet_ppp_ops; - ap->chan.mtu = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN); - ap->chan.hdrlen = 2 + TTP_MAX_HEADER; /* for A/C + Max IrDA hdr */ - /* PPP parameters */ - ap->mru = (2048 - TTP_MAX_HEADER - 2 - PPP_HDRLEN); - ap->xaccm[0] = ~0U; - ap->xaccm[3] = 0x60000000U; - ap->raccm = ~0U; - - /* Setup the IrDA part... */ - err = irda_irnet_create(ap); - if(err) - { - DERROR(FS_ERROR, "Can't setup IrDA link...\n"); - kfree(ap); - - return err; - } - - /* For the control channel */ - ap->event_index = irnet_events.index; /* Cancel all past events */ - - mutex_init(&ap->lock); - - /* Put our stuff where we will be able to find it later */ - file->private_data = ap; - - DEXIT(FS_TRACE, " - ap=0x%p\n", ap); - - return 0; -} - - -/*------------------------------------------------------------------*/ -/* - * Close : when somebody close /dev/irnet - * Destroy the instance of /dev/irnet - */ -static int -dev_irnet_close(struct inode * inode, - struct file * file) -{ - irnet_socket * ap = file->private_data; - - DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n", - file, ap); - DABORT(ap == NULL, 0, FS_ERROR, "ap is NULL !!!\n"); - - /* Detach ourselves */ - file->private_data = NULL; - - /* Close IrDA stuff */ - irda_irnet_destroy(ap); - - /* Disconnect from the generic PPP layer if not already done */ - if(ap->ppp_open) - { - DERROR(FS_ERROR, "Channel still registered - deregistering !\n"); - ap->ppp_open = 0; - ppp_unregister_channel(&ap->chan); - } - - kfree(ap); - - DEXIT(FS_TRACE, "\n"); - return 0; -} - -/*------------------------------------------------------------------*/ -/* - * Write does nothing. - * (we receive packet from ppp_generic through ppp_irnet_send()) - */ -static ssize_t -dev_irnet_write(struct file * file, - const char __user *buf, - size_t count, - loff_t * ppos) -{ - irnet_socket * ap = file->private_data; - - DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n", - file, ap, count); - DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); - - /* If we are connected to ppp_generic, let it handle the job */ - if(ap->ppp_open) - return -EAGAIN; - else - return irnet_ctrl_write(ap, buf, count); -} - -/*------------------------------------------------------------------*/ -/* - * Read doesn't do much either. - * (pppd poll us, but ultimately reads through /dev/ppp) - */ -static ssize_t -dev_irnet_read(struct file * file, - char __user * buf, - size_t count, - loff_t * ppos) -{ - irnet_socket * ap = file->private_data; - - DPASS(FS_TRACE, "(file=0x%p, ap=0x%p, count=%zd)\n", - file, ap, count); - DABORT(ap == NULL, -ENXIO, FS_ERROR, "ap is NULL !!!\n"); - - /* If we are connected to ppp_generic, let it handle the job */ - if(ap->ppp_open) - return -EAGAIN; - else - return irnet_ctrl_read(ap, file, buf, count); -} - -/*------------------------------------------------------------------*/ -/* - * Poll : called when someone do a select on /dev/irnet - */ -static unsigned int -dev_irnet_poll(struct file * file, - poll_table * wait) -{ - irnet_socket * ap = file->private_data; - unsigned int mask; - - DENTER(FS_TRACE, "(file=0x%p, ap=0x%p)\n", - file, ap); - - mask = POLLOUT | POLLWRNORM; - DABORT(ap == NULL, mask, FS_ERROR, "ap is NULL !!!\n"); - - /* If we are connected to ppp_generic, let it handle the job */ - if(!ap->ppp_open) - mask |= irnet_ctrl_poll(ap, file, wait); - - DEXIT(FS_TRACE, " - mask=0x%X\n", mask); - return mask; -} - -/*------------------------------------------------------------------*/ -/* - * IOCtl : Called when someone does some ioctls on /dev/irnet - * This is the way pppd configure us and control us while the PPP - * instance is active. - */ -static long -dev_irnet_ioctl( - struct file * file, - unsigned int cmd, - unsigned long arg) -{ - irnet_socket * ap = file->private_data; - int err; - int val; - void __user *argp = (void __user *)arg; - - DENTER(FS_TRACE, "(file=0x%p, ap=0x%p, cmd=0x%X)\n", - file, ap, cmd); - - /* Basic checks... */ - DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); -#ifdef SECURE_DEVIRNET - if(!capable(CAP_NET_ADMIN)) - return -EPERM; -#endif /* SECURE_DEVIRNET */ - - err = -EFAULT; - switch(cmd) - { - /* Set discipline (should be N_SYNC_PPP or N_TTY) */ - case TIOCSETD: - if(get_user(val, (int __user *)argp)) - break; - if((val == N_SYNC_PPP) || (val == N_PPP)) - { - DEBUG(FS_INFO, "Entering PPP discipline.\n"); - /* PPP channel setup (ap->chan in configured in dev_irnet_open())*/ - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - err = ppp_register_channel(&ap->chan); - if(err == 0) - { - /* Our ppp side is active */ - ap->ppp_open = 1; - - DEBUG(FS_INFO, "Trying to establish a connection.\n"); - /* Setup the IrDA link now - may fail... */ - irda_irnet_connect(ap); - } - else - DERROR(FS_ERROR, "Can't setup PPP channel...\n"); - - mutex_unlock(&ap->lock); - } - else - { - /* In theory, should be N_TTY */ - DEBUG(FS_INFO, "Exiting PPP discipline.\n"); - /* Disconnect from the generic PPP layer */ - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - if(ap->ppp_open) - { - ap->ppp_open = 0; - ppp_unregister_channel(&ap->chan); - } - else - DERROR(FS_ERROR, "Channel not registered !\n"); - err = 0; - - mutex_unlock(&ap->lock); - } - break; - - /* Query PPP channel and unit number */ - case PPPIOCGCHAN: - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - if(ap->ppp_open && !put_user(ppp_channel_index(&ap->chan), - (int __user *)argp)) - err = 0; - - mutex_unlock(&ap->lock); - break; - case PPPIOCGUNIT: - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - if(ap->ppp_open && !put_user(ppp_unit_number(&ap->chan), - (int __user *)argp)) - err = 0; - - mutex_unlock(&ap->lock); - break; - - /* All these ioctls can be passed both directly and from ppp_generic, - * so we just deal with them in one place... - */ - case PPPIOCGFLAGS: - case PPPIOCSFLAGS: - case PPPIOCGASYNCMAP: - case PPPIOCSASYNCMAP: - case PPPIOCGRASYNCMAP: - case PPPIOCSRASYNCMAP: - case PPPIOCGXASYNCMAP: - case PPPIOCSXASYNCMAP: - case PPPIOCGMRU: - case PPPIOCSMRU: - DEBUG(FS_INFO, "Standard PPP ioctl.\n"); - if(!capable(CAP_NET_ADMIN)) - err = -EPERM; - else { - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - - err = ppp_irnet_ioctl(&ap->chan, cmd, arg); - - mutex_unlock(&ap->lock); - } - break; - - /* TTY IOCTLs : Pretend that we are a tty, to keep pppd happy */ - /* Get termios */ - case TCGETS: - DEBUG(FS_INFO, "Get termios.\n"); - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - -#ifndef TCGETS2 - if(!kernel_termios_to_user_termios((struct termios __user *)argp, &ap->termios)) - err = 0; -#else - if(kernel_termios_to_user_termios_1((struct termios __user *)argp, &ap->termios)) - err = 0; -#endif - - mutex_unlock(&ap->lock); - break; - /* Set termios */ - case TCSETSF: - DEBUG(FS_INFO, "Set termios.\n"); - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - -#ifndef TCGETS2 - if(!user_termios_to_kernel_termios(&ap->termios, (struct termios __user *)argp)) - err = 0; -#else - if(!user_termios_to_kernel_termios_1(&ap->termios, (struct termios __user *)argp)) - err = 0; -#endif - - mutex_unlock(&ap->lock); - break; - - /* Set DTR/RTS */ - case TIOCMBIS: - case TIOCMBIC: - /* Set exclusive/non-exclusive mode */ - case TIOCEXCL: - case TIOCNXCL: - DEBUG(FS_INFO, "TTY compatibility.\n"); - err = 0; - break; - - case TCGETA: - DEBUG(FS_INFO, "TCGETA\n"); - break; - - case TCFLSH: - DEBUG(FS_INFO, "TCFLSH\n"); - /* Note : this will flush buffers in PPP, so it *must* be done - * We should also worry that we don't accept junk here and that - * we get rid of our own buffers */ -#ifdef FLUSH_TO_PPP - if (mutex_lock_interruptible(&ap->lock)) - return -EINTR; - ppp_output_wakeup(&ap->chan); - mutex_unlock(&ap->lock); -#endif /* FLUSH_TO_PPP */ - err = 0; - break; - - case FIONREAD: - DEBUG(FS_INFO, "FIONREAD\n"); - val = 0; - if(put_user(val, (int __user *)argp)) - break; - err = 0; - break; - - default: - DERROR(FS_ERROR, "Unsupported ioctl (0x%X)\n", cmd); - err = -ENOTTY; - } - - DEXIT(FS_TRACE, " - err = 0x%X\n", err); - return err; -} - -/************************** PPP CALLBACKS **************************/ -/* - * This are the functions that the generic PPP driver in the kernel - * will call to communicate to us. - */ - -/*------------------------------------------------------------------*/ -/* - * Prepare the ppp frame for transmission over the IrDA socket. - * We make sure that the header space is enough, and we change ppp header - * according to flags passed by pppd. - * This is not a callback, but just a helper function used in ppp_irnet_send() - */ -static inline struct sk_buff * -irnet_prepare_skb(irnet_socket * ap, - struct sk_buff * skb) -{ - unsigned char * data; - int proto; /* PPP protocol */ - int islcp; /* Protocol == LCP */ - int needaddr; /* Need PPP address */ - - DENTER(PPP_TRACE, "(ap=0x%p, skb=0x%p)\n", - ap, skb); - - /* Extract PPP protocol from the frame */ - data = skb->data; - proto = (data[0] << 8) + data[1]; - - /* LCP packets with codes between 1 (configure-request) - * and 7 (code-reject) must be sent as though no options - * have been negotiated. */ - islcp = (proto == PPP_LCP) && (1 <= data[2]) && (data[2] <= 7); - - /* compress protocol field if option enabled */ - if((data[0] == 0) && (ap->flags & SC_COMP_PROT) && (!islcp)) - skb_pull(skb,1); - - /* Check if we need address/control fields */ - needaddr = 2*((ap->flags & SC_COMP_AC) == 0 || islcp); - - /* Is the skb headroom large enough to contain all IrDA-headers? */ - if((skb_headroom(skb) < (ap->max_header_size + needaddr)) || - (skb_shared(skb))) - { - struct sk_buff * new_skb; - - DEBUG(PPP_INFO, "Reallocating skb\n"); - - /* Create a new skb */ - new_skb = skb_realloc_headroom(skb, ap->max_header_size + needaddr); - - /* We have to free the original skb anyway */ - dev_kfree_skb(skb); - - /* Did the realloc succeed ? */ - DABORT(new_skb == NULL, NULL, PPP_ERROR, "Could not realloc skb\n"); - - /* Use the new skb instead */ - skb = new_skb; - } - - /* prepend address/control fields if necessary */ - if(needaddr) - { - skb_push(skb, 2); - skb->data[0] = PPP_ALLSTATIONS; - skb->data[1] = PPP_UI; - } - - DEXIT(PPP_TRACE, "\n"); - - return skb; -} - -/*------------------------------------------------------------------*/ -/* - * Send a packet to the peer over the IrTTP connection. - * Returns 1 iff the packet was accepted. - * Returns 0 iff packet was not consumed. - * If the packet was not accepted, we will call ppp_output_wakeup - * at some later time to reactivate flow control in ppp_generic. - */ -static int -ppp_irnet_send(struct ppp_channel * chan, - struct sk_buff * skb) -{ - irnet_socket * self = (struct irnet_socket *) chan->private; - int ret; - - DENTER(PPP_TRACE, "(channel=0x%p, ap/self=0x%p)\n", - chan, self); - - /* Check if things are somewhat valid... */ - DASSERT(self != NULL, 0, PPP_ERROR, "Self is NULL !!!\n"); - - /* Check if we are connected */ - if(!(test_bit(0, &self->ttp_open))) - { -#ifdef CONNECT_IN_SEND - /* Let's try to connect one more time... */ - /* Note : we won't be connected after this call, but we should be - * ready for next packet... */ - /* If we are already connecting, this will fail */ - irda_irnet_connect(self); -#endif /* CONNECT_IN_SEND */ - - DEBUG(PPP_INFO, "IrTTP not ready ! (%ld-%ld)\n", - self->ttp_open, self->ttp_connect); - - /* Note : we can either drop the packet or block the packet. - * - * Blocking the packet allow us a better connection time, - * because by calling ppp_output_wakeup() we can have - * ppp_generic resending the LCP request immediately to us, - * rather than waiting for one of pppd periodic transmission of - * LCP request. - * - * On the other hand, if we block all packet, all those periodic - * transmissions of pppd accumulate in ppp_generic, creating a - * backlog of LCP request. When we eventually connect later on, - * we have to transmit all this backlog before we can connect - * proper (if we don't timeout before). - * - * The current strategy is as follow : - * While we are attempting to connect, we block packets to get - * a better connection time. - * If we fail to connect, we drain the queue and start dropping packets - */ -#ifdef BLOCK_WHEN_CONNECT - /* If we are attempting to connect */ - if(test_bit(0, &self->ttp_connect)) - { - /* Blocking packet, ppp_generic will retry later */ - return 0; - } -#endif /* BLOCK_WHEN_CONNECT */ - - /* Dropping packet, pppd will retry later */ - dev_kfree_skb(skb); - return 1; - } - - /* Check if the queue can accept any packet, otherwise block */ - if(self->tx_flow != FLOW_START) - DRETURN(0, PPP_INFO, "IrTTP queue full (%d skbs)...\n", - skb_queue_len(&self->tsap->tx_queue)); - - /* Prepare ppp frame for transmission */ - skb = irnet_prepare_skb(self, skb); - DABORT(skb == NULL, 1, PPP_ERROR, "Prepare skb for Tx failed.\n"); - - /* Send the packet to IrTTP */ - ret = irttp_data_request(self->tsap, skb); - if(ret < 0) - { - /* - * > IrTTPs tx queue is full, so we just have to - * > drop the frame! You might think that we should - * > just return -1 and don't deallocate the frame, - * > but that is dangerous since it's possible that - * > we have replaced the original skb with a new - * > one with larger headroom, and that would really - * > confuse do_dev_queue_xmit() in dev.c! I have - * > tried :-) DB - * Correction : we verify the flow control above (self->tx_flow), - * so we come here only if IrTTP doesn't like the packet (empty, - * too large, IrTTP not connected). In those rare cases, it's ok - * to drop it, we don't want to see it here again... - * Jean II - */ - DERROR(PPP_ERROR, "IrTTP doesn't like this packet !!! (0x%X)\n", ret); - /* irttp_data_request already free the packet */ - } - - DEXIT(PPP_TRACE, "\n"); - return 1; /* Packet has been consumed */ -} - -/*------------------------------------------------------------------*/ -/* - * Take care of the ioctls that ppp_generic doesn't want to deal with... - * Note : we are also called from dev_irnet_ioctl(). - */ -static int -ppp_irnet_ioctl(struct ppp_channel * chan, - unsigned int cmd, - unsigned long arg) -{ - irnet_socket * ap = (struct irnet_socket *) chan->private; - int err; - int val; - u32 accm[8]; - void __user *argp = (void __user *)arg; - - DENTER(PPP_TRACE, "(channel=0x%p, ap=0x%p, cmd=0x%X)\n", - chan, ap, cmd); - - /* Basic checks... */ - DASSERT(ap != NULL, -ENXIO, PPP_ERROR, "ap is NULL...\n"); - - err = -EFAULT; - switch(cmd) - { - /* PPP flags */ - case PPPIOCGFLAGS: - val = ap->flags | ap->rbits; - if(put_user(val, (int __user *) argp)) - break; - err = 0; - break; - case PPPIOCSFLAGS: - if(get_user(val, (int __user *) argp)) - break; - ap->flags = val & ~SC_RCV_BITS; - ap->rbits = val & SC_RCV_BITS; - err = 0; - break; - - /* Async map stuff - all dummy to please pppd */ - case PPPIOCGASYNCMAP: - if(put_user(ap->xaccm[0], (u32 __user *) argp)) - break; - err = 0; - break; - case PPPIOCSASYNCMAP: - if(get_user(ap->xaccm[0], (u32 __user *) argp)) - break; - err = 0; - break; - case PPPIOCGRASYNCMAP: - if(put_user(ap->raccm, (u32 __user *) argp)) - break; - err = 0; - break; - case PPPIOCSRASYNCMAP: - if(get_user(ap->raccm, (u32 __user *) argp)) - break; - err = 0; - break; - case PPPIOCGXASYNCMAP: - if(copy_to_user(argp, ap->xaccm, sizeof(ap->xaccm))) - break; - err = 0; - break; - case PPPIOCSXASYNCMAP: - if(copy_from_user(accm, argp, sizeof(accm))) - break; - accm[2] &= ~0x40000000U; /* can't escape 0x5e */ - accm[3] |= 0x60000000U; /* must escape 0x7d, 0x7e */ - memcpy(ap->xaccm, accm, sizeof(ap->xaccm)); - err = 0; - break; - - /* Max PPP frame size */ - case PPPIOCGMRU: - if(put_user(ap->mru, (int __user *) argp)) - break; - err = 0; - break; - case PPPIOCSMRU: - if(get_user(val, (int __user *) argp)) - break; - if(val < PPP_MRU) - val = PPP_MRU; - ap->mru = val; - err = 0; - break; - - default: - DEBUG(PPP_INFO, "Unsupported ioctl (0x%X)\n", cmd); - err = -ENOIOCTLCMD; - } - - DEXIT(PPP_TRACE, " - err = 0x%X\n", err); - return err; -} - -/************************** INITIALISATION **************************/ -/* - * Module initialisation and all that jazz... - */ - -/*------------------------------------------------------------------*/ -/* - * Hook our device callbacks in the filesystem, to connect our code - * to /dev/irnet - */ -static inline int __init -ppp_irnet_init(void) -{ - int err = 0; - - DENTER(MODULE_TRACE, "()\n"); - - /* Allocate ourselves as a minor in the misc range */ - err = misc_register(&irnet_misc_device); - - DEXIT(MODULE_TRACE, "\n"); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Cleanup at exit... - */ -static inline void __exit -ppp_irnet_cleanup(void) -{ - DENTER(MODULE_TRACE, "()\n"); - - /* De-allocate /dev/irnet minor in misc range */ - misc_deregister(&irnet_misc_device); - - DEXIT(MODULE_TRACE, "\n"); -} - -/*------------------------------------------------------------------*/ -/* - * Module main entry point - */ -static int __init -irnet_init(void) -{ - int err; - - /* Initialise both parts... */ - err = irda_irnet_init(); - if(!err) - err = ppp_irnet_init(); - return err; -} - -/*------------------------------------------------------------------*/ -/* - * Module exit - */ -static void __exit -irnet_cleanup(void) -{ - irda_irnet_cleanup(); - ppp_irnet_cleanup(); -} - -/*------------------------------------------------------------------*/ -/* - * Module magic - */ -module_init(irnet_init); -module_exit(irnet_cleanup); -MODULE_AUTHOR("Jean Tourrilhes "); -MODULE_DESCRIPTION("IrNET : Synchronous PPP over IrDA"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS_CHARDEV(10, 187); diff --git a/net/irda/irnet/irnet_ppp.h b/net/irda/irnet/irnet_ppp.h deleted file mode 100644 index 32061442cc8e..000000000000 --- a/net/irda/irnet/irnet_ppp.h +++ /dev/null @@ -1,116 +0,0 @@ -/* - * IrNET protocol module : Synchronous PPP over an IrDA socket. - * - * Jean II - HPL `00 - - * - * This file contains all definitions and declarations necessary for the - * PPP part of the IrNET module. - * This file is a private header, so other modules don't want to know - * what's in there... - */ - -#ifndef IRNET_PPP_H -#define IRNET_PPP_H - -/***************************** INCLUDES *****************************/ - -#include "irnet.h" /* Module global include */ -#include - -/************************ CONSTANTS & MACROS ************************/ - -/* IrNET control channel stuff */ -#define IRNET_MAX_COMMAND 256 /* Max length of a command line */ - -/* PPP hardcore stuff */ - -/* Bits in rbits (PPP flags in irnet struct) */ -#define SC_RCV_BITS (SC_RCV_B7_1|SC_RCV_B7_0|SC_RCV_ODDP|SC_RCV_EVNP) - -/* Bit numbers in busy */ -#define XMIT_BUSY 0 -#define RECV_BUSY 1 -#define XMIT_WAKEUP 2 -#define XMIT_FULL 3 - -/* Queue management */ -#define PPPSYNC_MAX_RQLEN 32 /* arbitrary */ - -/****************************** TYPES ******************************/ - - -/**************************** PROTOTYPES ****************************/ - -/* ----------------------- CONTROL CHANNEL ----------------------- */ -static inline ssize_t - irnet_ctrl_write(irnet_socket *, - const char *, - size_t); -static inline ssize_t - irnet_ctrl_read(irnet_socket *, - struct file *, - char *, - size_t); -static inline unsigned int - irnet_ctrl_poll(irnet_socket *, - struct file *, - poll_table *); -/* ----------------------- CHARACTER DEVICE ----------------------- */ -static int - dev_irnet_open(struct inode *, /* fs callback : open */ - struct file *), - dev_irnet_close(struct inode *, - struct file *); -static ssize_t - dev_irnet_write(struct file *, - const char __user *, - size_t, - loff_t *), - dev_irnet_read(struct file *, - char __user *, - size_t, - loff_t *); -static unsigned int - dev_irnet_poll(struct file *, - poll_table *); -static long - dev_irnet_ioctl(struct file *, - unsigned int, - unsigned long); -/* ------------------------ PPP INTERFACE ------------------------ */ -static inline struct sk_buff * - irnet_prepare_skb(irnet_socket *, - struct sk_buff *); -static int - ppp_irnet_send(struct ppp_channel *, - struct sk_buff *); -static int - ppp_irnet_ioctl(struct ppp_channel *, - unsigned int, - unsigned long); - -/**************************** VARIABLES ****************************/ - -/* Filesystem callbacks (to call us) */ -static const struct file_operations irnet_device_fops = -{ - .owner = THIS_MODULE, - .read = dev_irnet_read, - .write = dev_irnet_write, - .poll = dev_irnet_poll, - .unlocked_ioctl = dev_irnet_ioctl, - .open = dev_irnet_open, - .release = dev_irnet_close, - .llseek = noop_llseek, - /* Also : llseek, readdir, mmap, flush, fsync, fasync, lock, readv, writev */ -}; - -/* Structure so that the misc major (drivers/char/misc.c) take care of us... */ -static struct miscdevice irnet_misc_device = -{ - .minor = IRNET_MINOR, - .name = "irnet", - .fops = &irnet_device_fops -}; - -#endif /* IRNET_PPP_H */ diff --git a/net/irda/irnetlink.c b/net/irda/irnetlink.c deleted file mode 100644 index 7fc340e574cf..000000000000 --- a/net/irda/irnetlink.c +++ /dev/null @@ -1,162 +0,0 @@ -/* - * IrDA netlink layer, for stack configuration. - * - * Copyright (c) 2007 Samuel Ortiz - * - * Partly based on the 802.11 nelink implementation - * (see net/wireless/nl80211.c) which is: - * Copyright 2006 Johannes Berg - * - * 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. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include - - - -static struct genl_family irda_nl_family; - -static struct net_device * ifname_to_netdev(struct net *net, struct genl_info *info) -{ - char * ifname; - - if (!info->attrs[IRDA_NL_ATTR_IFNAME]) - return NULL; - - ifname = nla_data(info->attrs[IRDA_NL_ATTR_IFNAME]); - - pr_debug("%s(): Looking for %s\n", __func__, ifname); - - return dev_get_by_name(net, ifname); -} - -static int irda_nl_set_mode(struct sk_buff *skb, struct genl_info *info) -{ - struct net_device * dev; - struct irlap_cb * irlap; - u32 mode; - - if (!info->attrs[IRDA_NL_ATTR_MODE]) - return -EINVAL; - - mode = nla_get_u32(info->attrs[IRDA_NL_ATTR_MODE]); - - pr_debug("%s(): Switching to mode: %d\n", __func__, mode); - - dev = ifname_to_netdev(&init_net, info); - if (!dev) - return -ENODEV; - - irlap = (struct irlap_cb *)dev->atalk_ptr; - if (!irlap) { - dev_put(dev); - return -ENODEV; - } - - irlap->mode = mode; - - dev_put(dev); - - return 0; -} - -static int irda_nl_get_mode(struct sk_buff *skb, struct genl_info *info) -{ - struct net_device * dev; - struct irlap_cb * irlap; - struct sk_buff *msg; - void *hdr; - int ret = -ENOBUFS; - - dev = ifname_to_netdev(&init_net, info); - if (!dev) - return -ENODEV; - - msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); - if (!msg) { - dev_put(dev); - return -ENOMEM; - } - - irlap = (struct irlap_cb *)dev->atalk_ptr; - if (!irlap) { - ret = -ENODEV; - goto err_out; - } - - hdr = genlmsg_put(msg, info->snd_portid, info->snd_seq, - &irda_nl_family, 0, IRDA_NL_CMD_GET_MODE); - if (hdr == NULL) { - ret = -EMSGSIZE; - goto err_out; - } - - if(nla_put_string(msg, IRDA_NL_ATTR_IFNAME, - dev->name)) - goto err_out; - - if(nla_put_u32(msg, IRDA_NL_ATTR_MODE, irlap->mode)) - goto err_out; - - genlmsg_end(msg, hdr); - - return genlmsg_reply(msg, info); - - err_out: - nlmsg_free(msg); - dev_put(dev); - - return ret; -} - -static const struct nla_policy irda_nl_policy[IRDA_NL_ATTR_MAX + 1] = { - [IRDA_NL_ATTR_IFNAME] = { .type = NLA_NUL_STRING, - .len = IFNAMSIZ-1 }, - [IRDA_NL_ATTR_MODE] = { .type = NLA_U32 }, -}; - -static const struct genl_ops irda_nl_ops[] = { - { - .cmd = IRDA_NL_CMD_SET_MODE, - .doit = irda_nl_set_mode, - .policy = irda_nl_policy, - .flags = GENL_ADMIN_PERM, - }, - { - .cmd = IRDA_NL_CMD_GET_MODE, - .doit = irda_nl_get_mode, - .policy = irda_nl_policy, - /* can be retrieved by unprivileged users */ - }, - -}; - -static struct genl_family irda_nl_family __ro_after_init = { - .name = IRDA_NL_NAME, - .hdrsize = 0, - .version = IRDA_NL_VERSION, - .maxattr = IRDA_NL_CMD_MAX, - .module = THIS_MODULE, - .ops = irda_nl_ops, - .n_ops = ARRAY_SIZE(irda_nl_ops), -}; - -int __init irda_nl_register(void) -{ - return genl_register_family(&irda_nl_family); -} - -void irda_nl_unregister(void) -{ - genl_unregister_family(&irda_nl_family); -} diff --git a/net/irda/irproc.c b/net/irda/irproc.c deleted file mode 100644 index 77cfdde9d82f..000000000000 --- a/net/irda/irproc.c +++ /dev/null @@ -1,96 +0,0 @@ -/********************************************************************* - * - * Filename: irproc.c - * Version: 1.0 - * Description: Various entries in the /proc file system - * Status: Experimental. - * Author: Thomas Davis, - * Created at: Sat Feb 21 21:33:24 1998 - * Modified at: Sun Nov 14 08:54:54 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-1999, Dag Brattli - * Copyright (c) 1998, Thomas Davis, , - * All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * I, Thomas Davis, provide no warranty for any of this software. - * This material is provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include - -#include -#include -#include - -extern const struct file_operations discovery_seq_fops; -extern const struct file_operations irlap_seq_fops; -extern const struct file_operations irlmp_seq_fops; -extern const struct file_operations irttp_seq_fops; -extern const struct file_operations irias_seq_fops; - -struct irda_entry { - const char *name; - const struct file_operations *fops; -}; - -struct proc_dir_entry *proc_irda; -EXPORT_SYMBOL(proc_irda); - -static const struct irda_entry irda_dirs[] = { - {"discovery", &discovery_seq_fops}, - {"irttp", &irttp_seq_fops}, - {"irlmp", &irlmp_seq_fops}, - {"irlap", &irlap_seq_fops}, - {"irias", &irias_seq_fops}, -}; - -/* - * Function irda_proc_register (void) - * - * Register irda entry in /proc file system - * - */ -void __init irda_proc_register(void) -{ - int i; - - proc_irda = proc_mkdir("irda", init_net.proc_net); - if (proc_irda == NULL) - return; - - for (i = 0; i < ARRAY_SIZE(irda_dirs); i++) - (void) proc_create(irda_dirs[i].name, 0, proc_irda, - irda_dirs[i].fops); -} - -/* - * Function irda_proc_unregister (void) - * - * Unregister irda entry in /proc file system - * - */ -void irda_proc_unregister(void) -{ - int i; - - if (proc_irda) { - for (i=0; i - * Created at: Tue Jun 9 13:29:31 1998 - * Modified at: Sun Dec 12 13:48:22 1999 - * Modified by: Dag Brattli - * Modified at: Thu Jan 4 14:29:10 CET 2001 - * Modified by: Marc Zyngier - * - * Copyright (C) 1998-1999, Aage Kvalnes - * Copyright (C) 1998, Dag Brattli, - * All Rights Reserved. - * - * This code is taken from the Vortex Operating System written by Aage - * Kvalnes. Aage has agreed that this code can use the GPL licence, - * although he does not use that licence in his own code. - * - * This copyright does however _not_ include the ELF hash() function - * which I currently don't know which licence or copyright it - * has. Please inform me if you know. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -/* - * NOTE : - * There are various problems with this package : - * o the hash function for ints is pathetic (but could be changed) - * o locking is sometime suspicious (especially during enumeration) - * o most users have only a few elements (== overhead) - * o most users never use search, so don't benefit from hashing - * Problem already fixed : - * o not 64 bit compliant (most users do hashv = (int) self) - * o hashbin_remove() is broken => use hashbin_remove_this() - * I think most users would be better served by a simple linked list - * (like include/linux/list.h) with a global spinlock per list. - * Jean II - */ - -/* - * Notes on the concurrent access to hashbin and other SMP issues - * ------------------------------------------------------------- - * Hashbins are very often in the IrDA stack a global repository of - * information, and therefore used in a very asynchronous manner following - * various events (driver calls, timers, user calls...). - * Therefore, very often it is highly important to consider the - * management of concurrent access to the hashbin and how to guarantee the - * consistency of the operations on it. - * - * First, we need to define the objective of locking : - * 1) Protect user data (content pointed by the hashbin) - * 2) Protect hashbin structure itself (linked list in each bin) - * - * OLD LOCKING - * ----------- - * - * The previous locking strategy, either HB_LOCAL or HB_GLOBAL were - * both inadequate in *both* aspect. - * o HB_GLOBAL was using a spinlock for each bin (local locking). - * o HB_LOCAL was disabling irq on *all* CPUs, so use a single - * global semaphore. - * The problems were : - * A) Global irq disabling is no longer supported by the kernel - * B) No protection for the hashbin struct global data - * o hashbin_delete() - * o hb_current - * C) No protection for user data in some cases - * - * A) HB_LOCAL use global irq disabling, so doesn't work on kernel - * 2.5.X. Even when it is supported (kernel 2.4.X and earlier), its - * performance is not satisfactory on SMP setups. Most hashbins were - * HB_LOCAL, so (A) definitely need fixing. - * B) HB_LOCAL could be modified to fix (B). However, because HB_GLOBAL - * lock only the individual bins, it will never be able to lock the - * global data, so can't do (B). - * C) Some functions return pointer to data that is still in the - * hashbin : - * o hashbin_find() - * o hashbin_get_first() - * o hashbin_get_next() - * As the data is still in the hashbin, it may be changed or free'd - * while the caller is examinimg the data. In those case, locking can't - * be done within the hashbin, but must include use of the data within - * the caller. - * The caller can easily do this with HB_LOCAL (just disable irqs). - * However, this is impossible with HB_GLOBAL because the caller has no - * way to know the proper bin, so don't know which spinlock to use. - * - * Quick summary : can no longer use HB_LOCAL, and HB_GLOBAL is - * fundamentally broken and will never work. - * - * NEW LOCKING - * ----------- - * - * To fix those problems, I've introduce a few changes in the - * hashbin locking : - * 1) New HB_LOCK scheme - * 2) hashbin->hb_spinlock - * 3) New hashbin usage policy - * - * HB_LOCK : - * ------- - * HB_LOCK is a locking scheme intermediate between the old HB_LOCAL - * and HB_GLOBAL. It uses a single spinlock to protect the whole content - * of the hashbin. As it is a single spinlock, it can protect the global - * data of the hashbin and not only the bins themselves. - * HB_LOCK can only protect some of the hashbin calls, so it only lock - * call that can be made 100% safe and leave other call unprotected. - * HB_LOCK in theory is slower than HB_GLOBAL, but as the hashbin - * content is always small contention is not high, so it doesn't matter - * much. HB_LOCK is probably faster than HB_LOCAL. - * - * hashbin->hb_spinlock : - * -------------------- - * The spinlock that HB_LOCK uses is available for caller, so that - * the caller can protect unprotected calls (see below). - * If the caller want to do entirely its own locking (HB_NOLOCK), he - * can do so and may use safely this spinlock. - * Locking is done like this : - * spin_lock_irqsave(&hashbin->hb_spinlock, flags); - * Releasing the lock : - * spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - * - * Safe & Protected calls : - * ---------------------- - * The following calls are safe or protected via HB_LOCK : - * o hashbin_new() -> safe - * o hashbin_delete() - * o hashbin_insert() - * o hashbin_remove_first() - * o hashbin_remove() - * o hashbin_remove_this() - * o HASHBIN_GET_SIZE() -> atomic - * - * The following calls only protect the hashbin itself : - * o hashbin_lock_find() - * o hashbin_find_next() - * - * Unprotected calls : - * ----------------- - * The following calls need to be protected by the caller : - * o hashbin_find() - * o hashbin_get_first() - * o hashbin_get_next() - * - * Locking Policy : - * -------------- - * If the hashbin is used only in a single thread of execution - * (explicitly or implicitely), you can use HB_NOLOCK - * If the calling module already provide concurrent access protection, - * you may use HB_NOLOCK. - * - * In all other cases, you need to use HB_LOCK and lock the hashbin - * every time before calling one of the unprotected calls. You also must - * use the pointer returned by the unprotected call within the locked - * region. - * - * Extra care for enumeration : - * -------------------------- - * hashbin_get_first() and hashbin_get_next() use the hashbin to - * store the current position, in hb_current. - * As long as the hashbin remains locked, this is safe. If you unlock - * the hashbin, the current position may change if anybody else modify - * or enumerate the hashbin. - * Summary : do the full enumeration while locked. - * - * Alternatively, you may use hashbin_find_next(). But, this will - * be slower, is more complex to use and doesn't protect the hashbin - * content. So, care is needed here as well. - * - * Other issues : - * ------------ - * I believe that we are overdoing it by using spin_lock_irqsave() - * and we should use only spin_lock_bh() or similar. But, I don't have - * the balls to try it out. - * Don't believe that because hashbin are now (somewhat) SMP safe - * that the rest of the code is. Higher layers tend to be safest, - * but LAP and LMP would need some serious dedicated love. - * - * Jean II - */ -#include -#include - -#include -#include - -/************************ QUEUE SUBROUTINES ************************/ - -/* - * Hashbin - */ -#define GET_HASHBIN(x) ( x & HASHBIN_MASK ) - -/* - * Function hash (name) - * - * This function hash the input string 'name' using the ELF hash - * function for strings. - */ -static __u32 hash( const char* name) -{ - __u32 h = 0; - __u32 g; - - while(*name) { - h = (h<<4) + *name++; - if ((g = (h & 0xf0000000))) - h ^=g>>24; - h &=~g; - } - return h; -} - -/* - * Function enqueue_first (queue, proc) - * - * Insert item first in queue. - * - */ -static void enqueue_first(irda_queue_t **queue, irda_queue_t* element) -{ - - /* - * Check if queue is empty. - */ - if ( *queue == NULL ) { - /* - * Queue is empty. Insert one element into the queue. - */ - element->q_next = element->q_prev = *queue = element; - - } else { - /* - * Queue is not empty. Insert element into front of queue. - */ - element->q_next = (*queue); - (*queue)->q_prev->q_next = element; - element->q_prev = (*queue)->q_prev; - (*queue)->q_prev = element; - (*queue) = element; - } -} - - -/* - * Function dequeue (queue) - * - * Remove first entry in queue - * - */ -static irda_queue_t *dequeue_first(irda_queue_t **queue) -{ - irda_queue_t *ret; - - pr_debug("dequeue_first()\n"); - - /* - * Set return value - */ - ret = *queue; - - if ( *queue == NULL ) { - /* - * Queue was empty. - */ - } else if ( (*queue)->q_next == *queue ) { - /* - * Queue only contained a single element. It will now be - * empty. - */ - *queue = NULL; - } else { - /* - * Queue contained several element. Remove the first one. - */ - (*queue)->q_prev->q_next = (*queue)->q_next; - (*queue)->q_next->q_prev = (*queue)->q_prev; - *queue = (*queue)->q_next; - } - - /* - * Return the removed entry (or NULL of queue was empty). - */ - return ret; -} - -/* - * Function dequeue_general (queue, element) - * - * - */ -static irda_queue_t *dequeue_general(irda_queue_t **queue, irda_queue_t* element) -{ - irda_queue_t *ret; - - pr_debug("dequeue_general()\n"); - - /* - * Set return value - */ - ret = *queue; - - if ( *queue == NULL ) { - /* - * Queue was empty. - */ - } else if ( (*queue)->q_next == *queue ) { - /* - * Queue only contained a single element. It will now be - * empty. - */ - *queue = NULL; - - } else { - /* - * Remove specific element. - */ - element->q_prev->q_next = element->q_next; - element->q_next->q_prev = element->q_prev; - if ( (*queue) == element) - (*queue) = element->q_next; - } - - /* - * Return the removed entry (or NULL of queue was empty). - */ - return ret; -} - -/************************ HASHBIN MANAGEMENT ************************/ - -/* - * Function hashbin_create ( type, name ) - * - * Create hashbin! - * - */ -hashbin_t *hashbin_new(int type) -{ - hashbin_t* hashbin; - - /* - * Allocate new hashbin - */ - hashbin = kzalloc(sizeof(*hashbin), GFP_ATOMIC); - if (!hashbin) - return NULL; - - /* - * Initialize structure - */ - hashbin->hb_type = type; - hashbin->magic = HB_MAGIC; - //hashbin->hb_current = NULL; - - /* Make sure all spinlock's are unlocked */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_init(&hashbin->hb_spinlock); - } - - return hashbin; -} -EXPORT_SYMBOL(hashbin_new); - - -/* - * Function hashbin_delete (hashbin, free_func) - * - * Destroy hashbin, the free_func can be a user supplied special routine - * for deallocating this structure if it's complex. If not the user can - * just supply kfree, which should take care of the job. - */ -int hashbin_delete( hashbin_t* hashbin, FREE_FUNC free_func) -{ - irda_queue_t* queue; - unsigned long flags = 0; - int i; - - IRDA_ASSERT(hashbin != NULL, return -1;); - IRDA_ASSERT(hashbin->magic == HB_MAGIC, return -1;); - - /* Synchronize */ - if (hashbin->hb_type & HB_LOCK) - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - - /* - * Free the entries in the hashbin, TODO: use hashbin_clear when - * it has been shown to work - */ - for (i = 0; i < HASHBIN_SIZE; i ++ ) { - while (1) { - queue = dequeue_first((irda_queue_t**) &hashbin->hb_queue[i]); - - if (!queue) - break; - - if (free_func) { - if (hashbin->hb_type & HB_LOCK) - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - free_func(queue); - if (hashbin->hb_type & HB_LOCK) - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } - } - } - - /* Cleanup local data */ - hashbin->hb_current = NULL; - hashbin->magic = ~HB_MAGIC; - - /* Release lock */ - if (hashbin->hb_type & HB_LOCK) - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - - /* - * Free the hashbin structure - */ - kfree(hashbin); - - return 0; -} -EXPORT_SYMBOL(hashbin_delete); - -/********************* HASHBIN LIST OPERATIONS *********************/ - -/* - * Function hashbin_insert (hashbin, entry, name) - * - * Insert an entry into the hashbin - * - */ -void hashbin_insert(hashbin_t* hashbin, irda_queue_t* entry, long hashv, - const char* name) -{ - unsigned long flags = 0; - int bin; - - IRDA_ASSERT( hashbin != NULL, return;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return;); - - /* - * Locate hashbin - */ - if ( name ) - hashv = hash( name ); - bin = GET_HASHBIN( hashv ); - - /* Synchronize */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - /* - * Store name and key - */ - entry->q_hash = hashv; - if ( name ) - strlcpy( entry->q_name, name, sizeof(entry->q_name)); - - /* - * Insert new entry first - */ - enqueue_first( (irda_queue_t**) &hashbin->hb_queue[ bin ], - entry); - hashbin->hb_size++; - - /* Release lock */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ -} -EXPORT_SYMBOL(hashbin_insert); - -/* - * Function hashbin_remove_first (hashbin) - * - * Remove first entry of the hashbin - * - * Note : this function no longer use hashbin_remove(), but does things - * similar to hashbin_remove_this(), so can be considered safe. - * Jean II - */ -void *hashbin_remove_first( hashbin_t *hashbin) -{ - unsigned long flags = 0; - irda_queue_t *entry = NULL; - - /* Synchronize */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - entry = hashbin_get_first( hashbin); - if ( entry != NULL) { - int bin; - long hashv; - /* - * Locate hashbin - */ - hashv = entry->q_hash; - bin = GET_HASHBIN( hashv ); - - /* - * Dequeue the entry... - */ - dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], - entry); - hashbin->hb_size--; - entry->q_next = NULL; - entry->q_prev = NULL; - - /* - * Check if this item is the currently selected item, and in - * that case we must reset hb_current - */ - if ( entry == hashbin->hb_current) - hashbin->hb_current = NULL; - } - - /* Release lock */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - return entry; -} - - -/* - * Function hashbin_remove (hashbin, hashv, name) - * - * Remove entry with the given name - * - * The use of this function is highly discouraged, because the whole - * concept behind hashbin_remove() is broken. In many cases, it's not - * possible to guarantee the unicity of the index (either hashv or name), - * leading to removing the WRONG entry. - * The only simple safe use is : - * hashbin_remove(hasbin, (int) self, NULL); - * In other case, you must think hard to guarantee unicity of the index. - * Jean II - */ -void* hashbin_remove( hashbin_t* hashbin, long hashv, const char* name) -{ - int bin, found = FALSE; - unsigned long flags = 0; - irda_queue_t* entry; - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - - /* - * Locate hashbin - */ - if ( name ) - hashv = hash( name ); - bin = GET_HASHBIN( hashv ); - - /* Synchronize */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - /* - * Search for entry - */ - entry = hashbin->hb_queue[ bin ]; - if ( entry ) { - do { - /* - * Check for key - */ - if ( entry->q_hash == hashv ) { - /* - * Name compare too? - */ - if ( name ) { - if ( strcmp( entry->q_name, name) == 0) - { - found = TRUE; - break; - } - } else { - found = TRUE; - break; - } - } - entry = entry->q_next; - } while ( entry != hashbin->hb_queue[ bin ] ); - } - - /* - * If entry was found, dequeue it - */ - if ( found ) { - dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], - entry); - hashbin->hb_size--; - - /* - * Check if this item is the currently selected item, and in - * that case we must reset hb_current - */ - if ( entry == hashbin->hb_current) - hashbin->hb_current = NULL; - } - - /* Release lock */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - - /* Return */ - if ( found ) - return entry; - else - return NULL; - -} -EXPORT_SYMBOL(hashbin_remove); - -/* - * Function hashbin_remove_this (hashbin, entry) - * - * Remove entry with the given name - * - * In some cases, the user of hashbin can't guarantee the unicity - * of either the hashv or name. - * In those cases, using the above function is guaranteed to cause troubles, - * so we use this one instead... - * And by the way, it's also faster, because we skip the search phase ;-) - */ -void* hashbin_remove_this( hashbin_t* hashbin, irda_queue_t* entry) -{ - unsigned long flags = 0; - int bin; - long hashv; - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - IRDA_ASSERT( entry != NULL, return NULL;); - - /* Synchronize */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - /* Check if valid and not already removed... */ - if((entry->q_next == NULL) || (entry->q_prev == NULL)) { - entry = NULL; - goto out; - } - - /* - * Locate hashbin - */ - hashv = entry->q_hash; - bin = GET_HASHBIN( hashv ); - - /* - * Dequeue the entry... - */ - dequeue_general( (irda_queue_t**) &hashbin->hb_queue[ bin ], - entry); - hashbin->hb_size--; - entry->q_next = NULL; - entry->q_prev = NULL; - - /* - * Check if this item is the currently selected item, and in - * that case we must reset hb_current - */ - if ( entry == hashbin->hb_current) - hashbin->hb_current = NULL; -out: - /* Release lock */ - if ( hashbin->hb_type & HB_LOCK ) { - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - } /* Default is no-lock */ - - return entry; -} -EXPORT_SYMBOL(hashbin_remove_this); - -/*********************** HASHBIN ENUMERATION ***********************/ - -/* - * Function hashbin_common_find (hashbin, hashv, name) - * - * Find item with the given hashv or name - * - */ -void* hashbin_find( hashbin_t* hashbin, long hashv, const char* name ) -{ - int bin; - irda_queue_t* entry; - - pr_debug("hashbin_find()\n"); - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - - /* - * Locate hashbin - */ - if ( name ) - hashv = hash( name ); - bin = GET_HASHBIN( hashv ); - - /* - * Search for entry - */ - entry = hashbin->hb_queue[ bin]; - if ( entry ) { - do { - /* - * Check for key - */ - if ( entry->q_hash == hashv ) { - /* - * Name compare too? - */ - if ( name ) { - if ( strcmp( entry->q_name, name ) == 0 ) { - return entry; - } - } else { - return entry; - } - } - entry = entry->q_next; - } while ( entry != hashbin->hb_queue[ bin ] ); - } - - return NULL; -} -EXPORT_SYMBOL(hashbin_find); - -/* - * Function hashbin_lock_find (hashbin, hashv, name) - * - * Find item with the given hashv or name - * - * Same, but with spinlock protection... - * I call it safe, but it's only safe with respect to the hashbin, not its - * content. - Jean II - */ -void* hashbin_lock_find( hashbin_t* hashbin, long hashv, const char* name ) -{ - unsigned long flags = 0; - irda_queue_t* entry; - - /* Synchronize */ - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - - /* - * Search for entry - */ - entry = hashbin_find(hashbin, hashv, name); - - /* Release lock */ - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - - return entry; -} -EXPORT_SYMBOL(hashbin_lock_find); - -/* - * Function hashbin_find (hashbin, hashv, name, pnext) - * - * Find an item with the given hashv or name, and its successor - * - * This function allow to do concurrent enumerations without the - * need to lock over the whole session, because the caller keep the - * context of the search. On the other hand, it might fail and return - * NULL if the entry is removed. - Jean II - */ -void* hashbin_find_next( hashbin_t* hashbin, long hashv, const char* name, - void ** pnext) -{ - unsigned long flags = 0; - irda_queue_t* entry; - - /* Synchronize */ - spin_lock_irqsave(&hashbin->hb_spinlock, flags); - - /* - * Search for current entry - * This allow to check if the current item is still in the - * hashbin or has been removed. - */ - entry = hashbin_find(hashbin, hashv, name); - - /* - * Trick hashbin_get_next() to return what we want - */ - if(entry) { - hashbin->hb_current = entry; - *pnext = hashbin_get_next( hashbin ); - } else - *pnext = NULL; - - /* Release lock */ - spin_unlock_irqrestore(&hashbin->hb_spinlock, flags); - - return entry; -} - -/* - * Function hashbin_get_first (hashbin) - * - * Get a pointer to first element in hashbin, this function must be - * called before any calls to hashbin_get_next()! - * - */ -irda_queue_t *hashbin_get_first( hashbin_t* hashbin) -{ - irda_queue_t *entry; - int i; - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - - if ( hashbin == NULL) - return NULL; - - for ( i = 0; i < HASHBIN_SIZE; i ++ ) { - entry = hashbin->hb_queue[ i]; - if ( entry) { - hashbin->hb_current = entry; - return entry; - } - } - /* - * Did not find any item in hashbin - */ - return NULL; -} -EXPORT_SYMBOL(hashbin_get_first); - -/* - * Function hashbin_get_next (hashbin) - * - * Get next item in hashbin. A series of hashbin_get_next() calls must - * be started by a call to hashbin_get_first(). The function returns - * NULL when all items have been traversed - * - * The context of the search is stored within the hashbin, so you must - * protect yourself from concurrent enumerations. - Jean II - */ -irda_queue_t *hashbin_get_next( hashbin_t *hashbin) -{ - irda_queue_t* entry; - int bin; - int i; - - IRDA_ASSERT( hashbin != NULL, return NULL;); - IRDA_ASSERT( hashbin->magic == HB_MAGIC, return NULL;); - - if ( hashbin->hb_current == NULL) { - IRDA_ASSERT( hashbin->hb_current != NULL, return NULL;); - return NULL; - } - entry = hashbin->hb_current->q_next; - bin = GET_HASHBIN( entry->q_hash); - - /* - * Make sure that we are not back at the beginning of the queue - * again - */ - if ( entry != hashbin->hb_queue[ bin ]) { - hashbin->hb_current = entry; - - return entry; - } - - /* - * Check that this is not the last queue in hashbin - */ - if ( bin >= HASHBIN_SIZE) - return NULL; - - /* - * Move to next queue in hashbin - */ - bin++; - for ( i = bin; i < HASHBIN_SIZE; i++ ) { - entry = hashbin->hb_queue[ i]; - if ( entry) { - hashbin->hb_current = entry; - - return entry; - } - } - return NULL; -} -EXPORT_SYMBOL(hashbin_get_next); diff --git a/net/irda/irsysctl.c b/net/irda/irsysctl.c deleted file mode 100644 index 873da5e7d428..000000000000 --- a/net/irda/irsysctl.c +++ /dev/null @@ -1,258 +0,0 @@ -/********************************************************************* - * - * Filename: irsysctl.c - * Version: 1.0 - * Description: Sysctl interface for IrDA - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sun May 24 22:12:06 1998 - * Modified at: Fri Jun 4 02:50:15 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999 Dag Brattli, All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include /* irda_debug */ -#include -#include -#include - -extern int sysctl_discovery; -extern int sysctl_discovery_slots; -extern int sysctl_discovery_timeout; -extern int sysctl_slot_timeout; -extern int sysctl_fast_poll_increase; -extern char sysctl_devname[]; -extern int sysctl_max_baud_rate; -extern unsigned int sysctl_min_tx_turn_time; -extern unsigned int sysctl_max_tx_data_size; -extern unsigned int sysctl_max_tx_window; -extern int sysctl_max_noreply_time; -extern int sysctl_warn_noreply_time; -extern int sysctl_lap_keepalive_time; - -extern struct irlmp_cb *irlmp; - -/* this is needed for the proc_dointvec_minmax - Jean II */ -static int max_discovery_slots = 16; /* ??? */ -static int min_discovery_slots = 1; -/* IrLAP 6.13.2 says 25ms to 10+70ms - allow higher since some devices - * seems to require it. (from Dag's comment) */ -static int max_slot_timeout = 160; -static int min_slot_timeout = 20; -static int max_max_baud_rate = 16000000; /* See qos.c - IrLAP spec */ -static int min_max_baud_rate = 2400; -static int max_min_tx_turn_time = 10000; /* See qos.c - IrLAP spec */ -static int min_min_tx_turn_time; -static int max_max_tx_data_size = 2048; /* See qos.c - IrLAP spec */ -static int min_max_tx_data_size = 64; -static int max_max_tx_window = 7; /* See qos.c - IrLAP spec */ -static int min_max_tx_window = 1; -static int max_max_noreply_time = 40; /* See qos.c - IrLAP spec */ -static int min_max_noreply_time = 3; -static int max_warn_noreply_time = 3; /* 3s == standard */ -static int min_warn_noreply_time = 1; /* 1s == min WD_TIMER */ -static int max_lap_keepalive_time = 10000; /* 10s */ -static int min_lap_keepalive_time = 100; /* 100us */ -/* For other sysctl, I've no idea of the range. Maybe Dag could help - * us on that - Jean II */ - -static int do_devname(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - int ret; - - ret = proc_dostring(table, write, buffer, lenp, ppos); - if (ret == 0 && write) { - struct ias_value *val; - - val = irias_new_string_value(sysctl_devname); - if (val) - irias_object_change_attribute("Device", "DeviceName", val); - } - return ret; -} - - -static int do_discovery(struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - int ret; - - ret = proc_dointvec(table, write, buffer, lenp, ppos); - if (ret) - return ret; - - if (irlmp == NULL) - return -ENODEV; - - if (sysctl_discovery) - irlmp_start_discovery_timer(irlmp, sysctl_discovery_timeout*HZ); - else - del_timer_sync(&irlmp->discovery_timer); - - return ret; -} - -/* One file */ -static struct ctl_table irda_table[] = { - { - .procname = "discovery", - .data = &sysctl_discovery, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = do_discovery, - }, - { - .procname = "devname", - .data = sysctl_devname, - .maxlen = 65, - .mode = 0644, - .proc_handler = do_devname, - }, -#ifdef CONFIG_IRDA_FAST_RR - { - .procname = "fast_poll_increase", - .data = &sysctl_fast_poll_increase, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, -#endif - { - .procname = "discovery_slots", - .data = &sysctl_discovery_slots, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_discovery_slots, - .extra2 = &max_discovery_slots - }, - { - .procname = "discovery_timeout", - .data = &sysctl_discovery_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec - }, - { - .procname = "slot_timeout", - .data = &sysctl_slot_timeout, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_slot_timeout, - .extra2 = &max_slot_timeout - }, - { - .procname = "max_baud_rate", - .data = &sysctl_max_baud_rate, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_max_baud_rate, - .extra2 = &max_max_baud_rate - }, - { - .procname = "min_tx_turn_time", - .data = &sysctl_min_tx_turn_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_min_tx_turn_time, - .extra2 = &max_min_tx_turn_time - }, - { - .procname = "max_tx_data_size", - .data = &sysctl_max_tx_data_size, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_max_tx_data_size, - .extra2 = &max_max_tx_data_size - }, - { - .procname = "max_tx_window", - .data = &sysctl_max_tx_window, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_max_tx_window, - .extra2 = &max_max_tx_window - }, - { - .procname = "max_noreply_time", - .data = &sysctl_max_noreply_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_max_noreply_time, - .extra2 = &max_max_noreply_time - }, - { - .procname = "warn_noreply_time", - .data = &sysctl_warn_noreply_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_warn_noreply_time, - .extra2 = &max_warn_noreply_time - }, - { - .procname = "lap_keepalive_time", - .data = &sysctl_lap_keepalive_time, - .maxlen = sizeof(int), - .mode = 0644, - .proc_handler = proc_dointvec_minmax, - .extra1 = &min_lap_keepalive_time, - .extra2 = &max_lap_keepalive_time - }, - { } -}; - -static struct ctl_table_header *irda_table_header; - -/* - * Function irda_sysctl_register (void) - * - * Register our sysctl interface - * - */ -int __init irda_sysctl_register(void) -{ - irda_table_header = register_net_sysctl(&init_net, "net/irda", irda_table); - if (!irda_table_header) - return -ENOMEM; - - return 0; -} - -/* - * Function irda_sysctl_unregister (void) - * - * Unregister our sysctl interface - * - */ -void irda_sysctl_unregister(void) -{ - unregister_net_sysctl_table(irda_table_header); -} - - - diff --git a/net/irda/irttp.c b/net/irda/irttp.c deleted file mode 100644 index b6ab41d5b3a3..000000000000 --- a/net/irda/irttp.c +++ /dev/null @@ -1,1891 +0,0 @@ -/********************************************************************* - * - * Filename: irttp.c - * Version: 1.2 - * Description: Tiny Transport Protocol (TTP) implementation - * Status: Stable - * Author: Dag Brattli - * Created at: Sun Aug 31 20:14:31 1997 - * Modified at: Wed Jan 5 11:31:27 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2003 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include -#include -#include -#include - -static struct irttp_cb *irttp; - -static void __irttp_close_tsap(struct tsap_cb *self); - -static int irttp_data_indication(void *instance, void *sap, - struct sk_buff *skb); -static int irttp_udata_indication(void *instance, void *sap, - struct sk_buff *skb); -static void irttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, struct sk_buff *); -static void irttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 header_size, struct sk_buff *skb); -static void irttp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, __u32 max_sdu_size, - __u8 header_size, struct sk_buff *skb); -static void irttp_run_tx_queue(struct tsap_cb *self); -static void irttp_run_rx_queue(struct tsap_cb *self); - -static void irttp_flush_queues(struct tsap_cb *self); -static void irttp_fragment_skb(struct tsap_cb *self, struct sk_buff *skb); -static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self); -static void irttp_todo_expired(unsigned long data); -static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, - int get); - -static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow); -static void irttp_status_indication(void *instance, - LINK_STATUS link, LOCK_STATUS lock); - -/* Information for parsing parameters in IrTTP */ -static const pi_minor_info_t pi_minor_call_table[] = { - { NULL, 0 }, /* 0x00 */ - { irttp_param_max_sdu_size, PV_INTEGER | PV_BIG_ENDIAN } /* 0x01 */ -}; -static const pi_major_info_t pi_major_call_table[] = { - { pi_minor_call_table, 2 } -}; -static pi_param_info_t param_info = { pi_major_call_table, 1, 0x0f, 4 }; - -/************************ GLOBAL PROCEDURES ************************/ - -/* - * Function irttp_init (void) - * - * Initialize the IrTTP layer. Called by module initialization code - * - */ -int __init irttp_init(void) -{ - irttp = kzalloc(sizeof(struct irttp_cb), GFP_KERNEL); - if (irttp == NULL) - return -ENOMEM; - - irttp->magic = TTP_MAGIC; - - irttp->tsaps = hashbin_new(HB_LOCK); - if (!irttp->tsaps) { - net_err_ratelimited("%s: can't allocate IrTTP hashbin!\n", - __func__); - kfree(irttp); - return -ENOMEM; - } - - return 0; -} - -/* - * Function irttp_cleanup (void) - * - * Called by module destruction/cleanup code - * - */ -void irttp_cleanup(void) -{ - /* Check for main structure */ - IRDA_ASSERT(irttp->magic == TTP_MAGIC, return;); - - /* - * Delete hashbin and close all TSAP instances in it - */ - hashbin_delete(irttp->tsaps, (FREE_FUNC) __irttp_close_tsap); - - irttp->magic = 0; - - /* De-allocate main structure */ - kfree(irttp); - - irttp = NULL; -} - -/*************************** SUBROUTINES ***************************/ - -/* - * Function irttp_start_todo_timer (self, timeout) - * - * Start todo timer. - * - * Made it more effient and unsensitive to race conditions - Jean II - */ -static inline void irttp_start_todo_timer(struct tsap_cb *self, int timeout) -{ - /* Set new value for timer */ - mod_timer(&self->todo_timer, jiffies + timeout); -} - -/* - * Function irttp_todo_expired (data) - * - * Todo timer has expired! - * - * One of the restriction of the timer is that it is run only on the timer - * interrupt which run every 10ms. This mean that even if you set the timer - * with a delay of 0, it may take up to 10ms before it's run. - * So, to minimise latency and keep cache fresh, we try to avoid using - * it as much as possible. - * Note : we can't use tasklets, because they can't be asynchronously - * killed (need user context), and we can't guarantee that here... - * Jean II - */ -static void irttp_todo_expired(unsigned long data) -{ - struct tsap_cb *self = (struct tsap_cb *) data; - - /* Check that we still exist */ - if (!self || self->magic != TTP_TSAP_MAGIC) - return; - - pr_debug("%s(instance=%p)\n", __func__, self); - - /* Try to make some progress, especially on Tx side - Jean II */ - irttp_run_rx_queue(self); - irttp_run_tx_queue(self); - - /* Check if time for disconnect */ - if (test_bit(0, &self->disconnect_pend)) { - /* Check if it's possible to disconnect yet */ - if (skb_queue_empty(&self->tx_queue)) { - /* Make sure disconnect is not pending anymore */ - clear_bit(0, &self->disconnect_pend); /* FALSE */ - - /* Note : self->disconnect_skb may be NULL */ - irttp_disconnect_request(self, self->disconnect_skb, - P_NORMAL); - self->disconnect_skb = NULL; - } else { - /* Try again later */ - irttp_start_todo_timer(self, HZ/10); - - /* No reason to try and close now */ - return; - } - } - - /* Check if it's closing time */ - if (self->close_pend) - /* Finish cleanup */ - irttp_close_tsap(self); -} - -/* - * Function irttp_flush_queues (self) - * - * Flushes (removes all frames) in transitt-buffer (tx_list) - */ -static void irttp_flush_queues(struct tsap_cb *self) -{ - struct sk_buff *skb; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - /* Deallocate frames waiting to be sent */ - while ((skb = skb_dequeue(&self->tx_queue)) != NULL) - dev_kfree_skb(skb); - - /* Deallocate received frames */ - while ((skb = skb_dequeue(&self->rx_queue)) != NULL) - dev_kfree_skb(skb); - - /* Deallocate received fragments */ - while ((skb = skb_dequeue(&self->rx_fragments)) != NULL) - dev_kfree_skb(skb); -} - -/* - * Function irttp_reassemble (self) - * - * Makes a new (continuous) skb of all the fragments in the fragment - * queue - * - */ -static struct sk_buff *irttp_reassemble_skb(struct tsap_cb *self) -{ - struct sk_buff *skb, *frag; - int n = 0; /* Fragment index */ - - IRDA_ASSERT(self != NULL, return NULL;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return NULL;); - - pr_debug("%s(), self->rx_sdu_size=%d\n", __func__, - self->rx_sdu_size); - - skb = dev_alloc_skb(TTP_HEADER + self->rx_sdu_size); - if (!skb) - return NULL; - - /* - * Need to reserve space for TTP header in case this skb needs to - * be requeued in case delivery failes - */ - skb_reserve(skb, TTP_HEADER); - skb_put(skb, self->rx_sdu_size); - - /* - * Copy all fragments to a new buffer - */ - while ((frag = skb_dequeue(&self->rx_fragments)) != NULL) { - skb_copy_to_linear_data_offset(skb, n, frag->data, frag->len); - n += frag->len; - - dev_kfree_skb(frag); - } - - pr_debug("%s(), frame len=%d, rx_sdu_size=%d, rx_max_sdu_size=%d\n", - __func__, n, self->rx_sdu_size, self->rx_max_sdu_size); - /* Note : irttp_run_rx_queue() calculate self->rx_sdu_size - * by summing the size of all fragments, so we should always - * have n == self->rx_sdu_size, except in cases where we - * droped the last fragment (when self->rx_sdu_size exceed - * self->rx_max_sdu_size), where n < self->rx_sdu_size. - * Jean II */ - IRDA_ASSERT(n <= self->rx_sdu_size, n = self->rx_sdu_size;); - - /* Set the new length */ - skb_trim(skb, n); - - self->rx_sdu_size = 0; - - return skb; -} - -/* - * Function irttp_fragment_skb (skb) - * - * Fragments a frame and queues all the fragments for transmission - * - */ -static inline void irttp_fragment_skb(struct tsap_cb *self, - struct sk_buff *skb) -{ - struct sk_buff *frag; - __u8 *frame; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - /* - * Split frame into a number of segments - */ - while (skb->len > self->max_seg_size) { - pr_debug("%s(), fragmenting ...\n", __func__); - - /* Make new segment */ - frag = alloc_skb(self->max_seg_size+self->max_header_size, - GFP_ATOMIC); - if (!frag) - return; - - skb_reserve(frag, self->max_header_size); - - /* Copy data from the original skb into this fragment. */ - skb_copy_from_linear_data(skb, skb_put(frag, self->max_seg_size), - self->max_seg_size); - - /* Insert TTP header, with the more bit set */ - frame = skb_push(frag, TTP_HEADER); - frame[0] = TTP_MORE; - - /* Hide the copied data from the original skb */ - skb_pull(skb, self->max_seg_size); - - /* Queue fragment */ - skb_queue_tail(&self->tx_queue, frag); - } - /* Queue what is left of the original skb */ - pr_debug("%s(), queuing last segment\n", __func__); - - frame = skb_push(skb, TTP_HEADER); - frame[0] = 0x00; /* Clear more bit */ - - /* Queue fragment */ - skb_queue_tail(&self->tx_queue, skb); -} - -/* - * Function irttp_param_max_sdu_size (self, param) - * - * Handle the MaxSduSize parameter in the connect frames, this function - * will be called both when this parameter needs to be inserted into, and - * extracted from the connect frames - */ -static int irttp_param_max_sdu_size(void *instance, irda_param_t *param, - int get) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->tx_max_sdu_size; - else - self->tx_max_sdu_size = param->pv.i; - - pr_debug("%s(), MaxSduSize=%d\n", __func__, param->pv.i); - - return 0; -} - -/*************************** CLIENT CALLS ***************************/ -/************************** LMP CALLBACKS **************************/ -/* Everything is happily mixed up. Waiting for next clean up - Jean II */ - -/* - * Initialization, that has to be done on new tsap - * instance allocation and on duplication - */ -static void irttp_init_tsap(struct tsap_cb *tsap) -{ - spin_lock_init(&tsap->lock); - init_timer(&tsap->todo_timer); - - skb_queue_head_init(&tsap->rx_queue); - skb_queue_head_init(&tsap->tx_queue); - skb_queue_head_init(&tsap->rx_fragments); -} - -/* - * Function irttp_open_tsap (stsap, notify) - * - * Create TSAP connection endpoint, - */ -struct tsap_cb *irttp_open_tsap(__u8 stsap_sel, int credit, notify_t *notify) -{ - struct tsap_cb *self; - struct lsap_cb *lsap; - notify_t ttp_notify; - - IRDA_ASSERT(irttp->magic == TTP_MAGIC, return NULL;); - - /* The IrLMP spec (IrLMP 1.1 p10) says that we have the right to - * use only 0x01-0x6F. Of course, we can use LSAP_ANY as well. - * JeanII */ - if ((stsap_sel != LSAP_ANY) && - ((stsap_sel < 0x01) || (stsap_sel >= 0x70))) { - pr_debug("%s(), invalid tsap!\n", __func__); - return NULL; - } - - self = kzalloc(sizeof(struct tsap_cb), GFP_ATOMIC); - if (self == NULL) - return NULL; - - /* Initialize internal objects */ - irttp_init_tsap(self); - - /* Initialise todo timer */ - self->todo_timer.data = (unsigned long) self; - self->todo_timer.function = &irttp_todo_expired; - - /* Initialize callbacks for IrLMP to use */ - irda_notify_init(&ttp_notify); - ttp_notify.connect_confirm = irttp_connect_confirm; - ttp_notify.connect_indication = irttp_connect_indication; - ttp_notify.disconnect_indication = irttp_disconnect_indication; - ttp_notify.data_indication = irttp_data_indication; - ttp_notify.udata_indication = irttp_udata_indication; - ttp_notify.flow_indication = irttp_flow_indication; - if (notify->status_indication != NULL) - ttp_notify.status_indication = irttp_status_indication; - ttp_notify.instance = self; - strncpy(ttp_notify.name, notify->name, NOTIFY_MAX_NAME); - - self->magic = TTP_TSAP_MAGIC; - self->connected = FALSE; - - /* - * Create LSAP at IrLMP layer - */ - lsap = irlmp_open_lsap(stsap_sel, &ttp_notify, 0); - if (lsap == NULL) { - pr_debug("%s: unable to allocate LSAP!!\n", __func__); - __irttp_close_tsap(self); - return NULL; - } - - /* - * If user specified LSAP_ANY as source TSAP selector, then IrLMP - * will replace it with whatever source selector which is free, so - * the stsap_sel we have might not be valid anymore - */ - self->stsap_sel = lsap->slsap_sel; - pr_debug("%s(), stsap_sel=%02x\n", __func__, self->stsap_sel); - - self->notify = *notify; - self->lsap = lsap; - - hashbin_insert(irttp->tsaps, (irda_queue_t *) self, (long) self, NULL); - - if (credit > TTP_RX_MAX_CREDIT) - self->initial_credit = TTP_RX_MAX_CREDIT; - else - self->initial_credit = credit; - - return self; -} -EXPORT_SYMBOL(irttp_open_tsap); - -/* - * Function irttp_close (handle) - * - * Remove an instance of a TSAP. This function should only deal with the - * deallocation of the TSAP, and resetting of the TSAPs values; - * - */ -static void __irttp_close_tsap(struct tsap_cb *self) -{ - /* First make sure we're connected. */ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - irttp_flush_queues(self); - - del_timer(&self->todo_timer); - - /* This one won't be cleaned up if we are disconnect_pend + close_pend - * and we receive a disconnect_indication */ - if (self->disconnect_skb) - dev_kfree_skb(self->disconnect_skb); - - self->connected = FALSE; - self->magic = ~TTP_TSAP_MAGIC; - - kfree(self); -} - -/* - * Function irttp_close (self) - * - * Remove TSAP from list of all TSAPs and then deallocate all resources - * associated with this TSAP - * - * Note : because we *free* the tsap structure, it is the responsibility - * of the caller to make sure we are called only once and to deal with - * possible race conditions. - Jean II - */ -int irttp_close_tsap(struct tsap_cb *self) -{ - struct tsap_cb *tsap; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - /* Make sure tsap has been disconnected */ - if (self->connected) { - /* Check if disconnect is not pending */ - if (!test_bit(0, &self->disconnect_pend)) { - net_warn_ratelimited("%s: TSAP still connected!\n", - __func__); - irttp_disconnect_request(self, NULL, P_NORMAL); - } - self->close_pend = TRUE; - irttp_start_todo_timer(self, HZ/10); - - return 0; /* Will be back! */ - } - - tsap = hashbin_remove(irttp->tsaps, (long) self, NULL); - - IRDA_ASSERT(tsap == self, return -1;); - - /* Close corresponding LSAP */ - if (self->lsap) { - irlmp_close_lsap(self->lsap); - self->lsap = NULL; - } - - __irttp_close_tsap(self); - - return 0; -} -EXPORT_SYMBOL(irttp_close_tsap); - -/* - * Function irttp_udata_request (self, skb) - * - * Send unreliable data on this TSAP - * - */ -int irttp_udata_request(struct tsap_cb *self, struct sk_buff *skb) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - /* Take shortcut on zero byte packets */ - if (skb->len == 0) { - ret = 0; - goto err; - } - - /* Check that nothing bad happens */ - if (!self->connected) { - net_warn_ratelimited("%s(), Not connected\n", __func__); - ret = -ENOTCONN; - goto err; - } - - if (skb->len > self->max_seg_size) { - net_err_ratelimited("%s(), UData is too large for IrLAP!\n", - __func__); - ret = -EMSGSIZE; - goto err; - } - - irlmp_udata_request(self->lsap, skb); - self->stats.tx_packets++; - - return 0; - -err: - dev_kfree_skb(skb); - return ret; -} -EXPORT_SYMBOL(irttp_udata_request); - - -/* - * Function irttp_data_request (handle, skb) - * - * Queue frame for transmission. If SAR is enabled, fragement the frame - * and queue the fragments for transmission - */ -int irttp_data_request(struct tsap_cb *self, struct sk_buff *skb) -{ - __u8 *frame; - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - pr_debug("%s() : queue len = %d\n", __func__, - skb_queue_len(&self->tx_queue)); - - /* Take shortcut on zero byte packets */ - if (skb->len == 0) { - ret = 0; - goto err; - } - - /* Check that nothing bad happens */ - if (!self->connected) { - net_warn_ratelimited("%s: Not connected\n", __func__); - ret = -ENOTCONN; - goto err; - } - - /* - * Check if SAR is disabled, and the frame is larger than what fits - * inside an IrLAP frame - */ - if ((self->tx_max_sdu_size == 0) && (skb->len > self->max_seg_size)) { - net_err_ratelimited("%s: SAR disabled, and data is too large for IrLAP!\n", - __func__); - ret = -EMSGSIZE; - goto err; - } - - /* - * Check if SAR is enabled, and the frame is larger than the - * TxMaxSduSize - */ - if ((self->tx_max_sdu_size != 0) && - (self->tx_max_sdu_size != TTP_SAR_UNBOUND) && - (skb->len > self->tx_max_sdu_size)) { - net_err_ratelimited("%s: SAR enabled, but data is larger than TxMaxSduSize!\n", - __func__); - ret = -EMSGSIZE; - goto err; - } - /* - * Check if transmit queue is full - */ - if (skb_queue_len(&self->tx_queue) >= TTP_TX_MAX_QUEUE) { - /* - * Give it a chance to empty itself - */ - irttp_run_tx_queue(self); - - /* Drop packet. This error code should trigger the caller - * to resend the data in the client code - Jean II */ - ret = -ENOBUFS; - goto err; - } - - /* Queue frame, or queue frame segments */ - if ((self->tx_max_sdu_size == 0) || (skb->len < self->max_seg_size)) { - /* Queue frame */ - IRDA_ASSERT(skb_headroom(skb) >= TTP_HEADER, return -1;); - frame = skb_push(skb, TTP_HEADER); - frame[0] = 0x00; /* Clear more bit */ - - skb_queue_tail(&self->tx_queue, skb); - } else { - /* - * Fragment the frame, this function will also queue the - * fragments, we don't care about the fact the transmit - * queue may be overfilled by all the segments for a little - * while - */ - irttp_fragment_skb(self, skb); - } - - /* Check if we can accept more data from client */ - if ((!self->tx_sdu_busy) && - (skb_queue_len(&self->tx_queue) > TTP_TX_HIGH_THRESHOLD)) { - /* Tx queue filling up, so stop client. */ - if (self->notify.flow_indication) { - self->notify.flow_indication(self->notify.instance, - self, FLOW_STOP); - } - /* self->tx_sdu_busy is the state of the client. - * Update state after notifying client to avoid - * race condition with irttp_flow_indication(). - * If the queue empty itself after our test but before - * we set the flag, we will fix ourselves below in - * irttp_run_tx_queue(). - * Jean II */ - self->tx_sdu_busy = TRUE; - } - - /* Try to make some progress */ - irttp_run_tx_queue(self); - - return 0; - -err: - dev_kfree_skb(skb); - return ret; -} -EXPORT_SYMBOL(irttp_data_request); - -/* - * Function irttp_run_tx_queue (self) - * - * Transmit packets queued for transmission (if possible) - * - */ -static void irttp_run_tx_queue(struct tsap_cb *self) -{ - struct sk_buff *skb; - unsigned long flags; - int n; - - pr_debug("%s() : send_credit = %d, queue_len = %d\n", - __func__, - self->send_credit, skb_queue_len(&self->tx_queue)); - - /* Get exclusive access to the tx queue, otherwise don't touch it */ - if (irda_lock(&self->tx_queue_lock) == FALSE) - return; - - /* Try to send out frames as long as we have credits - * and as long as LAP is not full. If LAP is full, it will - * poll us through irttp_flow_indication() - Jean II */ - while ((self->send_credit > 0) && - (!irlmp_lap_tx_queue_full(self->lsap)) && - (skb = skb_dequeue(&self->tx_queue))) { - /* - * Since we can transmit and receive frames concurrently, - * the code below is a critical region and we must assure that - * nobody messes with the credits while we update them. - */ - spin_lock_irqsave(&self->lock, flags); - - n = self->avail_credit; - self->avail_credit = 0; - - /* Only room for 127 credits in frame */ - if (n > 127) { - self->avail_credit = n-127; - n = 127; - } - self->remote_credit += n; - self->send_credit--; - - spin_unlock_irqrestore(&self->lock, flags); - - /* - * More bit must be set by the data_request() or fragment() - * functions - */ - skb->data[0] |= (n & 0x7f); - - /* Detach from socket. - * The current skb has a reference to the socket that sent - * it (skb->sk). When we pass it to IrLMP, the skb will be - * stored in in IrLAP (self->wx_list). When we are within - * IrLAP, we lose the notion of socket, so we should not - * have a reference to a socket. So, we drop it here. - * - * Why does it matter ? - * When the skb is freed (kfree_skb), if it is associated - * with a socket, it release buffer space on the socket - * (through sock_wfree() and sock_def_write_space()). - * If the socket no longer exist, we may crash. Hard. - * When we close a socket, we make sure that associated packets - * in IrTTP are freed. However, we have no way to cancel - * the packet that we have passed to IrLAP. So, if a packet - * remains in IrLAP (retry on the link or else) after we - * close the socket, we are dead ! - * Jean II */ - if (skb->sk != NULL) { - /* IrSOCK application, IrOBEX, ... */ - skb_orphan(skb); - } - /* IrCOMM over IrTTP, IrLAN, ... */ - - /* Pass the skb to IrLMP - done */ - irlmp_data_request(self->lsap, skb); - self->stats.tx_packets++; - } - - /* Check if we can accept more frames from client. - * We don't want to wait until the todo timer to do that, and we - * can't use tasklets (grr...), so we are obliged to give control - * to client. That's ok, this test will be true not too often - * (max once per LAP window) and we are called from places - * where we can spend a bit of time doing stuff. - Jean II */ - if ((self->tx_sdu_busy) && - (skb_queue_len(&self->tx_queue) < TTP_TX_LOW_THRESHOLD) && - (!self->close_pend)) { - if (self->notify.flow_indication) - self->notify.flow_indication(self->notify.instance, - self, FLOW_START); - - /* self->tx_sdu_busy is the state of the client. - * We don't really have a race here, but it's always safer - * to update our state after the client - Jean II */ - self->tx_sdu_busy = FALSE; - } - - /* Reset lock */ - self->tx_queue_lock = 0; -} - -/* - * Function irttp_give_credit (self) - * - * Send a dataless flowdata TTP-PDU and give available credit to peer - * TSAP - */ -static inline void irttp_give_credit(struct tsap_cb *self) -{ - struct sk_buff *tx_skb = NULL; - unsigned long flags; - int n; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - pr_debug("%s() send=%d,avail=%d,remote=%d\n", - __func__, - self->send_credit, self->avail_credit, self->remote_credit); - - /* Give credit to peer */ - tx_skb = alloc_skb(TTP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return; - - /* Reserve space for LMP, and LAP header */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - /* - * Since we can transmit and receive frames concurrently, - * the code below is a critical region and we must assure that - * nobody messes with the credits while we update them. - */ - spin_lock_irqsave(&self->lock, flags); - - n = self->avail_credit; - self->avail_credit = 0; - - /* Only space for 127 credits in frame */ - if (n > 127) { - self->avail_credit = n - 127; - n = 127; - } - self->remote_credit += n; - - spin_unlock_irqrestore(&self->lock, flags); - - skb_put(tx_skb, 1); - tx_skb->data[0] = (__u8) (n & 0x7f); - - irlmp_data_request(self->lsap, tx_skb); - self->stats.tx_packets++; -} - -/* - * Function irttp_udata_indication (instance, sap, skb) - * - * Received some unit-data (unreliable) - * - */ -static int irttp_udata_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct tsap_cb *self; - int err; - - self = instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - IRDA_ASSERT(skb != NULL, return -1;); - - self->stats.rx_packets++; - - /* Just pass data to layer above */ - if (self->notify.udata_indication) { - err = self->notify.udata_indication(self->notify.instance, - self, skb); - /* Same comment as in irttp_do_data_indication() */ - if (!err) - return 0; - } - /* Either no handler, or handler returns an error */ - dev_kfree_skb(skb); - - return 0; -} - -/* - * Function irttp_data_indication (instance, sap, skb) - * - * Receive segment from IrLMP. - * - */ -static int irttp_data_indication(void *instance, void *sap, - struct sk_buff *skb) -{ - struct tsap_cb *self; - unsigned long flags; - int n; - - self = instance; - - n = skb->data[0] & 0x7f; /* Extract the credits */ - - self->stats.rx_packets++; - - /* Deal with inbound credit - * Since we can transmit and receive frames concurrently, - * the code below is a critical region and we must assure that - * nobody messes with the credits while we update them. - */ - spin_lock_irqsave(&self->lock, flags); - self->send_credit += n; - if (skb->len > 1) - self->remote_credit--; - spin_unlock_irqrestore(&self->lock, flags); - - /* - * Data or dataless packet? Dataless frames contains only the - * TTP_HEADER. - */ - if (skb->len > 1) { - /* - * We don't remove the TTP header, since we must preserve the - * more bit, so the defragment routing knows what to do - */ - skb_queue_tail(&self->rx_queue, skb); - } else { - /* Dataless flowdata TTP-PDU */ - dev_kfree_skb(skb); - } - - - /* Push data to the higher layer. - * We do it synchronously because running the todo timer for each - * receive packet would be too much overhead and latency. - * By passing control to the higher layer, we run the risk that - * it may take time or grab a lock. Most often, the higher layer - * will only put packet in a queue. - * Anyway, packets are only dripping through the IrDA, so we can - * have time before the next packet. - * Further, we are run from NET_BH, so the worse that can happen is - * us missing the optimal time to send back the PF bit in LAP. - * Jean II */ - irttp_run_rx_queue(self); - - /* We now give credits to peer in irttp_run_rx_queue(). - * We need to send credit *NOW*, otherwise we are going - * to miss the next Tx window. The todo timer may take - * a while before it's run... - Jean II */ - - /* - * If the peer device has given us some credits and we didn't have - * anyone from before, then we need to shedule the tx queue. - * We need to do that because our Tx have stopped (so we may not - * get any LAP flow indication) and the user may be stopped as - * well. - Jean II - */ - if (self->send_credit == n) { - /* Restart pushing stuff to LAP */ - irttp_run_tx_queue(self); - /* Note : we don't want to schedule the todo timer - * because it has horrible latency. No tasklets - * because the tasklet API is broken. - Jean II */ - } - - return 0; -} - -/* - * Function irttp_status_indication (self, reason) - * - * Status_indication, just pass to the higher layer... - * - */ -static void irttp_status_indication(void *instance, - LINK_STATUS link, LOCK_STATUS lock) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - /* Check if client has already closed the TSAP and gone away */ - if (self->close_pend) - return; - - /* - * Inform service user if he has requested it - */ - if (self->notify.status_indication != NULL) - self->notify.status_indication(self->notify.instance, - link, lock); - else - pr_debug("%s(), no handler\n", __func__); -} - -/* - * Function irttp_flow_indication (self, reason) - * - * Flow_indication : IrLAP tells us to send more data. - * - */ -static void irttp_flow_indication(void *instance, void *sap, LOCAL_FLOW flow) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - pr_debug("%s(instance=%p)\n", __func__, self); - - /* We are "polled" directly from LAP, and the LAP want to fill - * its Tx window. We want to do our best to send it data, so that - * we maximise the window. On the other hand, we want to limit the - * amount of work here so that LAP doesn't hang forever waiting - * for packets. - Jean II */ - - /* Try to send some packets. Currently, LAP calls us every time - * there is one free slot, so we will send only one packet. - * This allow the scheduler to do its round robin - Jean II */ - irttp_run_tx_queue(self); - - /* Note regarding the interraction with higher layer. - * irttp_run_tx_queue() may call the client when its queue - * start to empty, via notify.flow_indication(). Initially. - * I wanted this to happen in a tasklet, to avoid client - * grabbing the CPU, but we can't use tasklets safely. And timer - * is definitely too slow. - * This will happen only once per LAP window, and usually at - * the third packet (unless window is smaller). LAP is still - * doing mtt and sending first packet so it's sort of OK - * to do that. Jean II */ - - /* If we need to send disconnect. try to do it now */ - if (self->disconnect_pend) - irttp_start_todo_timer(self, 0); -} - -/* - * Function irttp_flow_request (self, command) - * - * This function could be used by the upper layers to tell IrTTP to stop - * delivering frames if the receive queues are starting to get full, or - * to tell IrTTP to start delivering frames again. - */ -void irttp_flow_request(struct tsap_cb *self, LOCAL_FLOW flow) -{ - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - switch (flow) { - case FLOW_STOP: - pr_debug("%s(), flow stop\n", __func__); - self->rx_sdu_busy = TRUE; - break; - case FLOW_START: - pr_debug("%s(), flow start\n", __func__); - self->rx_sdu_busy = FALSE; - - /* Client say he can accept more data, try to free our - * queues ASAP - Jean II */ - irttp_run_rx_queue(self); - - break; - default: - pr_debug("%s(), Unknown flow command!\n", __func__); - } -} -EXPORT_SYMBOL(irttp_flow_request); - -/* - * Function irttp_connect_request (self, dtsap_sel, daddr, qos) - * - * Try to connect to remote destination TSAP selector - * - */ -int irttp_connect_request(struct tsap_cb *self, __u8 dtsap_sel, - __u32 saddr, __u32 daddr, - struct qos_info *qos, __u32 max_sdu_size, - struct sk_buff *userdata) -{ - struct sk_buff *tx_skb; - __u8 *frame; - __u8 n; - - pr_debug("%s(), max_sdu_size=%d\n", __func__, max_sdu_size); - - IRDA_ASSERT(self != NULL, return -EBADR;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -EBADR;); - - if (self->connected) { - if (userdata) - dev_kfree_skb(userdata); - return -EISCONN; - } - - /* Any userdata supplied? */ - if (userdata == NULL) { - tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, - GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER); - } else { - tx_skb = userdata; - /* - * Check that the client has reserved enough space for - * headers - */ - IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, - { dev_kfree_skb(userdata); return -1; }); - } - - /* Initialize connection parameters */ - self->connected = FALSE; - self->avail_credit = 0; - self->rx_max_sdu_size = max_sdu_size; - self->rx_sdu_size = 0; - self->rx_sdu_busy = FALSE; - self->dtsap_sel = dtsap_sel; - - n = self->initial_credit; - - self->remote_credit = 0; - self->send_credit = 0; - - /* - * Give away max 127 credits for now - */ - if (n > 127) { - self->avail_credit = n - 127; - n = 127; - } - - self->remote_credit = n; - - /* SAR enabled? */ - if (max_sdu_size > 0) { - IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), - { dev_kfree_skb(tx_skb); return -1; }); - - /* Insert SAR parameters */ - frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER); - - frame[0] = TTP_PARAMETERS | n; - frame[1] = 0x04; /* Length */ - frame[2] = 0x01; /* MaxSduSize */ - frame[3] = 0x02; /* Value length */ - - put_unaligned(cpu_to_be16((__u16) max_sdu_size), - (__be16 *)(frame+4)); - } else { - /* Insert plain TTP header */ - frame = skb_push(tx_skb, TTP_HEADER); - - /* Insert initial credit in frame */ - frame[0] = n & 0x7f; - } - - /* Connect with IrLMP. No QoS parameters for now */ - return irlmp_connect_request(self->lsap, dtsap_sel, saddr, daddr, qos, - tx_skb); -} -EXPORT_SYMBOL(irttp_connect_request); - -/* - * Function irttp_connect_confirm (handle, qos, skb) - * - * Service user confirms TSAP connection with peer. - * - */ -static void irttp_connect_confirm(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, - __u8 max_header_size, struct sk_buff *skb) -{ - struct tsap_cb *self; - int parameters; - int ret; - __u8 plen; - __u8 n; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - self->max_seg_size = max_seg_size - TTP_HEADER; - self->max_header_size = max_header_size + TTP_HEADER; - - /* - * Check if we have got some QoS parameters back! This should be the - * negotiated QoS for the link. - */ - if (qos) { - pr_debug("IrTTP, Negotiated BAUD_RATE: %02x\n", - qos->baud_rate.bits); - pr_debug("IrTTP, Negotiated BAUD_RATE: %d bps.\n", - qos->baud_rate.value); - } - - n = skb->data[0] & 0x7f; - - pr_debug("%s(), Initial send_credit=%d\n", __func__, n); - - self->send_credit = n; - self->tx_max_sdu_size = 0; - self->connected = TRUE; - - parameters = skb->data[0] & 0x80; - - IRDA_ASSERT(skb->len >= TTP_HEADER, return;); - skb_pull(skb, TTP_HEADER); - - if (parameters) { - plen = skb->data[0]; - - ret = irda_param_extract_all(self, skb->data+1, - IRDA_MIN(skb->len-1, plen), - ¶m_info); - - /* Any errors in the parameter list? */ - if (ret < 0) { - net_warn_ratelimited("%s: error extracting parameters\n", - __func__); - dev_kfree_skb(skb); - - /* Do not accept this connection attempt */ - return; - } - /* Remove parameters */ - skb_pull(skb, IRDA_MIN(skb->len, plen+1)); - } - - pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, - self->send_credit, self->avail_credit, self->remote_credit); - - pr_debug("%s(), MaxSduSize=%d\n", __func__, - self->tx_max_sdu_size); - - if (self->notify.connect_confirm) { - self->notify.connect_confirm(self->notify.instance, self, qos, - self->tx_max_sdu_size, - self->max_header_size, skb); - } else - dev_kfree_skb(skb); -} - -/* - * Function irttp_connect_indication (handle, skb) - * - * Some other device is connecting to this TSAP - * - */ -static void irttp_connect_indication(void *instance, void *sap, - struct qos_info *qos, __u32 max_seg_size, __u8 max_header_size, - struct sk_buff *skb) -{ - struct tsap_cb *self; - struct lsap_cb *lsap; - int parameters; - int ret; - __u8 plen; - __u8 n; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - IRDA_ASSERT(skb != NULL, return;); - - lsap = sap; - - self->max_seg_size = max_seg_size - TTP_HEADER; - self->max_header_size = max_header_size+TTP_HEADER; - - pr_debug("%s(), TSAP sel=%02x\n", __func__, self->stsap_sel); - - /* Need to update dtsap_sel if its equal to LSAP_ANY */ - self->dtsap_sel = lsap->dlsap_sel; - - n = skb->data[0] & 0x7f; - - self->send_credit = n; - self->tx_max_sdu_size = 0; - - parameters = skb->data[0] & 0x80; - - IRDA_ASSERT(skb->len >= TTP_HEADER, return;); - skb_pull(skb, TTP_HEADER); - - if (parameters) { - plen = skb->data[0]; - - ret = irda_param_extract_all(self, skb->data+1, - IRDA_MIN(skb->len-1, plen), - ¶m_info); - - /* Any errors in the parameter list? */ - if (ret < 0) { - net_warn_ratelimited("%s: error extracting parameters\n", - __func__); - dev_kfree_skb(skb); - - /* Do not accept this connection attempt */ - return; - } - - /* Remove parameters */ - skb_pull(skb, IRDA_MIN(skb->len, plen+1)); - } - - if (self->notify.connect_indication) { - self->notify.connect_indication(self->notify.instance, self, - qos, self->tx_max_sdu_size, - self->max_header_size, skb); - } else - dev_kfree_skb(skb); -} - -/* - * Function irttp_connect_response (handle, userdata) - * - * Service user is accepting the connection, just pass it down to - * IrLMP! - * - */ -int irttp_connect_response(struct tsap_cb *self, __u32 max_sdu_size, - struct sk_buff *userdata) -{ - struct sk_buff *tx_skb; - __u8 *frame; - int ret; - __u8 n; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - pr_debug("%s(), Source TSAP selector=%02x\n", __func__, - self->stsap_sel); - - /* Any userdata supplied? */ - if (userdata == NULL) { - tx_skb = alloc_skb(TTP_MAX_HEADER + TTP_SAR_HEADER, - GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* Reserve space for MUX_CONTROL and LAP header */ - skb_reserve(tx_skb, TTP_MAX_HEADER + TTP_SAR_HEADER); - } else { - tx_skb = userdata; - /* - * Check that the client has reserved enough space for - * headers - */ - IRDA_ASSERT(skb_headroom(userdata) >= TTP_MAX_HEADER, - { dev_kfree_skb(userdata); return -1; }); - } - - self->avail_credit = 0; - self->remote_credit = 0; - self->rx_max_sdu_size = max_sdu_size; - self->rx_sdu_size = 0; - self->rx_sdu_busy = FALSE; - - n = self->initial_credit; - - /* Frame has only space for max 127 credits (7 bits) */ - if (n > 127) { - self->avail_credit = n - 127; - n = 127; - } - - self->remote_credit = n; - self->connected = TRUE; - - /* SAR enabled? */ - if (max_sdu_size > 0) { - IRDA_ASSERT(skb_headroom(tx_skb) >= (TTP_MAX_HEADER + TTP_SAR_HEADER), - { dev_kfree_skb(tx_skb); return -1; }); - - /* Insert TTP header with SAR parameters */ - frame = skb_push(tx_skb, TTP_HEADER + TTP_SAR_HEADER); - - frame[0] = TTP_PARAMETERS | n; - frame[1] = 0x04; /* Length */ - - /* irda_param_insert(self, IRTTP_MAX_SDU_SIZE, frame+1, */ -/* TTP_SAR_HEADER, ¶m_info) */ - - frame[2] = 0x01; /* MaxSduSize */ - frame[3] = 0x02; /* Value length */ - - put_unaligned(cpu_to_be16((__u16) max_sdu_size), - (__be16 *)(frame+4)); - } else { - /* Insert TTP header */ - frame = skb_push(tx_skb, TTP_HEADER); - - frame[0] = n & 0x7f; - } - - ret = irlmp_connect_response(self->lsap, tx_skb); - - return ret; -} -EXPORT_SYMBOL(irttp_connect_response); - -/* - * Function irttp_dup (self, instance) - * - * Duplicate TSAP, can be used by servers to confirm a connection on a - * new TSAP so it can keep listening on the old one. - */ -struct tsap_cb *irttp_dup(struct tsap_cb *orig, void *instance) -{ - struct tsap_cb *new; - unsigned long flags; - - /* Protect our access to the old tsap instance */ - spin_lock_irqsave(&irttp->tsaps->hb_spinlock, flags); - - /* Find the old instance */ - if (!hashbin_find(irttp->tsaps, (long) orig, NULL)) { - pr_debug("%s(), unable to find TSAP\n", __func__); - spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); - return NULL; - } - - /* Allocate a new instance */ - new = kmemdup(orig, sizeof(struct tsap_cb), GFP_ATOMIC); - if (!new) { - pr_debug("%s(), unable to kmalloc\n", __func__); - spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); - return NULL; - } - spin_lock_init(&new->lock); - - /* We don't need the old instance any more */ - spin_unlock_irqrestore(&irttp->tsaps->hb_spinlock, flags); - - /* Try to dup the LSAP (may fail if we were too slow) */ - new->lsap = irlmp_dup(orig->lsap, new); - if (!new->lsap) { - pr_debug("%s(), dup failed!\n", __func__); - kfree(new); - return NULL; - } - - /* Not everything should be copied */ - new->notify.instance = instance; - - /* Initialize internal objects */ - irttp_init_tsap(new); - - /* This is locked */ - hashbin_insert(irttp->tsaps, (irda_queue_t *) new, (long) new, NULL); - - return new; -} -EXPORT_SYMBOL(irttp_dup); - -/* - * Function irttp_disconnect_request (self) - * - * Close this connection please! If priority is high, the queued data - * segments, if any, will be deallocated first - * - */ -int irttp_disconnect_request(struct tsap_cb *self, struct sk_buff *userdata, - int priority) -{ - int ret; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return -1;); - - /* Already disconnected? */ - if (!self->connected) { - pr_debug("%s(), already disconnected!\n", __func__); - if (userdata) - dev_kfree_skb(userdata); - return -1; - } - - /* Disconnect already pending ? - * We need to use an atomic operation to prevent reentry. This - * function may be called from various context, like user, timer - * for following a disconnect_indication() (i.e. net_bh). - * Jean II */ - if (test_and_set_bit(0, &self->disconnect_pend)) { - pr_debug("%s(), disconnect already pending\n", - __func__); - if (userdata) - dev_kfree_skb(userdata); - - /* Try to make some progress */ - irttp_run_tx_queue(self); - return -1; - } - - /* - * Check if there is still data segments in the transmit queue - */ - if (!skb_queue_empty(&self->tx_queue)) { - if (priority == P_HIGH) { - /* - * No need to send the queued data, if we are - * disconnecting right now since the data will - * not have any usable connection to be sent on - */ - pr_debug("%s(): High priority!!()\n", __func__); - irttp_flush_queues(self); - } else if (priority == P_NORMAL) { - /* - * Must delay disconnect until after all data segments - * have been sent and the tx_queue is empty - */ - /* We'll reuse this one later for the disconnect */ - self->disconnect_skb = userdata; /* May be NULL */ - - irttp_run_tx_queue(self); - - irttp_start_todo_timer(self, HZ/10); - return -1; - } - } - /* Note : we don't need to check if self->rx_queue is full and the - * state of self->rx_sdu_busy because the disconnect response will - * be sent at the LMP level (so even if the peer has its Tx queue - * full of data). - Jean II */ - - pr_debug("%s(), Disconnecting ...\n", __func__); - self->connected = FALSE; - - if (!userdata) { - struct sk_buff *tx_skb; - tx_skb = alloc_skb(LMP_MAX_HEADER, GFP_ATOMIC); - if (!tx_skb) - return -ENOMEM; - - /* - * Reserve space for MUX and LAP header - */ - skb_reserve(tx_skb, LMP_MAX_HEADER); - - userdata = tx_skb; - } - ret = irlmp_disconnect_request(self->lsap, userdata); - - /* The disconnect is no longer pending */ - clear_bit(0, &self->disconnect_pend); /* FALSE */ - - return ret; -} -EXPORT_SYMBOL(irttp_disconnect_request); - -/* - * Function irttp_disconnect_indication (self, reason) - * - * Disconnect indication, TSAP disconnected by peer? - * - */ -static void irttp_disconnect_indication(void *instance, void *sap, - LM_REASON reason, struct sk_buff *skb) -{ - struct tsap_cb *self; - - self = instance; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == TTP_TSAP_MAGIC, return;); - - /* Prevent higher layer to send more data */ - self->connected = FALSE; - - /* Check if client has already tried to close the TSAP */ - if (self->close_pend) { - /* In this case, the higher layer is probably gone. Don't - * bother it and clean up the remains - Jean II */ - if (skb) - dev_kfree_skb(skb); - irttp_close_tsap(self); - return; - } - - /* If we are here, we assume that is the higher layer is still - * waiting for the disconnect notification and able to process it, - * even if he tried to disconnect. Otherwise, it would have already - * attempted to close the tsap and self->close_pend would be TRUE. - * Jean II */ - - /* No need to notify the client if has already tried to disconnect */ - if (self->notify.disconnect_indication) - self->notify.disconnect_indication(self->notify.instance, self, - reason, skb); - else - if (skb) - dev_kfree_skb(skb); -} - -/* - * Function irttp_do_data_indication (self, skb) - * - * Try to deliver reassembled skb to layer above, and requeue it if that - * for some reason should fail. We mark rx sdu as busy to apply back - * pressure is necessary. - */ -static void irttp_do_data_indication(struct tsap_cb *self, struct sk_buff *skb) -{ - int err; - - /* Check if client has already closed the TSAP and gone away */ - if (self->close_pend) { - dev_kfree_skb(skb); - return; - } - - err = self->notify.data_indication(self->notify.instance, self, skb); - - /* Usually the layer above will notify that it's input queue is - * starting to get filled by using the flow request, but this may - * be difficult, so it can instead just refuse to eat it and just - * give an error back - */ - if (err) { - pr_debug("%s() requeueing skb!\n", __func__); - - /* Make sure we take a break */ - self->rx_sdu_busy = TRUE; - - /* Need to push the header in again */ - skb_push(skb, TTP_HEADER); - skb->data[0] = 0x00; /* Make sure MORE bit is cleared */ - - /* Put skb back on queue */ - skb_queue_head(&self->rx_queue, skb); - } -} - -/* - * Function irttp_run_rx_queue (self) - * - * Check if we have any frames to be transmitted, or if we have any - * available credit to give away. - */ -static void irttp_run_rx_queue(struct tsap_cb *self) -{ - struct sk_buff *skb; - int more = 0; - - pr_debug("%s() send=%d,avail=%d,remote=%d\n", __func__, - self->send_credit, self->avail_credit, self->remote_credit); - - /* Get exclusive access to the rx queue, otherwise don't touch it */ - if (irda_lock(&self->rx_queue_lock) == FALSE) - return; - - /* - * Reassemble all frames in receive queue and deliver them - */ - while (!self->rx_sdu_busy && (skb = skb_dequeue(&self->rx_queue))) { - /* This bit will tell us if it's the last fragment or not */ - more = skb->data[0] & 0x80; - - /* Remove TTP header */ - skb_pull(skb, TTP_HEADER); - - /* Add the length of the remaining data */ - self->rx_sdu_size += skb->len; - - /* - * If SAR is disabled, or user has requested no reassembly - * of received fragments then we just deliver them - * immediately. This can be requested by clients that - * implements byte streams without any message boundaries - */ - if (self->rx_max_sdu_size == TTP_SAR_DISABLE) { - irttp_do_data_indication(self, skb); - self->rx_sdu_size = 0; - - continue; - } - - /* Check if this is a fragment, and not the last fragment */ - if (more) { - /* - * Queue the fragment if we still are within the - * limits of the maximum size of the rx_sdu - */ - if (self->rx_sdu_size <= self->rx_max_sdu_size) { - pr_debug("%s(), queueing frag\n", - __func__); - skb_queue_tail(&self->rx_fragments, skb); - } else { - /* Free the part of the SDU that is too big */ - dev_kfree_skb(skb); - } - continue; - } - /* - * This is the last fragment, so time to reassemble! - */ - if ((self->rx_sdu_size <= self->rx_max_sdu_size) || - (self->rx_max_sdu_size == TTP_SAR_UNBOUND)) { - /* - * A little optimizing. Only queue the fragment if - * there are other fragments. Since if this is the - * last and only fragment, there is no need to - * reassemble :-) - */ - if (!skb_queue_empty(&self->rx_fragments)) { - skb_queue_tail(&self->rx_fragments, - skb); - - skb = irttp_reassemble_skb(self); - } - - /* Now we can deliver the reassembled skb */ - irttp_do_data_indication(self, skb); - } else { - pr_debug("%s(), Truncated frame\n", __func__); - - /* Free the part of the SDU that is too big */ - dev_kfree_skb(skb); - - /* Deliver only the valid but truncated part of SDU */ - skb = irttp_reassemble_skb(self); - - irttp_do_data_indication(self, skb); - } - self->rx_sdu_size = 0; - } - - /* - * It's not trivial to keep track of how many credits are available - * by incrementing at each packet, because delivery may fail - * (irttp_do_data_indication() may requeue the frame) and because - * we need to take care of fragmentation. - * We want the other side to send up to initial_credit packets. - * We have some frames in our queues, and we have already allowed it - * to send remote_credit. - * No need to spinlock, write is atomic and self correcting... - * Jean II - */ - self->avail_credit = (self->initial_credit - - (self->remote_credit + - skb_queue_len(&self->rx_queue) + - skb_queue_len(&self->rx_fragments))); - - /* Do we have too much credits to send to peer ? */ - if ((self->remote_credit <= TTP_RX_MIN_CREDIT) && - (self->avail_credit > 0)) { - /* Send explicit credit frame */ - irttp_give_credit(self); - /* Note : do *NOT* check if tx_queue is non-empty, that - * will produce deadlocks. I repeat : send a credit frame - * even if we have something to send in our Tx queue. - * If we have credits, it means that our Tx queue is blocked. - * - * Let's suppose the peer can't keep up with our Tx. He will - * flow control us by not sending us any credits, and we - * will stop Tx and start accumulating credits here. - * Up to the point where the peer will stop its Tx queue, - * for lack of credits. - * Let's assume the peer application is single threaded. - * It will block on Tx and never consume any Rx buffer. - * Deadlock. Guaranteed. - Jean II - */ - } - - /* Reset lock */ - self->rx_queue_lock = 0; -} - -#ifdef CONFIG_PROC_FS -struct irttp_iter_state { - int id; -}; - -static void *irttp_seq_start(struct seq_file *seq, loff_t *pos) -{ - struct irttp_iter_state *iter = seq->private; - struct tsap_cb *self; - - /* Protect our access to the tsap list */ - spin_lock_irq(&irttp->tsaps->hb_spinlock); - iter->id = 0; - - for (self = (struct tsap_cb *) hashbin_get_first(irttp->tsaps); - self != NULL; - self = (struct tsap_cb *) hashbin_get_next(irttp->tsaps)) { - if (iter->id == *pos) - break; - ++iter->id; - } - - return self; -} - -static void *irttp_seq_next(struct seq_file *seq, void *v, loff_t *pos) -{ - struct irttp_iter_state *iter = seq->private; - - ++*pos; - ++iter->id; - return (void *) hashbin_get_next(irttp->tsaps); -} - -static void irttp_seq_stop(struct seq_file *seq, void *v) -{ - spin_unlock_irq(&irttp->tsaps->hb_spinlock); -} - -static int irttp_seq_show(struct seq_file *seq, void *v) -{ - const struct irttp_iter_state *iter = seq->private; - const struct tsap_cb *self = v; - - seq_printf(seq, "TSAP %d, ", iter->id); - seq_printf(seq, "stsap_sel: %02x, ", - self->stsap_sel); - seq_printf(seq, "dtsap_sel: %02x\n", - self->dtsap_sel); - seq_printf(seq, " connected: %s, ", - self->connected ? "TRUE" : "FALSE"); - seq_printf(seq, "avail credit: %d, ", - self->avail_credit); - seq_printf(seq, "remote credit: %d, ", - self->remote_credit); - seq_printf(seq, "send credit: %d\n", - self->send_credit); - seq_printf(seq, " tx packets: %lu, ", - self->stats.tx_packets); - seq_printf(seq, "rx packets: %lu, ", - self->stats.rx_packets); - seq_printf(seq, "tx_queue len: %u ", - skb_queue_len(&self->tx_queue)); - seq_printf(seq, "rx_queue len: %u\n", - skb_queue_len(&self->rx_queue)); - seq_printf(seq, " tx_sdu_busy: %s, ", - self->tx_sdu_busy ? "TRUE" : "FALSE"); - seq_printf(seq, "rx_sdu_busy: %s\n", - self->rx_sdu_busy ? "TRUE" : "FALSE"); - seq_printf(seq, " max_seg_size: %u, ", - self->max_seg_size); - seq_printf(seq, "tx_max_sdu_size: %u, ", - self->tx_max_sdu_size); - seq_printf(seq, "rx_max_sdu_size: %u\n", - self->rx_max_sdu_size); - - seq_printf(seq, " Used by (%s)\n\n", - self->notify.name); - return 0; -} - -static const struct seq_operations irttp_seq_ops = { - .start = irttp_seq_start, - .next = irttp_seq_next, - .stop = irttp_seq_stop, - .show = irttp_seq_show, -}; - -static int irttp_seq_open(struct inode *inode, struct file *file) -{ - return seq_open_private(file, &irttp_seq_ops, - sizeof(struct irttp_iter_state)); -} - -const struct file_operations irttp_seq_fops = { - .owner = THIS_MODULE, - .open = irttp_seq_open, - .read = seq_read, - .llseek = seq_lseek, - .release = seq_release_private, -}; - -#endif /* PROC_FS */ diff --git a/net/irda/parameters.c b/net/irda/parameters.c deleted file mode 100644 index 16ce32ffe004..000000000000 --- a/net/irda/parameters.c +++ /dev/null @@ -1,584 +0,0 @@ -/********************************************************************* - * - * Filename: parameters.c - * Version: 1.0 - * Description: A more general way to handle (pi,pl,pv) parameters - * Status: Experimental. - * Author: Dag Brattli - * Created at: Mon Jun 7 10:25:11 1999 - * Modified at: Sun Jan 30 14:08:39 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1999-2000 Dag Brattli, All Rights Reserved. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include -#include - -#include -#include - -#include -#include - -static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); -static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); -static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); -static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); - -static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); -static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func); - -static int irda_param_unpack(__u8 *buf, char *fmt, ...); - -/* Parameter value call table. Must match PV_TYPE */ -static const PV_HANDLER pv_extract_table[] = { - irda_extract_integer, /* Handler for any length integers */ - irda_extract_integer, /* Handler for 8 bits integers */ - irda_extract_integer, /* Handler for 16 bits integers */ - irda_extract_string, /* Handler for strings */ - irda_extract_integer, /* Handler for 32 bits integers */ - irda_extract_octseq, /* Handler for octet sequences */ - irda_extract_no_value /* Handler for no value parameters */ -}; - -static const PV_HANDLER pv_insert_table[] = { - irda_insert_integer, /* Handler for any length integers */ - irda_insert_integer, /* Handler for 8 bits integers */ - irda_insert_integer, /* Handler for 16 bits integers */ - NULL, /* Handler for strings */ - irda_insert_integer, /* Handler for 32 bits integers */ - NULL, /* Handler for octet sequences */ - irda_insert_no_value /* Handler for no value parameters */ -}; - -/* - * Function irda_insert_no_value (self, buf, len, pi, type, func) - */ -static int irda_insert_no_value(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - int ret; - - p.pi = pi; - p.pl = 0; - - /* Call handler for this parameter */ - ret = (*func)(self, &p, PV_GET); - - /* Extract values anyway, since handler may need them */ - irda_param_pack(buf, "bb", p.pi, p.pl); - - if (ret < 0) - return ret; - - return 2; /* Inserted pl+2 bytes */ -} - -/* - * Function irda_extract_no_value (self, buf, len, type, func) - * - * Extracts a parameter without a pv field (pl=0) - * - */ -static int irda_extract_no_value(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - int ret; - - /* Extract values anyway, since handler may need them */ - irda_param_unpack(buf, "bb", &p.pi, &p.pl); - - /* Call handler for this parameter */ - ret = (*func)(self, &p, PV_PUT); - - if (ret < 0) - return ret; - - return 2; /* Extracted pl+2 bytes */ -} - -/* - * Function irda_insert_integer (self, buf, len, pi, type, func) - */ -static int irda_insert_integer(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - int n = 0; - int err; - - p.pi = pi; /* In case handler needs to know */ - p.pl = type & PV_MASK; /* The integer type codes the length as well */ - p.pv.i = 0; /* Clear value */ - - /* Call handler for this parameter */ - err = (*func)(self, &p, PV_GET); - if (err < 0) - return err; - - /* - * If parameter length is still 0, then (1) this is an any length - * integer, and (2) the handler function does not care which length - * we choose to use, so we pick the one the gives the fewest bytes. - */ - if (p.pl == 0) { - if (p.pv.i < 0xff) { - pr_debug("%s(), using 1 byte\n", __func__); - p.pl = 1; - } else if (p.pv.i < 0xffff) { - pr_debug("%s(), using 2 bytes\n", __func__); - p.pl = 2; - } else { - pr_debug("%s(), using 4 bytes\n", __func__); - p.pl = 4; /* Default length */ - } - } - /* Check if buffer is long enough for insertion */ - if (len < (2+p.pl)) { - net_warn_ratelimited("%s: buffer too short for insertion!\n", - __func__); - return -1; - } - pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__, - p.pi, p.pl, p.pv.i); - switch (p.pl) { - case 1: - n += irda_param_pack(buf, "bbb", p.pi, p.pl, (__u8) p.pv.i); - break; - case 2: - if (type & PV_BIG_ENDIAN) - p.pv.i = cpu_to_be16((__u16) p.pv.i); - else - p.pv.i = cpu_to_le16((__u16) p.pv.i); - n += irda_param_pack(buf, "bbs", p.pi, p.pl, (__u16) p.pv.i); - break; - case 4: - if (type & PV_BIG_ENDIAN) - cpu_to_be32s(&p.pv.i); - else - cpu_to_le32s(&p.pv.i); - n += irda_param_pack(buf, "bbi", p.pi, p.pl, p.pv.i); - - break; - default: - net_warn_ratelimited("%s: length %d not supported\n", - __func__, p.pl); - /* Skip parameter */ - return -1; - } - - return p.pl+2; /* Inserted pl+2 bytes */ -} - -/* - * Function irda_extract integer (self, buf, len, pi, type, func) - * - * Extract a possibly variable length integer from buffer, and call - * handler for processing of the parameter - */ -static int irda_extract_integer(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - int n = 0; - int extract_len; /* Real length we extract */ - int err; - - p.pi = pi; /* In case handler needs to know */ - p.pl = buf[1]; /* Extract length of value */ - p.pv.i = 0; /* Clear value */ - extract_len = p.pl; /* Default : extract all */ - - /* Check if buffer is long enough for parsing */ - if (len < (2+p.pl)) { - net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", - __func__, p.pl, len); - return -1; - } - - /* - * Check that the integer length is what we expect it to be. If the - * handler want a 16 bits integer then a 32 bits is not good enough - * PV_INTEGER means that the handler is flexible. - */ - if (((type & PV_MASK) != PV_INTEGER) && ((type & PV_MASK) != p.pl)) { - net_err_ratelimited("%s: invalid parameter length! Expected %d bytes, but value had %d bytes!\n", - __func__, type & PV_MASK, p.pl); - - /* Most parameters are bit/byte fields or little endian, - * so it's ok to only extract a subset of it (the subset - * that the handler expect). This is necessary, as some - * broken implementations seems to add extra undefined bits. - * If the parameter is shorter than we expect or is big - * endian, we can't play those tricks. Jean II */ - if((p.pl < (type & PV_MASK)) || (type & PV_BIG_ENDIAN)) { - /* Skip parameter */ - return p.pl+2; - } else { - /* Extract subset of it, fallthrough */ - extract_len = type & PV_MASK; - } - } - - - switch (extract_len) { - case 1: - n += irda_param_unpack(buf+2, "b", &p.pv.i); - break; - case 2: - n += irda_param_unpack(buf+2, "s", &p.pv.i); - if (type & PV_BIG_ENDIAN) - p.pv.i = be16_to_cpu((__u16) p.pv.i); - else - p.pv.i = le16_to_cpu((__u16) p.pv.i); - break; - case 4: - n += irda_param_unpack(buf+2, "i", &p.pv.i); - if (type & PV_BIG_ENDIAN) - be32_to_cpus(&p.pv.i); - else - le32_to_cpus(&p.pv.i); - break; - default: - net_warn_ratelimited("%s: length %d not supported\n", - __func__, p.pl); - - /* Skip parameter */ - return p.pl+2; - } - - pr_debug("%s(), pi=%#x, pl=%d, pi=%d\n", __func__, - p.pi, p.pl, p.pv.i); - /* Call handler for this parameter */ - err = (*func)(self, &p, PV_PUT); - if (err < 0) - return err; - - return p.pl+2; /* Extracted pl+2 bytes */ -} - -/* - * Function irda_extract_string (self, buf, len, type, func) - */ -static int irda_extract_string(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - char str[33]; - irda_param_t p; - int err; - - p.pi = pi; /* In case handler needs to know */ - p.pl = buf[1]; /* Extract length of value */ - if (p.pl > 32) - p.pl = 32; - - pr_debug("%s(), pi=%#x, pl=%d\n", __func__, - p.pi, p.pl); - - /* Check if buffer is long enough for parsing */ - if (len < (2+p.pl)) { - net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", - __func__, p.pl, len); - return -1; - } - - /* Should be safe to copy string like this since we have already - * checked that the buffer is long enough */ - strncpy(str, buf+2, p.pl); - - pr_debug("%s(), str=0x%02x 0x%02x\n", - __func__, (__u8)str[0], (__u8)str[1]); - - /* Null terminate string */ - str[p.pl] = '\0'; - - p.pv.c = str; /* Handler will need to take a copy */ - - /* Call handler for this parameter */ - err = (*func)(self, &p, PV_PUT); - if (err < 0) - return err; - - return p.pl+2; /* Extracted pl+2 bytes */ -} - -/* - * Function irda_extract_octseq (self, buf, len, type, func) - */ -static int irda_extract_octseq(void *self, __u8 *buf, int len, __u8 pi, - PV_TYPE type, PI_HANDLER func) -{ - irda_param_t p; - - p.pi = pi; /* In case handler needs to know */ - p.pl = buf[1]; /* Extract length of value */ - - /* Check if buffer is long enough for parsing */ - if (len < (2+p.pl)) { - net_warn_ratelimited("%s: buffer too short for parsing! Need %d bytes, but len is only %d\n", - __func__, p.pl, len); - return -1; - } - - pr_debug("%s(), not impl\n", __func__); - - return p.pl+2; /* Extracted pl+2 bytes */ -} - -/* - * Function irda_param_pack (skb, fmt, ...) - * - * Format: - * 'i' = 32 bits integer - * 's' = string - * - */ -int irda_param_pack(__u8 *buf, char *fmt, ...) -{ - irda_pv_t arg; - va_list args; - char *p; - int n = 0; - - va_start(args, fmt); - - for (p = fmt; *p != '\0'; p++) { - switch (*p) { - case 'b': /* 8 bits unsigned byte */ - buf[n++] = (__u8)va_arg(args, int); - break; - case 's': /* 16 bits unsigned short */ - arg.i = (__u16)va_arg(args, int); - put_unaligned((__u16)arg.i, (__u16 *)(buf+n)); n+=2; - break; - case 'i': /* 32 bits unsigned integer */ - arg.i = va_arg(args, __u32); - put_unaligned(arg.i, (__u32 *)(buf+n)); n+=4; - break; -#if 0 - case 'c': /* \0 terminated string */ - arg.c = va_arg(args, char *); - strcpy(buf+n, arg.c); - n += strlen(arg.c) + 1; - break; -#endif - default: - va_end(args); - return -1; - } - } - va_end(args); - - return 0; -} -EXPORT_SYMBOL(irda_param_pack); - -/* - * Function irda_param_unpack (skb, fmt, ...) - */ -static int irda_param_unpack(__u8 *buf, char *fmt, ...) -{ - irda_pv_t arg; - va_list args; - char *p; - int n = 0; - - va_start(args, fmt); - - for (p = fmt; *p != '\0'; p++) { - switch (*p) { - case 'b': /* 8 bits byte */ - arg.ip = va_arg(args, __u32 *); - *arg.ip = buf[n++]; - break; - case 's': /* 16 bits short */ - arg.ip = va_arg(args, __u32 *); - *arg.ip = get_unaligned((__u16 *)(buf+n)); n+=2; - break; - case 'i': /* 32 bits unsigned integer */ - arg.ip = va_arg(args, __u32 *); - *arg.ip = get_unaligned((__u32 *)(buf+n)); n+=4; - break; -#if 0 - case 'c': /* \0 terminated string */ - arg.c = va_arg(args, char *); - strcpy(arg.c, buf+n); - n += strlen(arg.c) + 1; - break; -#endif - default: - va_end(args); - return -1; - } - - } - va_end(args); - - return 0; -} - -/* - * Function irda_param_insert (self, pi, buf, len, info) - * - * Insert the specified parameter (pi) into buffer. Returns number of - * bytes inserted - */ -int irda_param_insert(void *self, __u8 pi, __u8 *buf, int len, - pi_param_info_t *info) -{ - const pi_minor_info_t *pi_minor_info; - __u8 pi_minor; - __u8 pi_major; - int type; - int ret = -1; - int n = 0; - - IRDA_ASSERT(buf != NULL, return ret;); - IRDA_ASSERT(info != NULL, return ret;); - - pi_minor = pi & info->pi_mask; - pi_major = pi >> info->pi_major_offset; - - /* Check if the identifier value (pi) is valid */ - if ((pi_major > info->len-1) || - (pi_minor > info->tables[pi_major].len-1)) - { - pr_debug("%s(), no handler for parameter=0x%02x\n", - __func__, pi); - - /* Skip this parameter */ - return -1; - } - - /* Lookup the info on how to parse this parameter */ - pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor]; - - /* Find expected data type for this parameter identifier (pi)*/ - type = pi_minor_info->type; - - /* Check if handler has been implemented */ - if (!pi_minor_info->func) { - net_info_ratelimited("%s: no handler for pi=%#x\n", - __func__, pi); - /* Skip this parameter */ - return -1; - } - - /* Insert parameter value */ - ret = (*pv_insert_table[type & PV_MASK])(self, buf+n, len, pi, type, - pi_minor_info->func); - return ret; -} -EXPORT_SYMBOL(irda_param_insert); - -/* - * Function irda_param_extract (self, buf, len, info) - * - * Parse all parameters. If len is correct, then everything should be - * safe. Returns the number of bytes that was parsed - * - */ -static int irda_param_extract(void *self, __u8 *buf, int len, - pi_param_info_t *info) -{ - const pi_minor_info_t *pi_minor_info; - __u8 pi_minor; - __u8 pi_major; - int type; - int ret = -1; - int n = 0; - - IRDA_ASSERT(buf != NULL, return ret;); - IRDA_ASSERT(info != NULL, return ret;); - - pi_minor = buf[n] & info->pi_mask; - pi_major = buf[n] >> info->pi_major_offset; - - /* Check if the identifier value (pi) is valid */ - if ((pi_major > info->len-1) || - (pi_minor > info->tables[pi_major].len-1)) - { - pr_debug("%s(), no handler for parameter=0x%02x\n", - __func__, buf[0]); - - /* Skip this parameter */ - return 2 + buf[n + 1]; /* Continue */ - } - - /* Lookup the info on how to parse this parameter */ - pi_minor_info = &info->tables[pi_major].pi_minor_call_table[pi_minor]; - - /* Find expected data type for this parameter identifier (pi)*/ - type = pi_minor_info->type; - - pr_debug("%s(), pi=[%d,%d], type=%d\n", __func__, - pi_major, pi_minor, type); - - /* Check if handler has been implemented */ - if (!pi_minor_info->func) { - net_info_ratelimited("%s: no handler for pi=%#x\n", - __func__, buf[n]); - /* Skip this parameter */ - return 2 + buf[n + 1]; /* Continue */ - } - - /* Parse parameter value */ - ret = (*pv_extract_table[type & PV_MASK])(self, buf+n, len, buf[n], - type, pi_minor_info->func); - return ret; -} - -/* - * Function irda_param_extract_all (self, buf, len, info) - * - * Parse all parameters. If len is correct, then everything should be - * safe. Returns the number of bytes that was parsed - * - */ -int irda_param_extract_all(void *self, __u8 *buf, int len, - pi_param_info_t *info) -{ - int ret = -1; - int n = 0; - - IRDA_ASSERT(buf != NULL, return ret;); - IRDA_ASSERT(info != NULL, return ret;); - - /* - * Parse all parameters. Each parameter must be at least two bytes - * long or else there is no point in trying to parse it - */ - while (len > 2) { - ret = irda_param_extract(self, buf+n, len, info); - if (ret < 0) - return ret; - - n += ret; - len -= ret; - } - return n; -} -EXPORT_SYMBOL(irda_param_extract_all); diff --git a/net/irda/qos.c b/net/irda/qos.c deleted file mode 100644 index 25ba8509ad3e..000000000000 --- a/net/irda/qos.c +++ /dev/null @@ -1,771 +0,0 @@ -/********************************************************************* - * - * Filename: qos.c - * Version: 1.0 - * Description: IrLAP QoS parameter negotiation - * Status: Stable - * Author: Dag Brattli - * Created at: Tue Sep 9 00:00:26 1997 - * Modified at: Sun Jan 30 14:29:16 2000 - * Modified by: Dag Brattli - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2001 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * 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, see . - * - ********************************************************************/ - -#include - -#include - -#include -#include -#include -#include -#include - -/* - * Maximum values of the baud rate we negotiate with the other end. - * Most often, you don't have to change that, because Linux-IrDA will - * use the maximum offered by the link layer, which usually works fine. - * In some very rare cases, you may want to limit it to lower speeds... - */ -int sysctl_max_baud_rate = 16000000; -/* - * Maximum value of the lap disconnect timer we negotiate with the other end. - * Most often, the value below represent the best compromise, but some user - * may want to keep the LAP alive longer or shorter in case of link failure. - * Remember that the threshold time (early warning) is fixed to 3s... - */ -int sysctl_max_noreply_time = 12; -/* - * Minimum turn time to be applied before transmitting to the peer. - * Nonzero values (usec) are used as lower limit to the per-connection - * mtt value which was announced by the other end during negotiation. - * Might be helpful if the peer device provides too short mtt. - * Default is 10us which means using the unmodified value given by the - * peer except if it's 0 (0 is likely a bug in the other stack). - */ -unsigned int sysctl_min_tx_turn_time = 10; -/* - * Maximum data size to be used in transmission in payload of LAP frame. - * There is a bit of confusion in the IrDA spec : - * The LAP spec defines the payload of a LAP frame (I field) to be - * 2048 bytes max (IrLAP 1.1, chapt 6.6.5, p40). - * On the other hand, the PHY mention frames of 2048 bytes max (IrPHY - * 1.2, chapt 5.3.2.1, p41). But, this number includes the LAP header - * (2 bytes), and CRC (32 bits at 4 Mb/s). So, for the I field (LAP - * payload), that's only 2042 bytes. Oups ! - * My nsc-ircc hardware has troubles receiving 2048 bytes frames at 4 Mb/s, - * so adjust to 2042... I don't know if this bug applies only for 2048 - * bytes frames or all negotiated frame sizes, but you can use the sysctl - * to play with this value anyway. - * Jean II */ -unsigned int sysctl_max_tx_data_size = 2042; -/* - * Maximum transmit window, i.e. number of LAP frames between turn-around. - * This allow to override what the peer told us. Some peers are buggy and - * don't always support what they tell us. - * Jean II */ -unsigned int sysctl_max_tx_window = 7; - -static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get); -static int irlap_param_link_disconnect(void *instance, irda_param_t *parm, - int get); -static int irlap_param_max_turn_time(void *instance, irda_param_t *param, - int get); -static int irlap_param_data_size(void *instance, irda_param_t *param, int get); -static int irlap_param_window_size(void *instance, irda_param_t *param, - int get); -static int irlap_param_additional_bofs(void *instance, irda_param_t *parm, - int get); -static int irlap_param_min_turn_time(void *instance, irda_param_t *param, - int get); - -#ifndef CONFIG_IRDA_DYNAMIC_WINDOW -static __u32 irlap_requested_line_capacity(struct qos_info *qos); -#endif - -static __u32 min_turn_times[] = { 10000, 5000, 1000, 500, 100, 50, 10, 0 }; /* us */ -static __u32 baud_rates[] = { 2400, 9600, 19200, 38400, 57600, 115200, 576000, - 1152000, 4000000, 16000000 }; /* bps */ -static __u32 data_sizes[] = { 64, 128, 256, 512, 1024, 2048 }; /* bytes */ -static __u32 add_bofs[] = { 48, 24, 12, 5, 3, 2, 1, 0 }; /* bytes */ -static __u32 max_turn_times[] = { 500, 250, 100, 50 }; /* ms */ -static __u32 link_disc_times[] = { 3, 8, 12, 16, 20, 25, 30, 40 }; /* secs */ - -static __u32 max_line_capacities[10][4] = { - /* 500 ms 250 ms 100 ms 50 ms (max turn time) */ - { 100, 0, 0, 0 }, /* 2400 bps */ - { 400, 0, 0, 0 }, /* 9600 bps */ - { 800, 0, 0, 0 }, /* 19200 bps */ - { 1600, 0, 0, 0 }, /* 38400 bps */ - { 2360, 0, 0, 0 }, /* 57600 bps */ - { 4800, 2400, 960, 480 }, /* 115200 bps */ - { 28800, 11520, 5760, 2880 }, /* 576000 bps */ - { 57600, 28800, 11520, 5760 }, /* 1152000 bps */ - { 200000, 100000, 40000, 20000 }, /* 4000000 bps */ - { 800000, 400000, 160000, 80000 }, /* 16000000 bps */ -}; - -static const pi_minor_info_t pi_minor_call_table_type_0[] = { - { NULL, 0 }, -/* 01 */{ irlap_param_baud_rate, PV_INTEGER | PV_LITTLE_ENDIAN }, - { NULL, 0 }, - { NULL, 0 }, - { NULL, 0 }, - { NULL, 0 }, - { NULL, 0 }, - { NULL, 0 }, -/* 08 */{ irlap_param_link_disconnect, PV_INT_8_BITS } -}; - -static const pi_minor_info_t pi_minor_call_table_type_1[] = { - { NULL, 0 }, - { NULL, 0 }, -/* 82 */{ irlap_param_max_turn_time, PV_INT_8_BITS }, -/* 83 */{ irlap_param_data_size, PV_INT_8_BITS }, -/* 84 */{ irlap_param_window_size, PV_INT_8_BITS }, -/* 85 */{ irlap_param_additional_bofs, PV_INT_8_BITS }, -/* 86 */{ irlap_param_min_turn_time, PV_INT_8_BITS }, -}; - -static const pi_major_info_t pi_major_call_table[] = { - { pi_minor_call_table_type_0, 9 }, - { pi_minor_call_table_type_1, 7 }, -}; - -static pi_param_info_t irlap_param_info = { pi_major_call_table, 2, 0x7f, 7 }; - -/* ---------------------- LOCAL SUBROUTINES ---------------------- */ -/* Note : we start with a bunch of local subroutines. - * As the compiler is "one pass", this is the only way to get them to - * inline properly... - * Jean II - */ -/* - * Function value_index (value, array, size) - * - * Returns the index to the value in the specified array - */ -static inline int value_index(__u32 value, __u32 *array, int size) -{ - int i; - - for (i=0; i < size; i++) - if (array[i] == value) - break; - return i; -} - -/* - * Function index_value (index, array) - * - * Returns value to index in array, easy! - * - */ -static inline __u32 index_value(int index, __u32 *array) -{ - return array[index]; -} - -/* - * Function msb_index (word) - * - * Returns index to most significant bit (MSB) in word - * - */ -static int msb_index (__u16 word) -{ - __u16 msb = 0x8000; - int index = 15; /* Current MSB */ - - /* Check for buggy peers. - * Note : there is a small probability that it could be us, but I - * would expect driver authors to catch that pretty early and be - * able to check precisely what's going on. If a end user sees this, - * it's very likely the peer. - Jean II */ - if (word == 0) { - net_warn_ratelimited("%s(), Detected buggy peer, adjust null PV to 0x1!\n", - __func__); - /* The only safe choice (we don't know the array size) */ - word = 0x1; - } - - while (msb) { - if (word & msb) - break; /* Found it! */ - msb >>=1; - index--; - } - return index; -} - -/* - * Function value_lower_bits (value, array) - * - * Returns a bit field marking all possibility lower than value. - */ -static inline int value_lower_bits(__u32 value, __u32 *array, int size, __u16 *field) -{ - int i; - __u16 mask = 0x1; - __u16 result = 0x0; - - for (i=0; i < size; i++) { - /* Add the current value to the bit field, shift mask */ - result |= mask; - mask <<= 1; - /* Finished ? */ - if (array[i] >= value) - break; - } - /* Send back a valid index */ - if(i >= size) - i = size - 1; /* Last item */ - *field = result; - return i; -} - -/* - * Function value_highest_bit (value, array) - * - * Returns a bit field marking the highest possibility lower than value. - */ -static inline int value_highest_bit(__u32 value, __u32 *array, int size, __u16 *field) -{ - int i; - __u16 mask = 0x1; - __u16 result = 0x0; - - for (i=0; i < size; i++) { - /* Finished ? */ - if (array[i] <= value) - break; - /* Shift mask */ - mask <<= 1; - } - /* Set the current value to the bit field */ - result |= mask; - /* Send back a valid index */ - if(i >= size) - i = size - 1; /* Last item */ - *field = result; - return i; -} - -/* -------------------------- MAIN CALLS -------------------------- */ - -/* - * Function irda_qos_compute_intersection (qos, new) - * - * Compute the intersection of the old QoS capabilities with new ones - * - */ -void irda_qos_compute_intersection(struct qos_info *qos, struct qos_info *new) -{ - IRDA_ASSERT(qos != NULL, return;); - IRDA_ASSERT(new != NULL, return;); - - /* Apply */ - qos->baud_rate.bits &= new->baud_rate.bits; - qos->window_size.bits &= new->window_size.bits; - qos->min_turn_time.bits &= new->min_turn_time.bits; - qos->max_turn_time.bits &= new->max_turn_time.bits; - qos->data_size.bits &= new->data_size.bits; - qos->link_disc_time.bits &= new->link_disc_time.bits; - qos->additional_bofs.bits &= new->additional_bofs.bits; - - irda_qos_bits_to_value(qos); -} - -/* - * Function irda_init_max_qos_capabilies (qos) - * - * The purpose of this function is for layers and drivers to be able to - * set the maximum QoS possible and then "and in" their own limitations - * - */ -void irda_init_max_qos_capabilies(struct qos_info *qos) -{ - int i; - /* - * These are the maximum supported values as specified on pages - * 39-43 in IrLAP - */ - - /* Use sysctl to set some configurable values... */ - /* Set configured max speed */ - i = value_lower_bits(sysctl_max_baud_rate, baud_rates, 10, - &qos->baud_rate.bits); - sysctl_max_baud_rate = index_value(i, baud_rates); - - /* Set configured max disc time */ - i = value_lower_bits(sysctl_max_noreply_time, link_disc_times, 8, - &qos->link_disc_time.bits); - sysctl_max_noreply_time = index_value(i, link_disc_times); - - /* LSB is first byte, MSB is second byte */ - qos->baud_rate.bits &= 0x03ff; - - qos->window_size.bits = 0x7f; - qos->min_turn_time.bits = 0xff; - qos->max_turn_time.bits = 0x0f; - qos->data_size.bits = 0x3f; - qos->link_disc_time.bits &= 0xff; - qos->additional_bofs.bits = 0xff; -} -EXPORT_SYMBOL(irda_init_max_qos_capabilies); - -/* - * Function irlap_adjust_qos_settings (qos) - * - * Adjust QoS settings in case some values are not possible to use because - * of other settings - */ -static void irlap_adjust_qos_settings(struct qos_info *qos) -{ - __u32 line_capacity; - int index; - - /* - * Make sure the mintt is sensible. - * Main culprit : Ericsson T39. - Jean II - */ - if (sysctl_min_tx_turn_time > qos->min_turn_time.value) { - int i; - - net_warn_ratelimited("%s(), Detected buggy peer, adjust mtt to %dus!\n", - __func__, sysctl_min_tx_turn_time); - - /* We don't really need bits, but easier this way */ - i = value_highest_bit(sysctl_min_tx_turn_time, min_turn_times, - 8, &qos->min_turn_time.bits); - sysctl_min_tx_turn_time = index_value(i, min_turn_times); - qos->min_turn_time.value = sysctl_min_tx_turn_time; - } - - /* - * Not allowed to use a max turn time less than 500 ms if the baudrate - * is less than 115200 - */ - if ((qos->baud_rate.value < 115200) && - (qos->max_turn_time.value < 500)) - { - pr_debug("%s(), adjusting max turn time from %d to 500 ms\n", - __func__, qos->max_turn_time.value); - qos->max_turn_time.value = 500; - } - - /* - * The data size must be adjusted according to the baud rate and max - * turn time - */ - index = value_index(qos->data_size.value, data_sizes, 6); - line_capacity = irlap_max_line_capacity(qos->baud_rate.value, - qos->max_turn_time.value); - -#ifdef CONFIG_IRDA_DYNAMIC_WINDOW - while ((qos->data_size.value > line_capacity) && (index > 0)) { - qos->data_size.value = data_sizes[index--]; - pr_debug("%s(), reducing data size to %d\n", - __func__, qos->data_size.value); - } -#else /* Use method described in section 6.6.11 of IrLAP */ - while (irlap_requested_line_capacity(qos) > line_capacity) { - IRDA_ASSERT(index != 0, return;); - - /* Must be able to send at least one frame */ - if (qos->window_size.value > 1) { - qos->window_size.value--; - pr_debug("%s(), reducing window size to %d\n", - __func__, qos->window_size.value); - } else if (index > 1) { - qos->data_size.value = data_sizes[index--]; - pr_debug("%s(), reducing data size to %d\n", - __func__, qos->data_size.value); - } else { - net_warn_ratelimited("%s(), nothing more we can do!\n", - __func__); - } - } -#endif /* CONFIG_IRDA_DYNAMIC_WINDOW */ - /* - * Fix tx data size according to user limits - Jean II - */ - if (qos->data_size.value > sysctl_max_tx_data_size) - /* Allow non discrete adjustement to avoid losing capacity */ - qos->data_size.value = sysctl_max_tx_data_size; - /* - * Override Tx window if user request it. - Jean II - */ - if (qos->window_size.value > sysctl_max_tx_window) - qos->window_size.value = sysctl_max_tx_window; -} - -/* - * Function irlap_negotiate (qos_device, qos_session, skb) - * - * Negotiate QoS values, not really that much negotiation :-) - * We just set the QoS capabilities for the peer station - * - */ -int irlap_qos_negotiate(struct irlap_cb *self, struct sk_buff *skb) -{ - int ret; - - ret = irda_param_extract_all(self, skb->data, skb->len, - &irlap_param_info); - - /* Convert the negotiated bits to values */ - irda_qos_bits_to_value(&self->qos_tx); - irda_qos_bits_to_value(&self->qos_rx); - - irlap_adjust_qos_settings(&self->qos_tx); - - pr_debug("Setting BAUD_RATE to %d bps.\n", - self->qos_tx.baud_rate.value); - pr_debug("Setting DATA_SIZE to %d bytes\n", - self->qos_tx.data_size.value); - pr_debug("Setting WINDOW_SIZE to %d\n", - self->qos_tx.window_size.value); - pr_debug("Setting XBOFS to %d\n", - self->qos_tx.additional_bofs.value); - pr_debug("Setting MAX_TURN_TIME to %d ms.\n", - self->qos_tx.max_turn_time.value); - pr_debug("Setting MIN_TURN_TIME to %d usecs.\n", - self->qos_tx.min_turn_time.value); - pr_debug("Setting LINK_DISC to %d secs.\n", - self->qos_tx.link_disc_time.value); - return ret; -} - -/* - * Function irlap_insert_negotiation_params (qos, fp) - * - * Insert QoS negotiaion pararameters into frame - * - */ -int irlap_insert_qos_negotiation_params(struct irlap_cb *self, - struct sk_buff *skb) -{ - int ret; - - /* Insert data rate */ - ret = irda_param_insert(self, PI_BAUD_RATE, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert max turnaround time */ - ret = irda_param_insert(self, PI_MAX_TURN_TIME, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert data size */ - ret = irda_param_insert(self, PI_DATA_SIZE, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert window size */ - ret = irda_param_insert(self, PI_WINDOW_SIZE, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert additional BOFs */ - ret = irda_param_insert(self, PI_ADD_BOFS, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert minimum turnaround time */ - ret = irda_param_insert(self, PI_MIN_TURN_TIME, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - /* Insert link disconnect/threshold time */ - ret = irda_param_insert(self, PI_LINK_DISC, skb_tail_pointer(skb), - skb_tailroom(skb), &irlap_param_info); - if (ret < 0) - return ret; - skb_put(skb, ret); - - return 0; -} - -/* - * Function irlap_param_baud_rate (instance, param, get) - * - * Negotiate data-rate - * - */ -static int irlap_param_baud_rate(void *instance, irda_param_t *param, int get) -{ - __u16 final; - - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) { - param->pv.i = self->qos_rx.baud_rate.bits; - pr_debug("%s(), baud rate = 0x%02x\n", - __func__, param->pv.i); - } else { - /* - * Stations must agree on baud rate, so calculate - * intersection - */ - pr_debug("Requested BAUD_RATE: 0x%04x\n", (__u16)param->pv.i); - final = (__u16) param->pv.i & self->qos_rx.baud_rate.bits; - - pr_debug("Final BAUD_RATE: 0x%04x\n", final); - self->qos_tx.baud_rate.bits = final; - self->qos_rx.baud_rate.bits = final; - } - - return 0; -} - -/* - * Function irlap_param_link_disconnect (instance, param, get) - * - * Negotiate link disconnect/threshold time. - * - */ -static int irlap_param_link_disconnect(void *instance, irda_param_t *param, - int get) -{ - __u16 final; - - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.link_disc_time.bits; - else { - /* - * Stations must agree on link disconnect/threshold - * time. - */ - pr_debug("LINK_DISC: %02x\n", (__u8)param->pv.i); - final = (__u8) param->pv.i & self->qos_rx.link_disc_time.bits; - - pr_debug("Final LINK_DISC: %02x\n", final); - self->qos_tx.link_disc_time.bits = final; - self->qos_rx.link_disc_time.bits = final; - } - return 0; -} - -/* - * Function irlap_param_max_turn_time (instance, param, get) - * - * Negotiate the maximum turnaround time. This is a type 1 parameter and - * will be negotiated independently for each station - * - */ -static int irlap_param_max_turn_time(void *instance, irda_param_t *param, - int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.max_turn_time.bits; - else - self->qos_tx.max_turn_time.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_param_data_size (instance, param, get) - * - * Negotiate the data size. This is a type 1 parameter and - * will be negotiated independently for each station - * - */ -static int irlap_param_data_size(void *instance, irda_param_t *param, int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.data_size.bits; - else - self->qos_tx.data_size.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_param_window_size (instance, param, get) - * - * Negotiate the window size. This is a type 1 parameter and - * will be negotiated independently for each station - * - */ -static int irlap_param_window_size(void *instance, irda_param_t *param, - int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.window_size.bits; - else - self->qos_tx.window_size.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_param_additional_bofs (instance, param, get) - * - * Negotiate additional BOF characters. This is a type 1 parameter and - * will be negotiated independently for each station. - */ -static int irlap_param_additional_bofs(void *instance, irda_param_t *param, int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.additional_bofs.bits; - else - self->qos_tx.additional_bofs.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_param_min_turn_time (instance, param, get) - * - * Negotiate the minimum turn around time. This is a type 1 parameter and - * will be negotiated independently for each station - */ -static int irlap_param_min_turn_time(void *instance, irda_param_t *param, - int get) -{ - struct irlap_cb *self = (struct irlap_cb *) instance; - - IRDA_ASSERT(self != NULL, return -1;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return -1;); - - if (get) - param->pv.i = self->qos_rx.min_turn_time.bits; - else - self->qos_tx.min_turn_time.bits = (__u8) param->pv.i; - - return 0; -} - -/* - * Function irlap_max_line_capacity (speed, max_turn_time, min_turn_time) - * - * Calculate the maximum line capacity - * - */ -__u32 irlap_max_line_capacity(__u32 speed, __u32 max_turn_time) -{ - __u32 line_capacity; - int i,j; - - pr_debug("%s(), speed=%d, max_turn_time=%d\n", - __func__, speed, max_turn_time); - - i = value_index(speed, baud_rates, 10); - j = value_index(max_turn_time, max_turn_times, 4); - - IRDA_ASSERT(((i >=0) && (i <10)), return 0;); - IRDA_ASSERT(((j >=0) && (j <4)), return 0;); - - line_capacity = max_line_capacities[i][j]; - - pr_debug("%s(), line capacity=%d bytes\n", - __func__, line_capacity); - - return line_capacity; -} - -#ifndef CONFIG_IRDA_DYNAMIC_WINDOW -static __u32 irlap_requested_line_capacity(struct qos_info *qos) -{ - __u32 line_capacity; - - line_capacity = qos->window_size.value * - (qos->data_size.value + 6 + qos->additional_bofs.value) + - irlap_min_turn_time_in_bytes(qos->baud_rate.value, - qos->min_turn_time.value); - - pr_debug("%s(), requested line capacity=%d\n", - __func__, line_capacity); - - return line_capacity; -} -#endif - -void irda_qos_bits_to_value(struct qos_info *qos) -{ - int index; - - IRDA_ASSERT(qos != NULL, return;); - - index = msb_index(qos->baud_rate.bits); - qos->baud_rate.value = baud_rates[index]; - - index = msb_index(qos->data_size.bits); - qos->data_size.value = data_sizes[index]; - - index = msb_index(qos->window_size.bits); - qos->window_size.value = index+1; - - index = msb_index(qos->min_turn_time.bits); - qos->min_turn_time.value = min_turn_times[index]; - - index = msb_index(qos->max_turn_time.bits); - qos->max_turn_time.value = max_turn_times[index]; - - index = msb_index(qos->link_disc_time.bits); - qos->link_disc_time.value = link_disc_times[index]; - - index = msb_index(qos->additional_bofs.bits); - qos->additional_bofs.value = add_bofs[index]; -} -EXPORT_SYMBOL(irda_qos_bits_to_value); diff --git a/net/irda/timer.c b/net/irda/timer.c deleted file mode 100644 index f2280f73b057..000000000000 --- a/net/irda/timer.c +++ /dev/null @@ -1,231 +0,0 @@ -/********************************************************************* - * - * Filename: timer.c - * Version: - * Description: - * Status: Experimental. - * Author: Dag Brattli - * Created at: Sat Aug 16 00:59:29 1997 - * Modified at: Wed Dec 8 12:50:34 1999 - * Modified by: Dag Brattli - * - * Copyright (c) 1997, 1999 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include - -#include -#include -#include -#include -#include - -extern int sysctl_slot_timeout; - -static void irlap_slot_timer_expired(void* data); -static void irlap_query_timer_expired(void* data); -static void irlap_final_timer_expired(void* data); -static void irlap_wd_timer_expired(void* data); -static void irlap_backoff_timer_expired(void* data); -static void irlap_media_busy_expired(void* data); - -void irlap_start_slot_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->slot_timer, timeout, (void *) self, - irlap_slot_timer_expired); -} - -void irlap_start_query_timer(struct irlap_cb *self, int S, int s) -{ - int timeout; - - /* Calculate when the peer discovery should end. Normally, we - * get the end-of-discovery frame, so this is just in case - * we miss it. - * Basically, we multiply the number of remaining slots by our - * slot time, plus add some extra time to properly receive the last - * discovery packet (which is longer due to extra discovery info), - * to avoid messing with for incoming connections requests and - * to accommodate devices that perform discovery slower than us. - * Jean II */ - timeout = msecs_to_jiffies(sysctl_slot_timeout) * (S - s) - + XIDEXTRA_TIMEOUT + SMALLBUSY_TIMEOUT; - - /* Set or re-set the timer. We reset the timer for each received - * discovery query, which allow us to automatically adjust to - * the speed of the peer discovery (faster or slower). Jean II */ - irda_start_timer( &self->query_timer, timeout, (void *) self, - irlap_query_timer_expired); -} - -void irlap_start_final_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->final_timer, timeout, (void *) self, - irlap_final_timer_expired); -} - -void irlap_start_wd_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->wd_timer, timeout, (void *) self, - irlap_wd_timer_expired); -} - -void irlap_start_backoff_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->backoff_timer, timeout, (void *) self, - irlap_backoff_timer_expired); -} - -void irlap_start_mbusy_timer(struct irlap_cb *self, int timeout) -{ - irda_start_timer(&self->media_busy_timer, timeout, - (void *) self, irlap_media_busy_expired); -} - -void irlap_stop_mbusy_timer(struct irlap_cb *self) -{ - /* If timer is activated, kill it! */ - del_timer(&self->media_busy_timer); - - /* If we are in NDM, there is a bunch of events in LAP that - * that be pending due to the media_busy condition, such as - * CONNECT_REQUEST and SEND_UI_FRAME. If we don't generate - * an event, they will wait forever... - * Jean II */ - if (self->state == LAP_NDM) - irlap_do_event(self, MEDIA_BUSY_TIMER_EXPIRED, NULL, NULL); -} - -void irlmp_start_watchdog_timer(struct lsap_cb *self, int timeout) -{ - irda_start_timer(&self->watchdog_timer, timeout, (void *) self, - irlmp_watchdog_timer_expired); -} - -void irlmp_start_discovery_timer(struct irlmp_cb *self, int timeout) -{ - irda_start_timer(&self->discovery_timer, timeout, (void *) self, - irlmp_discovery_timer_expired); -} - -void irlmp_start_idle_timer(struct lap_cb *self, int timeout) -{ - irda_start_timer(&self->idle_timer, timeout, (void *) self, - irlmp_idle_timer_expired); -} - -void irlmp_stop_idle_timer(struct lap_cb *self) -{ - /* If timer is activated, kill it! */ - del_timer(&self->idle_timer); -} - -/* - * Function irlap_slot_timer_expired (data) - * - * IrLAP slot timer has expired - * - */ -static void irlap_slot_timer_expired(void *data) -{ - struct irlap_cb *self = (struct irlap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, SLOT_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Function irlap_query_timer_expired (data) - * - * IrLAP query timer has expired - * - */ -static void irlap_query_timer_expired(void *data) -{ - struct irlap_cb *self = (struct irlap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, QUERY_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Function irda_final_timer_expired (data) - * - * - * - */ -static void irlap_final_timer_expired(void *data) -{ - struct irlap_cb *self = (struct irlap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, FINAL_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Function irda_wd_timer_expired (data) - * - * - * - */ -static void irlap_wd_timer_expired(void *data) -{ - struct irlap_cb *self = (struct irlap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, WD_TIMER_EXPIRED, NULL, NULL); -} - -/* - * Function irda_backoff_timer_expired (data) - * - * - * - */ -static void irlap_backoff_timer_expired(void *data) -{ - struct irlap_cb *self = (struct irlap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - IRDA_ASSERT(self->magic == LAP_MAGIC, return;); - - irlap_do_event(self, BACKOFF_TIMER_EXPIRED, NULL, NULL); -} - - -/* - * Function irtty_media_busy_expired (data) - * - * - */ -static void irlap_media_busy_expired(void *data) -{ - struct irlap_cb *self = (struct irlap_cb *) data; - - IRDA_ASSERT(self != NULL, return;); - - irda_device_set_media_busy(self->netdev, FALSE); - /* Note : the LAP event will be send in irlap_stop_mbusy_timer(), - * to catch other cases where the flag is cleared (for example - * after a discovery) - Jean II */ -} diff --git a/net/irda/wrapper.c b/net/irda/wrapper.c deleted file mode 100644 index 40a0f993bf13..000000000000 --- a/net/irda/wrapper.c +++ /dev/null @@ -1,492 +0,0 @@ -/********************************************************************* - * - * Filename: wrapper.c - * Version: 1.2 - * Description: IrDA SIR async wrapper layer - * Status: Stable - * Author: Dag Brattli - * Created at: Mon Aug 4 20:40:53 1997 - * Modified at: Fri Jan 28 13:21:09 2000 - * Modified by: Dag Brattli - * Modified at: Fri May 28 3:11 CST 1999 - * Modified by: Horst von Brand - * - * Copyright (c) 1998-2000 Dag Brattli , - * All Rights Reserved. - * Copyright (c) 2000-2002 Jean Tourrilhes - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of - * the License, or (at your option) any later version. - * - * Neither Dag Brattli nor University of Tromsø admit liability nor - * provide warranty for any of this software. This material is - * provided "AS-IS" and at no charge. - * - ********************************************************************/ - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -/************************** FRAME WRAPPING **************************/ -/* - * Unwrap and unstuff SIR frames - * - * Note : at FIR and MIR, HDLC framing is used and usually handled - * by the controller, so we come here only for SIR... Jean II - */ - -/* - * Function stuff_byte (byte, buf) - * - * Byte stuff one single byte and put the result in buffer pointed to by - * buf. The buffer must at all times be able to have two bytes inserted. - * - * This is in a tight loop, better inline it, so need to be prior to callers. - * (2000 bytes on P6 200MHz, non-inlined ~370us, inline ~170us) - Jean II - */ -static inline int stuff_byte(__u8 byte, __u8 *buf) -{ - switch (byte) { - case BOF: /* FALLTHROUGH */ - case EOF: /* FALLTHROUGH */ - case CE: - /* Insert transparently coded */ - buf[0] = CE; /* Send link escape */ - buf[1] = byte^IRDA_TRANS; /* Complement bit 5 */ - return 2; - /* break; */ - default: - /* Non-special value, no transparency required */ - buf[0] = byte; - return 1; - /* break; */ - } -} - -/* - * Function async_wrap (skb, *tx_buff, buffsize) - * - * Makes a new buffer with wrapping and stuffing, should check that - * we don't get tx buffer overflow. - */ -int async_wrap_skb(struct sk_buff *skb, __u8 *tx_buff, int buffsize) -{ - struct irda_skb_cb *cb = (struct irda_skb_cb *) skb->cb; - int xbofs; - int i; - int n; - union { - __u16 value; - __u8 bytes[2]; - } fcs; - - /* Initialize variables */ - fcs.value = INIT_FCS; - n = 0; - - /* - * Send XBOF's for required min. turn time and for the negotiated - * additional XBOFS - */ - - if (cb->magic != LAP_MAGIC) { - /* - * This will happen for all frames sent from user-space. - * Nothing to worry about, but we set the default number of - * BOF's - */ - pr_debug("%s(), wrong magic in skb!\n", __func__); - xbofs = 10; - } else - xbofs = cb->xbofs + cb->xbofs_delay; - - pr_debug("%s(), xbofs=%d\n", __func__, xbofs); - - /* Check that we never use more than 115 + 48 xbofs */ - if (xbofs > 163) { - pr_debug("%s(), too many xbofs (%d)\n", __func__, - xbofs); - xbofs = 163; - } - - memset(tx_buff + n, XBOF, xbofs); - n += xbofs; - - /* Start of packet character BOF */ - tx_buff[n++] = BOF; - - /* Insert frame and calc CRC */ - for (i=0; i < skb->len; i++) { - /* - * Check for the possibility of tx buffer overflow. We use - * bufsize-5 since the maximum number of bytes that can be - * transmitted after this point is 5. - */ - if(n >= (buffsize-5)) { - net_err_ratelimited("%s(), tx buffer overflow (n=%d)\n", - __func__, n); - return n; - } - - n += stuff_byte(skb->data[i], tx_buff+n); - fcs.value = irda_fcs(fcs.value, skb->data[i]); - } - - /* Insert CRC in little endian format (LSB first) */ - fcs.value = ~fcs.value; -#ifdef __LITTLE_ENDIAN - n += stuff_byte(fcs.bytes[0], tx_buff+n); - n += stuff_byte(fcs.bytes[1], tx_buff+n); -#else /* ifdef __BIG_ENDIAN */ - n += stuff_byte(fcs.bytes[1], tx_buff+n); - n += stuff_byte(fcs.bytes[0], tx_buff+n); -#endif - tx_buff[n++] = EOF; - - return n; -} -EXPORT_SYMBOL(async_wrap_skb); - -/************************* FRAME UNWRAPPING *************************/ -/* - * Unwrap and unstuff SIR frames - * - * Complete rewrite by Jean II : - * More inline, faster, more compact, more logical. Jean II - * (16 bytes on P6 200MHz, old 5 to 7 us, new 4 to 6 us) - * (24 bytes on P6 200MHz, old 9 to 10 us, new 7 to 8 us) - * (for reference, 115200 b/s is 1 byte every 69 us) - * And reduce wrapper.o by ~900B in the process ;-) - * - * Then, we have the addition of ZeroCopy, which is optional - * (i.e. the driver must initiate it) and improve final processing. - * (2005 B frame + EOF on P6 200MHz, without 30 to 50 us, with 10 to 25 us) - * - * Note : at FIR and MIR, HDLC framing is used and usually handled - * by the controller, so we come here only for SIR... Jean II - */ - -/* - * We can also choose where we want to do the CRC calculation. We can - * do it "inline", as we receive the bytes, or "postponed", when - * receiving the End-Of-Frame. - * (16 bytes on P6 200MHz, inlined 4 to 6 us, postponed 4 to 5 us) - * (24 bytes on P6 200MHz, inlined 7 to 8 us, postponed 5 to 7 us) - * With ZeroCopy : - * (2005 B frame on P6 200MHz, inlined 10 to 25 us, postponed 140 to 180 us) - * Without ZeroCopy : - * (2005 B frame on P6 200MHz, inlined 30 to 50 us, postponed 150 to 180 us) - * (Note : numbers taken with irq disabled) - * - * From those numbers, it's not clear which is the best strategy, because - * we end up running through a lot of data one way or another (i.e. cache - * misses). I personally prefer to avoid the huge latency spike of the - * "postponed" solution, because it come just at the time when we have - * lot's of protocol processing to do and it will hurt our ability to - * reach low link turnaround times... Jean II - */ -//#define POSTPONE_RX_CRC - -/* - * Function async_bump (buf, len, stats) - * - * Got a frame, make a copy of it, and pass it up the stack! We can try - * to inline it since it's only called from state_inside_frame - */ -static inline void -async_bump(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff) -{ - struct sk_buff *newskb; - struct sk_buff *dataskb; - int docopy; - - /* Check if we need to copy the data to a new skb or not. - * If the driver doesn't use ZeroCopy Rx, we have to do it. - * With ZeroCopy Rx, the rx_buff already point to a valid - * skb. But, if the frame is small, it is more efficient to - * copy it to save memory (copy will be fast anyway - that's - * called Rx-copy-break). Jean II */ - docopy = ((rx_buff->skb == NULL) || - (rx_buff->len < IRDA_RX_COPY_THRESHOLD)); - - /* Allocate a new skb */ - newskb = dev_alloc_skb(docopy ? rx_buff->len + 1 : rx_buff->truesize); - if (!newskb) { - stats->rx_dropped++; - /* We could deliver the current skb if doing ZeroCopy Rx, - * but this would stall the Rx path. Better drop the - * packet... Jean II */ - return; - } - - /* Align IP header to 20 bytes (i.e. increase skb->data) - * Note this is only useful with IrLAN, as PPP has a variable - * header size (2 or 1 bytes) - Jean II */ - skb_reserve(newskb, 1); - - if(docopy) { - /* Copy data without CRC (length already checked) */ - skb_copy_to_linear_data(newskb, rx_buff->data, - rx_buff->len - 2); - /* Deliver this skb */ - dataskb = newskb; - } else { - /* We are using ZeroCopy. Deliver old skb */ - dataskb = rx_buff->skb; - /* And hook the new skb to the rx_buff */ - rx_buff->skb = newskb; - rx_buff->head = newskb->data; /* NOT newskb->head */ - //printk(KERN_DEBUG "ZeroCopy : len = %d, dataskb = %p, newskb = %p\n", rx_buff->len, dataskb, newskb); - } - - /* Set proper length on skb (without CRC) */ - skb_put(dataskb, rx_buff->len - 2); - - /* Feed it to IrLAP layer */ - dataskb->dev = dev; - skb_reset_mac_header(dataskb); - dataskb->protocol = htons(ETH_P_IRDA); - - netif_rx(dataskb); - - stats->rx_packets++; - stats->rx_bytes += rx_buff->len; - - /* Clean up rx_buff (redundant with async_unwrap_bof() ???) */ - rx_buff->data = rx_buff->head; - rx_buff->len = 0; -} - -/* - * Function async_unwrap_bof(dev, byte) - * - * Handle Beginning Of Frame character received within a frame - * - */ -static inline void -async_unwrap_bof(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ - switch(rx_buff->state) { - case LINK_ESCAPE: - case INSIDE_FRAME: - /* Not supposed to happen, the previous frame is not - * finished - Jean II */ - pr_debug("%s(), Discarding incomplete frame\n", - __func__); - stats->rx_errors++; - stats->rx_missed_errors++; - irda_device_set_media_busy(dev, TRUE); - break; - - case OUTSIDE_FRAME: - case BEGIN_FRAME: - default: - /* We may receive multiple BOF at the start of frame */ - break; - } - - /* Now receiving frame */ - rx_buff->state = BEGIN_FRAME; - rx_buff->in_frame = TRUE; - - /* Time to initialize receive buffer */ - rx_buff->data = rx_buff->head; - rx_buff->len = 0; - rx_buff->fcs = INIT_FCS; -} - -/* - * Function async_unwrap_eof(dev, byte) - * - * Handle End Of Frame character received within a frame - * - */ -static inline void -async_unwrap_eof(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ -#ifdef POSTPONE_RX_CRC - int i; -#endif - - switch(rx_buff->state) { - case OUTSIDE_FRAME: - /* Probably missed the BOF */ - stats->rx_errors++; - stats->rx_missed_errors++; - irda_device_set_media_busy(dev, TRUE); - break; - - case BEGIN_FRAME: - case LINK_ESCAPE: - case INSIDE_FRAME: - default: - /* Note : in the case of BEGIN_FRAME and LINK_ESCAPE, - * the fcs will most likely not match and generate an - * error, as expected - Jean II */ - rx_buff->state = OUTSIDE_FRAME; - rx_buff->in_frame = FALSE; - -#ifdef POSTPONE_RX_CRC - /* If we haven't done the CRC as we receive bytes, we - * must do it now... Jean II */ - for(i = 0; i < rx_buff->len; i++) - rx_buff->fcs = irda_fcs(rx_buff->fcs, - rx_buff->data[i]); -#endif - - /* Test FCS and signal success if the frame is good */ - if (rx_buff->fcs == GOOD_FCS) { - /* Deliver frame */ - async_bump(dev, stats, rx_buff); - break; - } else { - /* Wrong CRC, discard frame! */ - irda_device_set_media_busy(dev, TRUE); - - pr_debug("%s(), crc error\n", __func__); - stats->rx_errors++; - stats->rx_crc_errors++; - } - break; - } -} - -/* - * Function async_unwrap_ce(dev, byte) - * - * Handle Character Escape character received within a frame - * - */ -static inline void -async_unwrap_ce(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ - switch(rx_buff->state) { - case OUTSIDE_FRAME: - /* Activate carrier sense */ - irda_device_set_media_busy(dev, TRUE); - break; - - case LINK_ESCAPE: - net_warn_ratelimited("%s: state not defined\n", __func__); - break; - - case BEGIN_FRAME: - case INSIDE_FRAME: - default: - /* Stuffed byte coming */ - rx_buff->state = LINK_ESCAPE; - break; - } -} - -/* - * Function async_unwrap_other(dev, byte) - * - * Handle other characters received within a frame - * - */ -static inline void -async_unwrap_other(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ - switch(rx_buff->state) { - /* This is on the critical path, case are ordered by - * probability (most frequent first) - Jean II */ - case INSIDE_FRAME: - /* Must be the next byte of the frame */ - if (rx_buff->len < rx_buff->truesize) { - rx_buff->data[rx_buff->len++] = byte; -#ifndef POSTPONE_RX_CRC - rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); -#endif - } else { - pr_debug("%s(), Rx buffer overflow, aborting\n", - __func__); - rx_buff->state = OUTSIDE_FRAME; - } - break; - - case LINK_ESCAPE: - /* - * Stuffed char, complement bit 5 of byte - * following CE, IrLAP p.114 - */ - byte ^= IRDA_TRANS; - if (rx_buff->len < rx_buff->truesize) { - rx_buff->data[rx_buff->len++] = byte; -#ifndef POSTPONE_RX_CRC - rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); -#endif - rx_buff->state = INSIDE_FRAME; - } else { - pr_debug("%s(), Rx buffer overflow, aborting\n", - __func__); - rx_buff->state = OUTSIDE_FRAME; - } - break; - - case OUTSIDE_FRAME: - /* Activate carrier sense */ - if(byte != XBOF) - irda_device_set_media_busy(dev, TRUE); - break; - - case BEGIN_FRAME: - default: - rx_buff->data[rx_buff->len++] = byte; -#ifndef POSTPONE_RX_CRC - rx_buff->fcs = irda_fcs(rx_buff->fcs, byte); -#endif - rx_buff->state = INSIDE_FRAME; - break; - } -} - -/* - * Function async_unwrap_char (dev, rx_buff, byte) - * - * Parse and de-stuff frame received from the IrDA-port - * - * This is the main entry point for SIR drivers. - */ -void async_unwrap_char(struct net_device *dev, - struct net_device_stats *stats, - iobuff_t *rx_buff, __u8 byte) -{ - switch(byte) { - case CE: - async_unwrap_ce(dev, stats, rx_buff, byte); - break; - case BOF: - async_unwrap_bof(dev, stats, rx_buff, byte); - break; - case EOF: - async_unwrap_eof(dev, stats, rx_buff, byte); - break; - default: - async_unwrap_other(dev, stats, rx_buff, byte); - break; - } -} -EXPORT_SYMBOL(async_unwrap_char); - -- cgit v1.2.3 From 8579a67e139b77a6881c405f9272afb9a7891201 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Mon, 28 Aug 2017 16:18:41 +1000 Subject: net/ncsi: Fix several packet definitions Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David S. Miller --- net/ncsi/ncsi-cmd.c | 10 +++++----- net/ncsi/ncsi-pkt.h | 2 +- net/ncsi/ncsi-rsp.c | 3 ++- 3 files changed, 8 insertions(+), 7 deletions(-) (limited to 'net') diff --git a/net/ncsi/ncsi-cmd.c b/net/ncsi/ncsi-cmd.c index 5e03ed190e18..7567ca63aae2 100644 --- a/net/ncsi/ncsi-cmd.c +++ b/net/ncsi/ncsi-cmd.c @@ -139,9 +139,9 @@ static int ncsi_cmd_handler_svf(struct sk_buff *skb, struct ncsi_cmd_svf_pkt *cmd; cmd = skb_put_zero(skb, sizeof(*cmd)); - cmd->vlan = htons(nca->words[0]); - cmd->index = nca->bytes[2]; - cmd->enable = nca->bytes[3]; + cmd->vlan = htons(nca->words[1]); + cmd->index = nca->bytes[6]; + cmd->enable = nca->bytes[7]; ncsi_cmd_build_header(&cmd->cmd.common, nca); return 0; @@ -153,7 +153,7 @@ static int ncsi_cmd_handler_ev(struct sk_buff *skb, struct ncsi_cmd_ev_pkt *cmd; cmd = skb_put_zero(skb, sizeof(*cmd)); - cmd->mode = nca->bytes[0]; + cmd->mode = nca->bytes[3]; ncsi_cmd_build_header(&cmd->cmd.common, nca); return 0; @@ -228,7 +228,7 @@ static struct ncsi_cmd_handler { { NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae }, { NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl }, { NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default }, - { NCSI_PKT_CMD_SVF, 4, ncsi_cmd_handler_svf }, + { NCSI_PKT_CMD_SVF, 8, ncsi_cmd_handler_svf }, { NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev }, { NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default }, { NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma }, diff --git a/net/ncsi/ncsi-pkt.h b/net/ncsi/ncsi-pkt.h index 3ea49ed0a935..91b4b66438df 100644 --- a/net/ncsi/ncsi-pkt.h +++ b/net/ncsi/ncsi-pkt.h @@ -104,7 +104,7 @@ struct ncsi_cmd_svf_pkt { unsigned char index; /* VLAN table index */ unsigned char enable; /* Enable or disable */ __be32 checksum; /* Checksum */ - unsigned char pad[14]; + unsigned char pad[18]; }; /* Enable VLAN */ diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c index 087db775b3dc..c1a191d790e2 100644 --- a/net/ncsi/ncsi-rsp.c +++ b/net/ncsi/ncsi-rsp.c @@ -354,7 +354,8 @@ static int ncsi_rsp_handler_svf(struct ncsi_request *nr) /* Add or remove the VLAN filter */ if (!(cmd->enable & 0x1)) { - ret = ncsi_remove_filter(nc, NCSI_FILTER_VLAN, cmd->index); + /* HW indexes from 1 */ + ret = ncsi_remove_filter(nc, NCSI_FILTER_VLAN, cmd->index - 1); } else { vlan = ntohs(cmd->vlan); ret = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan); -- cgit v1.2.3 From 21acf63013ed3d6fce3176cc34b74064052a31b4 Mon Sep 17 00:00:00 2001 From: Samuel Mendoza-Jonas Date: Mon, 28 Aug 2017 16:18:42 +1000 Subject: net/ncsi: Configure VLAN tag filter Make use of the ndo_vlan_rx_{add,kill}_vid callbacks to have the NCSI stack process new VLAN tags and configure the channel VLAN filter appropriately. Several VLAN tags can be set and a "Set VLAN Filter" packet must be sent for each one, meaning the ncsi_dev_state_config_svf state must be repeated. An internal list of VLAN tags is maintained, and compared against the current channel's ncsi_channel_filter in order to keep track within the state. VLAN filters are removed in a similar manner, with the introduction of the ncsi_dev_state_config_clear_vids state. The maximum number of VLAN tag filters is determined by the "Get Capabilities" response from the channel. Signed-off-by: Samuel Mendoza-Jonas Signed-off-by: David S. Miller --- include/net/ncsi.h | 2 + net/ncsi/internal.h | 11 ++ net/ncsi/ncsi-manage.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++++- net/ncsi/ncsi-rsp.c | 9 +- 4 files changed, 326 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/ncsi.h b/include/net/ncsi.h index 68680baac0fd..1f96af46df49 100644 --- a/include/net/ncsi.h +++ b/include/net/ncsi.h @@ -28,6 +28,8 @@ struct ncsi_dev { }; #ifdef CONFIG_NET_NCSI +int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid); +int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid); struct ncsi_dev *ncsi_register_dev(struct net_device *dev, void (*notifier)(struct ncsi_dev *nd)); int ncsi_start_dev(struct ncsi_dev *nd); diff --git a/net/ncsi/internal.h b/net/ncsi/internal.h index 1308a56f2591..af3d636534ef 100644 --- a/net/ncsi/internal.h +++ b/net/ncsi/internal.h @@ -180,6 +180,7 @@ struct ncsi_channel { #define NCSI_CHANNEL_INACTIVE 1 #define NCSI_CHANNEL_ACTIVE 2 #define NCSI_CHANNEL_INVISIBLE 3 + bool reconfigure_needed; spinlock_t lock; /* Protect filters etc */ struct ncsi_package *package; struct ncsi_channel_version version; @@ -235,6 +236,9 @@ enum { ncsi_dev_state_probe_dp, ncsi_dev_state_config_sp = 0x0301, ncsi_dev_state_config_cis, + ncsi_dev_state_config_clear_vids, + ncsi_dev_state_config_svf, + ncsi_dev_state_config_ev, ncsi_dev_state_config_sma, ncsi_dev_state_config_ebf, #if IS_ENABLED(CONFIG_IPV6) @@ -253,6 +257,12 @@ enum { ncsi_dev_state_suspend_done }; +struct vlan_vid { + struct list_head list; + __be16 proto; + u16 vid; +}; + struct ncsi_dev_priv { struct ncsi_dev ndev; /* Associated NCSI device */ unsigned int flags; /* NCSI device flags */ @@ -276,6 +286,7 @@ struct ncsi_dev_priv { struct work_struct work; /* For channel management */ struct packet_type ptype; /* NCSI packet Rx handler */ struct list_head node; /* Form NCSI device list */ + struct list_head vlan_vids; /* List of active VLAN IDs */ }; struct ncsi_cmd_arg { diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c index a3bd5fa8ad09..11904b3b702d 100644 --- a/net/ncsi/ncsi-manage.c +++ b/net/ncsi/ncsi-manage.c @@ -38,6 +38,25 @@ static inline int ncsi_filter_size(int table) return sizes[table]; } +u32 *ncsi_get_filter(struct ncsi_channel *nc, int table, int index) +{ + struct ncsi_channel_filter *ncf; + int size; + + ncf = nc->filters[table]; + if (!ncf) + return NULL; + + size = ncsi_filter_size(table); + if (size < 0) + return NULL; + + return ncf->data + size * index; +} + +/* Find the first active filter in a filter table that matches the given + * data parameter. If data is NULL, this returns the first active filter. + */ int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data) { struct ncsi_channel_filter *ncf; @@ -58,7 +77,7 @@ int ncsi_find_filter(struct ncsi_channel *nc, int table, void *data) index = -1; while ((index = find_next_bit(bitmap, ncf->total, index + 1)) < ncf->total) { - if (!memcmp(ncf->data + size * index, data, size)) { + if (!data || !memcmp(ncf->data + size * index, data, size)) { spin_unlock_irqrestore(&nc->lock, flags); return index; } @@ -639,6 +658,95 @@ error: nd->state = ncsi_dev_state_functional; } +/* Check the VLAN filter bitmap for a set filter, and construct a + * "Set VLAN Filter - Disable" packet if found. + */ +static int clear_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc, + struct ncsi_cmd_arg *nca) +{ + int index; + u32 *data; + u16 vid; + + index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, NULL); + if (index < 0) { + /* Filter table empty */ + return -1; + } + + data = ncsi_get_filter(nc, NCSI_FILTER_VLAN, index); + if (!data) { + netdev_err(ndp->ndev.dev, + "ncsi: failed to retrieve filter %d\n", index); + /* Set the VLAN id to 0 - this will still disable the entry in + * the filter table, but we won't know what it was. + */ + vid = 0; + } else { + vid = *(u16 *)data; + } + + netdev_printk(KERN_DEBUG, ndp->ndev.dev, + "ncsi: removed vlan tag %u at index %d\n", + vid, index + 1); + ncsi_remove_filter(nc, NCSI_FILTER_VLAN, index); + + nca->type = NCSI_PKT_CMD_SVF; + nca->words[1] = vid; + /* HW filter index starts at 1 */ + nca->bytes[6] = index + 1; + nca->bytes[7] = 0x00; + return 0; +} + +/* Find an outstanding VLAN tag and constuct a "Set VLAN Filter - Enable" + * packet. + */ +static int set_one_vid(struct ncsi_dev_priv *ndp, struct ncsi_channel *nc, + struct ncsi_cmd_arg *nca) +{ + struct vlan_vid *vlan = NULL; + int index = 0; + + list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) { + index = ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan->vid); + if (index < 0) { + /* New tag to add */ + netdev_printk(KERN_DEBUG, ndp->ndev.dev, + "ncsi: new vlan id to set: %u\n", + vlan->vid); + break; + } + netdev_printk(KERN_DEBUG, ndp->ndev.dev, + "vid %u already at filter pos %d\n", + vlan->vid, index); + } + + if (!vlan || index >= 0) { + netdev_printk(KERN_DEBUG, ndp->ndev.dev, + "no vlan ids left to set\n"); + return -1; + } + + index = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan->vid); + if (index < 0) { + netdev_err(ndp->ndev.dev, + "Failed to add new VLAN tag, error %d\n", index); + return -1; + } + + netdev_printk(KERN_DEBUG, ndp->ndev.dev, + "ncsi: set vid %u in packet, index %u\n", + vlan->vid, index + 1); + nca->type = NCSI_PKT_CMD_SVF; + nca->words[1] = vlan->vid; + /* HW filter index starts at 1 */ + nca->bytes[6] = index + 1; + nca->bytes[7] = 0x01; + + return 0; +} + static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) { struct ncsi_dev *nd = &ndp->ndev; @@ -683,8 +791,11 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) if (ret) goto error; - nd->state = ncsi_dev_state_config_sma; + nd->state = ncsi_dev_state_config_clear_vids; break; + case ncsi_dev_state_config_clear_vids: + case ncsi_dev_state_config_svf: + case ncsi_dev_state_config_ev: case ncsi_dev_state_config_sma: case ncsi_dev_state_config_ebf: #if IS_ENABLED(CONFIG_IPV6) @@ -699,11 +810,40 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) nca.package = np->id; nca.channel = nc->id; + /* Clear any active filters on the channel before setting */ + if (nd->state == ncsi_dev_state_config_clear_vids) { + ret = clear_one_vid(ndp, nc, &nca); + if (ret) { + nd->state = ncsi_dev_state_config_svf; + schedule_work(&ndp->work); + break; + } + /* Repeat */ + nd->state = ncsi_dev_state_config_clear_vids; + /* Add known VLAN tags to the filter */ + } else if (nd->state == ncsi_dev_state_config_svf) { + ret = set_one_vid(ndp, nc, &nca); + if (ret) { + nd->state = ncsi_dev_state_config_ev; + schedule_work(&ndp->work); + break; + } + /* Repeat */ + nd->state = ncsi_dev_state_config_svf; + /* Enable/Disable the VLAN filter */ + } else if (nd->state == ncsi_dev_state_config_ev) { + if (list_empty(&ndp->vlan_vids)) { + nca.type = NCSI_PKT_CMD_DV; + } else { + nca.type = NCSI_PKT_CMD_EV; + nca.bytes[3] = NCSI_CAP_VLAN_NO; + } + nd->state = ncsi_dev_state_config_sma; + } else if (nd->state == ncsi_dev_state_config_sma) { /* Use first entry in unicast filter table. Note that * the MAC filter table starts from entry 1 instead of * 0. */ - if (nd->state == ncsi_dev_state_config_sma) { nca.type = NCSI_PKT_CMD_SMA; for (index = 0; index < 6; index++) nca.bytes[index] = dev->dev_addr[index]; @@ -751,6 +891,25 @@ static void ncsi_configure_channel(struct ncsi_dev_priv *ndp) break; case ncsi_dev_state_config_done: spin_lock_irqsave(&nc->lock, flags); + if (nc->reconfigure_needed) { + /* This channel's configuration has been updated + * part-way during the config state - start the + * channel configuration over + */ + nc->reconfigure_needed = false; + nc->state = NCSI_CHANNEL_INACTIVE; + spin_unlock_irqrestore(&nc->lock, flags); + + spin_lock_irqsave(&ndp->lock, flags); + list_add_tail_rcu(&nc->link, &ndp->channel_queue); + spin_unlock_irqrestore(&ndp->lock, flags); + + netdev_printk(KERN_DEBUG, dev, + "Dirty NCSI channel state reset\n"); + ncsi_process_next_channel(ndp); + break; + } + if (nc->modes[NCSI_MODE_LINK].data[2] & 0x1) { hot_nc = nc; nc->state = NCSI_CHANNEL_ACTIVE; @@ -1191,6 +1350,148 @@ static struct notifier_block ncsi_inet6addr_notifier = { }; #endif /* CONFIG_IPV6 */ +static int ncsi_kick_channels(struct ncsi_dev_priv *ndp) +{ + struct ncsi_dev *nd = &ndp->ndev; + struct ncsi_channel *nc; + struct ncsi_package *np; + unsigned long flags; + unsigned int n = 0; + + NCSI_FOR_EACH_PACKAGE(ndp, np) { + NCSI_FOR_EACH_CHANNEL(np, nc) { + spin_lock_irqsave(&nc->lock, flags); + + /* Channels may be busy, mark dirty instead of + * kicking if; + * a) not ACTIVE (configured) + * b) in the channel_queue (to be configured) + * c) it's ndev is in the config state + */ + if (nc->state != NCSI_CHANNEL_ACTIVE) { + if ((ndp->ndev.state & 0xff00) == + ncsi_dev_state_config || + !list_empty(&nc->link)) { + netdev_printk(KERN_DEBUG, nd->dev, + "ncsi: channel %p marked dirty\n", + nc); + nc->reconfigure_needed = true; + } + spin_unlock_irqrestore(&nc->lock, flags); + continue; + } + + spin_unlock_irqrestore(&nc->lock, flags); + + ncsi_stop_channel_monitor(nc); + spin_lock_irqsave(&nc->lock, flags); + nc->state = NCSI_CHANNEL_INACTIVE; + spin_unlock_irqrestore(&nc->lock, flags); + + spin_lock_irqsave(&ndp->lock, flags); + list_add_tail_rcu(&nc->link, &ndp->channel_queue); + spin_unlock_irqrestore(&ndp->lock, flags); + + netdev_printk(KERN_DEBUG, nd->dev, + "ncsi: kicked channel %p\n", nc); + n++; + } + } + + return n; +} + +int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) +{ + struct ncsi_channel_filter *ncf; + struct ncsi_dev_priv *ndp; + unsigned int n_vids = 0; + struct vlan_vid *vlan; + struct ncsi_dev *nd; + bool found = false; + + if (vid == 0) + return 0; + + nd = ncsi_find_dev(dev); + if (!nd) { + netdev_warn(dev, "ncsi: No net_device?\n"); + return 0; + } + + ndp = TO_NCSI_DEV_PRIV(nd); + ncf = ndp->hot_channel->filters[NCSI_FILTER_VLAN]; + + /* Add the VLAN id to our internal list */ + list_for_each_entry_rcu(vlan, &ndp->vlan_vids, list) { + n_vids++; + if (vlan->vid == vid) { + netdev_printk(KERN_DEBUG, dev, + "vid %u already registered\n", vid); + return 0; + } + } + + if (n_vids >= ncf->total) { + netdev_info(dev, + "NCSI Channel supports up to %u VLAN tags but %u are already set\n", + ncf->total, n_vids); + return -EINVAL; + } + + vlan = kzalloc(sizeof(*vlan), GFP_KERNEL); + if (!vlan) + return -ENOMEM; + + vlan->proto = proto; + vlan->vid = vid; + list_add_rcu(&vlan->list, &ndp->vlan_vids); + + netdev_printk(KERN_DEBUG, dev, "Added new vid %u\n", vid); + + found = ncsi_kick_channels(ndp) != 0; + + return found ? ncsi_process_next_channel(ndp) : 0; +} + +int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) +{ + struct vlan_vid *vlan, *tmp; + struct ncsi_dev_priv *ndp; + struct ncsi_dev *nd; + bool found = false; + + if (vid == 0) + return 0; + + nd = ncsi_find_dev(dev); + if (!nd) { + netdev_warn(dev, "ncsi: no net_device?\n"); + return 0; + } + + ndp = TO_NCSI_DEV_PRIV(nd); + + /* Remove the VLAN id from our internal list */ + list_for_each_entry_safe(vlan, tmp, &ndp->vlan_vids, list) + if (vlan->vid == vid) { + netdev_printk(KERN_DEBUG, dev, + "vid %u found, removing\n", vid); + list_del_rcu(&vlan->list); + found = true; + kfree(vlan); + } + + if (!found) { + netdev_err(dev, "ncsi: vid %u wasn't registered!\n", vid); + return -EINVAL; + } + + found = ncsi_kick_channels(ndp) != 0; + + return found ? ncsi_process_next_channel(ndp) : 0; +} + struct ncsi_dev *ncsi_register_dev(struct net_device *dev, void (*handler)(struct ncsi_dev *ndev)) { @@ -1215,6 +1516,7 @@ struct ncsi_dev *ncsi_register_dev(struct net_device *dev, nd->handler = handler; ndp->pending_req_num = 0; INIT_LIST_HEAD(&ndp->channel_queue); + INIT_LIST_HEAD(&ndp->vlan_vids); INIT_WORK(&ndp->work, ncsi_dev_work); /* Initialize private NCSI device */ diff --git a/net/ncsi/ncsi-rsp.c b/net/ncsi/ncsi-rsp.c index c1a191d790e2..265b9a892d41 100644 --- a/net/ncsi/ncsi-rsp.c +++ b/net/ncsi/ncsi-rsp.c @@ -694,7 +694,14 @@ static int ncsi_rsp_handler_gc(struct ncsi_request *nr) ncf->index = i; ncf->total = cnt; - ncf->bitmap = 0x0ul; + if (i == NCSI_FILTER_VLAN) { + /* Set VLAN filters active so they are cleared in + * first configuration state + */ + ncf->bitmap = U64_MAX; + } else { + ncf->bitmap = 0x0ul; + } nc->filters[i] = ncf; } -- cgit v1.2.3 From 10674a03c633379fadb5b314abde975fba270058 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Tue, 29 Aug 2017 10:15:40 +0100 Subject: net: rxrpc: Replace time_t type with time64_t type Since the 'expiry' variable of 'struct key_preparsed_payload' has been changed to 'time64_t' type, which is year 2038 safe on 32bits system. In net/rxrpc subsystem, we need convert 'u32' type to 'time64_t' type when copying ticket expires time to 'prep->expiry', then this patch introduces two helper functions to help convert 'u32' to 'time64_t' type. This patch also uses ktime_get_real_seconds() to get current time instead of get_seconds() which is not year 2038 safe on 32bits system. Signed-off-by: Baolin Wang Signed-off-by: David Howells --- include/keys/rxrpc-type.h | 23 +++++++++++++++++++++++ net/rxrpc/ar-internal.h | 2 +- net/rxrpc/key.c | 22 ++++++++++++++-------- net/rxrpc/rxkad.c | 14 +++++++------- 4 files changed, 45 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/include/keys/rxrpc-type.h b/include/keys/rxrpc-type.h index 5de0673f333b..8cf829dbf20e 100644 --- a/include/keys/rxrpc-type.h +++ b/include/keys/rxrpc-type.h @@ -127,4 +127,27 @@ struct rxrpc_key_data_v1 { #define AFSTOKEN_K5_ADDRESSES_MAX 16 /* max K5 addresses */ #define AFSTOKEN_K5_AUTHDATA_MAX 16 /* max K5 pieces of auth data */ +/* + * Truncate a time64_t to the range from 1970 to 2106 as in the network + * protocol. + */ +static inline u32 rxrpc_time64_to_u32(time64_t time) +{ + if (time < 0) + return 0; + + if (time > UINT_MAX) + return UINT_MAX; + + return (u32)time; +} + +/* + * Extend u32 back to time64_t using the same 1970-2106 range. + */ +static inline time64_t rxrpc_u32_to_time64(u32 time) +{ + return (time64_t)time; +} + #endif /* _KEYS_RXRPC_TYPE_H */ diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 8c0db9b3e4ab..8cac66774de1 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -894,7 +894,7 @@ extern struct key_type key_type_rxrpc_s; int rxrpc_request_key(struct rxrpc_sock *, char __user *, int); int rxrpc_server_keyring(struct rxrpc_sock *, char __user *, int); -int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, time_t, +int rxrpc_get_server_data_key(struct rxrpc_connection *, const void *, time64_t, u32); /* diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index 54369225766e..e2d36619b53a 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -92,6 +92,7 @@ static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep, const __be32 *xdr, unsigned int toklen) { struct rxrpc_key_token *token, **pptoken; + time64_t expiry; size_t plen; u32 tktlen; @@ -158,8 +159,9 @@ static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep, pptoken = &(*pptoken)->next) continue; *pptoken = token; - if (token->kad->expiry < prep->expiry) - prep->expiry = token->kad->expiry; + expiry = rxrpc_u32_to_time64(token->kad->expiry); + if (expiry < prep->expiry) + prep->expiry = expiry; _leave(" = 0"); return 0; @@ -433,6 +435,7 @@ static int rxrpc_preparse_xdr_rxk5(struct key_preparsed_payload *prep, struct rxrpc_key_token *token, **pptoken; struct rxk5_key *rxk5; const __be32 *end_xdr = xdr + (toklen >> 2); + time64_t expiry; int ret; _enter(",{%x,%x,%x,%x},%u", @@ -533,8 +536,9 @@ static int rxrpc_preparse_xdr_rxk5(struct key_preparsed_payload *prep, pptoken = &(*pptoken)->next) continue; *pptoken = token; - if (token->kad->expiry < prep->expiry) - prep->expiry = token->kad->expiry; + expiry = rxrpc_u32_to_time64(token->kad->expiry); + if (expiry < prep->expiry) + prep->expiry = expiry; _leave(" = 0"); return 0; @@ -691,6 +695,7 @@ static int rxrpc_preparse(struct key_preparsed_payload *prep) { const struct rxrpc_key_data_v1 *v1; struct rxrpc_key_token *token, **pp; + time64_t expiry; size_t plen; u32 kver; int ret; @@ -777,8 +782,9 @@ static int rxrpc_preparse(struct key_preparsed_payload *prep) while (*pp) pp = &(*pp)->next; *pp = token; - if (token->kad->expiry < prep->expiry) - prep->expiry = token->kad->expiry; + expiry = rxrpc_u32_to_time64(token->kad->expiry); + if (expiry < prep->expiry) + prep->expiry = expiry; token = NULL; ret = 0; @@ -955,7 +961,7 @@ int rxrpc_server_keyring(struct rxrpc_sock *rx, char __user *optval, */ int rxrpc_get_server_data_key(struct rxrpc_connection *conn, const void *session_key, - time_t expiry, + time64_t expiry, u32 kvno) { const struct cred *cred = current_cred(); @@ -982,7 +988,7 @@ int rxrpc_get_server_data_key(struct rxrpc_connection *conn, data.kver = 1; data.v1.security_index = RXRPC_SECURITY_RXKAD; data.v1.ticket_length = 0; - data.v1.expiry = expiry; + data.v1.expiry = rxrpc_time64_to_u32(expiry); data.v1.kvno = 0; memcpy(&data.v1.session_key, session_key, sizeof(data.v1.session_key)); diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 46d1a1f0b55b..34c86d2bcae5 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -854,7 +854,7 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, struct sk_buff *skb, void *ticket, size_t ticket_len, struct rxrpc_crypt *_session_key, - time_t *_expiry, + time64_t *_expiry, u32 *_abort_code) { struct skcipher_request *req; @@ -864,7 +864,7 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, struct in_addr addr; unsigned int life; const char *eproto; - time_t issue, now; + time64_t issue, now; bool little_endian; int ret; u32 abort_code; @@ -960,15 +960,15 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, if (little_endian) { __le32 stamp; memcpy(&stamp, p, 4); - issue = le32_to_cpu(stamp); + issue = rxrpc_u32_to_time64(le32_to_cpu(stamp)); } else { __be32 stamp; memcpy(&stamp, p, 4); - issue = be32_to_cpu(stamp); + issue = rxrpc_u32_to_time64(be32_to_cpu(stamp)); } p += 4; - now = get_seconds(); - _debug("KIV ISSUE: %lx [%lx]", issue, now); + now = ktime_get_real_seconds(); + _debug("KIV ISSUE: %llx [%llx]", issue, now); /* check the ticket is in date */ if (issue > now) { @@ -1053,7 +1053,7 @@ static int rxkad_verify_response(struct rxrpc_connection *conn, struct rxrpc_skb_priv *sp = rxrpc_skb(skb); struct rxrpc_crypt session_key; const char *eproto; - time_t expiry; + time64_t expiry; void *ticket; u32 abort_code, version, kvno, ticket_len, level; __be32 csum; -- cgit v1.2.3 From 0a3785855edcdb326ebf052c4f92ebc2710614c6 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 29 Aug 2017 10:15:40 +0100 Subject: rxrpc: Use correct timestamp from Kerberos 5 ticket When an XDR-encoded Kerberos 5 ticket is added as an rxrpc-type key, the expiry time should be drawn from the k5 part of the token union (which was what was filled in), rather than the kad part of the union. Reported-by: Arnd Bergmann Signed-off-by: David Howells --- net/rxrpc/key.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/rxrpc/key.c b/net/rxrpc/key.c index e2d36619b53a..e7f6b8823eb6 100644 --- a/net/rxrpc/key.c +++ b/net/rxrpc/key.c @@ -536,7 +536,7 @@ static int rxrpc_preparse_xdr_rxk5(struct key_preparsed_payload *prep, pptoken = &(*pptoken)->next) continue; *pptoken = token; - expiry = rxrpc_u32_to_time64(token->kad->expiry); + expiry = rxrpc_u32_to_time64(token->k5->endtime); if (expiry < prep->expiry) prep->expiry = expiry; -- cgit v1.2.3 From 7b674e390e513ea443c5a0f27ec95e93fb8a94fa Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 29 Aug 2017 10:18:37 +0100 Subject: rxrpc: Fix IPv6 support Fix IPv6 support in AF_RXRPC in the following ways: (1) When extracting the address from a received IPv4 packet, if the local transport socket is open for IPv6 then fill out the sockaddr_rxrpc struct for an IPv4-mapped-to-IPv6 AF_INET6 transport address instead of an AF_INET one. (2) When sending CHALLENGE or RESPONSE packets, the transport length needs to be set from the sockaddr_rxrpc::transport_len field rather than sizeof() on the IPv4 transport address. (3) When processing an IPv4 ICMP packet received by an IPv6 socket, set up the address correctly before searching for the affected peer. Signed-off-by: David Howells --- net/rxrpc/ar-internal.h | 4 ++-- net/rxrpc/call_accept.c | 2 +- net/rxrpc/conn_object.c | 2 +- net/rxrpc/local_event.c | 2 +- net/rxrpc/output.c | 2 +- net/rxrpc/peer_event.c | 6 ++++-- net/rxrpc/rxkad.c | 8 ++++---- net/rxrpc/utils.c | 23 +++++++++++++++++------ 8 files changed, 31 insertions(+), 18 deletions(-) (limited to 'net') diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 8cac66774de1..227e24794055 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -830,7 +830,6 @@ void rxrpc_process_connection(struct work_struct *); */ extern unsigned int rxrpc_connection_expiry; -int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *); struct rxrpc_connection *rxrpc_alloc_connection(gfp_t); struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *, struct sk_buff *); @@ -1060,7 +1059,8 @@ static inline void rxrpc_sysctl_exit(void) {} /* * utils.c */ -int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *, struct sk_buff *); +int rxrpc_extract_addr_from_skb(struct rxrpc_local *, struct sockaddr_rxrpc *, + struct sk_buff *); static inline bool before(u32 seq1, u32 seq2) { diff --git a/net/rxrpc/call_accept.c b/net/rxrpc/call_accept.c index ec3383f97d4c..cbd1701e813a 100644 --- a/net/rxrpc/call_accept.c +++ b/net/rxrpc/call_accept.c @@ -277,7 +277,7 @@ static struct rxrpc_call *rxrpc_alloc_incoming_call(struct rxrpc_sock *rx, * anticipation - and to save on stack space. */ xpeer = b->peer_backlog[peer_tail]; - if (rxrpc_extract_addr_from_skb(&xpeer->srx, skb) < 0) + if (rxrpc_extract_addr_from_skb(local, &xpeer->srx, skb) < 0) return NULL; peer = rxrpc_lookup_incoming_peer(local, xpeer); diff --git a/net/rxrpc/conn_object.c b/net/rxrpc/conn_object.c index 929b50d5afe8..fe575798592f 100644 --- a/net/rxrpc/conn_object.c +++ b/net/rxrpc/conn_object.c @@ -72,7 +72,7 @@ struct rxrpc_connection *rxrpc_find_connection_rcu(struct rxrpc_local *local, _enter(",%x", sp->hdr.cid & RXRPC_CIDMASK); - if (rxrpc_extract_addr_from_skb(&srx, skb) < 0) + if (rxrpc_extract_addr_from_skb(local, &srx, skb) < 0) goto not_found; k.epoch = sp->hdr.epoch; diff --git a/net/rxrpc/local_event.c b/net/rxrpc/local_event.c index 540d3955c1bc..93b5d910b4a1 100644 --- a/net/rxrpc/local_event.c +++ b/net/rxrpc/local_event.c @@ -39,7 +39,7 @@ static void rxrpc_send_version_request(struct rxrpc_local *local, _enter(""); - if (rxrpc_extract_addr_from_skb(&srx, skb) < 0) + if (rxrpc_extract_addr_from_skb(local, &srx, skb) < 0) return; msg.msg_name = &srx.transport; diff --git a/net/rxrpc/output.c b/net/rxrpc/output.c index 5bd2d0fa4a03..71e6f713fbe7 100644 --- a/net/rxrpc/output.c +++ b/net/rxrpc/output.c @@ -444,7 +444,7 @@ void rxrpc_reject_packets(struct rxrpc_local *local) rxrpc_see_skb(skb, rxrpc_skb_rx_seen); sp = rxrpc_skb(skb); - if (rxrpc_extract_addr_from_skb(&srx, skb) == 0) { + if (rxrpc_extract_addr_from_skb(local, &srx, skb) == 0) { msg.msg_namelen = srx.transport_len; code = htonl(skb->priority); diff --git a/net/rxrpc/peer_event.c b/net/rxrpc/peer_event.c index 1ed9c0c2e94f..7f749505e699 100644 --- a/net/rxrpc/peer_event.c +++ b/net/rxrpc/peer_event.c @@ -37,6 +37,7 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, memset(&srx, 0, sizeof(srx)); srx.transport_type = local->srx.transport_type; + srx.transport_len = local->srx.transport_len; srx.transport.family = local->srx.transport.family; /* Can we see an ICMP4 packet on an ICMP6 listening socket? and vice @@ -45,7 +46,6 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, switch (srx.transport.family) { case AF_INET: srx.transport.sin.sin_port = serr->port; - srx.transport_len = sizeof(struct sockaddr_in); switch (serr->ee.ee_origin) { case SO_EE_ORIGIN_ICMP: _net("Rx ICMP"); @@ -69,7 +69,6 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, #ifdef CONFIG_AF_RXRPC_IPV6 case AF_INET6: srx.transport.sin6.sin6_port = serr->port; - srx.transport_len = sizeof(struct sockaddr_in6); switch (serr->ee.ee_origin) { case SO_EE_ORIGIN_ICMP6: _net("Rx ICMP6"); @@ -79,6 +78,9 @@ static struct rxrpc_peer *rxrpc_lookup_peer_icmp_rcu(struct rxrpc_local *local, break; case SO_EE_ORIGIN_ICMP: _net("Rx ICMP on v6 sock"); + srx.transport.sin6.sin6_addr.s6_addr32[0] = 0; + srx.transport.sin6.sin6_addr.s6_addr32[1] = 0; + srx.transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); memcpy(srx.transport.sin6.sin6_addr.s6_addr + 12, skb_network_header(skb) + serr->addr_offset, sizeof(struct in_addr)); diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index 34c86d2bcae5..c38b3a1de56c 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -634,8 +634,8 @@ static int rxkad_issue_challenge(struct rxrpc_connection *conn) challenge.min_level = htonl(0); challenge.__padding = 0; - msg.msg_name = &conn->params.peer->srx.transport.sin; - msg.msg_namelen = sizeof(conn->params.peer->srx.transport.sin); + msg.msg_name = &conn->params.peer->srx.transport; + msg.msg_namelen = conn->params.peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; @@ -689,8 +689,8 @@ static int rxkad_send_response(struct rxrpc_connection *conn, _enter(""); - msg.msg_name = &conn->params.peer->srx.transport.sin; - msg.msg_namelen = sizeof(conn->params.peer->srx.transport.sin); + msg.msg_name = &conn->params.peer->srx.transport; + msg.msg_namelen = conn->params.peer->srx.transport_len; msg.msg_control = NULL; msg.msg_controllen = 0; msg.msg_flags = 0; diff --git a/net/rxrpc/utils.c b/net/rxrpc/utils.c index ff7af71c4b49..e801171fa351 100644 --- a/net/rxrpc/utils.c +++ b/net/rxrpc/utils.c @@ -17,17 +17,28 @@ /* * Fill out a peer address from a socket buffer containing a packet. */ -int rxrpc_extract_addr_from_skb(struct sockaddr_rxrpc *srx, struct sk_buff *skb) +int rxrpc_extract_addr_from_skb(struct rxrpc_local *local, + struct sockaddr_rxrpc *srx, + struct sk_buff *skb) { memset(srx, 0, sizeof(*srx)); switch (ntohs(skb->protocol)) { case ETH_P_IP: - srx->transport_type = SOCK_DGRAM; - srx->transport_len = sizeof(srx->transport.sin); - srx->transport.sin.sin_family = AF_INET; - srx->transport.sin.sin_port = udp_hdr(skb)->source; - srx->transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + if (local->srx.transport.family == AF_INET6) { + srx->transport_type = SOCK_DGRAM; + srx->transport_len = sizeof(srx->transport.sin6); + srx->transport.sin6.sin6_family = AF_INET6; + srx->transport.sin6.sin6_port = udp_hdr(skb)->source; + srx->transport.sin6.sin6_addr.s6_addr32[2] = htonl(0xffff); + srx->transport.sin6.sin6_addr.s6_addr32[3] = ip_hdr(skb)->saddr; + } else { + srx->transport_type = SOCK_DGRAM; + srx->transport_len = sizeof(srx->transport.sin); + srx->transport.sin.sin_family = AF_INET; + srx->transport.sin.sin_port = udp_hdr(skb)->source; + srx->transport.sin.sin_addr.s_addr = ip_hdr(skb)->saddr; + } return 0; #ifdef CONFIG_AF_RXRPC_IPV6 -- cgit v1.2.3 From bd2db2d231b4a9daccdba6712c3113e650260c6c Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 29 Aug 2017 10:18:43 +0100 Subject: rxrpc: Don't negate call->error before returning it call->error is stored as 0 or a negative error code. Don't negate this value (ie. make it positive) before returning it from a kernel function (though it should still be negated before passing to userspace through a control message). Signed-off-by: David Howells --- net/rxrpc/sendmsg.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index b0d2cda6ec0a..bc7f0241d92b 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -61,7 +61,7 @@ static int rxrpc_wait_for_tx_window(struct rxrpc_sock *rx, call->cong_cwnd + call->cong_extra)) break; if (call->state >= RXRPC_CALL_COMPLETE) { - ret = -call->error; + ret = call->error; break; } if (signal_pending(current)) { @@ -364,8 +364,8 @@ out: call_terminated: rxrpc_free_skb(skb, rxrpc_skb_tx_freed); - _leave(" = %d", -call->error); - return -call->error; + _leave(" = %d", call->error); + return call->error; maybe_error: if (copied) @@ -660,7 +660,7 @@ int rxrpc_kernel_send_data(struct socket *sock, struct rxrpc_call *call, break; case RXRPC_CALL_COMPLETE: read_lock_bh(&call->state_lock); - ret = -call->error; + ret = call->error; read_unlock_bh(&call->state_lock); break; default: -- cgit v1.2.3 From 3ec0efde582e7e86a1a853ef96468aff86090d20 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 29 Aug 2017 10:18:50 +0100 Subject: rxrpc: Remove some excess whitespace Remove indentation from some blank lines. Signed-off-by: David Howells --- net/rxrpc/af_rxrpc.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index a2ad4482376f..31e97f714ca9 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -591,13 +591,13 @@ static int rxrpc_getsockopt(struct socket *sock, int level, int optname, char __user *optval, int __user *_optlen) { int optlen; - + if (level != SOL_RXRPC) return -EOPNOTSUPP; if (get_user(optlen, _optlen)) return -EFAULT; - + switch (optname) { case RXRPC_SUPPORTED_CMSG: if (optlen < sizeof(int)) @@ -606,7 +606,7 @@ static int rxrpc_getsockopt(struct socket *sock, int level, int optname, put_user(sizeof(int), _optlen)) return -EFAULT; return 0; - + default: return -EOPNOTSUPP; } -- cgit v1.2.3 From e833251ad813168253fef9915aaf6a8c883337b0 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 29 Aug 2017 10:18:56 +0100 Subject: rxrpc: Add notification of end-of-Tx phase Add a callback to rxrpc_kernel_send_data() so that a kernel service can get a notification that the AF_RXRPC call has transitioned out the Tx phase and is now waiting for a reply or a final ACK. This is called from AF_RXRPC with the call state lock held so the notification is guaranteed to come before any reply is passed back. Further, modify the AFS filesystem to make use of this so that we don't have to change the afs_call state before sending the last bit of data. Signed-off-by: David Howells --- Documentation/networking/rxrpc.txt | 12 +++++++++- fs/afs/rxrpc.c | 46 +++++++++++++++++++++++++++++--------- include/net/af_rxrpc.h | 5 ++++- net/rxrpc/sendmsg.c | 34 ++++++++++++++++++++++------ 4 files changed, 77 insertions(+), 20 deletions(-) (limited to 'net') diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt index 8c70ba5dee4d..92a3c3bd5ac3 100644 --- a/Documentation/networking/rxrpc.txt +++ b/Documentation/networking/rxrpc.txt @@ -818,10 +818,15 @@ The kernel interface functions are as follows: (*) Send data through a call. + typedef void (*rxrpc_notify_end_tx_t)(struct sock *sk, + unsigned long user_call_ID, + struct sk_buff *skb); + int rxrpc_kernel_send_data(struct socket *sock, struct rxrpc_call *call, struct msghdr *msg, - size_t len); + size_t len, + rxrpc_notify_end_tx_t notify_end_rx); This is used to supply either the request part of a client call or the reply part of a server call. msg.msg_iovlen and msg.msg_iov specify the @@ -832,6 +837,11 @@ The kernel interface functions are as follows: The msg must not specify a destination address, control data or any flags other than MSG_MORE. len is the total amount of data to transmit. + notify_end_rx can be NULL or it can be used to specify a function to be + called when the call changes state to end the Tx phase. This function is + called with the call-state spinlock held to prevent any reply or final ACK + from being delivered first. + (*) Receive data from a call. int rxrpc_kernel_recv_data(struct socket *sock, diff --git a/fs/afs/rxrpc.c b/fs/afs/rxrpc.c index 10743043d431..0bf191f0dbaf 100644 --- a/fs/afs/rxrpc.c +++ b/fs/afs/rxrpc.c @@ -291,6 +291,19 @@ static void afs_load_bvec(struct afs_call *call, struct msghdr *msg, iov_iter_bvec(&msg->msg_iter, WRITE | ITER_BVEC, bv, nr, bytes); } +/* + * Advance the AFS call state when the RxRPC call ends the transmit phase. + */ +static void afs_notify_end_request_tx(struct sock *sock, + struct rxrpc_call *rxcall, + unsigned long call_user_ID) +{ + struct afs_call *call = (struct afs_call *)call_user_ID; + + if (call->state == AFS_CALL_REQUESTING) + call->state = AFS_CALL_AWAIT_REPLY; +} + /* * attach the data from a bunch of pages on an inode to a call */ @@ -310,14 +323,8 @@ static int afs_send_pages(struct afs_call *call, struct msghdr *msg) bytes = msg->msg_iter.count; nr = msg->msg_iter.nr_segs; - /* Have to change the state *before* sending the last - * packet as RxRPC might give us the reply before it - * returns from sending the request. - */ - if (first + nr - 1 >= last) - call->state = AFS_CALL_AWAIT_REPLY; - ret = rxrpc_kernel_send_data(afs_socket, call->rxcall, - msg, bytes); + ret = rxrpc_kernel_send_data(afs_socket, call->rxcall, msg, + bytes, afs_notify_end_request_tx); for (loop = 0; loop < nr; loop++) put_page(bv[loop].bv_page); if (ret < 0) @@ -409,7 +416,8 @@ int afs_make_call(struct in_addr *addr, struct afs_call *call, gfp_t gfp, if (!call->send_pages) call->state = AFS_CALL_AWAIT_REPLY; ret = rxrpc_kernel_send_data(afs_socket, rxcall, - &msg, call->request_size); + &msg, call->request_size, + afs_notify_end_request_tx); if (ret < 0) goto error_do_abort; @@ -740,6 +748,20 @@ static int afs_deliver_cm_op_id(struct afs_call *call) return call->type->deliver(call); } +/* + * Advance the AFS call state when an RxRPC service call ends the transmit + * phase. + */ +static void afs_notify_end_reply_tx(struct sock *sock, + struct rxrpc_call *rxcall, + unsigned long call_user_ID) +{ + struct afs_call *call = (struct afs_call *)call_user_ID; + + if (call->state == AFS_CALL_REPLYING) + call->state = AFS_CALL_AWAIT_ACK; +} + /* * send an empty reply */ @@ -759,7 +781,8 @@ void afs_send_empty_reply(struct afs_call *call) msg.msg_flags = 0; call->state = AFS_CALL_AWAIT_ACK; - switch (rxrpc_kernel_send_data(afs_socket, call->rxcall, &msg, 0)) { + switch (rxrpc_kernel_send_data(afs_socket, call->rxcall, &msg, 0, + afs_notify_end_reply_tx)) { case 0: _leave(" [replied]"); return; @@ -797,7 +820,8 @@ void afs_send_simple_reply(struct afs_call *call, const void *buf, size_t len) msg.msg_flags = 0; call->state = AFS_CALL_AWAIT_ACK; - n = rxrpc_kernel_send_data(afs_socket, call->rxcall, &msg, len); + n = rxrpc_kernel_send_data(afs_socket, call->rxcall, &msg, len, + afs_notify_end_reply_tx); if (n >= 0) { /* Success */ _leave(" [replied]"); diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index c172709787af..07a47ee6f783 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -21,6 +21,8 @@ struct rxrpc_call; typedef void (*rxrpc_notify_rx_t)(struct sock *, struct rxrpc_call *, unsigned long); +typedef void (*rxrpc_notify_end_tx_t)(struct sock *, struct rxrpc_call *, + unsigned long); typedef void (*rxrpc_notify_new_call_t)(struct sock *, struct rxrpc_call *, unsigned long); typedef void (*rxrpc_discard_new_call_t)(struct rxrpc_call *, unsigned long); @@ -37,7 +39,8 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *, gfp_t, rxrpc_notify_rx_t); int rxrpc_kernel_send_data(struct socket *, struct rxrpc_call *, - struct msghdr *, size_t); + struct msghdr *, size_t, + rxrpc_notify_end_tx_t); int rxrpc_kernel_recv_data(struct socket *, struct rxrpc_call *, void *, size_t, size_t *, bool, u32 *); bool rxrpc_kernel_abort_call(struct socket *, struct rxrpc_call *, diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index bc7f0241d92b..344fdce89823 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -100,12 +100,24 @@ static inline void rxrpc_instant_resend(struct rxrpc_call *call, int ix) spin_unlock_bh(&call->lock); } +/* + * Notify the owner of the call that the transmit phase is ended and the last + * packet has been queued. + */ +static void rxrpc_notify_end_tx(struct rxrpc_sock *rx, struct rxrpc_call *call, + rxrpc_notify_end_tx_t notify_end_tx) +{ + if (notify_end_tx) + notify_end_tx(&rx->sk, call, call->user_call_ID); +} + /* * Queue a DATA packet for transmission, set the resend timeout and send the * packet immediately */ -static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb, - bool last) +static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, + struct sk_buff *skb, bool last, + rxrpc_notify_end_tx_t notify_end_tx) { struct rxrpc_skb_priv *sp = rxrpc_skb(skb); rxrpc_seq_t seq = sp->hdr.seq; @@ -141,6 +153,7 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb, switch (call->state) { case RXRPC_CALL_CLIENT_SEND_REQUEST: call->state = RXRPC_CALL_CLIENT_AWAIT_REPLY; + rxrpc_notify_end_tx(rx, call, notify_end_tx); break; case RXRPC_CALL_SERVER_ACK_REQUEST: call->state = RXRPC_CALL_SERVER_SEND_REPLY; @@ -153,6 +166,7 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb, break; case RXRPC_CALL_SERVER_SEND_REPLY: call->state = RXRPC_CALL_SERVER_AWAIT_ACK; + rxrpc_notify_end_tx(rx, call, notify_end_tx); break; default: break; @@ -189,7 +203,8 @@ static void rxrpc_queue_packet(struct rxrpc_call *call, struct sk_buff *skb, */ static int rxrpc_send_data(struct rxrpc_sock *rx, struct rxrpc_call *call, - struct msghdr *msg, size_t len) + struct msghdr *msg, size_t len, + rxrpc_notify_end_tx_t notify_end_tx) { struct rxrpc_skb_priv *sp; struct sk_buff *skb; @@ -350,7 +365,9 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, if (ret < 0) goto out; - rxrpc_queue_packet(call, skb, !msg_data_left(msg) && !more); + rxrpc_queue_packet(rx, call, skb, + !msg_data_left(msg) && !more, + notify_end_tx); skb = NULL; } } while (msg_data_left(msg) > 0); @@ -611,7 +628,7 @@ int rxrpc_do_sendmsg(struct rxrpc_sock *rx, struct msghdr *msg, size_t len) /* Reply phase not begun or not complete for service call. */ ret = -EPROTO; } else { - ret = rxrpc_send_data(rx, call, msg, len); + ret = rxrpc_send_data(rx, call, msg, len, NULL); } mutex_unlock(&call->user_mutex); @@ -631,6 +648,7 @@ error_release_sock: * @call: The call to send data through * @msg: The data to send * @len: The amount of data to send + * @notify_end_tx: Notification that the last packet is queued. * * Allow a kernel service to send data on a call. The call must be in an state * appropriate to sending data. No control data should be supplied in @msg, @@ -638,7 +656,8 @@ error_release_sock: * more data to come, otherwise this data will end the transmission phase. */ int rxrpc_kernel_send_data(struct socket *sock, struct rxrpc_call *call, - struct msghdr *msg, size_t len) + struct msghdr *msg, size_t len, + rxrpc_notify_end_tx_t notify_end_tx) { int ret; @@ -656,7 +675,8 @@ int rxrpc_kernel_send_data(struct socket *sock, struct rxrpc_call *call, case RXRPC_CALL_CLIENT_SEND_REQUEST: case RXRPC_CALL_SERVER_ACK_REQUEST: case RXRPC_CALL_SERVER_SEND_REPLY: - ret = rxrpc_send_data(rxrpc_sk(sock->sk), call, msg, len); + ret = rxrpc_send_data(rxrpc_sk(sock->sk), call, msg, len, + notify_end_tx); break; case RXRPC_CALL_COMPLETE: read_lock_bh(&call->state_lock); -- cgit v1.2.3 From c038a58ccfd6704d4d7d60ed3d6a0fca13cf13a4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 29 Aug 2017 10:19:01 +0100 Subject: rxrpc: Allow failed client calls to be retried Allow a client call that failed on network error to be retried, provided that the Tx queue still holds DATA packet 1. This allows an operation to be submitted to another server or another address for the same server without having to repackage and re-encrypt the data so far processed. Two new functions are provided: (1) rxrpc_kernel_check_call() - This is used to find out the completion state of a call to guess whether it can be retried and whether it should be retried. (2) rxrpc_kernel_retry_call() - Disconnect the call from its current connection, reset the state and submit it as a new client call to a new address. The new address need not match the previous address. A call may be retried even if all the data hasn't been loaded into it yet; a partially constructed will be retained at the same point it was at when an error condition was detected. msg_data_left() can be used to find out how much data was packaged before the error occurred. Signed-off-by: David Howells --- Documentation/networking/rxrpc.txt | 45 ++++++++++++++++ include/net/af_rxrpc.h | 16 ++++++ net/rxrpc/af_rxrpc.c | 69 +++++++++++++++++++++++++ net/rxrpc/ar-internal.h | 19 +++---- net/rxrpc/call_object.c | 102 +++++++++++++++++++++++++++++++++++-- net/rxrpc/conn_client.c | 17 +++++-- net/rxrpc/sendmsg.c | 24 +++++---- 7 files changed, 261 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/Documentation/networking/rxrpc.txt b/Documentation/networking/rxrpc.txt index 92a3c3bd5ac3..810620153a44 100644 --- a/Documentation/networking/rxrpc.txt +++ b/Documentation/networking/rxrpc.txt @@ -975,6 +975,51 @@ The kernel interface functions are as follows: size should be set when the call is begun. tx_total_len may not be less than zero. + (*) Check to see the completion state of a call so that the caller can assess + whether it needs to be retried. + + enum rxrpc_call_completion { + RXRPC_CALL_SUCCEEDED, + RXRPC_CALL_REMOTELY_ABORTED, + RXRPC_CALL_LOCALLY_ABORTED, + RXRPC_CALL_LOCAL_ERROR, + RXRPC_CALL_NETWORK_ERROR, + }; + + int rxrpc_kernel_check_call(struct socket *sock, struct rxrpc_call *call, + enum rxrpc_call_completion *_compl, + u32 *_abort_code); + + On return, -EINPROGRESS will be returned if the call is still ongoing; if + it is finished, *_compl will be set to indicate the manner of completion, + *_abort_code will be set to any abort code that occurred. 0 will be + returned on a successful completion, -ECONNABORTED will be returned if the + client failed due to a remote abort and anything else will return an + appropriate error code. + + The caller should look at this information to decide if it's worth + retrying the call. + + (*) Retry a client call. + + int rxrpc_kernel_retry_call(struct socket *sock, + struct rxrpc_call *call, + struct sockaddr_rxrpc *srx, + struct key *key); + + This attempts to partially reinitialise a call and submit it again whilst + reusing the original call's Tx queue to avoid the need to repackage and + re-encrypt the data to be sent. call indicates the call to retry, srx the + new address to send it to and key the encryption key to use for signing or + encrypting the packets. + + For this to work, the first Tx data packet must still be in the transmit + queue, and currently this is only permitted for local and network errors + and the call must not have been aborted. Any partially constructed Tx + packet is left as is and can continue being filled afterwards. + + It returns 0 if the call was requeued and an error otherwise. + ======================= CONFIGURABLE PARAMETERS diff --git a/include/net/af_rxrpc.h b/include/net/af_rxrpc.h index 07a47ee6f783..3ac79150291f 100644 --- a/include/net/af_rxrpc.h +++ b/include/net/af_rxrpc.h @@ -19,6 +19,18 @@ struct sock; struct socket; struct rxrpc_call; +/* + * Call completion condition (state == RXRPC_CALL_COMPLETE). + */ +enum rxrpc_call_completion { + RXRPC_CALL_SUCCEEDED, /* - Normal termination */ + RXRPC_CALL_REMOTELY_ABORTED, /* - call aborted by peer */ + RXRPC_CALL_LOCALLY_ABORTED, /* - call aborted locally on error or close */ + RXRPC_CALL_LOCAL_ERROR, /* - call failed due to local error */ + RXRPC_CALL_NETWORK_ERROR, /* - call terminated by network error */ + NR__RXRPC_CALL_COMPLETIONS +}; + typedef void (*rxrpc_notify_rx_t)(struct sock *, struct rxrpc_call *, unsigned long); typedef void (*rxrpc_notify_end_tx_t)(struct sock *, struct rxrpc_call *, @@ -51,5 +63,9 @@ void rxrpc_kernel_get_peer(struct socket *, struct rxrpc_call *, int rxrpc_kernel_charge_accept(struct socket *, rxrpc_notify_rx_t, rxrpc_user_attach_call_t, unsigned long, gfp_t); void rxrpc_kernel_set_tx_length(struct socket *, struct rxrpc_call *, s64); +int rxrpc_kernel_retry_call(struct socket *, struct rxrpc_call *, + struct sockaddr_rxrpc *, struct key *); +int rxrpc_kernel_check_call(struct socket *, struct rxrpc_call *, + enum rxrpc_call_completion *, u32 *); #endif /* _NET_RXRPC_H */ diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 31e97f714ca9..fb17552fd292 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -336,6 +336,75 @@ void rxrpc_kernel_end_call(struct socket *sock, struct rxrpc_call *call) } EXPORT_SYMBOL(rxrpc_kernel_end_call); +/** + * rxrpc_kernel_check_call - Check a call's state + * @sock: The socket the call is on + * @call: The call to check + * @_compl: Where to store the completion state + * @_abort_code: Where to store any abort code + * + * Allow a kernel service to query the state of a call and find out the manner + * of its termination if it has completed. Returns -EINPROGRESS if the call is + * still going, 0 if the call finished successfully, -ECONNABORTED if the call + * was aborted and an appropriate error if the call failed in some other way. + */ +int rxrpc_kernel_check_call(struct socket *sock, struct rxrpc_call *call, + enum rxrpc_call_completion *_compl, u32 *_abort_code) +{ + if (call->state != RXRPC_CALL_COMPLETE) + return -EINPROGRESS; + smp_rmb(); + *_compl = call->completion; + *_abort_code = call->abort_code; + return call->error; +} +EXPORT_SYMBOL(rxrpc_kernel_check_call); + +/** + * rxrpc_kernel_retry_call - Allow a kernel service to retry a call + * @sock: The socket the call is on + * @call: The call to retry + * @srx: The address of the peer to contact + * @key: The security context to use (defaults to socket setting) + * + * Allow a kernel service to try resending a client call that failed due to a + * network error to a new address. The Tx queue is maintained intact, thereby + * relieving the need to re-encrypt any request data that has already been + * buffered. + */ +int rxrpc_kernel_retry_call(struct socket *sock, struct rxrpc_call *call, + struct sockaddr_rxrpc *srx, struct key *key) +{ + struct rxrpc_conn_parameters cp; + struct rxrpc_sock *rx = rxrpc_sk(sock->sk); + int ret; + + _enter("%d{%d}", call->debug_id, atomic_read(&call->usage)); + + if (!key) + key = rx->key; + if (key && !key->payload.data[0]) + key = NULL; /* a no-security key */ + + memset(&cp, 0, sizeof(cp)); + cp.local = rx->local; + cp.key = key; + cp.security_level = 0; + cp.exclusive = false; + cp.service_id = srx->srx_service; + + mutex_lock(&call->user_mutex); + + ret = rxrpc_prepare_call_for_retry(rx, call); + if (ret == 0) + ret = rxrpc_retry_client_call(rx, call, &cp, srx, GFP_KERNEL); + + mutex_unlock(&call->user_mutex); + _leave(" = %d", ret); + return ret; +} +EXPORT_SYMBOL(rxrpc_kernel_retry_call); + /** * rxrpc_kernel_new_call_notification - Get notifications of new calls * @sock: The socket to intercept received messages on diff --git a/net/rxrpc/ar-internal.h b/net/rxrpc/ar-internal.h index 227e24794055..ea5600b747cc 100644 --- a/net/rxrpc/ar-internal.h +++ b/net/rxrpc/ar-internal.h @@ -445,6 +445,7 @@ enum rxrpc_call_flag { RXRPC_CALL_EXPOSED, /* The call was exposed to the world */ RXRPC_CALL_RX_LAST, /* Received the last packet (at rxtx_top) */ RXRPC_CALL_TX_LAST, /* Last packet in Tx buffer (at rxtx_top) */ + RXRPC_CALL_TX_LASTQ, /* Last packet has been queued */ RXRPC_CALL_SEND_PING, /* A ping will need to be sent */ RXRPC_CALL_PINGING, /* Ping in process */ RXRPC_CALL_RETRANS_TIMEOUT, /* Retransmission due to timeout occurred */ @@ -481,18 +482,6 @@ enum rxrpc_call_state { NR__RXRPC_CALL_STATES }; -/* - * Call completion condition (state == RXRPC_CALL_COMPLETE). - */ -enum rxrpc_call_completion { - RXRPC_CALL_SUCCEEDED, /* - Normal termination */ - RXRPC_CALL_REMOTELY_ABORTED, /* - call aborted by peer */ - RXRPC_CALL_LOCALLY_ABORTED, /* - call aborted locally on error or close */ - RXRPC_CALL_LOCAL_ERROR, /* - call failed due to local error */ - RXRPC_CALL_NETWORK_ERROR, /* - call terminated by network error */ - NR__RXRPC_CALL_COMPLETIONS -}; - /* * Call Tx congestion management modes. */ @@ -687,9 +676,15 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *, struct rxrpc_conn_parameters *, struct sockaddr_rxrpc *, unsigned long, s64, gfp_t); +int rxrpc_retry_client_call(struct rxrpc_sock *, + struct rxrpc_call *, + struct rxrpc_conn_parameters *, + struct sockaddr_rxrpc *, + gfp_t); void rxrpc_incoming_call(struct rxrpc_sock *, struct rxrpc_call *, struct sk_buff *); void rxrpc_release_call(struct rxrpc_sock *, struct rxrpc_call *); +int rxrpc_prepare_call_for_retry(struct rxrpc_sock *, struct rxrpc_call *); void rxrpc_release_calls_on_socket(struct rxrpc_sock *); bool __rxrpc_queue_call(struct rxrpc_call *); bool rxrpc_queue_call(struct rxrpc_call *); diff --git a/net/rxrpc/call_object.c b/net/rxrpc/call_object.c index d7809a0620b4..fcdd6555a820 100644 --- a/net/rxrpc/call_object.c +++ b/net/rxrpc/call_object.c @@ -269,11 +269,6 @@ struct rxrpc_call *rxrpc_new_client_call(struct rxrpc_sock *rx, trace_rxrpc_call(call, rxrpc_call_connected, atomic_read(&call->usage), here, NULL); - spin_lock_bh(&call->conn->params.peer->lock); - hlist_add_head(&call->error_link, - &call->conn->params.peer->error_targets); - spin_unlock_bh(&call->conn->params.peer->lock); - rxrpc_start_call_timer(call); _net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id); @@ -303,6 +298,48 @@ error: return ERR_PTR(ret); } +/* + * Retry a call to a new address. It is expected that the Tx queue of the call + * will contain data previously packaged for an old call. + */ +int rxrpc_retry_client_call(struct rxrpc_sock *rx, + struct rxrpc_call *call, + struct rxrpc_conn_parameters *cp, + struct sockaddr_rxrpc *srx, + gfp_t gfp) +{ + const void *here = __builtin_return_address(0); + int ret; + + /* Set up or get a connection record and set the protocol parameters, + * including channel number and call ID. + */ + ret = rxrpc_connect_call(call, cp, srx, gfp); + if (ret < 0) + goto error; + + trace_rxrpc_call(call, rxrpc_call_connected, atomic_read(&call->usage), + here, NULL); + + rxrpc_start_call_timer(call); + + _net("CALL new %d on CONN %d", call->debug_id, call->conn->debug_id); + + if (!test_and_set_bit(RXRPC_CALL_EV_RESEND, &call->events)) + rxrpc_queue_call(call); + + _leave(" = 0"); + return 0; + +error: + rxrpc_set_call_completion(call, RXRPC_CALL_LOCAL_ERROR, + RX_CALL_DEAD, ret); + trace_rxrpc_call(call, rxrpc_call_error, atomic_read(&call->usage), + here, ERR_PTR(ret)); + _leave(" = %d", ret); + return ret; +} + /* * Set up an incoming call. call->conn points to the connection. * This is called in BH context and isn't allowed to fail. @@ -470,6 +507,61 @@ void rxrpc_release_call(struct rxrpc_sock *rx, struct rxrpc_call *call) _leave(""); } +/* + * Prepare a kernel service call for retry. + */ +int rxrpc_prepare_call_for_retry(struct rxrpc_sock *rx, struct rxrpc_call *call) +{ + const void *here = __builtin_return_address(0); + int i; + u8 last = 0; + + _enter("{%d,%d}", call->debug_id, atomic_read(&call->usage)); + + trace_rxrpc_call(call, rxrpc_call_release, atomic_read(&call->usage), + here, (const void *)call->flags); + + ASSERTCMP(call->state, ==, RXRPC_CALL_COMPLETE); + ASSERTCMP(call->completion, !=, RXRPC_CALL_REMOTELY_ABORTED); + ASSERTCMP(call->completion, !=, RXRPC_CALL_LOCALLY_ABORTED); + ASSERT(list_empty(&call->recvmsg_link)); + + del_timer_sync(&call->timer); + + _debug("RELEASE CALL %p (%d CONN %p)", call, call->debug_id, call->conn); + + if (call->conn) + rxrpc_disconnect_call(call); + + if (rxrpc_is_service_call(call) || + !call->tx_phase || + call->tx_hard_ack != 0 || + call->rx_hard_ack != 0 || + call->rx_top != 0) + return -EINVAL; + + call->state = RXRPC_CALL_UNINITIALISED; + call->completion = RXRPC_CALL_SUCCEEDED; + call->call_id = 0; + call->cid = 0; + call->cong_cwnd = 0; + call->cong_extra = 0; + call->cong_ssthresh = 0; + call->cong_mode = 0; + call->cong_dup_acks = 0; + call->cong_cumul_acks = 0; + call->acks_lowest_nak = 0; + + for (i = 0; i < RXRPC_RXTX_BUFF_SIZE; i++) { + last |= call->rxtx_annotations[i]; + call->rxtx_annotations[i] &= RXRPC_TX_ANNO_LAST; + call->rxtx_annotations[i] |= RXRPC_TX_ANNO_RETRANS; + } + + _leave(" = 0"); + return 0; +} + /* * release all the calls associated with a socket */ diff --git a/net/rxrpc/conn_client.c b/net/rxrpc/conn_client.c index eb2157680399..5f9624bd311c 100644 --- a/net/rxrpc/conn_client.c +++ b/net/rxrpc/conn_client.c @@ -555,7 +555,10 @@ static void rxrpc_activate_one_channel(struct rxrpc_connection *conn, trace_rxrpc_client(conn, channel, rxrpc_client_chan_activate); write_lock_bh(&call->state_lock); - call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; + if (!test_bit(RXRPC_CALL_TX_LASTQ, &call->flags)) + call->state = RXRPC_CALL_CLIENT_SEND_REQUEST; + else + call->state = RXRPC_CALL_CLIENT_AWAIT_REPLY; write_unlock_bh(&call->state_lock); rxrpc_see_call(call); @@ -688,15 +691,23 @@ int rxrpc_connect_call(struct rxrpc_call *call, ret = rxrpc_get_client_conn(call, cp, srx, gfp); if (ret < 0) - return ret; + goto out; rxrpc_animate_client_conn(rxnet, call->conn); rxrpc_activate_channels(call->conn); ret = rxrpc_wait_for_channel(call, gfp); - if (ret < 0) + if (ret < 0) { rxrpc_disconnect_client_call(call); + goto out; + } + + spin_lock_bh(&call->conn->params.peer->lock); + hlist_add_head(&call->error_link, + &call->conn->params.peer->error_targets); + spin_unlock_bh(&call->conn->params.peer->lock); +out: _leave(" = %d", ret); return ret; } diff --git a/net/rxrpc/sendmsg.c b/net/rxrpc/sendmsg.c index 344fdce89823..9ea6f972767e 100644 --- a/net/rxrpc/sendmsg.c +++ b/net/rxrpc/sendmsg.c @@ -128,8 +128,10 @@ static void rxrpc_queue_packet(struct rxrpc_sock *rx, struct rxrpc_call *call, ASSERTCMP(seq, ==, call->tx_top + 1); - if (last) + if (last) { annotation |= RXRPC_TX_ANNO_LAST; + set_bit(RXRPC_CALL_TX_LASTQ, &call->flags); + } /* We have to set the timestamp before queueing as the retransmit * algorithm can see the packet as soon as we queue it. @@ -326,11 +328,6 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, call->tx_total_len -= copy; } - /* check for the far side aborting the call or a network error - * occurring */ - if (call->state == RXRPC_CALL_COMPLETE) - goto call_terminated; - /* add the packet to the send queue if it's now full */ if (sp->remain <= 0 || (msg_data_left(msg) == 0 && !more)) { @@ -370,6 +367,16 @@ static int rxrpc_send_data(struct rxrpc_sock *rx, notify_end_tx); skb = NULL; } + + /* Check for the far side aborting the call or a network error + * occurring. If this happens, save any packet that was under + * construction so that in the case of a network error, the + * call can be retried or redirected. + */ + if (call->state == RXRPC_CALL_COMPLETE) { + ret = call->error; + goto out; + } } while (msg_data_left(msg) > 0); success: @@ -379,11 +386,6 @@ out: _leave(" = %d", ret); return ret; -call_terminated: - rxrpc_free_skb(skb, rxrpc_skb_tx_freed); - _leave(" = %d", call->error); - return call->error; - maybe_error: if (copied) goto success; -- cgit v1.2.3 From a6f57028d60328c06ea23982464de2f2e4a9ea3f Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 29 Aug 2017 13:29:42 +0200 Subject: addrlabel: add/delete/get can run without rtnl There appears to be no need to use rtnl, addrlabel entries are refcounted and add/delete is serialized by the addrlabel table spinlock. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- net/ipv6/addrlabel.c | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) (limited to 'net') diff --git a/net/ipv6/addrlabel.c b/net/ipv6/addrlabel.c index cea5eb488013..b055bc79f56d 100644 --- a/net/ipv6/addrlabel.c +++ b/net/ipv6/addrlabel.c @@ -405,6 +405,18 @@ static const struct nla_policy ifal_policy[IFAL_MAX+1] = { [IFAL_LABEL] = { .len = sizeof(u32), }, }; +static bool addrlbl_ifindex_exists(struct net *net, int ifindex) +{ + + struct net_device *dev; + + rcu_read_lock(); + dev = dev_get_by_index_rcu(net, ifindex); + rcu_read_unlock(); + + return dev != NULL; +} + static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh, struct netlink_ext_ack *extack) { @@ -439,7 +451,7 @@ static int ip6addrlbl_newdel(struct sk_buff *skb, struct nlmsghdr *nlh, switch (nlh->nlmsg_type) { case RTM_NEWADDRLABEL: if (ifal->ifal_index && - !__dev_get_by_index(net, ifal->ifal_index)) + !addrlbl_ifindex_exists(net, ifal->ifal_index)) return -EINVAL; err = ip6addrlbl_add(net, pfx, ifal->ifal_prefixlen, @@ -548,7 +560,7 @@ static int ip6addrlbl_get(struct sk_buff *in_skb, struct nlmsghdr *nlh, return -EINVAL; if (ifal->ifal_index && - !__dev_get_by_index(net, ifal->ifal_index)) + !addrlbl_ifindex_exists(net, ifal->ifal_index)) return -EINVAL; if (!tb[IFAL_ADDRESS]) @@ -593,10 +605,10 @@ out: void __init ipv6_addr_label_rtnl_register(void) { __rtnl_register(PF_INET6, RTM_NEWADDRLABEL, ip6addrlbl_newdel, - NULL, 0); + NULL, RTNL_FLAG_DOIT_UNLOCKED); __rtnl_register(PF_INET6, RTM_DELADDRLABEL, ip6addrlbl_newdel, - NULL, 0); + NULL, RTNL_FLAG_DOIT_UNLOCKED); __rtnl_register(PF_INET6, RTM_GETADDRLABEL, ip6addrlbl_get, - ip6addrlbl_dump, 0); + ip6addrlbl_dump, RTNL_FLAG_DOIT_UNLOCKED); } -- cgit v1.2.3 From c31e5a4876cdfbfcc28c4fb201ad872c65671067 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 29 Aug 2017 16:37:40 +0200 Subject: xdp: remove redundant argument to trace_xdp_redirect Supplying the action argument XDP_REDIRECT to the tracepoint xdp_redirect is redundant as it is only called in-case this action was specified. Remove the argument, but keep "act" member of the tracepoint struct and populate it with XDP_REDIRECT. This makes it easier to write a common bpf_prog processing events. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/trace/events/xdp.h | 6 +++--- net/core/filter.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index 27cf2ef35f5f..f684f3b36bca 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -52,10 +52,10 @@ TRACE_EVENT(xdp_exception, TRACE_EVENT(xdp_redirect, TP_PROTO(const struct net_device *dev, - const struct bpf_prog *xdp, u32 act, + const struct bpf_prog *xdp, int to_index, int err), - TP_ARGS(dev, xdp, act, to_index, err), + TP_ARGS(dev, xdp, to_index, err), TP_STRUCT__entry( __array(u8, prog_tag, 8) @@ -68,7 +68,7 @@ TRACE_EVENT(xdp_redirect, TP_fast_assign( BUILD_BUG_ON(sizeof(__entry->prog_tag) != sizeof(xdp->tag)); memcpy(__entry->prog_tag, xdp->tag, sizeof(xdp->tag)); - __entry->act = act; + __entry->act = XDP_REDIRECT; __entry->ifindex = dev->ifindex; __entry->to_index = to_index; __entry->err = err; diff --git a/net/core/filter.c b/net/core/filter.c index 4bcd6baa80c9..de31fb684ad4 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2524,7 +2524,7 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, if (likely(!err)) ri->map_to_flush = map; out: - trace_xdp_redirect(dev, xdp_prog, XDP_REDIRECT, index, err); + trace_xdp_redirect(dev, xdp_prog, index, err); return err; } @@ -2548,7 +2548,7 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, err = __bpf_tx_xdp(fwd, NULL, xdp, 0); out: - trace_xdp_redirect(dev, xdp_prog, XDP_REDIRECT, index, err); + trace_xdp_redirect(dev, xdp_prog, index, err); return err; } EXPORT_SYMBOL_GPL(xdp_do_redirect); @@ -2582,7 +2582,7 @@ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb, skb->dev = fwd; out: - trace_xdp_redirect(dev, xdp_prog, XDP_REDIRECT, index, err); + trace_xdp_redirect(dev, xdp_prog, index, err); return err; } EXPORT_SYMBOL_GPL(xdp_do_generic_redirect); -- cgit v1.2.3 From 8d3b778ff544b369f0847e6c15f3e73057298aa4 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 29 Aug 2017 16:37:45 +0200 Subject: xdp: tracepoint xdp_redirect also need a map argument To make sense of the map index, the tracepoint user also need to know that map we are talking about. Supply the map pointer but only expose the map->id. The 'to_index' is renamed 'to_ifindex'. In the xdp_redirect_map case, this is the result of the devmap lookup. The map lookup key is exposed as map_index, which is needed to troubleshoot in case the lookup failed. The 'to_ifindex' is placed after 'err' to keep TP_STRUCT as common as possible. This also keeps the TP_STRUCT similar enough, that userspace can write a monitor program, that doesn't need to care about whether bpf_redirect or bpf_redirect_map were used. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/trace/events/xdp.h | 38 ++++++++++++++++++++++++++++++-------- net/core/filter.c | 6 +++--- 2 files changed, 33 insertions(+), 11 deletions(-) (limited to 'net') diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index f684f3b36bca..573dcfa1aeaa 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -49,20 +49,23 @@ TRACE_EVENT(xdp_exception, __entry->ifindex) ); -TRACE_EVENT(xdp_redirect, +DECLARE_EVENT_CLASS(xdp_redirect_template, TP_PROTO(const struct net_device *dev, const struct bpf_prog *xdp, - int to_index, int err), + int to_ifindex, int err, + const struct bpf_map *map, u32 map_index), - TP_ARGS(dev, xdp, to_index, err), + TP_ARGS(dev, xdp, to_ifindex, err, map, map_index), TP_STRUCT__entry( __array(u8, prog_tag, 8) __field(u32, act) __field(int, ifindex) - __field(int, to_index) __field(int, err) + __field(int, to_ifindex) + __field(u32, map_id) + __field(int, map_index) ), TP_fast_assign( @@ -70,16 +73,35 @@ TRACE_EVENT(xdp_redirect, memcpy(__entry->prog_tag, xdp->tag, sizeof(xdp->tag)); __entry->act = XDP_REDIRECT; __entry->ifindex = dev->ifindex; - __entry->to_index = to_index; __entry->err = err; + __entry->to_ifindex = to_ifindex; + __entry->map_id = map ? map->id : 0; + __entry->map_index = map_index; ), - TP_printk("prog=%s action=%s ifindex=%d to_index=%d err=%d", + TP_printk("prog=%s action=%s ifindex=%d to_ifindex=%d err=%d" + " map_id=%d map_index=%d", __print_hex_str(__entry->prog_tag, 8), __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB), - __entry->ifindex, __entry->to_index, - __entry->err) + __entry->ifindex, __entry->to_ifindex, + __entry->err, + __entry->map_id, __entry->map_index) ); + +DEFINE_EVENT(xdp_redirect_template, xdp_redirect, + TP_PROTO(const struct net_device *dev, + const struct bpf_prog *xdp, + int to_ifindex, int err, + const struct bpf_map *map, u32 map_index), + TP_ARGS(dev, xdp, to_ifindex, err, map, map_index) +); + +#define _trace_xdp_redirect(dev, xdp, to, err) \ + trace_xdp_redirect(dev, xdp, to, err, NULL, 0); + +#define trace_xdp_redirect_map(dev, xdp, fwd, err, map, idx) \ + trace_xdp_redirect(dev, xdp, fwd ? fwd->ifindex : 0, err, map, idx); + #endif /* _TRACE_XDP_H */ #include diff --git a/net/core/filter.c b/net/core/filter.c index de31fb684ad4..31eab77cc842 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2524,7 +2524,7 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, if (likely(!err)) ri->map_to_flush = map; out: - trace_xdp_redirect(dev, xdp_prog, index, err); + trace_xdp_redirect_map(dev, xdp_prog, fwd, err, map, index); return err; } @@ -2548,7 +2548,7 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, err = __bpf_tx_xdp(fwd, NULL, xdp, 0); out: - trace_xdp_redirect(dev, xdp_prog, index, err); + _trace_xdp_redirect(dev, xdp_prog, index, err); return err; } EXPORT_SYMBOL_GPL(xdp_do_redirect); @@ -2582,7 +2582,7 @@ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb, skb->dev = fwd; out: - trace_xdp_redirect(dev, xdp_prog, index, err); + _trace_xdp_redirect(dev, xdp_prog, index, err); return err; } EXPORT_SYMBOL_GPL(xdp_do_generic_redirect); -- cgit v1.2.3 From f5836ca5e9867fa6ab88cadb9873af56d9ceb589 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 29 Aug 2017 16:37:56 +0200 Subject: xdp: separate xdp_redirect tracepoint in error case There is a need to separate the xdp_redirect tracepoint into two tracepoints, for separating the error case from the normal forward case. Due to the extreme speeds XDP is operating at, loading a tracepoint have a measurable impact. Single core XDP REDIRECT (ethtool tuned rx-usecs 25) can do 13.7 Mpps forwarding, but loading a simple bpf_prog at the tracepoint (with a return 0) reduce perf to 10.2 Mpps (CPU E5-1650 v4 @ 3.60GHz, driver: ixgbe) The overhead of loading a bpf-based tracepoint can be calculated to cost 25 nanosec ((1/13782002-1/10267937)*10^9 = -24.83 ns). Using perf record on the tracepoint event, with a non-matching --filter expression, the overhead is much larger. Performance drops to 8.3 Mpps, cost 48 nanosec ((1/13782002-1/8312497)*10^9 = -47.74)) Having a separate tracepoint for err cases, which should be less frequent, allow running a continuous monitor for errors while not affecting the redirect forward performance (this have also been verified by measurements). Signed-off-by: Jesper Dangaard Brouer Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- include/trace/events/xdp.h | 22 ++++++++++++++++++---- net/core/filter.c | 37 ++++++++++++++++++++++++------------- 2 files changed, 42 insertions(+), 17 deletions(-) (limited to 'net') diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index 89eba564e199..1eebad55ebd4 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -94,11 +94,25 @@ DEFINE_EVENT(xdp_redirect_template, xdp_redirect, TP_ARGS(dev, xdp, to_ifindex, err, map, map_index) ); -#define _trace_xdp_redirect(dev, xdp, to, err) \ - trace_xdp_redirect(dev, xdp, to, err, NULL, 0); +DEFINE_EVENT(xdp_redirect_template, xdp_redirect_err, + TP_PROTO(const struct net_device *dev, + const struct bpf_prog *xdp, + int to_ifindex, int err, + const struct bpf_map *map, u32 map_index), + TP_ARGS(dev, xdp, to_ifindex, err, map, map_index) +); + +#define _trace_xdp_redirect(dev, xdp, to) \ + trace_xdp_redirect(dev, xdp, to, 0, NULL, 0); + +#define _trace_xdp_redirect_err(dev, xdp, to, err) \ + trace_xdp_redirect_err(dev, xdp, to, err, NULL, 0); + +#define trace_xdp_redirect_map(dev, xdp, fwd, map, idx) \ + trace_xdp_redirect(dev, xdp, fwd ? fwd->ifindex : 0, 0, map, idx); -#define trace_xdp_redirect_map(dev, xdp, fwd, err, map, idx) \ - trace_xdp_redirect(dev, xdp, fwd ? fwd->ifindex : 0, err, map, idx); +#define trace_xdp_redirect_map_err(dev, xdp, fwd, map, idx, err) \ + trace_xdp_redirect_err(dev, xdp, fwd ? fwd->ifindex : 0, err, map, idx); #endif /* _TRACE_XDP_H */ diff --git a/net/core/filter.c b/net/core/filter.c index 31eab77cc842..096e78de0b97 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2515,16 +2515,20 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, fwd = __dev_map_lookup_elem(map, index); if (!fwd) { err = -EINVAL; - goto out; + goto err; } if (ri->map_to_flush && ri->map_to_flush != map) xdp_do_flush_map(); err = __bpf_tx_xdp(fwd, map, xdp, index); - if (likely(!err)) - ri->map_to_flush = map; -out: - trace_xdp_redirect_map(dev, xdp_prog, fwd, err, map, index); + if (unlikely(err)) + goto err; + + ri->map_to_flush = map; + trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index); + return 0; +err: + trace_xdp_redirect_map_err(dev, xdp_prog, fwd, map, index, err); return err; } @@ -2543,12 +2547,17 @@ int xdp_do_redirect(struct net_device *dev, struct xdp_buff *xdp, ri->ifindex = 0; if (unlikely(!fwd)) { err = -EINVAL; - goto out; + goto err; } err = __bpf_tx_xdp(fwd, NULL, xdp, 0); -out: - _trace_xdp_redirect(dev, xdp_prog, index, err); + if (unlikely(err)) + goto err; + + _trace_xdp_redirect(dev, xdp_prog, index); + return 0; +err: + _trace_xdp_redirect_err(dev, xdp_prog, index, err); return err; } EXPORT_SYMBOL_GPL(xdp_do_redirect); @@ -2566,23 +2575,25 @@ int xdp_do_generic_redirect(struct net_device *dev, struct sk_buff *skb, ri->ifindex = 0; if (unlikely(!fwd)) { err = -EINVAL; - goto out; + goto err; } if (unlikely(!(fwd->flags & IFF_UP))) { err = -ENETDOWN; - goto out; + goto err; } len = fwd->mtu + fwd->hard_header_len + VLAN_HLEN; if (skb->len > len) { err = -EMSGSIZE; - goto out; + goto err; } skb->dev = fwd; -out: - _trace_xdp_redirect(dev, xdp_prog, index, err); + _trace_xdp_redirect(dev, xdp_prog, index); + return 0; +err: + _trace_xdp_redirect_err(dev, xdp_prog, index, err); return err; } EXPORT_SYMBOL_GPL(xdp_do_generic_redirect); -- cgit v1.2.3 From 59a308967589f5b3f1f42793ab49bc2e18069769 Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Tue, 29 Aug 2017 16:38:01 +0200 Subject: xdp: separate xdp_redirect tracepoint in map case Creating as specific xdp_redirect_map variant of the xdp tracepoints allow users to write simpler/faster BPF progs that get attached to these tracepoints. Goal is to still keep the tracepoints in xdp_redirect and xdp_redirect_map similar enough, that a tool can read the top part of the TP_STRUCT and produce similar monitor statistics. Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/trace/events/xdp.h | 46 ++++++++++++++++++++++++++++++++++++++-------- net/core/filter.c | 4 ++-- 2 files changed, 40 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/include/trace/events/xdp.h b/include/trace/events/xdp.h index 1eebad55ebd4..862575ac8da9 100644 --- a/include/trace/events/xdp.h +++ b/include/trace/events/xdp.h @@ -77,13 +77,11 @@ DECLARE_EVENT_CLASS(xdp_redirect_template, __entry->map_index = map_index; ), - TP_printk("prog_id=%d action=%s ifindex=%d to_ifindex=%d err=%d" - " map_id=%d map_index=%d", + TP_printk("prog_id=%d action=%s ifindex=%d to_ifindex=%d err=%d", __entry->prog_id, __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB), __entry->ifindex, __entry->to_ifindex, - __entry->err, - __entry->map_id, __entry->map_index) + __entry->err) ); DEFINE_EVENT(xdp_redirect_template, xdp_redirect, @@ -108,11 +106,43 @@ DEFINE_EVENT(xdp_redirect_template, xdp_redirect_err, #define _trace_xdp_redirect_err(dev, xdp, to, err) \ trace_xdp_redirect_err(dev, xdp, to, err, NULL, 0); -#define trace_xdp_redirect_map(dev, xdp, fwd, map, idx) \ - trace_xdp_redirect(dev, xdp, fwd ? fwd->ifindex : 0, 0, map, idx); +DEFINE_EVENT_PRINT(xdp_redirect_template, xdp_redirect_map, + TP_PROTO(const struct net_device *dev, + const struct bpf_prog *xdp, + int to_ifindex, int err, + const struct bpf_map *map, u32 map_index), + TP_ARGS(dev, xdp, to_ifindex, err, map, map_index), + TP_printk("prog_id=%d action=%s ifindex=%d to_ifindex=%d err=%d" + " map_id=%d map_index=%d", + __entry->prog_id, + __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB), + __entry->ifindex, __entry->to_ifindex, + __entry->err, + __entry->map_id, __entry->map_index) +); + +DEFINE_EVENT_PRINT(xdp_redirect_template, xdp_redirect_map_err, + TP_PROTO(const struct net_device *dev, + const struct bpf_prog *xdp, + int to_ifindex, int err, + const struct bpf_map *map, u32 map_index), + TP_ARGS(dev, xdp, to_ifindex, err, map, map_index), + TP_printk("prog_id=%d action=%s ifindex=%d to_ifindex=%d err=%d" + " map_id=%d map_index=%d", + __entry->prog_id, + __print_symbolic(__entry->act, __XDP_ACT_SYM_TAB), + __entry->ifindex, __entry->to_ifindex, + __entry->err, + __entry->map_id, __entry->map_index) +); + +#define _trace_xdp_redirect_map(dev, xdp, fwd, map, idx) \ + trace_xdp_redirect_map(dev, xdp, fwd ? fwd->ifindex : 0, \ + 0, map, idx); -#define trace_xdp_redirect_map_err(dev, xdp, fwd, map, idx, err) \ - trace_xdp_redirect_err(dev, xdp, fwd ? fwd->ifindex : 0, err, map, idx); +#define _trace_xdp_redirect_map_err(dev, xdp, fwd, map, idx, err) \ + trace_xdp_redirect_map_err(dev, xdp, fwd ? fwd->ifindex : 0, \ + err, map, idx); #endif /* _TRACE_XDP_H */ diff --git a/net/core/filter.c b/net/core/filter.c index 096e78de0b97..c6a37fe0285b 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -2525,10 +2525,10 @@ static int xdp_do_redirect_map(struct net_device *dev, struct xdp_buff *xdp, goto err; ri->map_to_flush = map; - trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index); + _trace_xdp_redirect_map(dev, xdp_prog, fwd, map, index); return 0; err: - trace_xdp_redirect_map_err(dev, xdp_prog, fwd, map, index, err); + _trace_xdp_redirect_map_err(dev, xdp_prog, fwd, map, index, err); return err; } -- cgit v1.2.3 From b74fd306ef2d48781e807dd506cca818bb83d749 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Tue, 29 Aug 2017 13:16:57 -0700 Subject: bridge: fdb add and delete tracepoints A few useful tracepoints to trace bridge forwarding database updates. Signed-off-by: Roopa Prabhu Reviewed-by: Florian Fainelli Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/trace/events/bridge.h | 98 +++++++++++++++++++++++++++++++++++++++++++ net/bridge/br_fdb.c | 7 ++++ net/core/net-traces.c | 6 +++ 3 files changed, 111 insertions(+) create mode 100644 include/trace/events/bridge.h (limited to 'net') diff --git a/include/trace/events/bridge.h b/include/trace/events/bridge.h new file mode 100644 index 000000000000..0f1cde00af11 --- /dev/null +++ b/include/trace/events/bridge.h @@ -0,0 +1,98 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM bridge + +#if !defined(_TRACE_BRIDGE_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_BRIDGE_H + +#include +#include + +#include "../../../net/bridge/br_private.h" + +TRACE_EVENT(br_fdb_add, + + TP_PROTO(struct ndmsg *ndm, struct net_device *dev, + const unsigned char *addr, u16 vid, u16 nlh_flags), + + TP_ARGS(ndm, dev, addr, vid, nlh_flags), + + TP_STRUCT__entry( + __field(u8, ndm_flags) + __string(dev, dev->name) + __array(unsigned char, addr, ETH_ALEN) + __field(u16, vid) + __field(u16, nlh_flags) + ), + + TP_fast_assign( + __assign_str(dev, dev->name); + memcpy(__entry->addr, addr, ETH_ALEN); + __entry->vid = vid; + __entry->nlh_flags = nlh_flags; + __entry->ndm_flags = ndm->ndm_flags; + ), + + TP_printk("dev %s addr %02x:%02x:%02x:%02x:%02x:%02x vid %u nlh_flags %04x ndm_flags %02x", + __get_str(dev), __entry->addr[0], __entry->addr[1], + __entry->addr[2], __entry->addr[3], __entry->addr[4], + __entry->addr[5], __entry->vid, + __entry->nlh_flags, __entry->ndm_flags) +); + +TRACE_EVENT(br_fdb_external_learn_add, + + TP_PROTO(struct net_bridge *br, struct net_bridge_port *p, + const unsigned char *addr, u16 vid), + + TP_ARGS(br, p, addr, vid), + + TP_STRUCT__entry( + __string(br_dev, br->dev->name) + __string(dev, p ? p->dev->name : "null") + __array(unsigned char, addr, ETH_ALEN) + __field(u16, vid) + ), + + TP_fast_assign( + __assign_str(br_dev, br->dev->name); + __assign_str(dev, p ? p->dev->name : "null"); + memcpy(__entry->addr, addr, ETH_ALEN); + __entry->vid = vid; + ), + + TP_printk("br_dev %s port %s addr %02x:%02x:%02x:%02x:%02x:%02x vid %u", + __get_str(br_dev), __get_str(dev), __entry->addr[0], + __entry->addr[1], __entry->addr[2], __entry->addr[3], + __entry->addr[4], __entry->addr[5], __entry->vid) +); + +TRACE_EVENT(fdb_delete, + + TP_PROTO(struct net_bridge *br, struct net_bridge_fdb_entry *f), + + TP_ARGS(br, f), + + TP_STRUCT__entry( + __string(br_dev, br->dev->name) + __string(dev, f->dst ? f->dst->dev->name : "null") + __array(unsigned char, addr, ETH_ALEN) + __field(u16, vid) + ), + + TP_fast_assign( + __assign_str(br_dev, br->dev->name); + __assign_str(dev, f->dst ? f->dst->dev->name : "null"); + memcpy(__entry->addr, f->addr.addr, ETH_ALEN); + __entry->vid = f->vlan_id; + ), + + TP_printk("br_dev %s dev %s addr %02x:%02x:%02x:%02x:%02x:%02x vid %u", + __get_str(br_dev), __get_str(dev), __entry->addr[0], + __entry->addr[1], __entry->addr[2], __entry->addr[3], + __entry->addr[4], __entry->addr[5], __entry->vid) +); + +#endif /* _TRACE_BRIDGE_H */ + +/* This part must be outside protection */ +#include diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index a79b648aac88..be5e1da6d824 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "br_private.h" static struct kmem_cache *br_fdb_cache __read_mostly; @@ -171,6 +172,8 @@ static void fdb_del_hw_addr(struct net_bridge *br, const unsigned char *addr) static void fdb_delete(struct net_bridge *br, struct net_bridge_fdb_entry *f) { + trace_fdb_delete(br, f); + if (f->is_static) fdb_del_hw_addr(br, f->addr.addr); @@ -870,6 +873,8 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[], struct net_bridge *br = NULL; int err = 0; + trace_br_fdb_add(ndm, dev, addr, vid, nlh_flags); + if (!(ndm->ndm_state & (NUD_PERMANENT|NUD_NOARP|NUD_REACHABLE))) { pr_info("bridge: RTM_NEWNEIGH with invalid state %#x\n", ndm->ndm_state); return -EINVAL; @@ -1066,6 +1071,8 @@ int br_fdb_external_learn_add(struct net_bridge *br, struct net_bridge_port *p, bool modified = false; int err = 0; + trace_br_fdb_external_learn_add(br, p, addr, vid); + spin_lock_bh(&br->hash_lock); head = &br->hash[br_mac_hash(addr, vid)]; diff --git a/net/core/net-traces.c b/net/core/net-traces.c index 4f1468ccd056..4a0292c97070 100644 --- a/net/core/net-traces.c +++ b/net/core/net-traces.c @@ -37,6 +37,12 @@ #include EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup); #endif +#if IS_ENABLED(CONFIG_BRIDGE) +#include +EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_add); +EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_external_learn_add); +EXPORT_TRACEPOINT_SYMBOL_GPL(fdb_delete); +#endif EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb); -- cgit v1.2.3 From b522ed6ed665ba6a349d31136fa3f40c408f2b5a Mon Sep 17 00:00:00 2001 From: Alexander Aring Date: Mon, 28 Aug 2017 15:03:14 -0400 Subject: act_ife: use registered ife_type as fallback This patch handles a default IFE type if it's not given by user space netlink api. The default IFE type will be the registered ethertype by IEEE for IFE ForCES. Signed-off-by: Alexander Aring Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/act_ife.c | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) (limited to 'net') diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index c5dec308b8b1..7ed1be80ee86 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -435,8 +435,8 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, struct nlattr *tb[TCA_IFE_MAX + 1]; struct nlattr *tb2[IFE_META_MAX + 1]; struct tcf_ife_info *ife; + u16 ife_type = ETH_P_IFE; struct tc_ife *parm; - u16 ife_type = 0; u8 *daddr = NULL; u8 *saddr = NULL; bool exists = false; @@ -456,18 +456,6 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, if (exists && bind) return 0; - if (parm->flags & IFE_ENCODE) { - /* Until we get issued the ethertype, we cant have - * a default.. - **/ - if (!tb[TCA_IFE_TYPE]) { - if (exists) - tcf_hash_release(*a, bind); - pr_info("You MUST pass etherype for encoding\n"); - return -EINVAL; - } - } - if (!exists) { ret = tcf_hash_create(tn, parm->index, est, a, &act_ife_ops, bind, false); @@ -484,7 +472,8 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, ife->flags = parm->flags; if (parm->flags & IFE_ENCODE) { - ife_type = nla_get_u16(tb[TCA_IFE_TYPE]); + if (tb[TCA_IFE_TYPE]) + ife_type = nla_get_u16(tb[TCA_IFE_TYPE]); if (tb[TCA_IFE_DMAC]) daddr = nla_data(tb[TCA_IFE_DMAC]); if (tb[TCA_IFE_SMAC]) -- cgit v1.2.3 From c411ed854584a71b0e86ac3019b60e4789d88086 Mon Sep 17 00:00:00 2001 From: Jiri Benc Date: Mon, 28 Aug 2017 21:43:24 +0200 Subject: nsh: add GSO support Add a new nsh/ directory. It currently holds only GSO functions but more will come: in particular, code shared by openvswitch and tc to manipulate NSH headers. For now, assume there's no hardware support for NSH segmentation. We can always introduce netdev->nsh_features later. Signed-off-by: Jiri Benc Signed-off-by: David S. Miller --- net/Kconfig | 1 + net/Makefile | 1 + net/nsh/Kconfig | 9 ++++++ net/nsh/Makefile | 1 + net/nsh/nsh.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 103 insertions(+) create mode 100644 net/nsh/Kconfig create mode 100644 net/nsh/Makefile create mode 100644 net/nsh/nsh.c (limited to 'net') diff --git a/net/Kconfig b/net/Kconfig index 8614a9ccfbef..e0e7c62c88f0 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -235,6 +235,7 @@ source "net/openvswitch/Kconfig" source "net/vmw_vsock/Kconfig" source "net/netlink/Kconfig" source "net/mpls/Kconfig" +source "net/nsh/Kconfig" source "net/hsr/Kconfig" source "net/switchdev/Kconfig" source "net/l3mdev/Kconfig" diff --git a/net/Makefile b/net/Makefile index 3d3feff3643b..ae2fe2283d2f 100644 --- a/net/Makefile +++ b/net/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_NET_IFE) += ife/ obj-$(CONFIG_OPENVSWITCH) += openvswitch/ obj-$(CONFIG_VSOCKETS) += vmw_vsock/ obj-$(CONFIG_MPLS) += mpls/ +obj-$(CONFIG_NET_NSH) += nsh/ obj-$(CONFIG_HSR) += hsr/ ifneq ($(CONFIG_NET_SWITCHDEV),) obj-y += switchdev/ diff --git a/net/nsh/Kconfig b/net/nsh/Kconfig new file mode 100644 index 000000000000..bafc3dd60c2c --- /dev/null +++ b/net/nsh/Kconfig @@ -0,0 +1,9 @@ +menuconfig NET_NSH + tristate "Network Service Header (NSH) protocol" + default n + ---help--- + Network Service Header is an implementation of Service Function + Chaining (RFC 7665). The current implementation in Linux supports + only MD type 1 and only with the openvswitch module. + + If unsure, say N. diff --git a/net/nsh/Makefile b/net/nsh/Makefile new file mode 100644 index 000000000000..c93c787385ca --- /dev/null +++ b/net/nsh/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_NET_NSH) += nsh.o diff --git a/net/nsh/nsh.c b/net/nsh/nsh.c new file mode 100644 index 000000000000..58fb827439a8 --- /dev/null +++ b/net/nsh/nsh.c @@ -0,0 +1,91 @@ +/* + * Network Service Header + * + * Copyright (c) 2017 Red Hat, Inc. -- Jiri Benc + * + * 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. + */ + +#include +#include +#include +#include +#include + +static struct sk_buff *nsh_gso_segment(struct sk_buff *skb, + netdev_features_t features) +{ + struct sk_buff *segs = ERR_PTR(-EINVAL); + unsigned int nsh_len, mac_len; + __be16 proto; + int nhoff; + + skb_reset_network_header(skb); + + nhoff = skb->network_header - skb->mac_header; + mac_len = skb->mac_len; + + if (unlikely(!pskb_may_pull(skb, NSH_BASE_HDR_LEN))) + goto out; + nsh_len = nsh_hdr_len(nsh_hdr(skb)); + if (unlikely(!pskb_may_pull(skb, nsh_len))) + goto out; + + proto = tun_p_to_eth_p(nsh_hdr(skb)->np); + if (!proto) + goto out; + + __skb_pull(skb, nsh_len); + + skb_reset_mac_header(skb); + skb_reset_mac_len(skb); + skb->protocol = proto; + + features &= NETIF_F_SG; + segs = skb_mac_gso_segment(skb, features); + if (IS_ERR_OR_NULL(segs)) { + skb_gso_error_unwind(skb, htons(ETH_P_NSH), nsh_len, + skb->network_header - nhoff, + mac_len); + goto out; + } + + for (skb = segs; skb; skb = skb->next) { + skb->protocol = htons(ETH_P_NSH); + __skb_push(skb, nsh_len); + skb_set_mac_header(skb, -nhoff); + skb->network_header = skb->mac_header + mac_len; + skb->mac_len = mac_len; + } + +out: + return segs; +} + +static struct packet_offload nsh_packet_offload __read_mostly = { + .type = htons(ETH_P_NSH), + .priority = 15, + .callbacks = { + .gso_segment = nsh_gso_segment, + }, +}; + +static int __init nsh_init_module(void) +{ + dev_add_offload(&nsh_packet_offload); + return 0; +} + +static void __exit nsh_cleanup_module(void) +{ + dev_remove_offload(&nsh_packet_offload); +} + +module_init(nsh_init_module); +module_exit(nsh_cleanup_module); + +MODULE_AUTHOR("Jiri Benc "); +MODULE_DESCRIPTION("NSH protocol"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 1b70d792cf6775fb5d0737524387893daeb5374a Mon Sep 17 00:00:00 2001 From: David Ahern Date: Mon, 28 Aug 2017 13:53:34 -0700 Subject: ipv6: Use rt6i_idev index for echo replies to a local address Tariq repored local pings to linklocal address is failing: $ ifconfig ens8 ens8: flags=4163 mtu 1500 inet 11.141.16.6 netmask 255.255.0.0 broadcast 11.141.255.255 inet6 fe80::7efe:90ff:fecb:7502 prefixlen 64 scopeid 0x20 ether 7c:fe:90:cb:75:02 txqueuelen 1000 (Ethernet) RX packets 12 bytes 1164 (1.1 KiB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 30 bytes 2484 (2.4 KiB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 $ /bin/ping6 -c 3 fe80::7efe:90ff:fecb:7502%ens8 PING fe80::7efe:90ff:fecb:7502%ens8(fe80::7efe:90ff:fecb:7502) 56 data bytes Signed-off-by: David S. Miller --- include/net/ip6_route.h | 10 ++++++++++ net/ipv6/icmp.c | 33 ++++++++++++++++++++------------- 2 files changed, 30 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/include/net/ip6_route.h b/include/net/ip6_route.h index 882bc3c7ccde..ee96f402cb75 100644 --- a/include/net/ip6_route.h +++ b/include/net/ip6_route.h @@ -164,6 +164,16 @@ void rt6_mtu_change(struct net_device *dev, unsigned int mtu); void rt6_remove_prefsrc(struct inet6_ifaddr *ifp); void rt6_clean_tohost(struct net *net, struct in6_addr *gateway); +static inline const struct rt6_info *skb_rt6_info(const struct sk_buff *skb) +{ + const struct dst_entry *dst = skb_dst(skb); + const struct rt6_info *rt6 = NULL; + + if (dst) + rt6 = container_of(dst, struct rt6_info, dst); + + return rt6; +} /* * Store a destination cache entry in a socket diff --git a/net/ipv6/icmp.c b/net/ipv6/icmp.c index dd7608cf1d72..5acb54405b10 100644 --- a/net/ipv6/icmp.c +++ b/net/ipv6/icmp.c @@ -399,6 +399,24 @@ relookup_failed: return ERR_PTR(err); } +static int icmp6_iif(const struct sk_buff *skb) +{ + int iif = skb->dev->ifindex; + + /* for local traffic to local address, skb dev is the loopback + * device. Check if there is a dst attached to the skb and if so + * get the real device index. + */ + if (unlikely(iif == LOOPBACK_IFINDEX)) { + const struct rt6_info *rt6 = skb_rt6_info(skb); + + if (rt6) + iif = rt6->rt6i_idev->dev->ifindex; + } + + return iif; +} + /* * Send an ICMP message in response to a packet in error */ @@ -460,18 +478,7 @@ static void icmp6_send(struct sk_buff *skb, u8 type, u8 code, __u32 info, */ if (__ipv6_addr_needs_scope_id(addr_type)) { - iif = skb->dev->ifindex; - - /* for local packets, get the real device index */ - if (iif == LOOPBACK_IFINDEX) { - dst = skb_dst(skb); - if (dst) { - struct rt6_info *rt; - - rt = container_of(dst, struct rt6_info, dst); - iif = rt->rt6i_idev->dev->ifindex; - } - } + iif = icmp6_iif(skb); } else { dst = skb_dst(skb); iif = l3mdev_master_ifindex(dst ? dst->dev : skb->dev); @@ -694,7 +701,7 @@ static void icmpv6_echo_reply(struct sk_buff *skb) fl6.daddr = ipv6_hdr(skb)->saddr; if (saddr) fl6.saddr = *saddr; - fl6.flowi6_oif = skb->dev->ifindex; + fl6.flowi6_oif = icmp6_iif(skb); fl6.fl6_icmp_type = ICMPV6_ECHO_REPLY; fl6.flowi6_mark = mark; fl6.flowi6_uid = sock_net_uid(net, NULL); -- cgit v1.2.3 From eaa72dc47488d599439cd0fd0f8c4f1bcb3906bb Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Tue, 29 Aug 2017 15:16:01 -0700 Subject: neigh: increase queue_len_bytes to match wmem_default Florian reported UDP xmit drops that could be root caused to the too small neigh limit. Current limit is 64 KB, meaning that even a single UDP socket would hit it, since its default sk_sndbuf comes from net.core.wmem_default (~212992 bytes on 64bit arches). Once ARP/ND resolution is in progress, we should allow a little more packets to be queued, at least for one producer. Once neigh arp_queue is filled, a rogue socket should hit its sk_sndbuf limit and either block in sendmsg() or return -EAGAIN. Signed-off-by: Eric Dumazet Reported-by: Florian Fainelli Signed-off-by: David S. Miller --- Documentation/networking/ip-sysctl.txt | 7 +++++-- include/net/sock.h | 10 ++++++++++ net/core/sock.c | 10 ---------- net/decnet/dn_neigh.c | 2 +- net/ipv4/arp.c | 2 +- net/ipv4/tcp_input.c | 2 +- net/ipv6/ndisc.c | 2 +- 7 files changed, 19 insertions(+), 16 deletions(-) (limited to 'net') diff --git a/Documentation/networking/ip-sysctl.txt b/Documentation/networking/ip-sysctl.txt index 6b0bc0f71534..b3345d0fe0a6 100644 --- a/Documentation/networking/ip-sysctl.txt +++ b/Documentation/networking/ip-sysctl.txt @@ -109,7 +109,10 @@ neigh/default/unres_qlen_bytes - INTEGER queued for each unresolved address by other network layers. (added in linux 3.3) Setting negative value is meaningless and will return error. - Default: 65536 Bytes(64KB) + Default: SK_WMEM_MAX, (same as net.core.wmem_default). + Exact value depends on architecture and kernel options, + but should be enough to allow queuing 256 packets + of medium size. neigh/default/unres_qlen - INTEGER The maximum number of packets which may be queued for each @@ -119,7 +122,7 @@ neigh/default/unres_qlen - INTEGER unexpected packet loss. The current default value is calculated according to default value of unres_qlen_bytes and true size of packet. - Default: 31 + Default: 101 mtu_expires - INTEGER Time, in seconds, that cached PMTU information is kept. diff --git a/include/net/sock.h b/include/net/sock.h index 1c2912d433e8..03a362568357 100644 --- a/include/net/sock.h +++ b/include/net/sock.h @@ -2368,6 +2368,16 @@ bool sk_net_capable(const struct sock *sk, int cap); void sk_get_meminfo(const struct sock *sk, u32 *meminfo); +/* Take into consideration the size of the struct sk_buff overhead in the + * determination of these values, since that is non-constant across + * platforms. This makes socket queueing behavior and performance + * not depend upon such differences. + */ +#define _SK_MEM_PACKETS 256 +#define _SK_MEM_OVERHEAD SKB_TRUESIZE(256) +#define SK_WMEM_MAX (_SK_MEM_OVERHEAD * _SK_MEM_PACKETS) +#define SK_RMEM_MAX (_SK_MEM_OVERHEAD * _SK_MEM_PACKETS) + extern __u32 sysctl_wmem_max; extern __u32 sysctl_rmem_max; diff --git a/net/core/sock.c b/net/core/sock.c index dfdd14cac775..9b7b6bbb2a23 100644 --- a/net/core/sock.c +++ b/net/core/sock.c @@ -307,16 +307,6 @@ static struct lock_class_key af_wlock_keys[AF_MAX]; static struct lock_class_key af_elock_keys[AF_MAX]; static struct lock_class_key af_kern_callback_keys[AF_MAX]; -/* Take into consideration the size of the struct sk_buff overhead in the - * determination of these values, since that is non-constant across - * platforms. This makes socket queueing behavior and performance - * not depend upon such differences. - */ -#define _SK_MEM_PACKETS 256 -#define _SK_MEM_OVERHEAD SKB_TRUESIZE(256) -#define SK_WMEM_MAX (_SK_MEM_OVERHEAD * _SK_MEM_PACKETS) -#define SK_RMEM_MAX (_SK_MEM_OVERHEAD * _SK_MEM_PACKETS) - /* Run time adjustable parameters. */ __u32 sysctl_wmem_max __read_mostly = SK_WMEM_MAX; EXPORT_SYMBOL(sysctl_wmem_max); diff --git a/net/decnet/dn_neigh.c b/net/decnet/dn_neigh.c index 21dedf6fd0f7..22bf0b95d6ed 100644 --- a/net/decnet/dn_neigh.c +++ b/net/decnet/dn_neigh.c @@ -94,7 +94,7 @@ struct neigh_table dn_neigh_table = { [NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ, [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ, [NEIGH_VAR_GC_STALETIME] = 60 * HZ, - [NEIGH_VAR_QUEUE_LEN_BYTES] = 64*1024, + [NEIGH_VAR_QUEUE_LEN_BYTES] = SK_WMEM_MAX, [NEIGH_VAR_PROXY_QLEN] = 0, [NEIGH_VAR_ANYCAST_DELAY] = 0, [NEIGH_VAR_PROXY_DELAY] = 0, diff --git a/net/ipv4/arp.c b/net/ipv4/arp.c index 8b52179ddc6e..7c45b8896709 100644 --- a/net/ipv4/arp.c +++ b/net/ipv4/arp.c @@ -171,7 +171,7 @@ struct neigh_table arp_tbl = { [NEIGH_VAR_BASE_REACHABLE_TIME] = 30 * HZ, [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ, [NEIGH_VAR_GC_STALETIME] = 60 * HZ, - [NEIGH_VAR_QUEUE_LEN_BYTES] = 64 * 1024, + [NEIGH_VAR_QUEUE_LEN_BYTES] = SK_WMEM_MAX, [NEIGH_VAR_PROXY_QLEN] = 64, [NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ, [NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10, diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 568ccfd6dd37..7616cd76f6f6 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -6086,9 +6086,9 @@ int tcp_conn_request(struct request_sock_ops *rsk_ops, struct tcp_sock *tp = tcp_sk(sk); struct net *net = sock_net(sk); struct sock *fastopen_sk = NULL; - struct dst_entry *dst = NULL; struct request_sock *req; bool want_cookie = false; + struct dst_entry *dst; struct flowi fl; /* TW buckets are converted to open requests without diff --git a/net/ipv6/ndisc.c b/net/ipv6/ndisc.c index 5e338eb89509..266a530414d7 100644 --- a/net/ipv6/ndisc.c +++ b/net/ipv6/ndisc.c @@ -127,7 +127,7 @@ struct neigh_table nd_tbl = { [NEIGH_VAR_BASE_REACHABLE_TIME] = ND_REACHABLE_TIME, [NEIGH_VAR_DELAY_PROBE_TIME] = 5 * HZ, [NEIGH_VAR_GC_STALETIME] = 60 * HZ, - [NEIGH_VAR_QUEUE_LEN_BYTES] = 64 * 1024, + [NEIGH_VAR_QUEUE_LEN_BYTES] = SK_WMEM_MAX, [NEIGH_VAR_PROXY_QLEN] = 64, [NEIGH_VAR_ANYCAST_DELAY] = 1 * HZ, [NEIGH_VAR_PROXY_DELAY] = (8 * HZ) / 10, -- cgit v1.2.3 From c1d2b4c3e204e602c97680335d082b8d012d08cd Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 30 Aug 2017 19:24:57 +0200 Subject: tcp: Revert "tcp: remove CA_ACK_SLOWPATH" This change was a followup to the header prediction removal, so first revert this as a prerequisite to back out hp removal. Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/tcp.h | 5 +++-- net/ipv4/tcp_input.c | 35 +++++++++++++++++++---------------- net/ipv4/tcp_westwood.c | 31 +++++++++++++++++++++++++++---- 3 files changed, 49 insertions(+), 22 deletions(-) (limited to 'net') diff --git a/include/net/tcp.h b/include/net/tcp.h index c614ff135b66..c546d13ffbca 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -910,8 +910,9 @@ enum tcp_ca_event { /* Information about inbound ACK, passed to cong_ops->in_ack_event() */ enum tcp_ca_ack_event_flags { - CA_ACK_WIN_UPDATE = (1 << 0), /* ACK updated window */ - CA_ACK_ECE = (1 << 1), /* ECE bit is set on ack */ + CA_ACK_SLOWPATH = (1 << 0), /* In slow path processing */ + CA_ACK_WIN_UPDATE = (1 << 1), /* ACK updated window */ + CA_ACK_ECE = (1 << 2), /* ECE bit is set on ack */ }; /* diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index 7616cd76f6f6..a0e436366d31 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -3552,7 +3552,6 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) u32 lost = tp->lost; int acked = 0; /* Number of packets newly acked */ int rexmit = REXMIT_NONE; /* Flag to (re)transmit to recover losses */ - u32 ack_ev_flags = 0; sack_state.first_sackt = 0; sack_state.rate = &rs; @@ -3593,26 +3592,30 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (flag & FLAG_UPDATE_TS_RECENT) tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq); - if (ack_seq != TCP_SKB_CB(skb)->end_seq) - flag |= FLAG_DATA; - else - NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPUREACKS); + { + u32 ack_ev_flags = CA_ACK_SLOWPATH; - flag |= tcp_ack_update_window(sk, skb, ack, ack_seq); + if (ack_seq != TCP_SKB_CB(skb)->end_seq) + flag |= FLAG_DATA; + else + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPPUREACKS); - if (TCP_SKB_CB(skb)->sacked) - flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una, - &sack_state); + flag |= tcp_ack_update_window(sk, skb, ack, ack_seq); - if (tcp_ecn_rcv_ecn_echo(tp, tcp_hdr(skb))) { - flag |= FLAG_ECE; - ack_ev_flags = CA_ACK_ECE; - } + if (TCP_SKB_CB(skb)->sacked) + flag |= tcp_sacktag_write_queue(sk, skb, prior_snd_una, + &sack_state); + + if (tcp_ecn_rcv_ecn_echo(tp, tcp_hdr(skb))) { + flag |= FLAG_ECE; + ack_ev_flags |= CA_ACK_ECE; + } - if (flag & FLAG_WIN_UPDATE) - ack_ev_flags |= CA_ACK_WIN_UPDATE; + if (flag & FLAG_WIN_UPDATE) + ack_ev_flags |= CA_ACK_WIN_UPDATE; - tcp_in_ack_event(sk, ack_ev_flags); + tcp_in_ack_event(sk, ack_ev_flags); + } /* We passed data and got it acked, remove any soft error * log. Something worked... diff --git a/net/ipv4/tcp_westwood.c b/net/ipv4/tcp_westwood.c index e5de84310949..bec9cafbe3f9 100644 --- a/net/ipv4/tcp_westwood.c +++ b/net/ipv4/tcp_westwood.c @@ -153,6 +153,24 @@ static inline void update_rtt_min(struct westwood *w) w->rtt_min = min(w->rtt, w->rtt_min); } +/* + * @westwood_fast_bw + * It is called when we are in fast path. In particular it is called when + * header prediction is successful. In such case in fact update is + * straight forward and doesn't need any particular care. + */ +static inline void westwood_fast_bw(struct sock *sk) +{ + const struct tcp_sock *tp = tcp_sk(sk); + struct westwood *w = inet_csk_ca(sk); + + westwood_update_window(sk); + + w->bk += tp->snd_una - w->snd_una; + w->snd_una = tp->snd_una; + update_rtt_min(w); +} + /* * @westwood_acked_count * This function evaluates cumul_ack for evaluating bk in case of @@ -205,12 +223,17 @@ static u32 tcp_westwood_bw_rttmin(const struct sock *sk) static void tcp_westwood_ack(struct sock *sk, u32 ack_flags) { - struct westwood *w = inet_csk_ca(sk); + if (ack_flags & CA_ACK_SLOWPATH) { + struct westwood *w = inet_csk_ca(sk); - westwood_update_window(sk); - w->bk += westwood_acked_count(sk); + westwood_update_window(sk); + w->bk += westwood_acked_count(sk); - update_rtt_min(w); + update_rtt_min(w); + return; + } + + westwood_fast_bw(sk); } static void tcp_westwood_event(struct sock *sk, enum tcp_ca_event event) -- cgit v1.2.3 From 31770e34e43d6c8dee129bfee77e56c34e61f0e5 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Wed, 30 Aug 2017 19:24:58 +0200 Subject: tcp: Revert "tcp: remove header prediction" This reverts commit 45f119bf936b1f9f546a0b139c5b56f9bb2bdc78. Eric Dumazet says: We found at Google a significant regression caused by 45f119bf936b1f9f546a0b139c5b56f9bb2bdc78 tcp: remove header prediction In typical RPC (TCP_RR), when a TCP socket receives data, we now call tcp_ack() while we used to not call it. This touches enough cache lines to cause a slowdown. so problem does not seem to be HP removal itself but the tcp_ack() call. Therefore, it might be possible to remove HP after all, provided one finds a way to elide tcp_ack for most cases. Reported-by: Eric Dumazet Signed-off-by: Florian Westphal Signed-off-by: David S. Miller --- include/linux/tcp.h | 6 ++ include/net/tcp.h | 23 ++++++ include/uapi/linux/snmp.h | 2 + net/ipv4/proc.c | 2 + net/ipv4/tcp.c | 4 +- net/ipv4/tcp_input.c | 188 ++++++++++++++++++++++++++++++++++++++++++++-- net/ipv4/tcp_minisocks.c | 2 + net/ipv4/tcp_output.c | 2 + 8 files changed, 223 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/linux/tcp.h b/include/linux/tcp.h index 267164a1d559..4aa40ef02d32 100644 --- a/include/linux/tcp.h +++ b/include/linux/tcp.h @@ -147,6 +147,12 @@ struct tcp_sock { u16 tcp_header_len; /* Bytes of tcp header to send */ u16 gso_segs; /* Max number of segs per GSO packet */ +/* + * Header prediction flags + * 0x5?10 << 16 + snd_wnd in net byte order + */ + __be32 pred_flags; + /* * RFC793 variables by their proper names. This means you can * read the code and the spec side by side (and laugh ...) diff --git a/include/net/tcp.h b/include/net/tcp.h index c546d13ffbca..9c3db054e47f 100644 --- a/include/net/tcp.h +++ b/include/net/tcp.h @@ -634,6 +634,29 @@ static inline u32 __tcp_set_rto(const struct tcp_sock *tp) return usecs_to_jiffies((tp->srtt_us >> 3) + tp->rttvar_us); } +static inline void __tcp_fast_path_on(struct tcp_sock *tp, u32 snd_wnd) +{ + tp->pred_flags = htonl((tp->tcp_header_len << 26) | + ntohl(TCP_FLAG_ACK) | + snd_wnd); +} + +static inline void tcp_fast_path_on(struct tcp_sock *tp) +{ + __tcp_fast_path_on(tp, tp->snd_wnd >> tp->rx_opt.snd_wscale); +} + +static inline void tcp_fast_path_check(struct sock *sk) +{ + struct tcp_sock *tp = tcp_sk(sk); + + if (RB_EMPTY_ROOT(&tp->out_of_order_queue) && + tp->rcv_wnd && + atomic_read(&sk->sk_rmem_alloc) < sk->sk_rcvbuf && + !tp->urg_data) + tcp_fast_path_on(tp); +} + /* Compute the actual rto_min value */ static inline u32 tcp_rto_min(struct sock *sk) { diff --git a/include/uapi/linux/snmp.h b/include/uapi/linux/snmp.h index b3f346fb9fe3..758f12b58541 100644 --- a/include/uapi/linux/snmp.h +++ b/include/uapi/linux/snmp.h @@ -184,7 +184,9 @@ enum LINUX_MIB_DELAYEDACKLOST, /* DelayedACKLost */ LINUX_MIB_LISTENOVERFLOWS, /* ListenOverflows */ LINUX_MIB_LISTENDROPS, /* ListenDrops */ + LINUX_MIB_TCPHPHITS, /* TCPHPHits */ LINUX_MIB_TCPPUREACKS, /* TCPPureAcks */ + LINUX_MIB_TCPHPACKS, /* TCPHPAcks */ LINUX_MIB_TCPRENORECOVERY, /* TCPRenoRecovery */ LINUX_MIB_TCPSACKRECOVERY, /* TCPSackRecovery */ LINUX_MIB_TCPSACKRENEGING, /* TCPSACKReneging */ diff --git a/net/ipv4/proc.c b/net/ipv4/proc.c index b6d3fe03feb3..127153f1ed8a 100644 --- a/net/ipv4/proc.c +++ b/net/ipv4/proc.c @@ -206,7 +206,9 @@ static const struct snmp_mib snmp4_net_list[] = { SNMP_MIB_ITEM("DelayedACKLost", LINUX_MIB_DELAYEDACKLOST), SNMP_MIB_ITEM("ListenOverflows", LINUX_MIB_LISTENOVERFLOWS), SNMP_MIB_ITEM("ListenDrops", LINUX_MIB_LISTENDROPS), + SNMP_MIB_ITEM("TCPHPHits", LINUX_MIB_TCPHPHITS), SNMP_MIB_ITEM("TCPPureAcks", LINUX_MIB_TCPPUREACKS), + SNMP_MIB_ITEM("TCPHPAcks", LINUX_MIB_TCPHPACKS), SNMP_MIB_ITEM("TCPRenoRecovery", LINUX_MIB_TCPRENORECOVERY), SNMP_MIB_ITEM("TCPSackRecovery", LINUX_MIB_TCPSACKRECOVERY), SNMP_MIB_ITEM("TCPSACKReneging", LINUX_MIB_TCPSACKRENEGING), diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 566083ee2654..21ca2df274c5 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1963,8 +1963,10 @@ int tcp_recvmsg(struct sock *sk, struct msghdr *msg, size_t len, int nonblock, tcp_rcv_space_adjust(sk); skip_copy: - if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) + if (tp->urg_data && after(tp->copied_seq, tp->urg_seq)) { tp->urg_data = 0; + tcp_fast_path_check(sk); + } if (used + offset < skb->len) continue; diff --git a/net/ipv4/tcp_input.c b/net/ipv4/tcp_input.c index a0e436366d31..c5d7656beeee 100644 --- a/net/ipv4/tcp_input.c +++ b/net/ipv4/tcp_input.c @@ -103,6 +103,7 @@ int sysctl_tcp_invalid_ratelimit __read_mostly = HZ/2; #define FLAG_DATA_SACKED 0x20 /* New SACK. */ #define FLAG_ECE 0x40 /* ECE in this ACK */ #define FLAG_LOST_RETRANS 0x80 /* This ACK marks some retransmission lost */ +#define FLAG_SLOWPATH 0x100 /* Do not skip RFC checks for window update.*/ #define FLAG_ORIG_SACK_ACKED 0x200 /* Never retransmitted data are (s)acked */ #define FLAG_SND_UNA_ADVANCED 0x400 /* Snd_una was changed (!= FLAG_DATA_ACKED) */ #define FLAG_DSACKING_ACK 0x800 /* SACK blocks contained D-SACK info */ @@ -3371,6 +3372,12 @@ static int tcp_ack_update_window(struct sock *sk, const struct sk_buff *skb, u32 if (tp->snd_wnd != nwin) { tp->snd_wnd = nwin; + /* Note, it is the only place, where + * fast path is recovered for sending TCP. + */ + tp->pred_flags = 0; + tcp_fast_path_check(sk); + if (tcp_send_head(sk)) tcp_slow_start_after_idle_check(sk); @@ -3592,7 +3599,19 @@ static int tcp_ack(struct sock *sk, const struct sk_buff *skb, int flag) if (flag & FLAG_UPDATE_TS_RECENT) tcp_replace_ts_recent(tp, TCP_SKB_CB(skb)->seq); - { + if (!(flag & FLAG_SLOWPATH) && after(ack, prior_snd_una)) { + /* Window is constant, pure forward advance. + * No more checks are required. + * Note, we use the fact that SND.UNA>=SND.WL2. + */ + tcp_update_wl(tp, ack_seq); + tcp_snd_una_update(tp, ack); + flag |= FLAG_WIN_UPDATE; + + tcp_in_ack_event(sk, CA_ACK_WIN_UPDATE); + + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHPACKS); + } else { u32 ack_ev_flags = CA_ACK_SLOWPATH; if (ack_seq != TCP_SKB_CB(skb)->end_seq) @@ -4407,6 +4426,8 @@ static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb) if (TCP_SKB_CB(skb)->has_rxtstamp) TCP_SKB_CB(skb)->swtstamp = skb->tstamp; + /* Disable header prediction. */ + tp->pred_flags = 0; inet_csk_schedule_ack(sk); NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOQUEUE); @@ -4647,6 +4668,8 @@ queue_and_out: if (tp->rx_opt.num_sacks) tcp_sack_remove(tp); + tcp_fast_path_check(sk); + if (eaten > 0) kfree_skb_partial(skb, fragstolen); if (!sock_flag(sk, SOCK_DEAD)) @@ -4972,6 +4995,7 @@ static int tcp_prune_queue(struct sock *sk) NET_INC_STATS(sock_net(sk), LINUX_MIB_RCVPRUNED); /* Massive buffer overcommit. */ + tp->pred_flags = 0; return -1; } @@ -5143,6 +5167,9 @@ static void tcp_check_urg(struct sock *sk, const struct tcphdr *th) tp->urg_data = TCP_URG_NOTYET; tp->urg_seq = ptr; + + /* Disable header prediction. */ + tp->pred_flags = 0; } /* This is the 'fast' part of urgent handling. */ @@ -5301,6 +5328,26 @@ discard: /* * TCP receive function for the ESTABLISHED state. + * + * It is split into a fast path and a slow path. The fast path is + * disabled when: + * - A zero window was announced from us - zero window probing + * is only handled properly in the slow path. + * - Out of order segments arrived. + * - Urgent data is expected. + * - There is no buffer space left + * - Unexpected TCP flags/window values/header lengths are received + * (detected by checking the TCP header against pred_flags) + * - Data is sent in both directions. Fast path only supports pure senders + * or pure receivers (this means either the sequence number or the ack + * value must stay constant) + * - Unexpected TCP option. + * + * When these conditions are not satisfied it drops into a standard + * receive procedure patterned after RFC793 to handle all cases. + * The first three cases are guaranteed by proper pred_flags setting, + * the rest is checked inline. Fast processing is turned on in + * tcp_data_queue when everything is OK. */ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th) @@ -5311,19 +5358,144 @@ void tcp_rcv_established(struct sock *sk, struct sk_buff *skb, tcp_mstamp_refresh(tp); if (unlikely(!sk->sk_rx_dst)) inet_csk(sk)->icsk_af_ops->sk_rx_dst_set(sk, skb); + /* + * Header prediction. + * The code loosely follows the one in the famous + * "30 instruction TCP receive" Van Jacobson mail. + * + * Van's trick is to deposit buffers into socket queue + * on a device interrupt, to call tcp_recv function + * on the receive process context and checksum and copy + * the buffer to user space. smart... + * + * Our current scheme is not silly either but we take the + * extra cost of the net_bh soft interrupt processing... + * We do checksum and copy also but from device to kernel. + */ tp->rx_opt.saw_tstamp = 0; + /* pred_flags is 0xS?10 << 16 + snd_wnd + * if header_prediction is to be made + * 'S' will always be tp->tcp_header_len >> 2 + * '?' will be 0 for the fast path, otherwise pred_flags is 0 to + * turn it off (when there are holes in the receive + * space for instance) + * PSH flag is ignored. + */ + + if ((tcp_flag_word(th) & TCP_HP_BITS) == tp->pred_flags && + TCP_SKB_CB(skb)->seq == tp->rcv_nxt && + !after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt)) { + int tcp_header_len = tp->tcp_header_len; + + /* Timestamp header prediction: tcp_header_len + * is automatically equal to th->doff*4 due to pred_flags + * match. + */ + + /* Check timestamp */ + if (tcp_header_len == sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) { + /* No? Slow path! */ + if (!tcp_parse_aligned_timestamp(tp, th)) + goto slow_path; + + /* If PAWS failed, check it more carefully in slow path */ + if ((s32)(tp->rx_opt.rcv_tsval - tp->rx_opt.ts_recent) < 0) + goto slow_path; + + /* DO NOT update ts_recent here, if checksum fails + * and timestamp was corrupted part, it will result + * in a hung connection since we will drop all + * future packets due to the PAWS test. + */ + } + + if (len <= tcp_header_len) { + /* Bulk data transfer: sender */ + if (len == tcp_header_len) { + /* Predicted packet is in window by definition. + * seq == rcv_nxt and rcv_wup <= rcv_nxt. + * Hence, check seq<=rcv_wup reduces to: + */ + if (tcp_header_len == + (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) && + tp->rcv_nxt == tp->rcv_wup) + tcp_store_ts_recent(tp); + + /* We know that such packets are checksummed + * on entry. + */ + tcp_ack(sk, skb, 0); + __kfree_skb(skb); + tcp_data_snd_check(sk); + return; + } else { /* Header too small */ + TCP_INC_STATS(sock_net(sk), TCP_MIB_INERRS); + goto discard; + } + } else { + int eaten = 0; + bool fragstolen = false; + + if (tcp_checksum_complete(skb)) + goto csum_error; + + if ((int)skb->truesize > sk->sk_forward_alloc) + goto step5; + + /* Predicted packet is in window by definition. + * seq == rcv_nxt and rcv_wup <= rcv_nxt. + * Hence, check seq<=rcv_wup reduces to: + */ + if (tcp_header_len == + (sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED) && + tp->rcv_nxt == tp->rcv_wup) + tcp_store_ts_recent(tp); + + tcp_rcv_rtt_measure_ts(sk, skb); + + NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHPHITS); + + /* Bulk data transfer: receiver */ + eaten = tcp_queue_rcv(sk, skb, tcp_header_len, + &fragstolen); + + tcp_event_data_recv(sk, skb); + + if (TCP_SKB_CB(skb)->ack_seq != tp->snd_una) { + /* Well, only one small jumplet in fast path... */ + tcp_ack(sk, skb, FLAG_DATA); + tcp_data_snd_check(sk); + if (!inet_csk_ack_scheduled(sk)) + goto no_ack; + } + + __tcp_ack_snd_check(sk, 0); +no_ack: + if (eaten) + kfree_skb_partial(skb, fragstolen); + sk->sk_data_ready(sk); + return; + } + } + +slow_path: if (len < (th->doff << 2) || tcp_checksum_complete(skb)) goto csum_error; if (!th->ack && !th->rst && !th->syn) goto discard; + /* + * Standard slow path. + */ + if (!tcp_validate_incoming(sk, skb, th, 1)) return; - if (tcp_ack(sk, skb, FLAG_UPDATE_TS_RECENT) < 0) +step5: + if (tcp_ack(sk, skb, FLAG_SLOWPATH | FLAG_UPDATE_TS_RECENT) < 0) goto discard; tcp_rcv_rtt_measure_ts(sk, skb); @@ -5376,6 +5548,11 @@ void tcp_finish_connect(struct sock *sk, struct sk_buff *skb) if (sock_flag(sk, SOCK_KEEPOPEN)) inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tp)); + + if (!tp->rx_opt.snd_wscale) + __tcp_fast_path_on(tp, tp->snd_wnd); + else + tp->pred_flags = 0; } static bool tcp_rcv_fastopen_synack(struct sock *sk, struct sk_buff *synack, @@ -5504,7 +5681,7 @@ static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb, tcp_ecn_rcv_synack(tp, th); tcp_init_wl(tp, TCP_SKB_CB(skb)->seq); - tcp_ack(sk, skb, 0); + tcp_ack(sk, skb, FLAG_SLOWPATH); /* Ok.. it's good. Set up sequence numbers and * move to established. @@ -5740,8 +5917,8 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) return 0; /* step 5: check the ACK field */ - - acceptable = tcp_ack(sk, skb, FLAG_UPDATE_TS_RECENT | + acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH | + FLAG_UPDATE_TS_RECENT | FLAG_NO_CHALLENGE_ACK) > 0; if (!acceptable) { @@ -5809,6 +5986,7 @@ int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb) tp->lsndtime = tcp_jiffies32; tcp_initialize_rcv_mss(sk); + tcp_fast_path_on(tp); break; case TCP_FIN_WAIT1: { diff --git a/net/ipv4/tcp_minisocks.c b/net/ipv4/tcp_minisocks.c index 1537b87c657f..188a6f31356d 100644 --- a/net/ipv4/tcp_minisocks.c +++ b/net/ipv4/tcp_minisocks.c @@ -436,6 +436,8 @@ struct sock *tcp_create_openreq_child(const struct sock *sk, struct tcp_sock *newtp = tcp_sk(newsk); /* Now setup tcp_sock */ + newtp->pred_flags = 0; + newtp->rcv_wup = newtp->copied_seq = newtp->rcv_nxt = treq->rcv_isn + 1; newtp->segs_in = 1; diff --git a/net/ipv4/tcp_output.c b/net/ipv4/tcp_output.c index 3e0d19631534..5b6690d05abb 100644 --- a/net/ipv4/tcp_output.c +++ b/net/ipv4/tcp_output.c @@ -295,7 +295,9 @@ static u16 tcp_select_window(struct sock *sk) /* RFC1323 scaling applied */ new_win >>= tp->rx_opt.rcv_wscale; + /* If we advertise zero window, disable fast path. */ if (new_win == 0) { + tp->pred_flags = 0; if (old_win) NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPTOZEROWINDOWADV); -- cgit v1.2.3 From dbbccdc4ced015cdd4051299bd87fbe0254ad351 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Mon, 21 Aug 2017 01:29:08 -0700 Subject: Bluetooth: Add option for disabling legacy ioctl interfaces The legacy ioctl interfaces are only useful for BR/EDR operation and since Linux 3.4 no longer needed anyway. This options allows disabling them alltogether and use only management interfaces for setup and control. Signed-off-by: Marcel Holtmann Signed-off-by: Johan Hedberg --- net/bluetooth/Kconfig | 10 ++++++++++ net/bluetooth/hci_sock.c | 6 ++++++ 2 files changed, 16 insertions(+) (limited to 'net') diff --git a/net/bluetooth/Kconfig b/net/bluetooth/Kconfig index db82a40875e8..c18115d22f00 100644 --- a/net/bluetooth/Kconfig +++ b/net/bluetooth/Kconfig @@ -126,4 +126,14 @@ config BT_DEBUGFS Provide extensive information about internal Bluetooth states in debugfs. +config BT_LEGACY_IOCTL + bool "Enable legacy ioctl interfaces" + depends on BT && BT_BREDR + default y + help + Enable support for legacy ioctl interfaces. This is only needed + for old and deprecated applications using direct ioctl calls for + controller management. Since Linux 3.4 all configuration and + setup is done via mgmt interface and this is no longer needed. + source "drivers/bluetooth/Kconfig" diff --git a/net/bluetooth/hci_sock.c b/net/bluetooth/hci_sock.c index 65d734c165bd..0bad296fe0af 100644 --- a/net/bluetooth/hci_sock.c +++ b/net/bluetooth/hci_sock.c @@ -878,6 +878,7 @@ static int hci_sock_release(struct socket *sock) return 0; } +#ifdef CONFIG_BT_LEGACY_IOCTL static int hci_sock_blacklist_add(struct hci_dev *hdev, void __user *arg) { bdaddr_t bdaddr; @@ -1049,6 +1050,7 @@ done: release_sock(sk); return err; } +#endif static int hci_sock_bind(struct socket *sock, struct sockaddr *addr, int addr_len) @@ -1969,7 +1971,11 @@ static const struct proto_ops hci_sock_ops = { .getname = hci_sock_getname, .sendmsg = hci_sock_sendmsg, .recvmsg = hci_sock_recvmsg, +#ifdef CONFIG_BT_LEGACY_IOCTL .ioctl = hci_sock_ioctl, +#else + .ioctl = sock_no_ioctl, +#endif .poll = datagram_poll, .listen = sock_no_listen, .shutdown = sock_no_shutdown, -- cgit v1.2.3 From c15ab236d69dd6dad24541400f460c47853803f8 Mon Sep 17 00:00:00 2001 From: Chris Mi Date: Wed, 30 Aug 2017 02:31:58 -0400 Subject: net/sched: Change cls_flower to use IDR Currently, all filters with the same priority are linked in a doubly linked list. Every filter should have a unique handle. To make the handle unique, we need to iterate the list every time to see if the handle exists or not when inserting a new filter. It is time-consuming. For example, it takes about 5m3.169s to insert 64K rules. This patch changes cls_flower to use IDR. With this patch, it takes about 0m1.127s to insert 64K rules. The improvement is huge. But please note that in this testing, all filters share the same action. If every filter has a unique action, that is another bottleneck. Follow-up patch in this patchset addresses that. Signed-off-by: Chris Mi Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- net/sched/cls_flower.c | 55 +++++++++++++++++++++----------------------------- 1 file changed, 23 insertions(+), 32 deletions(-) (limited to 'net') diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index bd9dab41f8af..3d041d279b92 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -68,7 +68,6 @@ struct cls_fl_head { struct rhashtable ht; struct fl_flow_mask mask; struct flow_dissector dissector; - u32 hgen; bool mask_assigned; struct list_head filters; struct rhashtable_params ht_params; @@ -76,6 +75,7 @@ struct cls_fl_head { struct work_struct work; struct rcu_head rcu; }; + struct idr handle_idr; }; struct cls_fl_filter { @@ -210,6 +210,7 @@ static int fl_init(struct tcf_proto *tp) INIT_LIST_HEAD_RCU(&head->filters); rcu_assign_pointer(tp->root, head); + idr_init(&head->handle_idr); return 0; } @@ -295,6 +296,9 @@ static void fl_hw_update_stats(struct tcf_proto *tp, struct cls_fl_filter *f) static void __fl_delete(struct tcf_proto *tp, struct cls_fl_filter *f) { + struct cls_fl_head *head = rtnl_dereference(tp->root); + + idr_remove_ext(&head->handle_idr, f->handle); list_del_rcu(&f->list); if (!tc_skip_hw(f->flags)) fl_hw_destroy_filter(tp, f); @@ -327,6 +331,7 @@ static void fl_destroy(struct tcf_proto *tp) list_for_each_entry_safe(f, next, &head->filters, list) __fl_delete(tp, f); + idr_destroy(&head->handle_idr); __module_get(THIS_MODULE); call_rcu(&head->rcu, fl_destroy_rcu); @@ -335,12 +340,8 @@ static void fl_destroy(struct tcf_proto *tp) static void *fl_get(struct tcf_proto *tp, u32 handle) { struct cls_fl_head *head = rtnl_dereference(tp->root); - struct cls_fl_filter *f; - list_for_each_entry(f, &head->filters, list) - if (f->handle == handle) - return f; - return NULL; + return idr_find_ext(&head->handle_idr, handle); } static const struct nla_policy fl_policy[TCA_FLOWER_MAX + 1] = { @@ -859,27 +860,6 @@ static int fl_set_parms(struct net *net, struct tcf_proto *tp, return 0; } -static u32 fl_grab_new_handle(struct tcf_proto *tp, - struct cls_fl_head *head) -{ - unsigned int i = 0x80000000; - u32 handle; - - do { - if (++head->hgen == 0x7FFFFFFF) - head->hgen = 1; - } while (--i > 0 && fl_get(tp, head->hgen)); - - if (unlikely(i == 0)) { - pr_err("Insufficient number of handles\n"); - handle = 0; - } else { - handle = head->hgen; - } - - return handle; -} - static int fl_change(struct net *net, struct sk_buff *in_skb, struct tcf_proto *tp, unsigned long base, u32 handle, struct nlattr **tca, @@ -890,6 +870,7 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, struct cls_fl_filter *fnew; struct nlattr **tb; struct fl_flow_mask mask = {}; + unsigned long idr_index; int err; if (!tca[TCA_OPTIONS]) @@ -920,13 +901,21 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, goto errout; if (!handle) { - handle = fl_grab_new_handle(tp, head); - if (!handle) { - err = -EINVAL; + err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index, + 1, 0x80000000, GFP_KERNEL); + if (err) goto errout; - } + fnew->handle = idr_index; + } + + /* user specifies a handle and it doesn't exist */ + if (handle && !fold) { + err = idr_alloc_ext(&head->handle_idr, fnew, &idr_index, + handle, handle + 1, GFP_KERNEL); + if (err) + goto errout; + fnew->handle = idr_index; } - fnew->handle = handle; if (tb[TCA_FLOWER_FLAGS]) { fnew->flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]); @@ -980,6 +969,8 @@ static int fl_change(struct net *net, struct sk_buff *in_skb, *arg = fnew; if (fold) { + fnew->handle = handle; + idr_replace_ext(&head->handle_idr, fnew, fnew->handle); list_replace_rcu(&fold->list, &fnew->list); tcf_unbind_filter(tp, &fold->res); call_rcu(&fold->rcu, fl_destroy_filter); -- cgit v1.2.3 From 65a206c01e8e7ffe971477a36419422099216eff Mon Sep 17 00:00:00 2001 From: Chris Mi Date: Wed, 30 Aug 2017 02:31:59 -0400 Subject: net/sched: Change act_api and act_xxx modules to use IDR Typically, each TC filter has its own action. All the actions of the same type are saved in its hash table. But the hash buckets are too small that it degrades to a list. And the performance is greatly affected. For example, it takes about 0m11.914s to insert 64K rules. If we convert the hash table to IDR, it only takes about 0m1.500s. The improvement is huge. But please note that the test result is based on previous patch that cls_flower uses IDR. Signed-off-by: Chris Mi Signed-off-by: Jiri Pirko Acked-by: Jamal Hadi Salim Signed-off-by: David S. Miller --- include/net/act_api.h | 76 +++++--------- net/sched/act_api.c | 251 ++++++++++++++++++++++----------------------- net/sched/act_bpf.c | 17 ++- net/sched/act_connmark.c | 16 ++- net/sched/act_csum.c | 16 ++- net/sched/act_gact.c | 16 ++- net/sched/act_ife.c | 18 ++-- net/sched/act_ipt.c | 26 +++-- net/sched/act_mirred.c | 19 ++-- net/sched/act_nat.c | 16 ++- net/sched/act_pedit.c | 18 ++-- net/sched/act_police.c | 18 ++-- net/sched/act_sample.c | 17 ++- net/sched/act_simple.c | 20 ++-- net/sched/act_skbedit.c | 18 ++-- net/sched/act_skbmod.c | 18 ++-- net/sched/act_tunnel_key.c | 20 ++-- net/sched/act_vlan.c | 22 ++-- 18 files changed, 278 insertions(+), 344 deletions(-) (limited to 'net') diff --git a/include/net/act_api.h b/include/net/act_api.h index 26ffd8333f50..8f3d5d8b5ae0 100644 --- a/include/net/act_api.h +++ b/include/net/act_api.h @@ -10,12 +10,9 @@ #include #include - -struct tcf_hashinfo { - struct hlist_head *htab; - unsigned int hmask; - spinlock_t lock; - u32 index; +struct tcf_idrinfo { + spinlock_t lock; + struct idr action_idr; }; struct tc_action_ops; @@ -25,9 +22,8 @@ struct tc_action { __u32 type; /* for backward compat(TCA_OLD_COMPAT) */ __u32 order; struct list_head list; - struct tcf_hashinfo *hinfo; + struct tcf_idrinfo *idrinfo; - struct hlist_node tcfa_head; u32 tcfa_index; int tcfa_refcnt; int tcfa_bindcnt; @@ -44,7 +40,6 @@ struct tc_action { struct tc_cookie *act_cookie; struct tcf_chain *goto_chain; }; -#define tcf_head common.tcfa_head #define tcf_index common.tcfa_index #define tcf_refcnt common.tcfa_refcnt #define tcf_bindcnt common.tcfa_bindcnt @@ -57,27 +52,6 @@ struct tc_action { #define tcf_lock common.tcfa_lock #define tcf_rcu common.tcfa_rcu -static inline unsigned int tcf_hash(u32 index, unsigned int hmask) -{ - return index & hmask; -} - -static inline int tcf_hashinfo_init(struct tcf_hashinfo *hf, unsigned int mask) -{ - int i; - - spin_lock_init(&hf->lock); - hf->index = 0; - hf->hmask = mask; - hf->htab = kzalloc((mask + 1) * sizeof(struct hlist_head), - GFP_KERNEL); - if (!hf->htab) - return -ENOMEM; - for (i = 0; i < mask + 1; i++) - INIT_HLIST_HEAD(&hf->htab[i]); - return 0; -} - /* Update lastuse only if needed, to avoid dirtying a cache line. * We use a temp variable to avoid fetching jiffies twice. */ @@ -126,53 +100,51 @@ struct tc_action_ops { }; struct tc_action_net { - struct tcf_hashinfo *hinfo; + struct tcf_idrinfo *idrinfo; const struct tc_action_ops *ops; }; static inline int tc_action_net_init(struct tc_action_net *tn, - const struct tc_action_ops *ops, unsigned int mask) + const struct tc_action_ops *ops) { int err = 0; - tn->hinfo = kmalloc(sizeof(*tn->hinfo), GFP_KERNEL); - if (!tn->hinfo) + tn->idrinfo = kmalloc(sizeof(*tn->idrinfo), GFP_KERNEL); + if (!tn->idrinfo) return -ENOMEM; tn->ops = ops; - err = tcf_hashinfo_init(tn->hinfo, mask); - if (err) - kfree(tn->hinfo); + spin_lock_init(&tn->idrinfo->lock); + idr_init(&tn->idrinfo->action_idr); return err; } -void tcf_hashinfo_destroy(const struct tc_action_ops *ops, - struct tcf_hashinfo *hinfo); +void tcf_idrinfo_destroy(const struct tc_action_ops *ops, + struct tcf_idrinfo *idrinfo); static inline void tc_action_net_exit(struct tc_action_net *tn) { - tcf_hashinfo_destroy(tn->ops, tn->hinfo); - kfree(tn->hinfo); + tcf_idrinfo_destroy(tn->ops, tn->idrinfo); + kfree(tn->idrinfo); } int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb, struct netlink_callback *cb, int type, const struct tc_action_ops *ops); -int tcf_hash_search(struct tc_action_net *tn, struct tc_action **a, u32 index); -u32 tcf_hash_new_index(struct tc_action_net *tn); -bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action **a, +int tcf_idr_search(struct tc_action_net *tn, struct tc_action **a, u32 index); +bool tcf_idr_check(struct tc_action_net *tn, u32 index, struct tc_action **a, int bind); -int tcf_hash_create(struct tc_action_net *tn, u32 index, struct nlattr *est, - struct tc_action **a, const struct tc_action_ops *ops, int bind, - bool cpustats); -void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est); -void tcf_hash_insert(struct tc_action_net *tn, struct tc_action *a); +int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est, + struct tc_action **a, const struct tc_action_ops *ops, + int bind, bool cpustats); +void tcf_idr_cleanup(struct tc_action *a, struct nlattr *est); +void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a); -int __tcf_hash_release(struct tc_action *a, bool bind, bool strict); +int __tcf_idr_release(struct tc_action *a, bool bind, bool strict); -static inline int tcf_hash_release(struct tc_action *a, bool bind) +static inline int tcf_idr_release(struct tc_action *a, bool bind) { - return __tcf_hash_release(a, bind, false); + return __tcf_idr_release(a, bind, false); } int tcf_register_action(struct tc_action_ops *a, struct pernet_operations *ops); diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 02fcb0c78a28..0eb545bcb247 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -70,11 +70,11 @@ static void free_tcf(struct rcu_head *head) kfree(p); } -static void tcf_hash_destroy(struct tcf_hashinfo *hinfo, struct tc_action *p) +static void tcf_idr_remove(struct tcf_idrinfo *idrinfo, struct tc_action *p) { - spin_lock_bh(&hinfo->lock); - hlist_del(&p->tcfa_head); - spin_unlock_bh(&hinfo->lock); + spin_lock_bh(&idrinfo->lock); + idr_remove_ext(&idrinfo->action_idr, p->tcfa_index); + spin_unlock_bh(&idrinfo->lock); gen_kill_estimator(&p->tcfa_rate_est); /* * gen_estimator est_timer() might access p->tcfa_lock @@ -83,7 +83,7 @@ static void tcf_hash_destroy(struct tcf_hashinfo *hinfo, struct tc_action *p) call_rcu(&p->tcfa_rcu, free_tcf); } -int __tcf_hash_release(struct tc_action *p, bool bind, bool strict) +int __tcf_idr_release(struct tc_action *p, bool bind, bool strict) { int ret = 0; @@ -97,64 +97,60 @@ int __tcf_hash_release(struct tc_action *p, bool bind, bool strict) if (p->tcfa_bindcnt <= 0 && p->tcfa_refcnt <= 0) { if (p->ops->cleanup) p->ops->cleanup(p, bind); - tcf_hash_destroy(p->hinfo, p); + tcf_idr_remove(p->idrinfo, p); ret = ACT_P_DELETED; } } return ret; } -EXPORT_SYMBOL(__tcf_hash_release); +EXPORT_SYMBOL(__tcf_idr_release); -static int tcf_dump_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, +static int tcf_dump_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, struct netlink_callback *cb) { - int err = 0, index = -1, i = 0, s_i = 0, n_i = 0; + int err = 0, index = -1, s_i = 0, n_i = 0; u32 act_flags = cb->args[2]; unsigned long jiffy_since = cb->args[3]; struct nlattr *nest; + struct idr *idr = &idrinfo->action_idr; + struct tc_action *p; + unsigned long id = 1; - spin_lock_bh(&hinfo->lock); + spin_lock_bh(&idrinfo->lock); s_i = cb->args[0]; - for (i = 0; i < (hinfo->hmask + 1); i++) { - struct hlist_head *head; - struct tc_action *p; - - head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; - - hlist_for_each_entry_rcu(p, head, tcfa_head) { - index++; - if (index < s_i) - continue; - - if (jiffy_since && - time_after(jiffy_since, - (unsigned long)p->tcfa_tm.lastuse)) - continue; - - nest = nla_nest_start(skb, n_i); - if (nest == NULL) - goto nla_put_failure; - err = tcf_action_dump_1(skb, p, 0, 0); - if (err < 0) { - index--; - nlmsg_trim(skb, nest); - goto done; - } - nla_nest_end(skb, nest); - n_i++; - if (!(act_flags & TCA_FLAG_LARGE_DUMP_ON) && - n_i >= TCA_ACT_MAX_PRIO) - goto done; + idr_for_each_entry_ext(idr, p, id) { + index++; + if (index < s_i) + continue; + + if (jiffy_since && + time_after(jiffy_since, + (unsigned long)p->tcfa_tm.lastuse)) + continue; + + nest = nla_nest_start(skb, n_i); + if (!nest) + goto nla_put_failure; + err = tcf_action_dump_1(skb, p, 0, 0); + if (err < 0) { + index--; + nlmsg_trim(skb, nest); + goto done; } + nla_nest_end(skb, nest); + n_i++; + if (!(act_flags & TCA_FLAG_LARGE_DUMP_ON) && + n_i >= TCA_ACT_MAX_PRIO) + goto done; } done: if (index >= 0) cb->args[0] = index + 1; - spin_unlock_bh(&hinfo->lock); + spin_unlock_bh(&idrinfo->lock); if (n_i) { if (act_flags & TCA_FLAG_LARGE_DUMP_ON) cb->args[1] = n_i; @@ -166,31 +162,29 @@ nla_put_failure: goto done; } -static int tcf_del_walker(struct tcf_hashinfo *hinfo, struct sk_buff *skb, +static int tcf_del_walker(struct tcf_idrinfo *idrinfo, struct sk_buff *skb, const struct tc_action_ops *ops) { struct nlattr *nest; - int i = 0, n_i = 0; + int n_i = 0; int ret = -EINVAL; + struct idr *idr = &idrinfo->action_idr; + struct tc_action *p; + unsigned long id = 1; nest = nla_nest_start(skb, 0); if (nest == NULL) goto nla_put_failure; if (nla_put_string(skb, TCA_KIND, ops->kind)) goto nla_put_failure; - for (i = 0; i < (hinfo->hmask + 1); i++) { - struct hlist_head *head; - struct hlist_node *n; - struct tc_action *p; - - head = &hinfo->htab[tcf_hash(i, hinfo->hmask)]; - hlist_for_each_entry_safe(p, n, head, tcfa_head) { - ret = __tcf_hash_release(p, false, true); - if (ret == ACT_P_DELETED) { - module_put(p->ops->owner); - n_i++; - } else if (ret < 0) - goto nla_put_failure; + + idr_for_each_entry_ext(idr, p, id) { + ret = __tcf_idr_release(p, false, true); + if (ret == ACT_P_DELETED) { + module_put(p->ops->owner); + n_i++; + } else if (ret < 0) { + goto nla_put_failure; } } if (nla_put_u32(skb, TCA_FCNT, n_i)) @@ -207,12 +201,12 @@ int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb, struct netlink_callback *cb, int type, const struct tc_action_ops *ops) { - struct tcf_hashinfo *hinfo = tn->hinfo; + struct tcf_idrinfo *idrinfo = tn->idrinfo; if (type == RTM_DELACTION) { - return tcf_del_walker(hinfo, skb, ops); + return tcf_del_walker(idrinfo, skb, ops); } else if (type == RTM_GETACTION) { - return tcf_dump_walker(hinfo, skb, cb); + return tcf_dump_walker(idrinfo, skb, cb); } else { WARN(1, "tcf_generic_walker: unknown action %d\n", type); return -EINVAL; @@ -220,40 +214,21 @@ int tcf_generic_walker(struct tc_action_net *tn, struct sk_buff *skb, } EXPORT_SYMBOL(tcf_generic_walker); -static struct tc_action *tcf_hash_lookup(u32 index, struct tcf_hashinfo *hinfo) +static struct tc_action *tcf_idr_lookup(u32 index, struct tcf_idrinfo *idrinfo) { struct tc_action *p = NULL; - struct hlist_head *head; - spin_lock_bh(&hinfo->lock); - head = &hinfo->htab[tcf_hash(index, hinfo->hmask)]; - hlist_for_each_entry_rcu(p, head, tcfa_head) - if (p->tcfa_index == index) - break; - spin_unlock_bh(&hinfo->lock); + spin_lock_bh(&idrinfo->lock); + p = idr_find_ext(&idrinfo->action_idr, index); + spin_unlock_bh(&idrinfo->lock); return p; } -u32 tcf_hash_new_index(struct tc_action_net *tn) -{ - struct tcf_hashinfo *hinfo = tn->hinfo; - u32 val = hinfo->index; - - do { - if (++val == 0) - val = 1; - } while (tcf_hash_lookup(val, hinfo)); - - hinfo->index = val; - return val; -} -EXPORT_SYMBOL(tcf_hash_new_index); - -int tcf_hash_search(struct tc_action_net *tn, struct tc_action **a, u32 index) +int tcf_idr_search(struct tc_action_net *tn, struct tc_action **a, u32 index) { - struct tcf_hashinfo *hinfo = tn->hinfo; - struct tc_action *p = tcf_hash_lookup(index, hinfo); + struct tcf_idrinfo *idrinfo = tn->idrinfo; + struct tc_action *p = tcf_idr_lookup(index, idrinfo); if (p) { *a = p; @@ -261,15 +236,15 @@ int tcf_hash_search(struct tc_action_net *tn, struct tc_action **a, u32 index) } return 0; } -EXPORT_SYMBOL(tcf_hash_search); +EXPORT_SYMBOL(tcf_idr_search); -bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action **a, - int bind) +bool tcf_idr_check(struct tc_action_net *tn, u32 index, struct tc_action **a, + int bind) { - struct tcf_hashinfo *hinfo = tn->hinfo; - struct tc_action *p = NULL; + struct tcf_idrinfo *idrinfo = tn->idrinfo; + struct tc_action *p = tcf_idr_lookup(index, idrinfo); - if (index && (p = tcf_hash_lookup(index, hinfo)) != NULL) { + if (index && p) { if (bind) p->tcfa_bindcnt++; p->tcfa_refcnt++; @@ -278,23 +253,25 @@ bool tcf_hash_check(struct tc_action_net *tn, u32 index, struct tc_action **a, } return false; } -EXPORT_SYMBOL(tcf_hash_check); +EXPORT_SYMBOL(tcf_idr_check); -void tcf_hash_cleanup(struct tc_action *a, struct nlattr *est) +void tcf_idr_cleanup(struct tc_action *a, struct nlattr *est) { if (est) gen_kill_estimator(&a->tcfa_rate_est); call_rcu(&a->tcfa_rcu, free_tcf); } -EXPORT_SYMBOL(tcf_hash_cleanup); +EXPORT_SYMBOL(tcf_idr_cleanup); -int tcf_hash_create(struct tc_action_net *tn, u32 index, struct nlattr *est, - struct tc_action **a, const struct tc_action_ops *ops, - int bind, bool cpustats) +int tcf_idr_create(struct tc_action_net *tn, u32 index, struct nlattr *est, + struct tc_action **a, const struct tc_action_ops *ops, + int bind, bool cpustats) { struct tc_action *p = kzalloc(ops->size, GFP_KERNEL); - struct tcf_hashinfo *hinfo = tn->hinfo; + struct tcf_idrinfo *idrinfo = tn->idrinfo; + struct idr *idr = &idrinfo->action_idr; int err = -ENOMEM; + unsigned long idr_index; if (unlikely(!p)) return -ENOMEM; @@ -317,8 +294,28 @@ err2: } } spin_lock_init(&p->tcfa_lock); - INIT_HLIST_NODE(&p->tcfa_head); - p->tcfa_index = index ? index : tcf_hash_new_index(tn); + /* user doesn't specify an index */ + if (!index) { + spin_lock_bh(&idrinfo->lock); + err = idr_alloc_ext(idr, NULL, &idr_index, 1, 0, + GFP_KERNEL); + spin_unlock_bh(&idrinfo->lock); + if (err) { +err3: + free_percpu(p->cpu_qstats); + goto err2; + } + p->tcfa_index = idr_index; + } else { + spin_lock_bh(&idrinfo->lock); + err = idr_alloc_ext(idr, NULL, NULL, index, index + 1, + GFP_KERNEL); + spin_unlock_bh(&idrinfo->lock); + if (err) + goto err3; + p->tcfa_index = index; + } + p->tcfa_tm.install = jiffies; p->tcfa_tm.lastuse = jiffies; p->tcfa_tm.firstuse = 0; @@ -327,52 +324,46 @@ err2: &p->tcfa_rate_est, &p->tcfa_lock, NULL, est); if (err) { - free_percpu(p->cpu_qstats); - goto err2; + goto err3; } } - p->hinfo = hinfo; + p->idrinfo = idrinfo; p->ops = ops; INIT_LIST_HEAD(&p->list); *a = p; return 0; } -EXPORT_SYMBOL(tcf_hash_create); +EXPORT_SYMBOL(tcf_idr_create); -void tcf_hash_insert(struct tc_action_net *tn, struct tc_action *a) +void tcf_idr_insert(struct tc_action_net *tn, struct tc_action *a) { - struct tcf_hashinfo *hinfo = tn->hinfo; - unsigned int h = tcf_hash(a->tcfa_index, hinfo->hmask); + struct tcf_idrinfo *idrinfo = tn->idrinfo; - spin_lock_bh(&hinfo->lock); - hlist_add_head(&a->tcfa_head, &hinfo->htab[h]); - spin_unlock_bh(&hinfo->lock); + spin_lock_bh(&idrinfo->lock); + idr_replace_ext(&idrinfo->action_idr, a, a->tcfa_index); + spin_unlock_bh(&idrinfo->lock); } -EXPORT_SYMBOL(tcf_hash_insert); +EXPORT_SYMBOL(tcf_idr_insert); -void tcf_hashinfo_destroy(const struct tc_action_ops *ops, - struct tcf_hashinfo *hinfo) +void tcf_idrinfo_destroy(const struct tc_action_ops *ops, + struct tcf_idrinfo *idrinfo) { - int i; - - for (i = 0; i < hinfo->hmask + 1; i++) { - struct tc_action *p; - struct hlist_node *n; - - hlist_for_each_entry_safe(p, n, &hinfo->htab[i], tcfa_head) { - int ret; + struct idr *idr = &idrinfo->action_idr; + struct tc_action *p; + int ret; + unsigned long id = 1; - ret = __tcf_hash_release(p, false, true); - if (ret == ACT_P_DELETED) - module_put(ops->owner); - else if (ret < 0) - return; - } + idr_for_each_entry_ext(idr, p, id) { + ret = __tcf_idr_release(p, false, true); + if (ret == ACT_P_DELETED) + module_put(ops->owner); + else if (ret < 0) + return; } - kfree(hinfo->htab); + idr_destroy(&idrinfo->action_idr); } -EXPORT_SYMBOL(tcf_hashinfo_destroy); +EXPORT_SYMBOL(tcf_idrinfo_destroy); static LIST_HEAD(act_base); static DEFINE_RWLOCK(act_mod_lock); @@ -524,7 +515,7 @@ int tcf_action_destroy(struct list_head *actions, int bind) int ret = 0; list_for_each_entry_safe(a, tmp, actions, list) { - ret = __tcf_hash_release(a, bind, true); + ret = __tcf_idr_release(a, bind, true); if (ret == ACT_P_DELETED) module_put(a->ops->owner); else if (ret < 0) diff --git a/net/sched/act_bpf.c b/net/sched/act_bpf.c index 9afe1337cfd1..c0c707eb2c96 100644 --- a/net/sched/act_bpf.c +++ b/net/sched/act_bpf.c @@ -21,7 +21,6 @@ #include #include -#define BPF_TAB_MASK 15 #define ACT_BPF_NAME_LEN 256 struct tcf_bpf_cfg { @@ -295,9 +294,9 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, parm = nla_data(tb[TCA_ACT_BPF_PARMS]); - if (!tcf_hash_check(tn, parm->index, act, bind)) { - ret = tcf_hash_create(tn, parm->index, est, act, - &act_bpf_ops, bind, true); + if (!tcf_idr_check(tn, parm->index, act, bind)) { + ret = tcf_idr_create(tn, parm->index, est, act, + &act_bpf_ops, bind, true); if (ret < 0) return ret; @@ -307,7 +306,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, if (bind) return 0; - tcf_hash_release(*act, bind); + tcf_idr_release(*act, bind); if (!replace) return -EEXIST; } @@ -343,7 +342,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, rcu_assign_pointer(prog->filter, cfg.filter); if (res == ACT_P_CREATED) { - tcf_hash_insert(tn, *act); + tcf_idr_insert(tn, *act); } else { /* make sure the program being replaced is no longer executing */ synchronize_rcu(); @@ -353,7 +352,7 @@ static int tcf_bpf_init(struct net *net, struct nlattr *nla, return res; out: if (res == ACT_P_CREATED) - tcf_hash_cleanup(*act, est); + tcf_idr_cleanup(*act, est); return ret; } @@ -379,7 +378,7 @@ static int tcf_bpf_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, bpf_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_bpf_ops __read_mostly = { @@ -399,7 +398,7 @@ static __net_init int bpf_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, bpf_net_id); - return tc_action_net_init(tn, &act_bpf_ops, BPF_TAB_MASK); + return tc_action_net_init(tn, &act_bpf_ops); } static void __net_exit bpf_exit_net(struct net *net) diff --git a/net/sched/act_connmark.c b/net/sched/act_connmark.c index 2155bc6c6a1e..10b7a8855a6c 100644 --- a/net/sched/act_connmark.c +++ b/net/sched/act_connmark.c @@ -28,8 +28,6 @@ #include #include -#define CONNMARK_TAB_MASK 3 - static unsigned int connmark_net_id; static struct tc_action_ops act_connmark_ops; @@ -119,9 +117,9 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla, parm = nla_data(tb[TCA_CONNMARK_PARMS]); - if (!tcf_hash_check(tn, parm->index, a, bind)) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_connmark_ops, bind, false); + if (!tcf_idr_check(tn, parm->index, a, bind)) { + ret = tcf_idr_create(tn, parm->index, est, a, + &act_connmark_ops, bind, false); if (ret) return ret; @@ -130,13 +128,13 @@ static int tcf_connmark_init(struct net *net, struct nlattr *nla, ci->net = net; ci->zone = parm->zone; - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); ret = ACT_P_CREATED; } else { ci = to_connmark(*a); if (bind) return 0; - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; /* replacing action and zone */ @@ -189,7 +187,7 @@ static int tcf_connmark_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, connmark_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_connmark_ops = { @@ -208,7 +206,7 @@ static __net_init int connmark_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, connmark_net_id); - return tc_action_net_init(tn, &act_connmark_ops, CONNMARK_TAB_MASK); + return tc_action_net_init(tn, &act_connmark_ops); } static void __net_exit connmark_exit_net(struct net *net) diff --git a/net/sched/act_csum.c b/net/sched/act_csum.c index 67afc12df88b..1c40caadcff9 100644 --- a/net/sched/act_csum.c +++ b/net/sched/act_csum.c @@ -37,8 +37,6 @@ #include #include -#define CSUM_TAB_MASK 15 - static const struct nla_policy csum_policy[TCA_CSUM_MAX + 1] = { [TCA_CSUM_PARMS] = { .len = sizeof(struct tc_csum), }, }; @@ -67,16 +65,16 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla, return -EINVAL; parm = nla_data(tb[TCA_CSUM_PARMS]); - if (!tcf_hash_check(tn, parm->index, a, bind)) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_csum_ops, bind, false); + if (!tcf_idr_check(tn, parm->index, a, bind)) { + ret = tcf_idr_create(tn, parm->index, est, a, + &act_csum_ops, bind, false); if (ret) return ret; ret = ACT_P_CREATED; } else { if (bind)/* dont override defaults */ return 0; - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -88,7 +86,7 @@ static int tcf_csum_init(struct net *net, struct nlattr *nla, spin_unlock_bh(&p->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -609,7 +607,7 @@ static int tcf_csum_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, csum_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_csum_ops = { @@ -628,7 +626,7 @@ static __net_init int csum_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, csum_net_id); - return tc_action_net_init(tn, &act_csum_ops, CSUM_TAB_MASK); + return tc_action_net_init(tn, &act_csum_ops); } static void __net_exit csum_exit_net(struct net *net) diff --git a/net/sched/act_gact.c b/net/sched/act_gact.c index 99afe8b1f1fb..e29a48ef7fc3 100644 --- a/net/sched/act_gact.c +++ b/net/sched/act_gact.c @@ -23,8 +23,6 @@ #include #include -#define GACT_TAB_MASK 15 - static unsigned int gact_net_id; static struct tc_action_ops act_gact_ops; @@ -92,16 +90,16 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, } #endif - if (!tcf_hash_check(tn, parm->index, a, bind)) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_gact_ops, bind, true); + if (!tcf_idr_check(tn, parm->index, a, bind)) { + ret = tcf_idr_create(tn, parm->index, est, a, + &act_gact_ops, bind, true); if (ret) return ret; ret = ACT_P_CREATED; } else { if (bind)/* dont override defaults */ return 0; - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -122,7 +120,7 @@ static int tcf_gact_init(struct net *net, struct nlattr *nla, } #endif if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -214,7 +212,7 @@ static int tcf_gact_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, gact_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_gact_ops = { @@ -234,7 +232,7 @@ static __net_init int gact_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, gact_net_id); - return tc_action_net_init(tn, &act_gact_ops, GACT_TAB_MASK); + return tc_action_net_init(tn, &act_gact_ops); } static void __net_exit gact_exit_net(struct net *net) diff --git a/net/sched/act_ife.c b/net/sched/act_ife.c index 7ed1be80ee86..8ccd35825b6b 100644 --- a/net/sched/act_ife.c +++ b/net/sched/act_ife.c @@ -34,8 +34,6 @@ #include #include -#define IFE_TAB_MASK 15 - static unsigned int ife_net_id; static int max_metacnt = IFE_META_MAX + 1; static struct tc_action_ops act_ife_ops; @@ -452,18 +450,18 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, parm = nla_data(tb[TCA_IFE_PARMS]); - exists = tcf_hash_check(tn, parm->index, a, bind); + exists = tcf_idr_check(tn, parm->index, a, bind); if (exists && bind) return 0; if (!exists) { - ret = tcf_hash_create(tn, parm->index, est, a, &act_ife_ops, - bind, false); + ret = tcf_idr_create(tn, parm->index, est, a, &act_ife_ops, + bind, false); if (ret) return ret; ret = ACT_P_CREATED; } else { - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -507,7 +505,7 @@ static int tcf_ife_init(struct net *net, struct nlattr *nla, if (err) { metadata_parse_err: if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (ret == ACT_P_CREATED) _tcf_ife_cleanup(*a, bind); @@ -541,7 +539,7 @@ metadata_parse_err: spin_unlock_bh(&ife->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -800,7 +798,7 @@ static int tcf_ife_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, ife_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_ife_ops = { @@ -820,7 +818,7 @@ static __net_init int ife_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, ife_net_id); - return tc_action_net_init(tn, &act_ife_ops, IFE_TAB_MASK); + return tc_action_net_init(tn, &act_ife_ops); } static void __net_exit ife_exit_net(struct net *net) diff --git a/net/sched/act_ipt.c b/net/sched/act_ipt.c index 541707802a23..d9e399a7e3d5 100644 --- a/net/sched/act_ipt.c +++ b/net/sched/act_ipt.c @@ -28,8 +28,6 @@ #include -#define IPT_TAB_MASK 15 - static unsigned int ipt_net_id; static struct tc_action_ops act_ipt_ops; @@ -118,33 +116,33 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla, if (tb[TCA_IPT_INDEX] != NULL) index = nla_get_u32(tb[TCA_IPT_INDEX]); - exists = tcf_hash_check(tn, index, a, bind); + exists = tcf_idr_check(tn, index, a, bind); if (exists && bind) return 0; if (tb[TCA_IPT_HOOK] == NULL || tb[TCA_IPT_TARG] == NULL) { if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -EINVAL; } td = (struct xt_entry_target *)nla_data(tb[TCA_IPT_TARG]); if (nla_len(tb[TCA_IPT_TARG]) < td->u.target_size) { if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -EINVAL; } if (!exists) { - ret = tcf_hash_create(tn, index, est, a, ops, bind, - false); + ret = tcf_idr_create(tn, index, est, a, ops, bind, + false); if (ret) return ret; ret = ACT_P_CREATED; } else { if (bind)/* dont override defaults */ return 0; - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; @@ -180,7 +178,7 @@ static int __tcf_ipt_init(struct net *net, unsigned int id, struct nlattr *nla, ipt->tcfi_hook = hook; spin_unlock_bh(&ipt->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; err3: @@ -189,7 +187,7 @@ err2: kfree(tname); err1: if (ret == ACT_P_CREATED) - tcf_hash_cleanup(*a, est); + tcf_idr_cleanup(*a, est); return err; } @@ -316,7 +314,7 @@ static int tcf_ipt_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, ipt_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_ipt_ops = { @@ -336,7 +334,7 @@ static __net_init int ipt_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, ipt_net_id); - return tc_action_net_init(tn, &act_ipt_ops, IPT_TAB_MASK); + return tc_action_net_init(tn, &act_ipt_ops); } static void __net_exit ipt_exit_net(struct net *net) @@ -366,7 +364,7 @@ static int tcf_xt_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, xt_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_xt_ops = { @@ -386,7 +384,7 @@ static __net_init int xt_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, xt_net_id); - return tc_action_net_init(tn, &act_xt_ops, IPT_TAB_MASK); + return tc_action_net_init(tn, &act_xt_ops); } static void __net_exit xt_exit_net(struct net *net) diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index 1b5549ababd4..416627c66f08 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -28,7 +28,6 @@ #include #include -#define MIRRED_TAB_MASK 7 static LIST_HEAD(mirred_list); static DEFINE_SPINLOCK(mirred_list_lock); @@ -94,7 +93,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, return -EINVAL; parm = nla_data(tb[TCA_MIRRED_PARMS]); - exists = tcf_hash_check(tn, parm->index, a, bind); + exists = tcf_idr_check(tn, parm->index, a, bind); if (exists && bind) return 0; @@ -106,14 +105,14 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, break; default: if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -EINVAL; } if (parm->ifindex) { dev = __dev_get_by_index(net, parm->ifindex); if (dev == NULL) { if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -ENODEV; } mac_header_xmit = dev_is_mac_header_xmit(dev); @@ -124,13 +123,13 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, if (!exists) { if (dev == NULL) return -EINVAL; - ret = tcf_hash_create(tn, parm->index, est, a, - &act_mirred_ops, bind, true); + ret = tcf_idr_create(tn, parm->index, est, a, + &act_mirred_ops, bind, true); if (ret) return ret; ret = ACT_P_CREATED; } else { - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -152,7 +151,7 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, spin_lock_bh(&mirred_list_lock); list_add(&m->tcfm_list, &mirred_list); spin_unlock_bh(&mirred_list_lock); - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); } return ret; @@ -283,7 +282,7 @@ static int tcf_mirred_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, mirred_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static int mirred_device_event(struct notifier_block *unused, @@ -344,7 +343,7 @@ static __net_init int mirred_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, mirred_net_id); - return tc_action_net_init(tn, &act_mirred_ops, MIRRED_TAB_MASK); + return tc_action_net_init(tn, &act_mirred_ops); } static void __net_exit mirred_exit_net(struct net *net) diff --git a/net/sched/act_nat.c b/net/sched/act_nat.c index 9016ab8a0649..c365d01b99c8 100644 --- a/net/sched/act_nat.c +++ b/net/sched/act_nat.c @@ -29,8 +29,6 @@ #include -#define NAT_TAB_MASK 15 - static unsigned int nat_net_id; static struct tc_action_ops act_nat_ops; @@ -58,16 +56,16 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, return -EINVAL; parm = nla_data(tb[TCA_NAT_PARMS]); - if (!tcf_hash_check(tn, parm->index, a, bind)) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_nat_ops, bind, false); + if (!tcf_idr_check(tn, parm->index, a, bind)) { + ret = tcf_idr_create(tn, parm->index, est, a, + &act_nat_ops, bind, false); if (ret) return ret; ret = ACT_P_CREATED; } else { if (bind) return 0; - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -83,7 +81,7 @@ static int tcf_nat_init(struct net *net, struct nlattr *nla, struct nlattr *est, spin_unlock_bh(&p->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -290,7 +288,7 @@ static int tcf_nat_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, nat_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_nat_ops = { @@ -309,7 +307,7 @@ static __net_init int nat_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, nat_net_id); - return tc_action_net_init(tn, &act_nat_ops, NAT_TAB_MASK); + return tc_action_net_init(tn, &act_nat_ops); } static void __net_exit nat_exit_net(struct net *net) diff --git a/net/sched/act_pedit.c b/net/sched/act_pedit.c index 7dc5892671c8..491fe5deb09e 100644 --- a/net/sched/act_pedit.c +++ b/net/sched/act_pedit.c @@ -24,8 +24,6 @@ #include #include -#define PEDIT_TAB_MASK 15 - static unsigned int pedit_net_id; static struct tc_action_ops act_pedit_ops; @@ -168,17 +166,17 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, if (IS_ERR(keys_ex)) return PTR_ERR(keys_ex); - if (!tcf_hash_check(tn, parm->index, a, bind)) { + if (!tcf_idr_check(tn, parm->index, a, bind)) { if (!parm->nkeys) return -EINVAL; - ret = tcf_hash_create(tn, parm->index, est, a, - &act_pedit_ops, bind, false); + ret = tcf_idr_create(tn, parm->index, est, a, + &act_pedit_ops, bind, false); if (ret) return ret; p = to_pedit(*a); keys = kmalloc(ksize, GFP_KERNEL); if (keys == NULL) { - tcf_hash_cleanup(*a, est); + tcf_idr_cleanup(*a, est); kfree(keys_ex); return -ENOMEM; } @@ -186,7 +184,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, } else { if (bind) return 0; - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; p = to_pedit(*a); @@ -214,7 +212,7 @@ static int tcf_pedit_init(struct net *net, struct nlattr *nla, spin_unlock_bh(&p->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -432,7 +430,7 @@ static int tcf_pedit_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, pedit_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_pedit_ops = { @@ -452,7 +450,7 @@ static __net_init int pedit_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, pedit_net_id); - return tc_action_net_init(tn, &act_pedit_ops, PEDIT_TAB_MASK); + return tc_action_net_init(tn, &act_pedit_ops); } static void __net_exit pedit_exit_net(struct net *net) diff --git a/net/sched/act_police.c b/net/sched/act_police.c index b062bc80c7cb..3bb2ebf9e9ae 100644 --- a/net/sched/act_police.c +++ b/net/sched/act_police.c @@ -40,8 +40,6 @@ struct tcf_police { #define to_police(pc) ((struct tcf_police *)pc) -#define POL_TAB_MASK 15 - /* old policer structure from before tc actions */ struct tc_police_compat { u32 index; @@ -101,18 +99,18 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla, return -EINVAL; parm = nla_data(tb[TCA_POLICE_TBF]); - exists = tcf_hash_check(tn, parm->index, a, bind); + exists = tcf_idr_check(tn, parm->index, a, bind); if (exists && bind) return 0; if (!exists) { - ret = tcf_hash_create(tn, parm->index, NULL, a, - &act_police_ops, bind, false); + ret = tcf_idr_create(tn, parm->index, NULL, a, + &act_police_ops, bind, false); if (ret) return ret; ret = ACT_P_CREATED; } else { - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -188,7 +186,7 @@ static int tcf_act_police_init(struct net *net, struct nlattr *nla, return ret; police->tcfp_t_c = ktime_get_ns(); - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; @@ -196,7 +194,7 @@ failure: qdisc_put_rtab(P_tab); qdisc_put_rtab(R_tab); if (ret == ACT_P_CREATED) - tcf_hash_cleanup(*a, est); + tcf_idr_cleanup(*a, est); return err; } @@ -310,7 +308,7 @@ static int tcf_police_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, police_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } MODULE_AUTHOR("Alexey Kuznetsov"); @@ -333,7 +331,7 @@ static __net_init int police_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, police_net_id); - return tc_action_net_init(tn, &act_police_ops, POL_TAB_MASK); + return tc_action_net_init(tn, &act_police_ops); } static void __net_exit police_exit_net(struct net *net) diff --git a/net/sched/act_sample.c b/net/sched/act_sample.c index 59d6645a4007..ec986ae52808 100644 --- a/net/sched/act_sample.c +++ b/net/sched/act_sample.c @@ -25,7 +25,6 @@ #include -#define SAMPLE_TAB_MASK 7 static unsigned int sample_net_id; static struct tc_action_ops act_sample_ops; @@ -59,18 +58,18 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, parm = nla_data(tb[TCA_SAMPLE_PARMS]); - exists = tcf_hash_check(tn, parm->index, a, bind); + exists = tcf_idr_check(tn, parm->index, a, bind); if (exists && bind) return 0; if (!exists) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_sample_ops, bind, false); + ret = tcf_idr_create(tn, parm->index, est, a, + &act_sample_ops, bind, false); if (ret) return ret; ret = ACT_P_CREATED; } else { - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -82,7 +81,7 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, psample_group = psample_group_get(net, s->psample_group_num); if (!psample_group) { if (ret == ACT_P_CREATED) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -ENOMEM; } RCU_INIT_POINTER(s->psample_group, psample_group); @@ -93,7 +92,7 @@ static int tcf_sample_init(struct net *net, struct nlattr *nla, } if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -221,7 +220,7 @@ static int tcf_sample_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, sample_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_sample_ops = { @@ -241,7 +240,7 @@ static __net_init int sample_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, sample_net_id); - return tc_action_net_init(tn, &act_sample_ops, SAMPLE_TAB_MASK); + return tc_action_net_init(tn, &act_sample_ops); } static void __net_exit sample_exit_net(struct net *net) diff --git a/net/sched/act_simple.c b/net/sched/act_simple.c index 43605e7ce051..e7b57e5071a3 100644 --- a/net/sched/act_simple.c +++ b/net/sched/act_simple.c @@ -24,8 +24,6 @@ #include #include -#define SIMP_TAB_MASK 7 - static unsigned int simp_net_id; static struct tc_action_ops act_simp_ops; @@ -102,28 +100,28 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, return -EINVAL; parm = nla_data(tb[TCA_DEF_PARMS]); - exists = tcf_hash_check(tn, parm->index, a, bind); + exists = tcf_idr_check(tn, parm->index, a, bind); if (exists && bind) return 0; if (tb[TCA_DEF_DATA] == NULL) { if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -EINVAL; } defdata = nla_data(tb[TCA_DEF_DATA]); if (!exists) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_simp_ops, bind, false); + ret = tcf_idr_create(tn, parm->index, est, a, + &act_simp_ops, bind, false); if (ret) return ret; d = to_defact(*a); ret = alloc_defdata(d, defdata); if (ret < 0) { - tcf_hash_cleanup(*a, est); + tcf_idr_cleanup(*a, est); return ret; } d->tcf_action = parm->action; @@ -131,7 +129,7 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, } else { d = to_defact(*a); - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; @@ -139,7 +137,7 @@ static int tcf_simp_init(struct net *net, struct nlattr *nla, } if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -183,7 +181,7 @@ static int tcf_simp_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, simp_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_simp_ops = { @@ -203,7 +201,7 @@ static __net_init int simp_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, simp_net_id); - return tc_action_net_init(tn, &act_simp_ops, SIMP_TAB_MASK); + return tc_action_net_init(tn, &act_simp_ops); } static void __net_exit simp_exit_net(struct net *net) diff --git a/net/sched/act_skbedit.c b/net/sched/act_skbedit.c index 6b3e65d7de0c..59949d61f20d 100644 --- a/net/sched/act_skbedit.c +++ b/net/sched/act_skbedit.c @@ -27,8 +27,6 @@ #include #include -#define SKBEDIT_TAB_MASK 15 - static unsigned int skbedit_net_id; static struct tc_action_ops act_skbedit_ops; @@ -118,18 +116,18 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, parm = nla_data(tb[TCA_SKBEDIT_PARMS]); - exists = tcf_hash_check(tn, parm->index, a, bind); + exists = tcf_idr_check(tn, parm->index, a, bind); if (exists && bind) return 0; if (!flags) { - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -EINVAL; } if (!exists) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_skbedit_ops, bind, false); + ret = tcf_idr_create(tn, parm->index, est, a, + &act_skbedit_ops, bind, false); if (ret) return ret; @@ -137,7 +135,7 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, ret = ACT_P_CREATED; } else { d = to_skbedit(*a); - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -163,7 +161,7 @@ static int tcf_skbedit_init(struct net *net, struct nlattr *nla, spin_unlock_bh(&d->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -221,7 +219,7 @@ static int tcf_skbedit_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, skbedit_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_skbedit_ops = { @@ -240,7 +238,7 @@ static __net_init int skbedit_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, skbedit_net_id); - return tc_action_net_init(tn, &act_skbedit_ops, SKBEDIT_TAB_MASK); + return tc_action_net_init(tn, &act_skbedit_ops); } static void __net_exit skbedit_exit_net(struct net *net) diff --git a/net/sched/act_skbmod.c b/net/sched/act_skbmod.c index a73c4bbcada2..b642ad3d39dd 100644 --- a/net/sched/act_skbmod.c +++ b/net/sched/act_skbmod.c @@ -20,8 +20,6 @@ #include #include -#define SKBMOD_TAB_MASK 15 - static unsigned int skbmod_net_id; static struct tc_action_ops act_skbmod_ops; @@ -129,7 +127,7 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, if (parm->flags & SKBMOD_F_SWAPMAC) lflags = SKBMOD_F_SWAPMAC; - exists = tcf_hash_check(tn, parm->index, a, bind); + exists = tcf_idr_check(tn, parm->index, a, bind); if (exists && bind) return 0; @@ -137,14 +135,14 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, return -EINVAL; if (!exists) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_skbmod_ops, bind, true); + ret = tcf_idr_create(tn, parm->index, est, a, + &act_skbmod_ops, bind, true); if (ret) return ret; ret = ACT_P_CREATED; } else { - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -155,7 +153,7 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, p = kzalloc(sizeof(struct tcf_skbmod_params), GFP_KERNEL); if (unlikely(!p)) { if (ovr) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -ENOMEM; } @@ -182,7 +180,7 @@ static int tcf_skbmod_init(struct net *net, struct nlattr *nla, kfree_rcu(p_old, rcu); if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -245,7 +243,7 @@ static int tcf_skbmod_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, skbmod_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_skbmod_ops = { @@ -265,7 +263,7 @@ static __net_init int skbmod_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, skbmod_net_id); - return tc_action_net_init(tn, &act_skbmod_ops, SKBMOD_TAB_MASK); + return tc_action_net_init(tn, &act_skbmod_ops); } static void __net_exit skbmod_exit_net(struct net *net) diff --git a/net/sched/act_tunnel_key.c b/net/sched/act_tunnel_key.c index fd7e75679c69..30c96274c638 100644 --- a/net/sched/act_tunnel_key.c +++ b/net/sched/act_tunnel_key.c @@ -20,8 +20,6 @@ #include #include -#define TUNNEL_KEY_TAB_MASK 15 - static unsigned int tunnel_key_net_id; static struct tc_action_ops act_tunnel_key_ops; @@ -100,7 +98,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, return -EINVAL; parm = nla_data(tb[TCA_TUNNEL_KEY_PARMS]); - exists = tcf_hash_check(tn, parm->index, a, bind); + exists = tcf_idr_check(tn, parm->index, a, bind); if (exists && bind) return 0; @@ -159,14 +157,14 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, } if (!exists) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_tunnel_key_ops, bind, true); + ret = tcf_idr_create(tn, parm->index, est, a, + &act_tunnel_key_ops, bind, true); if (ret) return ret; ret = ACT_P_CREATED; } else { - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -177,7 +175,7 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, params_new = kzalloc(sizeof(*params_new), GFP_KERNEL); if (unlikely(!params_new)) { if (ret == ACT_P_CREATED) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -ENOMEM; } @@ -193,13 +191,13 @@ static int tunnel_key_init(struct net *net, struct nlattr *nla, kfree_rcu(params_old, rcu); if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; err_out: if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return ret; } @@ -304,7 +302,7 @@ static int tunnel_key_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_tunnel_key_ops = { @@ -324,7 +322,7 @@ static __net_init int tunnel_key_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, tunnel_key_net_id); - return tc_action_net_init(tn, &act_tunnel_key_ops, TUNNEL_KEY_TAB_MASK); + return tc_action_net_init(tn, &act_tunnel_key_ops); } static void __net_exit tunnel_key_exit_net(struct net *net) diff --git a/net/sched/act_vlan.c b/net/sched/act_vlan.c index 13ba3a89f675..16eb067a8d8f 100644 --- a/net/sched/act_vlan.c +++ b/net/sched/act_vlan.c @@ -19,8 +19,6 @@ #include #include -#define VLAN_TAB_MASK 15 - static unsigned int vlan_net_id; static struct tc_action_ops act_vlan_ops; @@ -128,7 +126,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, if (!tb[TCA_VLAN_PARMS]) return -EINVAL; parm = nla_data(tb[TCA_VLAN_PARMS]); - exists = tcf_hash_check(tn, parm->index, a, bind); + exists = tcf_idr_check(tn, parm->index, a, bind); if (exists && bind) return 0; @@ -139,13 +137,13 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, case TCA_VLAN_ACT_MODIFY: if (!tb[TCA_VLAN_PUSH_VLAN_ID]) { if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -EINVAL; } push_vid = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); if (push_vid >= VLAN_VID_MASK) { if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -ERANGE; } @@ -167,20 +165,20 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, break; default: if (exists) - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); return -EINVAL; } action = parm->v_action; if (!exists) { - ret = tcf_hash_create(tn, parm->index, est, a, - &act_vlan_ops, bind, false); + ret = tcf_idr_create(tn, parm->index, est, a, + &act_vlan_ops, bind, false); if (ret) return ret; ret = ACT_P_CREATED; } else { - tcf_hash_release(*a, bind); + tcf_idr_release(*a, bind); if (!ovr) return -EEXIST; } @@ -199,7 +197,7 @@ static int tcf_vlan_init(struct net *net, struct nlattr *nla, spin_unlock_bh(&v->tcf_lock); if (ret == ACT_P_CREATED) - tcf_hash_insert(tn, *a); + tcf_idr_insert(tn, *a); return ret; } @@ -252,7 +250,7 @@ static int tcf_vlan_search(struct net *net, struct tc_action **a, u32 index) { struct tc_action_net *tn = net_generic(net, vlan_net_id); - return tcf_hash_search(tn, a, index); + return tcf_idr_search(tn, a, index); } static struct tc_action_ops act_vlan_ops = { @@ -271,7 +269,7 @@ static __net_init int vlan_init_net(struct net *net) { struct tc_action_net *tn = net_generic(net, vlan_net_id); - return tc_action_net_init(tn, &act_vlan_ops, VLAN_TAB_MASK); + return tc_action_net_init(tn, &act_vlan_ops); } static void __net_exit vlan_exit_net(struct net *net) -- cgit v1.2.3 From 5829d70b0b6cd055e7a9af3abe573927e919e30b Mon Sep 17 00:00:00 2001 From: Ahmed Abdelsalam Date: Wed, 30 Aug 2017 10:50:37 +0200 Subject: ipv6: sr: fix get_srh() to comply with IPv6 standard "RFC 8200" IPv6 packet may carry more than one extension header, and IPv6 nodes must accept and attempt to process extension headers in any order and occurring any number of times in the same packet. Hence, there should be no assumption that Segment Routing extension header is to appear immediately after the IPv6 header. Moreover, section 4.1 of RFC 8200 gives a recommendation on the order of appearance of those extension headers within an IPv6 packet. According to this recommendation, Segment Routing extension header should appear after Hop-by-Hop and Destination Options headers (if they present). This patch fixes the get_srh(), so it gets the segment routing header regardless of its position in the chain of the extension headers in IPv6 packet, and makes sure that the IPv6 routing extension header is of Type 4. Signed-off-by: Ahmed Abdelsalam Acked-by: David Lebrun Signed-off-by: David S. Miller --- net/ipv6/seg6_local.c | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/net/ipv6/seg6_local.c b/net/ipv6/seg6_local.c index 9c1a885ee482..7ff54db73a48 100644 --- a/net/ipv6/seg6_local.c +++ b/net/ipv6/seg6_local.c @@ -62,17 +62,23 @@ static struct seg6_local_lwt *seg6_local_lwtunnel(struct lwtunnel_state *lwt) static struct ipv6_sr_hdr *get_srh(struct sk_buff *skb) { struct ipv6_sr_hdr *srh; - struct ipv6hdr *hdr; - int len; + int len, srhoff = 0; + + if (ipv6_find_hdr(skb, &srhoff, IPPROTO_ROUTING, NULL, NULL) < 0) + return NULL; + + if (!pskb_may_pull(skb, srhoff + sizeof(*srh))) + return NULL; + + srh = (struct ipv6_sr_hdr *)(skb->data + srhoff); - hdr = ipv6_hdr(skb); - if (hdr->nexthdr != IPPROTO_ROUTING) + /* make sure it's a Segment Routing header (Routing Type 4) */ + if (srh->type != IPV6_SRCRT_TYPE_4) return NULL; - srh = (struct ipv6_sr_hdr *)(hdr + 1); len = (srh->hdrlen + 1) << 3; - if (!pskb_may_pull(skb, sizeof(*hdr) + len)) + if (!pskb_may_pull(skb, srhoff + len)) return NULL; if (!seg6_validate_srh(srh, len)) -- cgit v1.2.3 From 12bdc5e1e58304d27cb16e30597e2e706e536fb1 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Wed, 30 Aug 2017 17:07:30 -0700 Subject: devlink: Maintain consistency in mac field name IPv4 name uses "destination ip" as does the IPv6 patch set. Make the mac field consistent. Signed-off-by: David Ahern Signed-off-by: David S. Miller --- net/core/devlink.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/devlink.c b/net/core/devlink.c index 194708aa5f11..cbc4b0461b0f 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -31,7 +31,7 @@ static struct devlink_dpipe_field devlink_dpipe_fields_ethernet[] = { { - .name = "destination_mac", + .name = "destination mac", .id = DEVLINK_DPIPE_FIELD_ETHERNET_DST_MAC, .bitwidth = 48, }, -- cgit v1.2.3 From 47ebcc0bb1d5eb7f1b1eeab675409ea7f67b4a5c Mon Sep 17 00:00:00 2001 From: Yossi Kuperman Date: Wed, 30 Aug 2017 11:30:39 +0300 Subject: xfrm: Add support for network devices capable of removing the ESP trailer In conjunction with crypto offload [1], removing the ESP trailer by hardware can potentially improve the performance by avoiding (1) a cache miss incurred by reading the nexthdr field and (2) the necessity to calculate the csum value of the trailer in order to keep skb->csum valid. This patch introduces the changes to the xfrm stack and merely serves as an infrastructure. Subsequent patch to mlx5 driver will put this to a good use. [1] https://www.mail-archive.com/netdev@vger.kernel.org/msg175733.html Signed-off-by: Yossi Kuperman Signed-off-by: Steffen Klassert --- include/net/xfrm.h | 1 + net/ipv4/esp4.c | 70 ++++++++++++++++++++++++++++++++++----------------- net/ipv6/esp6.c | 51 ++++++++++++++++++++++++++----------- net/xfrm/xfrm_input.c | 5 ++++ 4 files changed, 89 insertions(+), 38 deletions(-) (limited to 'net') diff --git a/include/net/xfrm.h b/include/net/xfrm.h index 9c7b70cce6d6..f002a2c5e33c 100644 --- a/include/net/xfrm.h +++ b/include/net/xfrm.h @@ -1019,6 +1019,7 @@ struct xfrm_offload { #define CRYPTO_FALLBACK 8 #define XFRM_GSO_SEGMENT 16 #define XFRM_GRO 32 +#define XFRM_ESP_NO_TRAILER 64 __u32 status; #define CRYPTO_SUCCESS 1 diff --git a/net/ipv4/esp4.c b/net/ipv4/esp4.c index 741acd7b9646..319000573bc7 100644 --- a/net/ipv4/esp4.c +++ b/net/ipv4/esp4.c @@ -499,19 +499,59 @@ static int esp_output(struct xfrm_state *x, struct sk_buff *skb) return esp_output_tail(x, skb, &esp); } +static inline int esp_remove_trailer(struct sk_buff *skb) +{ + struct xfrm_state *x = xfrm_input_state(skb); + struct xfrm_offload *xo = xfrm_offload(skb); + struct crypto_aead *aead = x->data; + int alen, hlen, elen; + int padlen, trimlen; + __wsum csumdiff; + u8 nexthdr[2]; + int ret; + + alen = crypto_aead_authsize(aead); + hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); + elen = skb->len - hlen; + + if (xo && (xo->flags & XFRM_ESP_NO_TRAILER)) { + ret = xo->proto; + goto out; + } + + if (skb_copy_bits(skb, skb->len - alen - 2, nexthdr, 2)) + BUG(); + + ret = -EINVAL; + padlen = nexthdr[0]; + if (padlen + 2 + alen >= elen) { + net_dbg_ratelimited("ipsec esp packet is garbage padlen=%d, elen=%d\n", + padlen + 2, elen - alen); + goto out; + } + + trimlen = alen + padlen + 2; + if (skb->ip_summed == CHECKSUM_COMPLETE) { + csumdiff = skb_checksum(skb, skb->len - trimlen, trimlen, 0); + skb->csum = csum_block_sub(skb->csum, csumdiff, + skb->len - trimlen); + } + pskb_trim(skb, skb->len - trimlen); + + ret = nexthdr[1]; + +out: + return ret; +} + int esp_input_done2(struct sk_buff *skb, int err) { const struct iphdr *iph; struct xfrm_state *x = xfrm_input_state(skb); struct xfrm_offload *xo = xfrm_offload(skb); struct crypto_aead *aead = x->data; - int alen = crypto_aead_authsize(aead); int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); - int elen = skb->len - hlen; int ihl; - u8 nexthdr[2]; - int padlen, trimlen; - __wsum csumdiff; if (!xo || (xo && !(xo->flags & CRYPTO_DONE))) kfree(ESP_SKB_CB(skb)->tmp); @@ -519,16 +559,10 @@ int esp_input_done2(struct sk_buff *skb, int err) if (unlikely(err)) goto out; - if (skb_copy_bits(skb, skb->len-alen-2, nexthdr, 2)) - BUG(); - - err = -EINVAL; - padlen = nexthdr[0]; - if (padlen + 2 + alen >= elen) + err = esp_remove_trailer(skb); + if (unlikely(err < 0)) goto out; - /* ... check padding bits here. Silly. :-) */ - iph = ip_hdr(skb); ihl = iph->ihl * 4; @@ -569,22 +603,12 @@ int esp_input_done2(struct sk_buff *skb, int err) skb->ip_summed = CHECKSUM_UNNECESSARY; } - trimlen = alen + padlen + 2; - if (skb->ip_summed == CHECKSUM_COMPLETE) { - csumdiff = skb_checksum(skb, skb->len - trimlen, trimlen, 0); - skb->csum = csum_block_sub(skb->csum, csumdiff, - skb->len - trimlen); - } - pskb_trim(skb, skb->len - trimlen); - skb_pull_rcsum(skb, hlen); if (x->props.mode == XFRM_MODE_TUNNEL) skb_reset_transport_header(skb); else skb_set_transport_header(skb, -ihl); - err = nexthdr[1]; - /* RFC4303: Drop dummy packets without any error */ if (err == IPPROTO_NONE) err = -EINVAL; diff --git a/net/ipv6/esp6.c b/net/ipv6/esp6.c index 74bde202eb9a..7fb41b0ad437 100644 --- a/net/ipv6/esp6.c +++ b/net/ipv6/esp6.c @@ -461,29 +461,30 @@ static int esp6_output(struct xfrm_state *x, struct sk_buff *skb) return esp6_output_tail(x, skb, &esp); } -int esp6_input_done2(struct sk_buff *skb, int err) +static inline int esp_remove_trailer(struct sk_buff *skb) { struct xfrm_state *x = xfrm_input_state(skb); struct xfrm_offload *xo = xfrm_offload(skb); struct crypto_aead *aead = x->data; - int alen = crypto_aead_authsize(aead); - int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); - int elen = skb->len - hlen; - int hdr_len = skb_network_header_len(skb); + int alen, hlen, elen; int padlen, trimlen; __wsum csumdiff; u8 nexthdr[2]; + int ret; - if (!xo || (xo && !(xo->flags & CRYPTO_DONE))) - kfree(ESP_SKB_CB(skb)->tmp); + alen = crypto_aead_authsize(aead); + hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); + elen = skb->len - hlen; - if (unlikely(err)) + if (xo && (xo->flags & XFRM_ESP_NO_TRAILER)) { + ret = xo->proto; goto out; + } if (skb_copy_bits(skb, skb->len - alen - 2, nexthdr, 2)) BUG(); - err = -EINVAL; + ret = -EINVAL; padlen = nexthdr[0]; if (padlen + 2 + alen >= elen) { net_dbg_ratelimited("ipsec esp packet is garbage padlen=%d, elen=%d\n", @@ -491,26 +492,46 @@ int esp6_input_done2(struct sk_buff *skb, int err) goto out; } - /* ... check padding bits here. Silly. :-) */ - trimlen = alen + padlen + 2; if (skb->ip_summed == CHECKSUM_COMPLETE) { - skb_postpull_rcsum(skb, skb_network_header(skb), - skb_network_header_len(skb)); csumdiff = skb_checksum(skb, skb->len - trimlen, trimlen, 0); skb->csum = csum_block_sub(skb->csum, csumdiff, skb->len - trimlen); } pskb_trim(skb, skb->len - trimlen); + ret = nexthdr[1]; + +out: + return ret; +} + +int esp6_input_done2(struct sk_buff *skb, int err) +{ + struct xfrm_state *x = xfrm_input_state(skb); + struct xfrm_offload *xo = xfrm_offload(skb); + struct crypto_aead *aead = x->data; + int hlen = sizeof(struct ip_esp_hdr) + crypto_aead_ivsize(aead); + int hdr_len = skb_network_header_len(skb); + + if (!xo || (xo && !(xo->flags & CRYPTO_DONE))) + kfree(ESP_SKB_CB(skb)->tmp); + + if (unlikely(err)) + goto out; + + err = esp_remove_trailer(skb); + if (unlikely(err < 0)) + goto out; + + skb_postpull_rcsum(skb, skb_network_header(skb), + skb_network_header_len(skb)); skb_pull_rcsum(skb, hlen); if (x->props.mode == XFRM_MODE_TUNNEL) skb_reset_transport_header(skb); else skb_set_transport_header(skb, -hdr_len); - err = nexthdr[1]; - /* RFC4303: Drop dummy packets without any error */ if (err == IPPROTO_NONE) err = -EINVAL; diff --git a/net/xfrm/xfrm_input.c b/net/xfrm/xfrm_input.c index f07eec59dcae..2515cd2bc5db 100644 --- a/net/xfrm/xfrm_input.c +++ b/net/xfrm/xfrm_input.c @@ -247,6 +247,11 @@ int xfrm_input(struct sk_buff *skb, int nexthdr, __be32 spi, int encap_type) goto drop; } + if (xo->status & CRYPTO_INVALID_PROTOCOL) { + XFRM_INC_STATS(net, LINUX_MIB_XFRMINSTATEPROTOERROR); + goto drop; + } + XFRM_INC_STATS(net, LINUX_MIB_XFRMINBUFFERERROR); goto drop; } -- cgit v1.2.3 From 8598112d04af21cf6c895670e72dcb8a9f58e74f Mon Sep 17 00:00:00 2001 From: Steffen Klassert Date: Thu, 31 Aug 2017 10:37:00 +0200 Subject: xfrm: Fix return value check of copy_sec_ctx. A recent commit added an output_mark. When copying this output_mark, the return value of copy_sec_ctx is overwitten without a check. Fix this by copying the output_mark before the security context. Fixes: 077fbac405bf ("net: xfrm: support setting an output mark.") Signed-off-by: Steffen Klassert --- net/xfrm/xfrm_user.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/xfrm/xfrm_user.c b/net/xfrm/xfrm_user.c index cc3268d814b4..490132d6dc36 100644 --- a/net/xfrm/xfrm_user.c +++ b/net/xfrm/xfrm_user.c @@ -900,13 +900,13 @@ static int copy_to_user_state_extra(struct xfrm_state *x, ret = copy_user_offload(&x->xso, skb); if (ret) goto out; - if (x->security) - ret = copy_sec_ctx(x->security, skb); if (x->props.output_mark) { ret = nla_put_u32(skb, XFRMA_OUTPUT_MARK, x->props.output_mark); if (ret) goto out; } + if (x->security) + ret = copy_sec_ctx(x->security, skb); out: return ret; } -- cgit v1.2.3 From 07d79fc7d94e3f884b8b1c95aa615b202bb5e4c1 Mon Sep 17 00:00:00 2001 From: Cong Wang Date: Wed, 30 Aug 2017 14:30:36 -0700 Subject: net_sched: add reverse binding for tc class TC filters when used as classifiers are bound to TC classes. However, there is a hidden difference when adding them in different orders: 1. If we add tc classes before its filters, everything is fine. Logically, the classes exist before we specify their ID's in filters, it is easy to bind them together, just as in the current code base. 2. If we add tc filters before the tc classes they bind, we have to do dynamic lookup in fast path. What's worse, this happens all the time not just once, because on fast path tcf_result is passed on stack, there is no way to propagate back to the one in tc filters. This hidden difference hurts performance silently if we have many tc classes in hierarchy. This patch intends to close this gap by doing the reverse binding when we create a new class, in this case we can actually search all the filters in its parent, match and fixup by classid. And because tcf_result is specific to each type of tc filter, we have to introduce a new ops for each filter to tell how to bind the class. Note, we still can NOT totally get rid of those class lookup in ->enqueue() because cgroup and flow filters have no way to determine the classid at setup time, they still have to go through dynamic lookup. Cc: Jamal Hadi Salim Signed-off-by: Cong Wang Signed-off-by: David S. Miller --- include/net/sch_generic.h | 1 + net/sched/cls_basic.c | 9 +++++++ net/sched/cls_bpf.c | 9 +++++++ net/sched/cls_flower.c | 9 +++++++ net/sched/cls_fw.c | 9 +++++++ net/sched/cls_matchall.c | 9 +++++++ net/sched/cls_route.c | 9 +++++++ net/sched/cls_rsvp.h | 9 +++++++ net/sched/cls_tcindex.c | 9 +++++++ net/sched/cls_u32.c | 9 +++++++ net/sched/sch_api.c | 68 +++++++++++++++++++++++++++++++++++++++++++++-- 11 files changed, 148 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/net/sch_generic.h b/include/net/sch_generic.h index c30b634c5f82..d6247a3c40df 100644 --- a/include/net/sch_generic.h +++ b/include/net/sch_generic.h @@ -217,6 +217,7 @@ struct tcf_proto_ops { void **, bool); int (*delete)(struct tcf_proto*, void *, bool*); void (*walk)(struct tcf_proto*, struct tcf_walker *arg); + void (*bind_class)(void *, u32, unsigned long); /* rtnetlink specific */ int (*dump)(struct net*, struct tcf_proto*, void *, diff --git a/net/sched/cls_basic.c b/net/sched/cls_basic.c index 73cc7f167a38..d89ebafd2239 100644 --- a/net/sched/cls_basic.c +++ b/net/sched/cls_basic.c @@ -235,6 +235,14 @@ skip: } } +static void basic_bind_class(void *fh, u32 classid, unsigned long cl) +{ + struct basic_filter *f = fh; + + if (f && f->res.classid == classid) + f->res.class = cl; +} + static int basic_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { @@ -280,6 +288,7 @@ static struct tcf_proto_ops cls_basic_ops __read_mostly = { .delete = basic_delete, .walk = basic_walk, .dump = basic_dump, + .bind_class = basic_bind_class, .owner = THIS_MODULE, }; diff --git a/net/sched/cls_bpf.c b/net/sched/cls_bpf.c index 6f2dffe30f25..520c5027646a 100644 --- a/net/sched/cls_bpf.c +++ b/net/sched/cls_bpf.c @@ -607,6 +607,14 @@ nla_put_failure: return -1; } +static void cls_bpf_bind_class(void *fh, u32 classid, unsigned long cl) +{ + struct cls_bpf_prog *prog = fh; + + if (prog && prog->res.classid == classid) + prog->res.class = cl; +} + static void cls_bpf_walk(struct tcf_proto *tp, struct tcf_walker *arg) { struct cls_bpf_head *head = rtnl_dereference(tp->root); @@ -635,6 +643,7 @@ static struct tcf_proto_ops cls_bpf_ops __read_mostly = { .delete = cls_bpf_delete, .walk = cls_bpf_walk, .dump = cls_bpf_dump, + .bind_class = cls_bpf_bind_class, }; static int __init cls_bpf_init_mod(void) diff --git a/net/sched/cls_flower.c b/net/sched/cls_flower.c index 3d041d279b92..1a267e77c6de 100644 --- a/net/sched/cls_flower.c +++ b/net/sched/cls_flower.c @@ -1351,6 +1351,14 @@ nla_put_failure: return -1; } +static void fl_bind_class(void *fh, u32 classid, unsigned long cl) +{ + struct cls_fl_filter *f = fh; + + if (f && f->res.classid == classid) + f->res.class = cl; +} + static struct tcf_proto_ops cls_fl_ops __read_mostly = { .kind = "flower", .classify = fl_classify, @@ -1361,6 +1369,7 @@ static struct tcf_proto_ops cls_fl_ops __read_mostly = { .delete = fl_delete, .walk = fl_walk, .dump = fl_dump, + .bind_class = fl_bind_class, .owner = THIS_MODULE, }; diff --git a/net/sched/cls_fw.c b/net/sched/cls_fw.c index 192255ec50bd..941245ad07fd 100644 --- a/net/sched/cls_fw.c +++ b/net/sched/cls_fw.c @@ -412,6 +412,14 @@ nla_put_failure: return -1; } +static void fw_bind_class(void *fh, u32 classid, unsigned long cl) +{ + struct fw_filter *f = fh; + + if (f && f->res.classid == classid) + f->res.class = cl; +} + static struct tcf_proto_ops cls_fw_ops __read_mostly = { .kind = "fw", .classify = fw_classify, @@ -422,6 +430,7 @@ static struct tcf_proto_ops cls_fw_ops __read_mostly = { .delete = fw_delete, .walk = fw_walk, .dump = fw_dump, + .bind_class = fw_bind_class, .owner = THIS_MODULE, }; diff --git a/net/sched/cls_matchall.c b/net/sched/cls_matchall.c index d4dc387f7a56..21cc45caf842 100644 --- a/net/sched/cls_matchall.c +++ b/net/sched/cls_matchall.c @@ -251,6 +251,14 @@ nla_put_failure: return -1; } +static void mall_bind_class(void *fh, u32 classid, unsigned long cl) +{ + struct cls_mall_head *head = fh; + + if (head && head->res.classid == classid) + head->res.class = cl; +} + static struct tcf_proto_ops cls_mall_ops __read_mostly = { .kind = "matchall", .classify = mall_classify, @@ -261,6 +269,7 @@ static struct tcf_proto_ops cls_mall_ops __read_mostly = { .delete = mall_delete, .walk = mall_walk, .dump = mall_dump, + .bind_class = mall_bind_class, .owner = THIS_MODULE, }; diff --git a/net/sched/cls_route.c b/net/sched/cls_route.c index 3b70982394ce..9ddde65915d2 100644 --- a/net/sched/cls_route.c +++ b/net/sched/cls_route.c @@ -624,6 +624,14 @@ nla_put_failure: return -1; } +static void route4_bind_class(void *fh, u32 classid, unsigned long cl) +{ + struct route4_filter *f = fh; + + if (f && f->res.classid == classid) + f->res.class = cl; +} + static struct tcf_proto_ops cls_route4_ops __read_mostly = { .kind = "route", .classify = route4_classify, @@ -634,6 +642,7 @@ static struct tcf_proto_ops cls_route4_ops __read_mostly = { .delete = route4_delete, .walk = route4_walk, .dump = route4_dump, + .bind_class = route4_bind_class, .owner = THIS_MODULE, }; diff --git a/net/sched/cls_rsvp.h b/net/sched/cls_rsvp.h index 26203ff817f3..98c05db85bcb 100644 --- a/net/sched/cls_rsvp.h +++ b/net/sched/cls_rsvp.h @@ -723,6 +723,14 @@ nla_put_failure: return -1; } +static void rsvp_bind_class(void *fh, u32 classid, unsigned long cl) +{ + struct rsvp_filter *f = fh; + + if (f && f->res.classid == classid) + f->res.class = cl; +} + static struct tcf_proto_ops RSVP_OPS __read_mostly = { .kind = RSVP_ID, .classify = rsvp_classify, @@ -733,6 +741,7 @@ static struct tcf_proto_ops RSVP_OPS __read_mostly = { .delete = rsvp_delete, .walk = rsvp_walk, .dump = rsvp_dump, + .bind_class = rsvp_bind_class, .owner = THIS_MODULE, }; diff --git a/net/sched/cls_tcindex.c b/net/sched/cls_tcindex.c index fb281b9b2c52..14a7e08b2fa9 100644 --- a/net/sched/cls_tcindex.c +++ b/net/sched/cls_tcindex.c @@ -606,6 +606,14 @@ nla_put_failure: return -1; } +static void tcindex_bind_class(void *fh, u32 classid, unsigned long cl) +{ + struct tcindex_filter_result *r = fh; + + if (r && r->res.classid == classid) + r->res.class = cl; +} + static struct tcf_proto_ops cls_tcindex_ops __read_mostly = { .kind = "tcindex", .classify = tcindex_classify, @@ -616,6 +624,7 @@ static struct tcf_proto_ops cls_tcindex_ops __read_mostly = { .delete = tcindex_delete, .walk = tcindex_walk, .dump = tcindex_dump, + .bind_class = tcindex_bind_class, .owner = THIS_MODULE, }; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index 99ea4c74dd5b..10b8d851fc6b 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -1112,6 +1112,14 @@ static void u32_walk(struct tcf_proto *tp, struct tcf_walker *arg) } } +static void u32_bind_class(void *fh, u32 classid, unsigned long cl) +{ + struct tc_u_knode *n = fh; + + if (n && n->res.classid == classid) + n->res.class = cl; +} + static int u32_dump(struct net *net, struct tcf_proto *tp, void *fh, struct sk_buff *skb, struct tcmsg *t) { @@ -1242,6 +1250,7 @@ static struct tcf_proto_ops cls_u32_ops __read_mostly = { .delete = u32_delete, .walk = u32_walk, .dump = u32_dump, + .bind_class = u32_bind_class, .owner = THIS_MODULE, }; diff --git a/net/sched/sch_api.c b/net/sched/sch_api.c index e7f8e4bfd4ec..929b024f41ba 100644 --- a/net/sched/sch_api.c +++ b/net/sched/sch_api.c @@ -35,6 +35,7 @@ #include #include #include +#include /* @@ -1648,6 +1649,64 @@ static int tclass_del_notify(struct net *net, n->nlmsg_flags & NLM_F_ECHO); } +#ifdef CONFIG_NET_CLS + +struct tcf_bind_args { + struct tcf_walker w; + u32 classid; + unsigned long cl; +}; + +static int tcf_node_bind(struct tcf_proto *tp, void *n, struct tcf_walker *arg) +{ + struct tcf_bind_args *a = (void *)arg; + + if (tp->ops->bind_class) { + tcf_tree_lock(tp); + tp->ops->bind_class(n, a->classid, a->cl); + tcf_tree_unlock(tp); + } + return 0; +} + +static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid, + unsigned long new_cl) +{ + const struct Qdisc_class_ops *cops = q->ops->cl_ops; + struct tcf_block *block; + struct tcf_chain *chain; + unsigned long cl; + + cl = cops->find(q, portid); + if (!cl) + return; + block = cops->tcf_block(q, cl); + if (!block) + return; + list_for_each_entry(chain, &block->chain_list, list) { + struct tcf_proto *tp; + + for (tp = rtnl_dereference(chain->filter_chain); + tp; tp = rtnl_dereference(tp->next)) { + struct tcf_bind_args arg = {}; + + arg.w.fn = tcf_node_bind; + arg.classid = clid; + arg.cl = new_cl; + tp->ops->walk(tp, &arg.w); + } + } +} + +#else + +static void tc_bind_tclass(struct Qdisc *q, u32 portid, u32 clid, + unsigned long new_cl) +{ +} + +#endif + static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, struct netlink_ext_ack *extack) { @@ -1753,6 +1812,8 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, break; case RTM_DELTCLASS: err = tclass_del_notify(net, cops, skb, n, q, cl); + /* Unbind the class with flilters with 0 */ + tc_bind_tclass(q, portid, clid, 0); goto out; case RTM_GETTCLASS: err = tclass_notify(net, skb, n, q, cl, RTM_NEWTCLASS); @@ -1767,9 +1828,12 @@ static int tc_ctl_tclass(struct sk_buff *skb, struct nlmsghdr *n, err = -EOPNOTSUPP; if (cops->change) err = cops->change(q, clid, portid, tca, &new_cl); - if (err == 0) + if (err == 0) { tclass_notify(net, skb, n, q, new_cl, RTM_NEWTCLASS); - + /* We just create a new class, need to do reverse binding. */ + if (cl != new_cl) + tc_bind_tclass(q, portid, clid, new_cl); + } out: return err; } -- cgit v1.2.3 From e3cfddd577e763496c25ddb855cfff48b1174b63 Mon Sep 17 00:00:00 2001 From: Roopa Prabhu Date: Wed, 30 Aug 2017 22:18:13 -0700 Subject: bridge: add tracepoint in br_fdb_update This extends bridge fdb table tracepoints to also cover learned fdb entries in the br_fdb_update path. Note that unlike other tracepoints I have moved this to when the fdb is modified because this is in the datapath and can generate a lot of noise in the trace output. br_fdb_update is also called from added_by_user context in the NTF_USE case which is already traced ..hence the !added_by_user check. Signed-off-by: Roopa Prabhu Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- include/trace/events/bridge.h | 31 +++++++++++++++++++++++++++++++ net/bridge/br_fdb.c | 5 ++++- net/core/net-traces.c | 1 + 3 files changed, 36 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/include/trace/events/bridge.h b/include/trace/events/bridge.h index 0f1cde00af11..1bee3e7fdf32 100644 --- a/include/trace/events/bridge.h +++ b/include/trace/events/bridge.h @@ -92,6 +92,37 @@ TRACE_EVENT(fdb_delete, __entry->addr[4], __entry->addr[5], __entry->vid) ); +TRACE_EVENT(br_fdb_update, + + TP_PROTO(struct net_bridge *br, struct net_bridge_port *source, + const unsigned char *addr, u16 vid, bool added_by_user), + + TP_ARGS(br, source, addr, vid, added_by_user), + + TP_STRUCT__entry( + __string(br_dev, br->dev->name) + __string(dev, source->dev->name) + __array(unsigned char, addr, ETH_ALEN) + __field(u16, vid) + __field(bool, added_by_user) + ), + + TP_fast_assign( + __assign_str(br_dev, br->dev->name); + __assign_str(dev, source->dev->name); + memcpy(__entry->addr, addr, ETH_ALEN); + __entry->vid = vid; + __entry->added_by_user = added_by_user; + ), + + TP_printk("br_dev %s source %s addr %02x:%02x:%02x:%02x:%02x:%02x vid %u added_by_user %d", + __get_str(br_dev), __get_str(dev), __entry->addr[0], + __entry->addr[1], __entry->addr[2], __entry->addr[3], + __entry->addr[4], __entry->addr[5], __entry->vid, + __entry->added_by_user) +); + + #endif /* _TRACE_BRIDGE_H */ /* This part must be outside protection */ diff --git a/net/bridge/br_fdb.c b/net/bridge/br_fdb.c index be5e1da6d824..4ea5c8bbe286 100644 --- a/net/bridge/br_fdb.c +++ b/net/bridge/br_fdb.c @@ -583,8 +583,10 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, fdb->updated = now; if (unlikely(added_by_user)) fdb->added_by_user = 1; - if (unlikely(fdb_modified)) + if (unlikely(fdb_modified)) { + trace_br_fdb_update(br, source, addr, vid, added_by_user); fdb_notify(br, fdb, RTM_NEWNEIGH); + } } } else { spin_lock(&br->hash_lock); @@ -593,6 +595,7 @@ void br_fdb_update(struct net_bridge *br, struct net_bridge_port *source, if (fdb) { if (unlikely(added_by_user)) fdb->added_by_user = 1; + trace_br_fdb_update(br, source, addr, vid, added_by_user); fdb_notify(br, fdb, RTM_NEWNEIGH); } } diff --git a/net/core/net-traces.c b/net/core/net-traces.c index 4a0292c97070..1132820c8e62 100644 --- a/net/core/net-traces.c +++ b/net/core/net-traces.c @@ -42,6 +42,7 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(fib6_table_lookup); EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_add); EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_external_learn_add); EXPORT_TRACEPOINT_SYMBOL_GPL(fdb_delete); +EXPORT_TRACEPOINT_SYMBOL_GPL(br_fdb_update); #endif EXPORT_TRACEPOINT_SYMBOL_GPL(kfree_skb); -- cgit v1.2.3 From 323fbd0edf3ffe8d9d71a651c26b97a14a3c9d0e Mon Sep 17 00:00:00 2001 From: Andrii Date: Thu, 31 Aug 2017 08:28:01 +0300 Subject: net: dccp: Add handling of IPV6_PKTOPTIONS to dccp_v6_do_rcv() Add handling of IPV6_PKTOPTIONS to dccp_v6_do_rcv() in net/dccp/ipv6.c, similar to the handling in net/ipv6/tcp_ipv6.c Signed-off-by: Andrii Vladyka Signed-off-by: David S. Miller --- net/dccp/ipv6.c | 47 +++++++++++++++++++++++++++++++++++------------ 1 file changed, 35 insertions(+), 12 deletions(-) (limited to 'net') diff --git a/net/dccp/ipv6.c b/net/dccp/ipv6.c index 47a7b59b355e..5df7857fc0f3 100644 --- a/net/dccp/ipv6.c +++ b/net/dccp/ipv6.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -30,6 +31,7 @@ #include #include #include +#include #include "dccp.h" #include "ipv6.h" @@ -597,19 +599,13 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) --ANK (980728) */ if (np->rxopt.all) - /* - * FIXME: Add handling of IPV6_PKTOPTIONS skb. See the comments below - * (wrt ipv6_pktopions) and net/ipv6/tcp_ipv6.c for an example. - */ opt_skb = skb_clone(skb, GFP_ATOMIC); if (sk->sk_state == DCCP_OPEN) { /* Fast path */ if (dccp_rcv_established(sk, skb, dccp_hdr(skb), skb->len)) goto reset; - if (opt_skb) { - /* XXX This is where we would goto ipv6_pktoptions. */ - __kfree_skb(opt_skb); - } + if (opt_skb) + goto ipv6_pktoptions; return 0; } @@ -640,10 +636,8 @@ static int dccp_v6_do_rcv(struct sock *sk, struct sk_buff *skb) if (dccp_rcv_state_process(sk, skb, dccp_hdr(skb), skb->len)) goto reset; - if (opt_skb) { - /* XXX This is where we would goto ipv6_pktoptions. */ - __kfree_skb(opt_skb); - } + if (opt_skb) + goto ipv6_pktoptions; return 0; reset: @@ -653,6 +647,35 @@ discard: __kfree_skb(opt_skb); kfree_skb(skb); return 0; + +/* Handling IPV6_PKTOPTIONS skb the similar + * way it's done for net/ipv6/tcp_ipv6.c + */ +ipv6_pktoptions: + if (!((1 << sk->sk_state) & (DCCPF_CLOSED | DCCPF_LISTEN))) { + if (np->rxopt.bits.rxinfo || np->rxopt.bits.rxoinfo) + np->mcast_oif = inet6_iif(opt_skb); + if (np->rxopt.bits.rxhlim || np->rxopt.bits.rxohlim) + np->mcast_hops = ipv6_hdr(opt_skb)->hop_limit; + if (np->rxopt.bits.rxflow || np->rxopt.bits.rxtclass) + np->rcv_flowinfo = ip6_flowinfo(ipv6_hdr(opt_skb)); + if (np->repflow) + np->flow_label = ip6_flowlabel(ipv6_hdr(opt_skb)); + if (ipv6_opt_accepted(sk, opt_skb, + &DCCP_SKB_CB(opt_skb)->header.h6)) { + skb_set_owner_r(opt_skb, sk); + memmove(IP6CB(opt_skb), + &DCCP_SKB_CB(opt_skb)->header.h6, + sizeof(struct inet6_skb_parm)); + opt_skb = xchg(&np->pktoptions, opt_skb); + } else { + __kfree_skb(opt_skb); + opt_skb = xchg(&np->pktoptions, NULL); + } + } + + kfree_skb(opt_skb); + return 0; } static int dccp_v6_rcv(struct sk_buff *skb) -- cgit v1.2.3 From 1797f5b3cf0b3a73c42b89f7a8fd897417373730 Mon Sep 17 00:00:00 2001 From: Arkadi Sharshevsky Date: Thu, 31 Aug 2017 17:59:12 +0200 Subject: devlink: Add IPv6 header for dpipe This will be used by the IPv6 host table which will be introduced in the following patches. The fields in the header are added per-use. This header is global and can be reused by many drivers. Signed-off-by: Arkadi Sharshevsky Signed-off-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/devlink.h | 1 + include/uapi/linux/devlink.h | 5 +++++ net/core/devlink.c | 17 +++++++++++++++++ 3 files changed, 23 insertions(+) (limited to 'net') diff --git a/include/net/devlink.h b/include/net/devlink.h index aaf7178127a2..b9654e133599 100644 --- a/include/net/devlink.h +++ b/include/net/devlink.h @@ -330,6 +330,7 @@ int devlink_dpipe_match_put(struct sk_buff *skb, struct devlink_dpipe_match *match); extern struct devlink_dpipe_header devlink_dpipe_header_ethernet; extern struct devlink_dpipe_header devlink_dpipe_header_ipv4; +extern struct devlink_dpipe_header devlink_dpipe_header_ipv6; #else diff --git a/include/uapi/linux/devlink.h b/include/uapi/linux/devlink.h index 6c172548589d..0cbca96c66b9 100644 --- a/include/uapi/linux/devlink.h +++ b/include/uapi/linux/devlink.h @@ -234,9 +234,14 @@ enum devlink_dpipe_field_ipv4_id { DEVLINK_DPIPE_FIELD_IPV4_DST_IP, }; +enum devlink_dpipe_field_ipv6_id { + DEVLINK_DPIPE_FIELD_IPV6_DST_IP, +}; + enum devlink_dpipe_header_id { DEVLINK_DPIPE_HEADER_ETHERNET, DEVLINK_DPIPE_HEADER_IPV4, + DEVLINK_DPIPE_HEADER_IPV6, }; #endif /* _UAPI_LINUX_DEVLINK_H_ */ diff --git a/net/core/devlink.c b/net/core/devlink.c index cbc4b0461b0f..7d430c1d9c3e 100644 --- a/net/core/devlink.c +++ b/net/core/devlink.c @@ -63,6 +63,23 @@ struct devlink_dpipe_header devlink_dpipe_header_ipv4 = { }; EXPORT_SYMBOL(devlink_dpipe_header_ipv4); +static struct devlink_dpipe_field devlink_dpipe_fields_ipv6[] = { + { + .name = "destination ip", + .id = DEVLINK_DPIPE_FIELD_IPV6_DST_IP, + .bitwidth = 128, + }, +}; + +struct devlink_dpipe_header devlink_dpipe_header_ipv6 = { + .name = "ipv6", + .id = DEVLINK_DPIPE_HEADER_IPV6, + .fields = devlink_dpipe_fields_ipv6, + .fields_count = ARRAY_SIZE(devlink_dpipe_fields_ipv6), + .global = true, +}; +EXPORT_SYMBOL(devlink_dpipe_header_ipv6); + EXPORT_TRACEPOINT_SYMBOL_GPL(devlink_hwmsg); static LIST_HEAD(devlink_list); -- cgit v1.2.3 From 482dca939fb7ee35ba20b944b4c2476133dbf0df Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 31 Aug 2017 15:05:44 -0700 Subject: bpf: Add mark and priority to sock options that can be set Add socket mark and priority to fields that can be set by ebpf program when a socket is created. Signed-off-by: David Ahern Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- include/uapi/linux/bpf.h | 2 ++ net/core/filter.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'net') diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h index 08c206a863e1..ba848b761cfb 100644 --- a/include/uapi/linux/bpf.h +++ b/include/uapi/linux/bpf.h @@ -758,6 +758,8 @@ struct bpf_sock { __u32 family; __u32 type; __u32 protocol; + __u32 mark; + __u32 priority; }; #define XDP_PACKET_HEADROOM 256 diff --git a/net/core/filter.c b/net/core/filter.c index c6a37fe0285b..f51b9690adf3 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3455,6 +3455,10 @@ static bool sock_filter_is_valid_access(int off, int size, switch (off) { case offsetof(struct bpf_sock, bound_dev_if): break; + case offsetof(struct bpf_sock, mark): + break; + case offsetof(struct bpf_sock, priority): + break; default: return false; } @@ -3958,6 +3962,28 @@ static u32 sock_filter_convert_ctx_access(enum bpf_access_type type, offsetof(struct sock, sk_bound_dev_if)); break; + case offsetof(struct bpf_sock, mark): + BUILD_BUG_ON(FIELD_SIZEOF(struct sock, sk_mark) != 4); + + if (type == BPF_WRITE) + *insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, si->src_reg, + offsetof(struct sock, sk_mark)); + else + *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, + offsetof(struct sock, sk_mark)); + break; + + case offsetof(struct bpf_sock, priority): + BUILD_BUG_ON(FIELD_SIZEOF(struct sock, sk_priority) != 4); + + if (type == BPF_WRITE) + *insn++ = BPF_STX_MEM(BPF_W, si->dst_reg, si->src_reg, + offsetof(struct sock, sk_priority)); + else + *insn++ = BPF_LDX_MEM(BPF_W, si->dst_reg, si->src_reg, + offsetof(struct sock, sk_priority)); + break; + case offsetof(struct bpf_sock, family): BUILD_BUG_ON(FIELD_SIZEOF(struct sock, sk_family) != 2); -- cgit v1.2.3 From ae2cf1c466bea99e927899707aed022942505d27 Mon Sep 17 00:00:00 2001 From: David Ahern Date: Thu, 31 Aug 2017 15:05:45 -0700 Subject: bpf: Allow cgroup sock filters to use get_current_uid_gid helper Allow BPF programs run on sock create to use the get_current_uid_gid helper. IPv4 and IPv6 sockets are created in a process context so there is always a valid uid/gid Signed-off-by: David Ahern Acked-by: Alexei Starovoitov Acked-by: Daniel Borkmann Signed-off-by: David S. Miller --- net/core/filter.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/core/filter.c b/net/core/filter.c index f51b9690adf3..9dad3e7e2e10 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3149,6 +3149,20 @@ bpf_base_func_proto(enum bpf_func_id func_id) } } +static const struct bpf_func_proto * +sock_filter_func_proto(enum bpf_func_id func_id) +{ + switch (func_id) { + /* inet and inet6 sockets are created in a process + * context so there is always a valid uid/gid + */ + case BPF_FUNC_get_current_uid_gid: + return &bpf_get_current_uid_gid_proto; + default: + return bpf_base_func_proto(func_id); + } +} + static const struct bpf_func_proto * sk_filter_func_proto(enum bpf_func_id func_id) { @@ -4233,7 +4247,7 @@ const struct bpf_verifier_ops lwt_xmit_prog_ops = { }; const struct bpf_verifier_ops cg_sock_prog_ops = { - .get_func_proto = bpf_base_func_proto, + .get_func_proto = sock_filter_func_proto, .is_valid_access = sock_filter_is_valid_access, .convert_ctx_access = sock_filter_convert_ctx_access, }; -- cgit v1.2.3 From 9df59055ed8e2f817149e786b2a52bc17832d84e Mon Sep 17 00:00:00 2001 From: David Ahern Date: Fri, 1 Sep 2017 08:18:07 -0700 Subject: bpf: Collapse offset checks in sock_filter_is_valid_access Make sock_filter_is_valid_access consistent with other is_valid_access helpers. Requested-by: Daniel Borkmann Signed-off-by: David Ahern Acked-by: Daniel Borkmann Acked-by: Alexei Starovoitov Signed-off-by: David S. Miller --- net/core/filter.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'net') diff --git a/net/core/filter.c b/net/core/filter.c index 9dad3e7e2e10..f9add024d92f 100644 --- a/net/core/filter.c +++ b/net/core/filter.c @@ -3468,9 +3468,7 @@ static bool sock_filter_is_valid_access(int off, int size, if (type == BPF_WRITE) { switch (off) { case offsetof(struct bpf_sock, bound_dev_if): - break; case offsetof(struct bpf_sock, mark): - break; case offsetof(struct bpf_sock, priority): break; default: -- cgit v1.2.3 From 65bce46298d064dff9db1282e17bb26602715819 Mon Sep 17 00:00:00 2001 From: Loic Poulain Date: Fri, 1 Sep 2017 13:41:17 -0700 Subject: Bluetooth: make baswap src const Signed-off-by: Loic Poulain Signed-off-by: Bjorn Andersson Signed-off-by: Marcel Holtmann --- include/net/bluetooth/bluetooth.h | 2 +- net/bluetooth/lib.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/net/bluetooth/bluetooth.h b/include/net/bluetooth/bluetooth.h index 01487192f628..020142bb9735 100644 --- a/include/net/bluetooth/bluetooth.h +++ b/include/net/bluetooth/bluetooth.h @@ -233,7 +233,7 @@ static inline void bacpy(bdaddr_t *dst, const bdaddr_t *src) memcpy(dst, src, sizeof(bdaddr_t)); } -void baswap(bdaddr_t *dst, bdaddr_t *src); +void baswap(bdaddr_t *dst, const bdaddr_t *src); /* Common socket structures and functions */ diff --git a/net/bluetooth/lib.c b/net/bluetooth/lib.c index aa4cf64e32a6..63e65d9b4b24 100644 --- a/net/bluetooth/lib.c +++ b/net/bluetooth/lib.c @@ -30,10 +30,10 @@ #include -void baswap(bdaddr_t *dst, bdaddr_t *src) +void baswap(bdaddr_t *dst, const bdaddr_t *src) { - unsigned char *d = (unsigned char *) dst; - unsigned char *s = (unsigned char *) src; + const unsigned char *s = (const unsigned char *)src; + unsigned char *d = (unsigned char *)dst; unsigned int i; for (i = 0; i < 6; i++) -- cgit v1.2.3 From 4cc5b44b29a9de9b3f841efedaa3f769066c63cc Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Fri, 1 Sep 2017 14:03:32 -0700 Subject: inetpeer: fix RCU lookup() Excess of seafood or something happened while I cooked the commit adding RB tree to inetpeer. Of course, RCU rules need to be respected or bad things can happen. In this particular loop, we need to read *pp once per iteration, not twice. Fixes: b145425f269a ("inetpeer: remove AVL implementation in favor of RB tree") Reported-by: John Sperbeck Signed-off-by: Eric Dumazet Signed-off-by: David S. Miller --- net/ipv4/inetpeer.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c index 337ad41bb80a..e7eb590c86ce 100644 --- a/net/ipv4/inetpeer.c +++ b/net/ipv4/inetpeer.c @@ -102,15 +102,18 @@ static struct inet_peer *lookup(const struct inetpeer_addr *daddr, struct rb_node **parent_p, struct rb_node ***pp_p) { - struct rb_node **pp, *parent; + struct rb_node **pp, *parent, *next; struct inet_peer *p; pp = &base->rb_root.rb_node; parent = NULL; - while (*pp) { + while (1) { int cmp; - parent = rcu_dereference_raw(*pp); + next = rcu_dereference_raw(*pp); + if (!next) + break; + parent = next; p = rb_entry(parent, struct inet_peer, rb_node); cmp = inetpeer_addr_cmp(daddr, &p->daddr); if (cmp == 0) { -- cgit v1.2.3 From 6391c4f67ad1643a17b54376e381038b9e660a0f Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Thu, 31 Aug 2017 16:18:15 +0200 Subject: ipv6: sr: Use ARRAY_SIZE macro Grepping for "sizeof\(.+\) / sizeof\(" found this as one of the first candidates. Maybe a coccinelle can catch all of those. Signed-off-by: Thomas Meyer Signed-off-by: David S. Miller --- net/ipv6/seg6_hmac.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/ipv6/seg6_hmac.c b/net/ipv6/seg6_hmac.c index f950cb53d5e3..33fb35cbfac1 100644 --- a/net/ipv6/seg6_hmac.c +++ b/net/ipv6/seg6_hmac.c @@ -12,6 +12,7 @@ */ #include +#include #include #include #include @@ -110,7 +111,7 @@ static struct seg6_hmac_algo *__hmac_get_algo(u8 alg_id) struct seg6_hmac_algo *algo; int i, alg_count; - alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo); + alg_count = ARRAY_SIZE(hmac_algos); for (i = 0; i < alg_count; i++) { algo = &hmac_algos[i]; if (algo->alg_id == alg_id) @@ -360,7 +361,7 @@ static int seg6_hmac_init_algo(void) struct shash_desc *shash; int i, alg_count, cpu; - alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo); + alg_count = ARRAY_SIZE(hmac_algos); for (i = 0; i < alg_count; i++) { struct crypto_shash **p_tfm; @@ -421,7 +422,7 @@ void seg6_hmac_exit(void) struct seg6_hmac_algo *algo = NULL; int i, alg_count, cpu; - alg_count = sizeof(hmac_algos) / sizeof(struct seg6_hmac_algo); + alg_count = ARRAY_SIZE(hmac_algos); for (i = 0; i < alg_count; i++) { algo = &hmac_algos[i]; for_each_possible_cpu(cpu) { -- cgit v1.2.3 From b37e88407c1d78f157778d73427cd7e9e1d6369d Mon Sep 17 00:00:00 2001 From: Ivan Delalande Date: Thu, 31 Aug 2017 09:59:38 -0700 Subject: inet_diag: allow protocols to provide additional data Extend inet_diag_handler to allow individual protocols to report additional data on INET_DIAG_INFO through idiag_get_aux. The size can be dynamic and is computed by idiag_get_aux_size. Signed-off-by: Ivan Delalande Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/linux/inet_diag.h | 7 +++++++ net/ipv4/inet_diag.c | 22 ++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) (limited to 'net') diff --git a/include/linux/inet_diag.h b/include/linux/inet_diag.h index 65da430e260f..ee251c585854 100644 --- a/include/linux/inet_diag.h +++ b/include/linux/inet_diag.h @@ -25,6 +25,13 @@ struct inet_diag_handler { struct inet_diag_msg *r, void *info); + int (*idiag_get_aux)(struct sock *sk, + bool net_admin, + struct sk_buff *skb); + + size_t (*idiag_get_aux_size)(struct sock *sk, + bool net_admin); + int (*destroy)(struct sk_buff *in_skb, const struct inet_diag_req_v2 *req); diff --git a/net/ipv4/inet_diag.c b/net/ipv4/inet_diag.c index 67325d5832d7..c9c35b61a027 100644 --- a/net/ipv4/inet_diag.c +++ b/net/ipv4/inet_diag.c @@ -93,8 +93,17 @@ void inet_diag_msg_common_fill(struct inet_diag_msg *r, struct sock *sk) } EXPORT_SYMBOL_GPL(inet_diag_msg_common_fill); -static size_t inet_sk_attr_size(void) +static size_t inet_sk_attr_size(struct sock *sk, + const struct inet_diag_req_v2 *req, + bool net_admin) { + const struct inet_diag_handler *handler; + size_t aux = 0; + + handler = inet_diag_table[req->sdiag_protocol]; + if (handler && handler->idiag_get_aux_size) + aux = handler->idiag_get_aux_size(sk, net_admin); + return nla_total_size(sizeof(struct tcp_info)) + nla_total_size(1) /* INET_DIAG_SHUTDOWN */ + nla_total_size(1) /* INET_DIAG_TOS */ @@ -105,6 +114,7 @@ static size_t inet_sk_attr_size(void) + nla_total_size(SK_MEMINFO_VARS * sizeof(u32)) + nla_total_size(TCP_CA_NAME_MAX) + nla_total_size(sizeof(struct tcpvegas_info)) + + aux + 64; } @@ -260,6 +270,10 @@ int inet_sk_diag_fill(struct sock *sk, struct inet_connection_sock *icsk, handler->idiag_get_info(sk, r, info); + if (ext & (1 << (INET_DIAG_INFO - 1)) && handler->idiag_get_aux) + if (handler->idiag_get_aux(sk, net_admin, skb) < 0) + goto errout; + if (sk->sk_state < TCP_TIME_WAIT) { union tcp_cc_info info; size_t sz = 0; @@ -449,6 +463,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, const struct nlmsghdr *nlh, const struct inet_diag_req_v2 *req) { + bool net_admin = netlink_net_capable(in_skb, CAP_NET_ADMIN); struct net *net = sock_net(in_skb->sk); struct sk_buff *rep; struct sock *sk; @@ -458,7 +473,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, if (IS_ERR(sk)) return PTR_ERR(sk); - rep = nlmsg_new(inet_sk_attr_size(), GFP_KERNEL); + rep = nlmsg_new(inet_sk_attr_size(sk, req, net_admin), GFP_KERNEL); if (!rep) { err = -ENOMEM; goto out; @@ -467,8 +482,7 @@ int inet_diag_dump_one_icsk(struct inet_hashinfo *hashinfo, err = sk_diag_fill(sk, rep, req, sk_user_ns(NETLINK_CB(in_skb).sk), NETLINK_CB(in_skb).portid, - nlh->nlmsg_seq, 0, nlh, - netlink_net_capable(in_skb, CAP_NET_ADMIN)); + nlh->nlmsg_seq, 0, nlh, net_admin); if (err < 0) { WARN_ON(err == -EMSGSIZE); nlmsg_free(rep); -- cgit v1.2.3 From c03fa9bcacd9ac04595cc13f34f3445f0a5ecf13 Mon Sep 17 00:00:00 2001 From: Ivan Delalande Date: Thu, 31 Aug 2017 09:59:39 -0700 Subject: tcp_diag: report TCP MD5 signing keys and addresses Report TCP MD5 (RFC2385) signing keys, addresses and address prefixes to processes with CAP_NET_ADMIN requesting INET_DIAG_INFO. Currently it is not possible to retrieve these from the kernel once they have been configured on sockets. Signed-off-by: Ivan Delalande Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- include/uapi/linux/inet_diag.h | 1 + include/uapi/linux/tcp.h | 9 ++++ net/ipv4/tcp_diag.c | 109 ++++++++++++++++++++++++++++++++++++++--- 3 files changed, 113 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/inet_diag.h b/include/uapi/linux/inet_diag.h index 678496897a68..f52ff62bfabe 100644 --- a/include/uapi/linux/inet_diag.h +++ b/include/uapi/linux/inet_diag.h @@ -143,6 +143,7 @@ enum { INET_DIAG_MARK, INET_DIAG_BBRINFO, INET_DIAG_CLASS_ID, + INET_DIAG_MD5SIG, __INET_DIAG_MAX, }; diff --git a/include/uapi/linux/tcp.h b/include/uapi/linux/tcp.h index 030e594bab45..15c25eccab2b 100644 --- a/include/uapi/linux/tcp.h +++ b/include/uapi/linux/tcp.h @@ -256,4 +256,13 @@ struct tcp_md5sig { __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; /* key (binary) */ }; +/* INET_DIAG_MD5SIG */ +struct tcp_diag_md5sig { + __u8 tcpm_family; + __u8 tcpm_prefixlen; + __u16 tcpm_keylen; + __be32 tcpm_addr[4]; + __u8 tcpm_key[TCP_MD5SIG_MAXKEYLEN]; +}; + #endif /* _UAPI_LINUX_TCP_H */ diff --git a/net/ipv4/tcp_diag.c b/net/ipv4/tcp_diag.c index a748c74aa8b7..abbf0edcf6c2 100644 --- a/net/ipv4/tcp_diag.c +++ b/net/ipv4/tcp_diag.c @@ -16,6 +16,7 @@ #include +#include #include static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, @@ -36,6 +37,100 @@ static void tcp_diag_get_info(struct sock *sk, struct inet_diag_msg *r, tcp_get_info(sk, info); } +#ifdef CONFIG_TCP_MD5SIG +static void tcp_diag_md5sig_fill(struct tcp_diag_md5sig *info, + const struct tcp_md5sig_key *key) +{ + info->tcpm_family = key->family; + info->tcpm_prefixlen = key->prefixlen; + info->tcpm_keylen = key->keylen; + memcpy(info->tcpm_key, key->key, key->keylen); + + if (key->family == AF_INET) + info->tcpm_addr[0] = key->addr.a4.s_addr; + #if IS_ENABLED(CONFIG_IPV6) + else if (key->family == AF_INET6) + memcpy(&info->tcpm_addr, &key->addr.a6, + sizeof(info->tcpm_addr)); + #endif +} + +static int tcp_diag_put_md5sig(struct sk_buff *skb, + const struct tcp_md5sig_info *md5sig) +{ + const struct tcp_md5sig_key *key; + struct tcp_diag_md5sig *info; + struct nlattr *attr; + int md5sig_count = 0; + + hlist_for_each_entry_rcu(key, &md5sig->head, node) + md5sig_count++; + if (md5sig_count == 0) + return 0; + + attr = nla_reserve(skb, INET_DIAG_MD5SIG, + md5sig_count * sizeof(struct tcp_diag_md5sig)); + if (!attr) + return -EMSGSIZE; + + info = nla_data(attr); + memset(info, 0, md5sig_count * sizeof(struct tcp_diag_md5sig)); + hlist_for_each_entry_rcu(key, &md5sig->head, node) { + tcp_diag_md5sig_fill(info++, key); + if (--md5sig_count == 0) + break; + } + + return 0; +} +#endif + +static int tcp_diag_get_aux(struct sock *sk, bool net_admin, + struct sk_buff *skb) +{ +#ifdef CONFIG_TCP_MD5SIG + if (net_admin) { + struct tcp_md5sig_info *md5sig; + int err = 0; + + rcu_read_lock(); + md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info); + if (md5sig) + err = tcp_diag_put_md5sig(skb, md5sig); + rcu_read_unlock(); + if (err < 0) + return err; + } +#endif + + return 0; +} + +static size_t tcp_diag_get_aux_size(struct sock *sk, bool net_admin) +{ + size_t size = 0; + +#ifdef CONFIG_TCP_MD5SIG + if (net_admin && sk_fullsock(sk)) { + const struct tcp_md5sig_info *md5sig; + const struct tcp_md5sig_key *key; + size_t md5sig_count = 0; + + rcu_read_lock(); + md5sig = rcu_dereference(tcp_sk(sk)->md5sig_info); + if (md5sig) { + hlist_for_each_entry_rcu(key, &md5sig->head, node) + md5sig_count++; + } + rcu_read_unlock(); + size += nla_total_size(md5sig_count * + sizeof(struct tcp_diag_md5sig)); + } +#endif + + return size; +} + static void tcp_diag_dump(struct sk_buff *skb, struct netlink_callback *cb, const struct inet_diag_req_v2 *r, struct nlattr *bc) { @@ -68,13 +163,15 @@ static int tcp_diag_destroy(struct sk_buff *in_skb, #endif static const struct inet_diag_handler tcp_diag_handler = { - .dump = tcp_diag_dump, - .dump_one = tcp_diag_dump_one, - .idiag_get_info = tcp_diag_get_info, - .idiag_type = IPPROTO_TCP, - .idiag_info_size = sizeof(struct tcp_info), + .dump = tcp_diag_dump, + .dump_one = tcp_diag_dump_one, + .idiag_get_info = tcp_diag_get_info, + .idiag_get_aux = tcp_diag_get_aux, + .idiag_get_aux_size = tcp_diag_get_aux_size, + .idiag_type = IPPROTO_TCP, + .idiag_info_size = sizeof(struct tcp_info), #ifdef CONFIG_INET_DIAG_DESTROY - .destroy = tcp_diag_destroy, + .destroy = tcp_diag_destroy, #endif }; -- cgit v1.2.3 From db5bce32fbe19f0c7482fb5a40a33178bbe7b11b Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 31 Aug 2017 16:48:21 -0700 Subject: net: prepare (struct ubuf_info)->refcnt conversion In order to convert this atomic_t refcnt to refcount_t, we need to init the refcount to one to not trigger a 0 -> 1 transition. This also removes one atomic operation in fast path. v2: removed dead code in sock_zerocopy_put_abort() as suggested by Willem. Signed-off-by: Eric Dumazet Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- net/core/skbuff.c | 10 ++-------- net/ipv4/tcp.c | 2 -- 2 files changed, 2 insertions(+), 10 deletions(-) (limited to 'net') diff --git a/net/core/skbuff.c b/net/core/skbuff.c index 246ca1c81715..a003f953a0a6 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -963,7 +963,7 @@ struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size) uarg->len = 1; uarg->bytelen = size; uarg->zerocopy = 1; - atomic_set(&uarg->refcnt, 0); + atomic_set(&uarg->refcnt, 1); sock_hold(sk); return uarg; @@ -1005,6 +1005,7 @@ struct ubuf_info *sock_zerocopy_realloc(struct sock *sk, size_t size, uarg->len++; uarg->bytelen = bytelen; atomic_set(&sk->sk_zckey, ++next); + sock_zerocopy_get(uarg); return uarg; } } @@ -1102,13 +1103,6 @@ void sock_zerocopy_put_abort(struct ubuf_info *uarg) atomic_dec(&sk->sk_zckey); uarg->len--; - /* sock_zerocopy_put expects a ref. Most sockets take one per - * skb, which is zero on abort. tcp_sendmsg holds one extra, to - * avoid an skb send inside the main loop triggering uarg free. - */ - if (sk->sk_type != SOCK_STREAM) - atomic_inc(&uarg->refcnt); - sock_zerocopy_put(uarg); } } diff --git a/net/ipv4/tcp.c b/net/ipv4/tcp.c index 7a3d84375836..5091402720ab 100644 --- a/net/ipv4/tcp.c +++ b/net/ipv4/tcp.c @@ -1190,8 +1190,6 @@ int tcp_sendmsg_locked(struct sock *sk, struct msghdr *msg, size_t size) goto out_err; } - /* skb may be freed in main loop, keep extra ref on uarg */ - sock_zerocopy_get(uarg); if (!(sk_check_csum_caps(sk) && sk->sk_route_caps & NETIF_F_SG)) uarg->zerocopy = 0; } -- cgit v1.2.3 From c1d1b437816f0afa99202be3cb650c9d174667bc Mon Sep 17 00:00:00 2001 From: Eric Dumazet Date: Thu, 31 Aug 2017 16:48:22 -0700 Subject: net: convert (struct ubuf_info)->refcnt to refcount_t refcount_t type and corresponding API should be used instead of atomic_t when the variable is used as a reference counter. This allows to avoid accidental refcounter overflows that might lead to use-after-free situations. v2: added the change in drivers/vhost/net.c as spotted by Willem. Signed-off-by: Eric Dumazet Acked-by: Willem de Bruijn Signed-off-by: David S. Miller --- drivers/vhost/net.c | 2 +- include/linux/skbuff.h | 5 +++-- net/core/skbuff.c | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) (limited to 'net') diff --git a/drivers/vhost/net.c b/drivers/vhost/net.c index ba08b78ed630..8d2bcae53a2e 100644 --- a/drivers/vhost/net.c +++ b/drivers/vhost/net.c @@ -533,7 +533,7 @@ static void handle_tx(struct vhost_net *net) ubuf->callback = vhost_zerocopy_callback; ubuf->ctx = nvq->ubufs; ubuf->desc = nvq->upend_idx; - atomic_set(&ubuf->refcnt, 1); + refcount_set(&ubuf->refcnt, 1); msg.msg_control = ubuf; msg.msg_controllen = sizeof(ubuf); ubufs = nvq->ubufs; diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index f93cc01064cb..f751f3b93039 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -22,6 +22,7 @@ #include #include #include +#include #include #include @@ -456,7 +457,7 @@ struct ubuf_info { u32 bytelen; }; }; - atomic_t refcnt; + refcount_t refcnt; struct mmpin { struct user_struct *user; @@ -472,7 +473,7 @@ struct ubuf_info *sock_zerocopy_realloc(struct sock *sk, size_t size, static inline void sock_zerocopy_get(struct ubuf_info *uarg) { - atomic_inc(&uarg->refcnt); + refcount_inc(&uarg->refcnt); } void sock_zerocopy_put(struct ubuf_info *uarg); diff --git a/net/core/skbuff.c b/net/core/skbuff.c index a003f953a0a6..68065d7d383f 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -963,7 +963,7 @@ struct ubuf_info *sock_zerocopy_alloc(struct sock *sk, size_t size) uarg->len = 1; uarg->bytelen = size; uarg->zerocopy = 1; - atomic_set(&uarg->refcnt, 1); + refcount_set(&uarg->refcnt, 1); sock_hold(sk); return uarg; @@ -1086,7 +1086,7 @@ EXPORT_SYMBOL_GPL(sock_zerocopy_callback); void sock_zerocopy_put(struct ubuf_info *uarg) { - if (uarg && atomic_dec_and_test(&uarg->refcnt)) { + if (uarg && refcount_dec_and_test(&uarg->refcnt)) { if (uarg->callback) uarg->callback(uarg, uarg->zerocopy); else @@ -1483,7 +1483,7 @@ int pskb_expand_head(struct sk_buff *skb, int nhead, int ntail, if (skb_orphan_frags(skb, gfp_mask)) goto nofrags; if (skb_zcopy(skb)) - atomic_inc(&skb_uarg(skb)->refcnt); + refcount_inc(&skb_uarg(skb)->refcnt); for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) skb_frag_ref(skb, i); -- cgit v1.2.3 From 864150dfa31dceab6ec5ca4579a2d35ede985cb7 Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Fri, 1 Sep 2017 12:15:17 +0300 Subject: net: Add module reference to FIB notifiers When a listener registers to the FIB notification chain it receives a dump of the FIB entries and rules from existing address families by invoking their dump operations. While we call into these modules we need to make sure they aren't removed. Do that by increasing their reference count before invoking their dump operations and decrease it afterwards. Fixes: 04b1d4e50e82 ("net: core: Make the FIB notification chain generic") Signed-off-by: Ido Schimmel Reviewed-by: Jiri Pirko Signed-off-by: David S. Miller --- include/net/fib_notifier.h | 2 ++ net/core/fib_notifier.c | 13 +++++++++++-- net/ipv4/fib_notifier.c | 2 ++ net/ipv6/fib6_notifier.c | 2 ++ 4 files changed, 17 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/net/fib_notifier.h b/include/net/fib_notifier.h index 241475224f74..669b9716dc7a 100644 --- a/include/net/fib_notifier.h +++ b/include/net/fib_notifier.h @@ -2,6 +2,7 @@ #define __NET_FIB_NOTIFIER_H #include +#include #include #include @@ -26,6 +27,7 @@ struct fib_notifier_ops { struct list_head list; unsigned int (*fib_seq_read)(struct net *net); int (*fib_dump)(struct net *net, struct notifier_block *nb); + struct module *owner; struct rcu_head rcu; }; diff --git a/net/core/fib_notifier.c b/net/core/fib_notifier.c index 292aab83702f..4fc202dbdfb6 100644 --- a/net/core/fib_notifier.c +++ b/net/core/fib_notifier.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -33,8 +34,12 @@ static unsigned int fib_seq_sum(void) rtnl_lock(); for_each_net(net) { - list_for_each_entry(ops, &net->fib_notifier_ops, list) + list_for_each_entry(ops, &net->fib_notifier_ops, list) { + if (!try_module_get(ops->owner)) + continue; fib_seq += ops->fib_seq_read(net); + module_put(ops->owner); + } } rtnl_unlock(); @@ -46,8 +51,12 @@ static int fib_net_dump(struct net *net, struct notifier_block *nb) struct fib_notifier_ops *ops; list_for_each_entry_rcu(ops, &net->fib_notifier_ops, list) { - int err = ops->fib_dump(net, nb); + int err; + if (!try_module_get(ops->owner)) + continue; + err = ops->fib_dump(net, nb); + module_put(ops->owner); if (err) return err; } diff --git a/net/ipv4/fib_notifier.c b/net/ipv4/fib_notifier.c index 5d7afb145562..cfd420b0572c 100644 --- a/net/ipv4/fib_notifier.c +++ b/net/ipv4/fib_notifier.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -49,6 +50,7 @@ static const struct fib_notifier_ops fib4_notifier_ops_template = { .family = AF_INET, .fib_seq_read = fib4_seq_read, .fib_dump = fib4_dump, + .owner = THIS_MODULE, }; int __net_init fib4_notifier_init(struct net *net) diff --git a/net/ipv6/fib6_notifier.c b/net/ipv6/fib6_notifier.c index 66a103ef7e86..05f82baaa99e 100644 --- a/net/ipv6/fib6_notifier.c +++ b/net/ipv6/fib6_notifier.c @@ -1,6 +1,7 @@ #include #include #include +#include #include #include #include @@ -41,6 +42,7 @@ static const struct fib_notifier_ops fib6_notifier_ops_template = { .family = AF_INET6, .fib_seq_read = fib6_seq_read, .fib_dump = fib6_dump, + .owner = THIS_MODULE, }; int __net_init fib6_notifier_init(struct net *net) -- cgit v1.2.3 From fb452a1aa3fd4034d7999e309c5466ff2d7005aa Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Fri, 1 Sep 2017 11:26:08 +0200 Subject: Revert "net: use lib/percpu_counter API for fragmentation mem accounting" This reverts commit 6d7b857d541ecd1d9bd997c97242d4ef94b19de2. There is a bug in fragmentation codes use of the percpu_counter API, that can cause issues on systems with many CPUs. The frag_mem_limit() just reads the global counter (fbc->count), without considering other CPUs can have upto batch size (130K) that haven't been subtracted yet. Due to the 3MBytes lower thresh limit, this become dangerous at >=24 CPUs (3*1024*1024/130000=24). The correct API usage would be to use __percpu_counter_compare() which does the right thing, and takes into account the number of (online) CPUs and batch size, to account for this and call __percpu_counter_sum() when needed. We choose to revert the use of the lib/percpu_counter API for frag memory accounting for several reasons: 1) On systems with CPUs > 24, the heavier fully locked __percpu_counter_sum() is always invoked, which will be more expensive than the atomic_t that is reverted to. Given systems with more than 24 CPUs are becoming common this doesn't seem like a good option. To mitigate this, the batch size could be decreased and thresh be increased. 2) The add_frag_mem_limit+sub_frag_mem_limit pairs happen on the RX CPU, before SKBs are pushed into sockets on remote CPUs. Given NICs can only hash on L2 part of the IP-header, the NIC-RXq's will likely be limited. Thus, a fair chance that atomic add+dec happen on the same CPU. Revert note that commit 1d6119baf061 ("net: fix percpu memory leaks") removed init_frag_mem_limit() and instead use inet_frags_init_net(). After this revert, inet_frags_uninit_net() becomes empty. Fixes: 6d7b857d541e ("net: use lib/percpu_counter API for fragmentation mem accounting") Fixes: 1d6119baf061 ("net: fix percpu memory leaks") Signed-off-by: Jesper Dangaard Brouer Acked-by: Florian Westphal Signed-off-by: David S. Miller --- include/net/inet_frag.h | 30 +++++++++--------------------- net/ipv4/inet_fragment.c | 4 +--- 2 files changed, 10 insertions(+), 24 deletions(-) (limited to 'net') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index 6fdcd2427776..fa635aa6d0b9 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -1,14 +1,9 @@ #ifndef __NET_FRAG_H__ #define __NET_FRAG_H__ -#include - struct netns_frags { - /* The percpu_counter "mem" need to be cacheline aligned. - * mem.count must not share cacheline with other writers - */ - struct percpu_counter mem ____cacheline_aligned_in_smp; - + /* Keep atomic mem on separate cachelines in structs that include it */ + atomic_t mem ____cacheline_aligned_in_smp; /* sysctls */ int timeout; int high_thresh; @@ -110,11 +105,11 @@ void inet_frags_fini(struct inet_frags *); static inline int inet_frags_init_net(struct netns_frags *nf) { - return percpu_counter_init(&nf->mem, 0, GFP_KERNEL); + atomic_set(&nf->mem, 0); + return 0; } static inline void inet_frags_uninit_net(struct netns_frags *nf) { - percpu_counter_destroy(&nf->mem); } void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f); @@ -140,31 +135,24 @@ static inline bool inet_frag_evicting(struct inet_frag_queue *q) /* Memory Tracking Functions. */ -/* The default percpu_counter batch size is not big enough to scale to - * fragmentation mem acct sizes. - * The mem size of a 64K fragment is approx: - * (44 fragments * 2944 truesize) + frag_queue struct(200) = 129736 bytes - */ -static unsigned int frag_percpu_counter_batch = 130000; - static inline int frag_mem_limit(struct netns_frags *nf) { - return percpu_counter_read(&nf->mem); + return atomic_read(&nf->mem); } static inline void sub_frag_mem_limit(struct netns_frags *nf, int i) { - percpu_counter_add_batch(&nf->mem, -i, frag_percpu_counter_batch); + atomic_sub(i, &nf->mem); } static inline void add_frag_mem_limit(struct netns_frags *nf, int i) { - percpu_counter_add_batch(&nf->mem, i, frag_percpu_counter_batch); + atomic_add(i, &nf->mem); } -static inline unsigned int sum_frag_mem_limit(struct netns_frags *nf) +static inline int sum_frag_mem_limit(struct netns_frags *nf) { - return percpu_counter_sum_positive(&nf->mem); + return atomic_read(&nf->mem); } /* RFC 3168 support : diff --git a/net/ipv4/inet_fragment.c b/net/ipv4/inet_fragment.c index 96e95e83cc61..af74d0433453 100644 --- a/net/ipv4/inet_fragment.c +++ b/net/ipv4/inet_fragment.c @@ -234,10 +234,8 @@ evict_again: cond_resched(); if (read_seqretry(&f->rnd_seqlock, seq) || - percpu_counter_sum(&nf->mem)) + sum_frag_mem_limit(nf)) goto evict_again; - - percpu_counter_destroy(&nf->mem); } EXPORT_SYMBOL(inet_frags_exit_net); -- cgit v1.2.3 From 5a63643e583b6a9789d7a225ae076fb4e603991c Mon Sep 17 00:00:00 2001 From: Jesper Dangaard Brouer Date: Fri, 1 Sep 2017 11:26:13 +0200 Subject: Revert "net: fix percpu memory leaks" This reverts commit 1d6119baf0610f813eb9d9580eb4fd16de5b4ceb. After reverting commit 6d7b857d541e ("net: use lib/percpu_counter API for fragmentation mem accounting") then here is no need for this fix-up patch. As percpu_counter is no longer used, it cannot memory leak it any-longer. Fixes: 6d7b857d541e ("net: use lib/percpu_counter API for fragmentation mem accounting") Fixes: 1d6119baf061 ("net: fix percpu memory leaks") Signed-off-by: Jesper Dangaard Brouer Signed-off-by: David S. Miller --- include/net/inet_frag.h | 7 +------ net/ieee802154/6lowpan/reassembly.c | 11 +++-------- net/ipv4/ip_fragment.c | 12 +++--------- net/ipv6/netfilter/nf_conntrack_reasm.c | 12 +++--------- net/ipv6/reassembly.c | 12 +++--------- 5 files changed, 13 insertions(+), 41 deletions(-) (limited to 'net') diff --git a/include/net/inet_frag.h b/include/net/inet_frag.h index fa635aa6d0b9..fc59e0775e00 100644 --- a/include/net/inet_frag.h +++ b/include/net/inet_frag.h @@ -103,15 +103,10 @@ struct inet_frags { int inet_frags_init(struct inet_frags *); void inet_frags_fini(struct inet_frags *); -static inline int inet_frags_init_net(struct netns_frags *nf) +static inline void inet_frags_init_net(struct netns_frags *nf) { atomic_set(&nf->mem, 0); - return 0; } -static inline void inet_frags_uninit_net(struct netns_frags *nf) -{ -} - void inet_frags_exit_net(struct netns_frags *nf, struct inet_frags *f); void inet_frag_kill(struct inet_frag_queue *q, struct inet_frags *f); diff --git a/net/ieee802154/6lowpan/reassembly.c b/net/ieee802154/6lowpan/reassembly.c index 30d875dff6b5..f85b08baff16 100644 --- a/net/ieee802154/6lowpan/reassembly.c +++ b/net/ieee802154/6lowpan/reassembly.c @@ -580,19 +580,14 @@ static int __net_init lowpan_frags_init_net(struct net *net) { struct netns_ieee802154_lowpan *ieee802154_lowpan = net_ieee802154_lowpan(net); - int res; ieee802154_lowpan->frags.high_thresh = IPV6_FRAG_HIGH_THRESH; ieee802154_lowpan->frags.low_thresh = IPV6_FRAG_LOW_THRESH; ieee802154_lowpan->frags.timeout = IPV6_FRAG_TIMEOUT; - res = inet_frags_init_net(&ieee802154_lowpan->frags); - if (res) - return res; - res = lowpan_frags_ns_sysctl_register(net); - if (res) - inet_frags_uninit_net(&ieee802154_lowpan->frags); - return res; + inet_frags_init_net(&ieee802154_lowpan->frags); + + return lowpan_frags_ns_sysctl_register(net); } static void __net_exit lowpan_frags_exit_net(struct net *net) diff --git a/net/ipv4/ip_fragment.c b/net/ipv4/ip_fragment.c index 9a8cfac503dc..46408c220d9d 100644 --- a/net/ipv4/ip_fragment.c +++ b/net/ipv4/ip_fragment.c @@ -844,8 +844,6 @@ static void __init ip4_frags_ctl_register(void) static int __net_init ipv4_frags_init_net(struct net *net) { - int res; - /* Fragment cache limits. * * The fragment memory accounting code, (tries to) account for @@ -871,13 +869,9 @@ static int __net_init ipv4_frags_init_net(struct net *net) net->ipv4.frags.max_dist = 64; - res = inet_frags_init_net(&net->ipv4.frags); - if (res) - return res; - res = ip4_frags_ns_ctl_register(net); - if (res) - inet_frags_uninit_net(&net->ipv4.frags); - return res; + inet_frags_init_net(&net->ipv4.frags); + + return ip4_frags_ns_ctl_register(net); } static void __net_exit ipv4_frags_exit_net(struct net *net) diff --git a/net/ipv6/netfilter/nf_conntrack_reasm.c b/net/ipv6/netfilter/nf_conntrack_reasm.c index 986d4ca38832..b263bf3a19f7 100644 --- a/net/ipv6/netfilter/nf_conntrack_reasm.c +++ b/net/ipv6/netfilter/nf_conntrack_reasm.c @@ -622,18 +622,12 @@ EXPORT_SYMBOL_GPL(nf_ct_frag6_gather); static int nf_ct_net_init(struct net *net) { - int res; - net->nf_frag.frags.high_thresh = IPV6_FRAG_HIGH_THRESH; net->nf_frag.frags.low_thresh = IPV6_FRAG_LOW_THRESH; net->nf_frag.frags.timeout = IPV6_FRAG_TIMEOUT; - res = inet_frags_init_net(&net->nf_frag.frags); - if (res) - return res; - res = nf_ct_frag6_sysctl_register(net); - if (res) - inet_frags_uninit_net(&net->nf_frag.frags); - return res; + inet_frags_init_net(&net->nf_frag.frags); + + return nf_ct_frag6_sysctl_register(net); } static void nf_ct_net_exit(struct net *net) diff --git a/net/ipv6/reassembly.c b/net/ipv6/reassembly.c index e1da5b888cc4..846012eae526 100644 --- a/net/ipv6/reassembly.c +++ b/net/ipv6/reassembly.c @@ -714,19 +714,13 @@ static void ip6_frags_sysctl_unregister(void) static int __net_init ipv6_frags_init_net(struct net *net) { - int res; - net->ipv6.frags.high_thresh = IPV6_FRAG_HIGH_THRESH; net->ipv6.frags.low_thresh = IPV6_FRAG_LOW_THRESH; net->ipv6.frags.timeout = IPV6_FRAG_TIMEOUT; - res = inet_frags_init_net(&net->ipv6.frags); - if (res) - return res; - res = ip6_frags_ns_sysctl_register(net); - if (res) - inet_frags_uninit_net(&net->ipv6.frags); - return res; + inet_frags_init_net(&net->ipv6.frags); + + return ip6_frags_ns_sysctl_register(net); } static void __net_exit ipv6_frags_exit_net(struct net *net) -- cgit v1.2.3 From f3c66d4e144a0904ea9b95d23ed9f8eb38c11bfb Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Fri, 1 Sep 2017 17:58:48 +0200 Subject: l2tp: prevent creation of sessions on terminated tunnels l2tp_tunnel_destruct() sets tunnel->sock to NULL, then removes the tunnel from the pernet list and finally closes all its sessions. Therefore, it's possible to add a session to a tunnel that is still reachable, but for which tunnel->sock has already been reset. This can make l2tp_session_create() dereference a NULL pointer when calling sock_hold(tunnel->sock). This patch adds the .acpt_newsess field to struct l2tp_tunnel, which is used by l2tp_tunnel_closeall() to prevent addition of new sessions to tunnels. Resetting tunnel->sock is done after l2tp_tunnel_closeall() returned, so that l2tp_session_add_to_tunnel() can safely take a reference on it when .acpt_newsess is true. The .acpt_newsess field is modified in l2tp_tunnel_closeall(), rather than in l2tp_tunnel_destruct(), so that it benefits all tunnel removal mechanisms. E.g. on UDP tunnels, a session could be added to a tunnel after l2tp_udp_encap_destroy() proceeded. This would prevent the tunnel from being removed because of the references held by this new session on the tunnel and its socket. Even though the session could be removed manually later on, this defeats the purpose of commit 9980d001cec8 ("l2tp: add udp encap socket destroy handler"). Fixes: fd558d186df2 ("l2tp: Split pppol2tp patch into separate l2tp and ppp parts") Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.c | 41 ++++++++++++++++++++++++++++------------- net/l2tp/l2tp_core.h | 4 ++++ 2 files changed, 32 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index 90165a6874bc..ee485df73ccd 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -329,13 +329,21 @@ static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel, struct hlist_head *g_head; struct hlist_head *head; struct l2tp_net *pn; + int err; head = l2tp_session_id_hash(tunnel, session->session_id); write_lock_bh(&tunnel->hlist_lock); + if (!tunnel->acpt_newsess) { + err = -ENODEV; + goto err_tlock; + } + hlist_for_each_entry(session_walk, head, hlist) - if (session_walk->session_id == session->session_id) - goto exist; + if (session_walk->session_id == session->session_id) { + err = -EEXIST; + goto err_tlock; + } if (tunnel->version == L2TP_HDR_VER_3) { pn = l2tp_pernet(tunnel->l2tp_net); @@ -343,12 +351,21 @@ static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel, session->session_id); spin_lock_bh(&pn->l2tp_session_hlist_lock); + hlist_for_each_entry(session_walk, g_head, global_hlist) - if (session_walk->session_id == session->session_id) - goto exist_glob; + if (session_walk->session_id == session->session_id) { + err = -EEXIST; + goto err_tlock_pnlock; + } + l2tp_tunnel_inc_refcount(tunnel); + sock_hold(tunnel->sock); hlist_add_head_rcu(&session->global_hlist, g_head); + spin_unlock_bh(&pn->l2tp_session_hlist_lock); + } else { + l2tp_tunnel_inc_refcount(tunnel); + sock_hold(tunnel->sock); } hlist_add_head(&session->hlist, head); @@ -356,12 +373,12 @@ static int l2tp_session_add_to_tunnel(struct l2tp_tunnel *tunnel, return 0; -exist_glob: +err_tlock_pnlock: spin_unlock_bh(&pn->l2tp_session_hlist_lock); -exist: +err_tlock: write_unlock_bh(&tunnel->hlist_lock); - return -EEXIST; + return err; } /* Lookup a tunnel by id @@ -1251,7 +1268,6 @@ static void l2tp_tunnel_destruct(struct sock *sk) /* Remove hooks into tunnel socket */ sk->sk_destruct = tunnel->old_sk_destruct; sk->sk_user_data = NULL; - tunnel->sock = NULL; /* Remove the tunnel struct from the tunnel list */ pn = l2tp_pernet(tunnel->l2tp_net); @@ -1261,6 +1277,8 @@ static void l2tp_tunnel_destruct(struct sock *sk) atomic_dec(&l2tp_tunnel_count); l2tp_tunnel_closeall(tunnel); + + tunnel->sock = NULL; l2tp_tunnel_dec_refcount(tunnel); /* Call the original destructor */ @@ -1285,6 +1303,7 @@ void l2tp_tunnel_closeall(struct l2tp_tunnel *tunnel) tunnel->name); write_lock_bh(&tunnel->hlist_lock); + tunnel->acpt_newsess = false; for (hash = 0; hash < L2TP_HASH_SIZE; hash++) { again: hlist_for_each_safe(walk, tmp, &tunnel->session_hlist[hash]) { @@ -1581,6 +1600,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32 tunnel->magic = L2TP_TUNNEL_MAGIC; sprintf(&tunnel->name[0], "tunl %u", tunnel_id); rwlock_init(&tunnel->hlist_lock); + tunnel->acpt_newsess = true; /* The net we belong to */ tunnel->l2tp_net = net; @@ -1829,11 +1849,6 @@ struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunn return ERR_PTR(err); } - l2tp_tunnel_inc_refcount(tunnel); - - /* Ensure tunnel socket isn't deleted */ - sock_hold(tunnel->sock); - /* Ignore management session in session count value */ if (session->session_id != 0) atomic_inc(&l2tp_session_count); diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 9101297f27ad..4593d48df953 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -162,6 +162,10 @@ struct l2tp_tunnel { int magic; /* Should be L2TP_TUNNEL_MAGIC */ struct rcu_head rcu; rwlock_t hlist_lock; /* protect session_hlist */ + bool acpt_newsess; /* Indicates whether this + * tunnel accepts new sessions. + * Protected by hlist_lock. + */ struct hlist_head session_hlist[L2TP_HASH_SIZE]; /* hashed list of sessions, * hashed by id */ -- cgit v1.2.3 From f026bc29a8e093edfbb2a77700454b285c97e8ad Mon Sep 17 00:00:00 2001 From: Guillaume Nault Date: Fri, 1 Sep 2017 17:58:51 +0200 Subject: l2tp: pass tunnel pointer to ->session_create() Using l2tp_tunnel_find() in pppol2tp_session_create() and l2tp_eth_create() is racy, because no reference is held on the returned session. These functions are only used to implement the ->session_create callback which is run by l2tp_nl_cmd_session_create(). Therefore searching for the parent tunnel isn't necessary because l2tp_nl_cmd_session_create() already has a pointer to it and holds a reference. This patch modifies ->session_create()'s prototype to directly pass the the parent tunnel as parameter, thus avoiding searching for it in pppol2tp_session_create() and l2tp_eth_create(). Since we have to touch the ->session_create() call in l2tp_nl_cmd_session_create(), let's also remove the useless conditional: we know that ->session_create isn't NULL at this point because it's already been checked earlier in this same function. Finally, one might be tempted to think that the removed l2tp_tunnel_find() calls were harmless because they would return the same tunnel as the one held by l2tp_nl_cmd_session_create() anyway. But that tunnel might be removed and a new one created with same tunnel Id before the l2tp_tunnel_find() call. In this case l2tp_tunnel_find() would return the new tunnel which wouldn't be protected by the reference held by l2tp_nl_cmd_session_create(). Fixes: 309795f4bec2 ("l2tp: Add netlink control API for L2TP") Fixes: d9e31d17ceba ("l2tp: Add L2TP ethernet pseudowire support") Signed-off-by: Guillaume Nault Signed-off-by: David S. Miller --- net/l2tp/l2tp_core.h | 4 +++- net/l2tp/l2tp_eth.c | 11 +++-------- net/l2tp/l2tp_netlink.c | 8 ++++---- net/l2tp/l2tp_ppp.c | 19 +++++++------------ 4 files changed, 17 insertions(+), 25 deletions(-) (limited to 'net') diff --git a/net/l2tp/l2tp_core.h b/net/l2tp/l2tp_core.h index 4593d48df953..a305e0c5925a 100644 --- a/net/l2tp/l2tp_core.h +++ b/net/l2tp/l2tp_core.h @@ -201,7 +201,9 @@ struct l2tp_tunnel { }; struct l2tp_nl_cmd_ops { - int (*session_create)(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg); + int (*session_create)(struct net *net, struct l2tp_tunnel *tunnel, + u32 session_id, u32 peer_session_id, + struct l2tp_session_cfg *cfg); int (*session_delete)(struct l2tp_session *session); }; diff --git a/net/l2tp/l2tp_eth.c b/net/l2tp/l2tp_eth.c index 4de2ec94b08c..87da9ef61860 100644 --- a/net/l2tp/l2tp_eth.c +++ b/net/l2tp/l2tp_eth.c @@ -262,24 +262,19 @@ static void l2tp_eth_adjust_mtu(struct l2tp_tunnel *tunnel, dev->needed_headroom += session->hdr_len; } -static int l2tp_eth_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) +static int l2tp_eth_create(struct net *net, struct l2tp_tunnel *tunnel, + u32 session_id, u32 peer_session_id, + struct l2tp_session_cfg *cfg) { unsigned char name_assign_type; struct net_device *dev; char name[IFNAMSIZ]; - struct l2tp_tunnel *tunnel; struct l2tp_session *session; struct l2tp_eth *priv; struct l2tp_eth_sess *spriv; int rc; struct l2tp_eth_net *pn; - tunnel = l2tp_tunnel_find(net, tunnel_id); - if (!tunnel) { - rc = -ENODEV; - goto out; - } - if (cfg->ifname) { strlcpy(name, cfg->ifname, IFNAMSIZ); name_assign_type = NET_NAME_USER; diff --git a/net/l2tp/l2tp_netlink.c b/net/l2tp/l2tp_netlink.c index 57427d430f10..7135f4645d3a 100644 --- a/net/l2tp/l2tp_netlink.c +++ b/net/l2tp/l2tp_netlink.c @@ -643,10 +643,10 @@ static int l2tp_nl_cmd_session_create(struct sk_buff *skb, struct genl_info *inf break; } - ret = -EPROTONOSUPPORT; - if (l2tp_nl_cmd_ops[cfg.pw_type]->session_create) - ret = (*l2tp_nl_cmd_ops[cfg.pw_type]->session_create)(net, tunnel_id, - session_id, peer_session_id, &cfg); + ret = l2tp_nl_cmd_ops[cfg.pw_type]->session_create(net, tunnel, + session_id, + peer_session_id, + &cfg); if (ret >= 0) { session = l2tp_session_get(net, tunnel, session_id, false); diff --git a/net/l2tp/l2tp_ppp.c b/net/l2tp/l2tp_ppp.c index f0edb7209079..50e3ee9a9d61 100644 --- a/net/l2tp/l2tp_ppp.c +++ b/net/l2tp/l2tp_ppp.c @@ -788,25 +788,20 @@ end: #ifdef CONFIG_L2TP_V3 -/* Called when creating sessions via the netlink interface. - */ -static int pppol2tp_session_create(struct net *net, u32 tunnel_id, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg) +/* Called when creating sessions via the netlink interface. */ +static int pppol2tp_session_create(struct net *net, struct l2tp_tunnel *tunnel, + u32 session_id, u32 peer_session_id, + struct l2tp_session_cfg *cfg) { int error; - struct l2tp_tunnel *tunnel; struct l2tp_session *session; struct pppol2tp_session *ps; - tunnel = l2tp_tunnel_find(net, tunnel_id); - - /* Error if we can't find the tunnel */ - error = -ENOENT; - if (tunnel == NULL) - goto out; - /* Error if tunnel socket is not prepped */ - if (tunnel->sock == NULL) + if (!tunnel->sock) { + error = -ENOENT; goto out; + } /* Default MTU values. */ if (cfg->mtu == 0) -- cgit v1.2.3 From bea74641e3786d51dcf1175527cc1781420961c9 Mon Sep 17 00:00:00 2001 From: Vishwanath Pai Date: Fri, 18 Aug 2017 20:58:59 +0200 Subject: netfilter: xt_hashlimit: add rate match mode This patch adds a new feature to hashlimit that allows matching on the current packet/byte rate without rate limiting. This can be enabled with a new flag --hashlimit-rate-match. The match returns true if the current rate of packets is above/below the user specified value. The main difference between the existing algorithm and the new one is that the existing algorithm rate-limits the flow whereas the new algorithm does not. Instead it *classifies* the flow based on whether it is above or below a certain rate. I will demonstrate this with an example below. Let us assume this rule: iptables -A INPUT -m hashlimit --hashlimit-above 10/s -j new_chain If the packet rate is 15/s, the existing algorithm would ACCEPT 10 packets every second and send 5 packets to "new_chain". But with the new algorithm, as long as the rate of 15/s is sustained, all packets will continue to match and every packet is sent to new_chain. This new functionality will let us classify different flows based on their current rate, so that further decisions can be made on them based on what the current rate is. This is how the new algorithm works: We divide time into intervals of 1 (sec/min/hour) as specified by the user. We keep track of the number of packets/bytes processed in the current interval. After each interval we reset the counter to 0. When we receive a packet for match, we look at the packet rate during the current interval and the previous interval to make a decision: if [ prev_rate < user and cur_rate < user ] return Below else return Above Where cur_rate is the number of packets/bytes seen in the current interval, prev is the number of packets/bytes seen in the previous interval and 'user' is the rate specified by the user. We also provide flexibility to the user for choosing the time interval using the option --hashilmit-interval. For example the user can keep a low rate like x/hour but still keep the interval as small as 1 second. To preserve backwards compatibility we have to add this feature in a new revision, so I've created revision 3 for hashlimit. The two new options we add are: --hashlimit-rate-match --hashlimit-rate-interval I have updated the help text to add these new options. Also added a few tests for the new options. Suggested-by: Igor Lubashev Reviewed-by: Josh Hunt Signed-off-by: Vishwanath Pai Signed-off-by: Pablo Neira Ayuso --- include/linux/netfilter/xt_hashlimit.h | 3 +- include/uapi/linux/netfilter/xt_hashlimit.h | 36 +++- net/netfilter/xt_hashlimit.c | 277 +++++++++++++++++++++++++--- 3 files changed, 285 insertions(+), 31 deletions(-) (limited to 'net') diff --git a/include/linux/netfilter/xt_hashlimit.h b/include/linux/netfilter/xt_hashlimit.h index 074790c0cf74..0fc458bde80b 100644 --- a/include/linux/netfilter/xt_hashlimit.h +++ b/include/linux/netfilter/xt_hashlimit.h @@ -5,5 +5,6 @@ #define XT_HASHLIMIT_ALL (XT_HASHLIMIT_HASH_DIP | XT_HASHLIMIT_HASH_DPT | \ XT_HASHLIMIT_HASH_SIP | XT_HASHLIMIT_HASH_SPT | \ - XT_HASHLIMIT_INVERT | XT_HASHLIMIT_BYTES) + XT_HASHLIMIT_INVERT | XT_HASHLIMIT_BYTES |\ + XT_HASHLIMIT_RATE_MATCH) #endif /*_XT_HASHLIMIT_H*/ diff --git a/include/uapi/linux/netfilter/xt_hashlimit.h b/include/uapi/linux/netfilter/xt_hashlimit.h index 79da349f1060..aa98573248b1 100644 --- a/include/uapi/linux/netfilter/xt_hashlimit.h +++ b/include/uapi/linux/netfilter/xt_hashlimit.h @@ -19,12 +19,13 @@ struct xt_hashlimit_htable; enum { - XT_HASHLIMIT_HASH_DIP = 1 << 0, - XT_HASHLIMIT_HASH_DPT = 1 << 1, - XT_HASHLIMIT_HASH_SIP = 1 << 2, - XT_HASHLIMIT_HASH_SPT = 1 << 3, - XT_HASHLIMIT_INVERT = 1 << 4, - XT_HASHLIMIT_BYTES = 1 << 5, + XT_HASHLIMIT_HASH_DIP = 1 << 0, + XT_HASHLIMIT_HASH_DPT = 1 << 1, + XT_HASHLIMIT_HASH_SIP = 1 << 2, + XT_HASHLIMIT_HASH_SPT = 1 << 3, + XT_HASHLIMIT_INVERT = 1 << 4, + XT_HASHLIMIT_BYTES = 1 << 5, + XT_HASHLIMIT_RATE_MATCH = 1 << 6, }; struct hashlimit_cfg { @@ -79,6 +80,21 @@ struct hashlimit_cfg2 { __u8 srcmask, dstmask; }; +struct hashlimit_cfg3 { + __u64 avg; /* Average secs between packets * scale */ + __u64 burst; /* Period multiplier for upper limit. */ + __u32 mode; /* bitmask of XT_HASHLIMIT_HASH_* */ + + /* user specified */ + __u32 size; /* how many buckets */ + __u32 max; /* max number of entries */ + __u32 gc_interval; /* gc interval */ + __u32 expire; /* when do entries expire? */ + + __u32 interval; + __u8 srcmask, dstmask; +}; + struct xt_hashlimit_mtinfo1 { char name[IFNAMSIZ]; struct hashlimit_cfg1 cfg; @@ -95,4 +111,12 @@ struct xt_hashlimit_mtinfo2 { struct xt_hashlimit_htable *hinfo __attribute__((aligned(8))); }; +struct xt_hashlimit_mtinfo3 { + char name[NAME_MAX]; + struct hashlimit_cfg3 cfg; + + /* Used internally by the kernel */ + struct xt_hashlimit_htable *hinfo __attribute__((aligned(8))); +}; + #endif /* _UAPI_XT_HASHLIMIT_H */ diff --git a/net/netfilter/xt_hashlimit.c b/net/netfilter/xt_hashlimit.c index ffdb611e54a2..10d48234f5f4 100644 --- a/net/netfilter/xt_hashlimit.c +++ b/net/netfilter/xt_hashlimit.c @@ -56,6 +56,7 @@ static inline struct hashlimit_net *hashlimit_pernet(struct net *net) } /* need to declare this at the top */ +static const struct file_operations dl_file_ops_v2; static const struct file_operations dl_file_ops_v1; static const struct file_operations dl_file_ops; @@ -87,8 +88,19 @@ struct dsthash_ent { unsigned long expires; /* precalculated expiry time */ struct { unsigned long prev; /* last modification */ - u_int64_t credit; - u_int64_t credit_cap, cost; + union { + struct { + u_int64_t credit; + u_int64_t credit_cap; + u_int64_t cost; + }; + struct { + u_int32_t interval, prev_window; + u_int64_t current_rate; + u_int64_t rate; + int64_t burst; + }; + }; } rateinfo; struct rcu_head rcu; }; @@ -99,7 +111,7 @@ struct xt_hashlimit_htable { u_int8_t family; bool rnd_initialized; - struct hashlimit_cfg2 cfg; /* config */ + struct hashlimit_cfg3 cfg; /* config */ /* used internally */ spinlock_t lock; /* lock for list_head */ @@ -116,10 +128,10 @@ struct xt_hashlimit_htable { }; static int -cfg_copy(struct hashlimit_cfg2 *to, void *from, int revision) +cfg_copy(struct hashlimit_cfg3 *to, const void *from, int revision) { if (revision == 1) { - struct hashlimit_cfg1 *cfg = from; + struct hashlimit_cfg1 *cfg = (struct hashlimit_cfg1 *)from; to->mode = cfg->mode; to->avg = cfg->avg; @@ -131,7 +143,19 @@ cfg_copy(struct hashlimit_cfg2 *to, void *from, int revision) to->srcmask = cfg->srcmask; to->dstmask = cfg->dstmask; } else if (revision == 2) { - memcpy(to, from, sizeof(struct hashlimit_cfg2)); + struct hashlimit_cfg2 *cfg = (struct hashlimit_cfg2 *)from; + + to->mode = cfg->mode; + to->avg = cfg->avg; + to->burst = cfg->burst; + to->size = cfg->size; + to->max = cfg->max; + to->gc_interval = cfg->gc_interval; + to->expire = cfg->expire; + to->srcmask = cfg->srcmask; + to->dstmask = cfg->dstmask; + } else if (revision == 3) { + memcpy(to, from, sizeof(struct hashlimit_cfg3)); } else { return -EINVAL; } @@ -240,13 +264,14 @@ dsthash_free(struct xt_hashlimit_htable *ht, struct dsthash_ent *ent) } static void htable_gc(struct work_struct *work); -static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg, +static int htable_create(struct net *net, struct hashlimit_cfg3 *cfg, const char *name, u_int8_t family, struct xt_hashlimit_htable **out_hinfo, int revision) { struct hashlimit_net *hashlimit_net = hashlimit_pernet(net); struct xt_hashlimit_htable *hinfo; + const struct file_operations *fops; unsigned int size, i; int ret; @@ -268,7 +293,7 @@ static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg, *out_hinfo = hinfo; /* copy match config into hashtable config */ - ret = cfg_copy(&hinfo->cfg, (void *)cfg, 2); + ret = cfg_copy(&hinfo->cfg, (void *)cfg, 3); if (ret) return ret; @@ -293,11 +318,21 @@ static int htable_create(struct net *net, struct hashlimit_cfg2 *cfg, } spin_lock_init(&hinfo->lock); + switch (revision) { + case 1: + fops = &dl_file_ops_v1; + break; + case 2: + fops = &dl_file_ops_v2; + break; + default: + fops = &dl_file_ops; + } + hinfo->pde = proc_create_data(name, 0, (family == NFPROTO_IPV4) ? hashlimit_net->ipt_hashlimit : hashlimit_net->ip6t_hashlimit, - (revision == 1) ? &dl_file_ops_v1 : &dl_file_ops, - hinfo); + fops, hinfo); if (hinfo->pde == NULL) { kfree(hinfo->name); vfree(hinfo); @@ -482,6 +517,25 @@ static u32 user2credits_byte(u32 user) return (u32) (us >> 32); } +static u64 user2rate(u64 user) +{ + if (user != 0) { + return div64_u64(XT_HASHLIMIT_SCALE_v2, user); + } else { + pr_warn("invalid rate from userspace: %llu\n", user); + return 0; + } +} + +static u64 user2rate_bytes(u64 user) +{ + u64 r; + + r = user ? 0xFFFFFFFFULL / user : 0xFFFFFFFFULL; + r = (r - 1) << 4; + return r; +} + static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now, u32 mode, int revision) { @@ -491,6 +545,21 @@ static void rateinfo_recalc(struct dsthash_ent *dh, unsigned long now, if (delta == 0) return; + if (revision >= 3 && mode & XT_HASHLIMIT_RATE_MATCH) { + u64 interval = dh->rateinfo.interval * HZ; + + if (delta < interval) + return; + + dh->rateinfo.prev = now; + dh->rateinfo.prev_window = + ((dh->rateinfo.current_rate * interval) > + (delta * dh->rateinfo.rate)); + dh->rateinfo.current_rate = 0; + + return; + } + dh->rateinfo.prev = now; if (mode & XT_HASHLIMIT_BYTES) { @@ -515,7 +584,23 @@ static void rateinfo_init(struct dsthash_ent *dh, struct xt_hashlimit_htable *hinfo, int revision) { dh->rateinfo.prev = jiffies; - if (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) { + if (revision >= 3 && hinfo->cfg.mode & XT_HASHLIMIT_RATE_MATCH) { + dh->rateinfo.prev_window = 0; + dh->rateinfo.current_rate = 0; + if (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) { + dh->rateinfo.rate = user2rate_bytes(hinfo->cfg.avg); + if (hinfo->cfg.burst) + dh->rateinfo.burst = + hinfo->cfg.burst * dh->rateinfo.rate; + else + dh->rateinfo.burst = dh->rateinfo.rate; + } else { + dh->rateinfo.rate = user2rate(hinfo->cfg.avg); + dh->rateinfo.burst = + hinfo->cfg.burst + dh->rateinfo.rate; + } + dh->rateinfo.interval = hinfo->cfg.interval; + } else if (hinfo->cfg.mode & XT_HASHLIMIT_BYTES) { dh->rateinfo.credit = CREDITS_PER_JIFFY_BYTES * HZ; dh->rateinfo.cost = user2credits_byte(hinfo->cfg.avg); dh->rateinfo.credit_cap = hinfo->cfg.burst; @@ -648,7 +733,7 @@ static u32 hashlimit_byte_cost(unsigned int len, struct dsthash_ent *dh) static bool hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par, struct xt_hashlimit_htable *hinfo, - const struct hashlimit_cfg2 *cfg, int revision) + const struct hashlimit_cfg3 *cfg, int revision) { unsigned long now = jiffies; struct dsthash_ent *dh; @@ -680,6 +765,20 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par, rateinfo_recalc(dh, now, hinfo->cfg.mode, revision); } + if (cfg->mode & XT_HASHLIMIT_RATE_MATCH) { + cost = (cfg->mode & XT_HASHLIMIT_BYTES) ? skb->len : 1; + dh->rateinfo.current_rate += cost; + + if (!dh->rateinfo.prev_window && + (dh->rateinfo.current_rate <= dh->rateinfo.burst)) { + spin_unlock(&dh->lock); + rcu_read_unlock_bh(); + return !(cfg->mode & XT_HASHLIMIT_INVERT); + } else { + goto overlimit; + } + } + if (cfg->mode & XT_HASHLIMIT_BYTES) cost = hashlimit_byte_cost(skb->len, dh); else @@ -693,6 +792,7 @@ hashlimit_mt_common(const struct sk_buff *skb, struct xt_action_param *par, return !(cfg->mode & XT_HASHLIMIT_INVERT); } +overlimit: spin_unlock(&dh->lock); local_bh_enable(); /* default match is underlimit - so over the limit, we need to invert */ @@ -708,7 +808,7 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_hashlimit_mtinfo1 *info = par->matchinfo; struct xt_hashlimit_htable *hinfo = info->hinfo; - struct hashlimit_cfg2 cfg = {}; + struct hashlimit_cfg3 cfg = {}; int ret; ret = cfg_copy(&cfg, (void *)&info->cfg, 1); @@ -720,17 +820,33 @@ hashlimit_mt_v1(const struct sk_buff *skb, struct xt_action_param *par) } static bool -hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) +hashlimit_mt_v2(const struct sk_buff *skb, struct xt_action_param *par) { const struct xt_hashlimit_mtinfo2 *info = par->matchinfo; struct xt_hashlimit_htable *hinfo = info->hinfo; + struct hashlimit_cfg3 cfg = {}; + int ret; + + ret = cfg_copy(&cfg, (void *)&info->cfg, 2); + + if (ret) + return ret; + + return hashlimit_mt_common(skb, par, hinfo, &cfg, 2); +} + +static bool +hashlimit_mt(const struct sk_buff *skb, struct xt_action_param *par) +{ + const struct xt_hashlimit_mtinfo3 *info = par->matchinfo; + struct xt_hashlimit_htable *hinfo = info->hinfo; - return hashlimit_mt_common(skb, par, hinfo, &info->cfg, 2); + return hashlimit_mt_common(skb, par, hinfo, &info->cfg, 3); } static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, struct xt_hashlimit_htable **hinfo, - struct hashlimit_cfg2 *cfg, + struct hashlimit_cfg3 *cfg, const char *name, int revision) { struct net *net = par->net; @@ -753,7 +869,17 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, } /* Check for overflow. */ - if (cfg->mode & XT_HASHLIMIT_BYTES) { + if (revision >= 3 && cfg->mode & XT_HASHLIMIT_RATE_MATCH) { + if (cfg->avg == 0) { + pr_info("hashlimit invalid rate\n"); + return -ERANGE; + } + + if (cfg->interval == 0) { + pr_info("hashlimit invalid interval\n"); + return -EINVAL; + } + } else if (cfg->mode & XT_HASHLIMIT_BYTES) { if (user2credits_byte(cfg->avg) == 0) { pr_info("overflow, rate too high: %llu\n", cfg->avg); return -EINVAL; @@ -784,7 +910,7 @@ static int hashlimit_mt_check_common(const struct xt_mtchk_param *par, static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par) { struct xt_hashlimit_mtinfo1 *info = par->matchinfo; - struct hashlimit_cfg2 cfg = {}; + struct hashlimit_cfg3 cfg = {}; int ret; if (info->name[sizeof(info->name) - 1] != '\0') @@ -799,15 +925,40 @@ static int hashlimit_mt_check_v1(const struct xt_mtchk_param *par) &cfg, info->name, 1); } -static int hashlimit_mt_check(const struct xt_mtchk_param *par) +static int hashlimit_mt_check_v2(const struct xt_mtchk_param *par) { struct xt_hashlimit_mtinfo2 *info = par->matchinfo; + struct hashlimit_cfg3 cfg = {}; + int ret; + + if (info->name[sizeof(info->name) - 1] != '\0') + return -EINVAL; + + ret = cfg_copy(&cfg, (void *)&info->cfg, 2); + + if (ret) + return ret; + + return hashlimit_mt_check_common(par, &info->hinfo, + &cfg, info->name, 2); +} + +static int hashlimit_mt_check(const struct xt_mtchk_param *par) +{ + struct xt_hashlimit_mtinfo3 *info = par->matchinfo; if (info->name[sizeof(info->name) - 1] != '\0') return -EINVAL; return hashlimit_mt_check_common(par, &info->hinfo, &info->cfg, - info->name, 2); + info->name, 3); +} + +static void hashlimit_mt_destroy_v2(const struct xt_mtdtor_param *par) +{ + const struct xt_hashlimit_mtinfo2 *info = par->matchinfo; + + htable_put(info->hinfo); } static void hashlimit_mt_destroy_v1(const struct xt_mtdtor_param *par) @@ -819,7 +970,7 @@ static void hashlimit_mt_destroy_v1(const struct xt_mtdtor_param *par) static void hashlimit_mt_destroy(const struct xt_mtdtor_param *par) { - const struct xt_hashlimit_mtinfo2 *info = par->matchinfo; + const struct xt_hashlimit_mtinfo3 *info = par->matchinfo; htable_put(info->hinfo); } @@ -840,9 +991,20 @@ static struct xt_match hashlimit_mt_reg[] __read_mostly = { .name = "hashlimit", .revision = 2, .family = NFPROTO_IPV4, - .match = hashlimit_mt, + .match = hashlimit_mt_v2, .matchsize = sizeof(struct xt_hashlimit_mtinfo2), .usersize = offsetof(struct xt_hashlimit_mtinfo2, hinfo), + .checkentry = hashlimit_mt_check_v2, + .destroy = hashlimit_mt_destroy_v2, + .me = THIS_MODULE, + }, + { + .name = "hashlimit", + .revision = 3, + .family = NFPROTO_IPV4, + .match = hashlimit_mt, + .matchsize = sizeof(struct xt_hashlimit_mtinfo3), + .usersize = offsetof(struct xt_hashlimit_mtinfo3, hinfo), .checkentry = hashlimit_mt_check, .destroy = hashlimit_mt_destroy, .me = THIS_MODULE, @@ -863,9 +1025,20 @@ static struct xt_match hashlimit_mt_reg[] __read_mostly = { .name = "hashlimit", .revision = 2, .family = NFPROTO_IPV6, - .match = hashlimit_mt, + .match = hashlimit_mt_v2, .matchsize = sizeof(struct xt_hashlimit_mtinfo2), .usersize = offsetof(struct xt_hashlimit_mtinfo2, hinfo), + .checkentry = hashlimit_mt_check_v2, + .destroy = hashlimit_mt_destroy_v2, + .me = THIS_MODULE, + }, + { + .name = "hashlimit", + .revision = 3, + .family = NFPROTO_IPV6, + .match = hashlimit_mt, + .matchsize = sizeof(struct xt_hashlimit_mtinfo3), + .usersize = offsetof(struct xt_hashlimit_mtinfo3, hinfo), .checkentry = hashlimit_mt_check, .destroy = hashlimit_mt_destroy, .me = THIS_MODULE, @@ -947,6 +1120,21 @@ static void dl_seq_print(struct dsthash_ent *ent, u_int8_t family, } } +static int dl_seq_real_show_v2(struct dsthash_ent *ent, u_int8_t family, + struct seq_file *s) +{ + const struct xt_hashlimit_htable *ht = s->private; + + spin_lock(&ent->lock); + /* recalculate to show accurate numbers */ + rateinfo_recalc(ent, jiffies, ht->cfg.mode, 2); + + dl_seq_print(ent, family, s); + + spin_unlock(&ent->lock); + return seq_has_overflowed(s); +} + static int dl_seq_real_show_v1(struct dsthash_ent *ent, u_int8_t family, struct seq_file *s) { @@ -969,7 +1157,7 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family, spin_lock(&ent->lock); /* recalculate to show accurate numbers */ - rateinfo_recalc(ent, jiffies, ht->cfg.mode, 2); + rateinfo_recalc(ent, jiffies, ht->cfg.mode, 3); dl_seq_print(ent, family, s); @@ -977,6 +1165,20 @@ static int dl_seq_real_show(struct dsthash_ent *ent, u_int8_t family, return seq_has_overflowed(s); } +static int dl_seq_show_v2(struct seq_file *s, void *v) +{ + struct xt_hashlimit_htable *htable = s->private; + unsigned int *bucket = (unsigned int *)v; + struct dsthash_ent *ent; + + if (!hlist_empty(&htable->hash[*bucket])) { + hlist_for_each_entry(ent, &htable->hash[*bucket], node) + if (dl_seq_real_show_v2(ent, htable->family, s)) + return -1; + } + return 0; +} + static int dl_seq_show_v1(struct seq_file *s, void *v) { struct xt_hashlimit_htable *htable = s->private; @@ -1012,6 +1214,13 @@ static const struct seq_operations dl_seq_ops_v1 = { .show = dl_seq_show_v1 }; +static const struct seq_operations dl_seq_ops_v2 = { + .start = dl_seq_start, + .next = dl_seq_next, + .stop = dl_seq_stop, + .show = dl_seq_show_v2 +}; + static const struct seq_operations dl_seq_ops = { .start = dl_seq_start, .next = dl_seq_next, @@ -1019,6 +1228,18 @@ static const struct seq_operations dl_seq_ops = { .show = dl_seq_show }; +static int dl_proc_open_v2(struct inode *inode, struct file *file) +{ + int ret = seq_open(file, &dl_seq_ops_v2); + + if (!ret) { + struct seq_file *sf = file->private_data; + + sf->private = PDE_DATA(inode); + } + return ret; +} + static int dl_proc_open_v1(struct inode *inode, struct file *file) { int ret = seq_open(file, &dl_seq_ops_v1); @@ -1042,6 +1263,14 @@ static int dl_proc_open(struct inode *inode, struct file *file) return ret; } +static const struct file_operations dl_file_ops_v2 = { + .owner = THIS_MODULE, + .open = dl_proc_open_v2, + .read = seq_read, + .llseek = seq_lseek, + .release = seq_release +}; + static const struct file_operations dl_file_ops_v1 = { .owner = THIS_MODULE, .open = dl_proc_open_v1, -- cgit v1.2.3 From dfc46034b54af3abf594de75a1ee43ef2ec2a60a Mon Sep 17 00:00:00 2001 From: "Pablo M. Bermudo Garay" Date: Wed, 23 Aug 2017 22:41:23 +0200 Subject: netfilter: nf_tables: add select_ops for stateful objects This patch adds support for overloading stateful objects operations through the select_ops() callback, just as it is implemented for expressions. This change is needed for upcoming additions to the stateful objects infrastructure. Signed-off-by: Pablo M. Bermudo Garay Signed-off-by: Pablo Neira Ayuso --- include/net/netfilter/nf_tables.h | 35 ++++++++++++++++++++++++----------- net/netfilter/nf_tables_api.c | 36 ++++++++++++++++++++++++------------ net/netfilter/nft_counter.c | 20 +++++++++++++------- net/netfilter/nft_ct.c | 18 ++++++++++++------ net/netfilter/nft_objref.c | 7 ++++--- net/netfilter/nft_quota.c | 20 +++++++++++++------- 6 files changed, 90 insertions(+), 46 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_tables.h b/include/net/netfilter/nf_tables.h index f9795fe394f3..0f5b12a4ad09 100644 --- a/include/net/netfilter/nf_tables.h +++ b/include/net/netfilter/nf_tables.h @@ -1007,12 +1007,12 @@ int nft_verdict_dump(struct sk_buff *skb, int type, * * @list: table stateful object list node * @table: table this object belongs to - * @type: pointer to object type - * @data: pointer to object data * @name: name of this stateful object * @genmask: generation mask * @use: number of references to this stateful object * @data: object data, layout depends on type + * @ops: object operations + * @data: pointer to object data */ struct nft_object { struct list_head list; @@ -1021,7 +1021,7 @@ struct nft_object { u32 genmask:2, use:30; /* runtime data below here */ - const struct nft_object_type *type ____cacheline_aligned; + const struct nft_object_ops *ops ____cacheline_aligned; unsigned char data[] __attribute__((aligned(__alignof__(u64)))); }; @@ -1044,27 +1044,39 @@ void nft_obj_notify(struct net *net, struct nft_table *table, /** * struct nft_object_type - stateful object type * - * @eval: stateful object evaluation function + * @select_ops: function to select nft_object_ops + * @ops: default ops, used when no select_ops functions is present * @list: list node in list of object types * @type: stateful object numeric type - * @size: stateful object size * @owner: module owner * @maxattr: maximum netlink attribute * @policy: netlink attribute policy + */ +struct nft_object_type { + const struct nft_object_ops *(*select_ops)(const struct nft_ctx *, + const struct nlattr * const tb[]); + const struct nft_object_ops *ops; + struct list_head list; + u32 type; + unsigned int maxattr; + struct module *owner; + const struct nla_policy *policy; +}; + +/** + * struct nft_object_ops - stateful object operations + * + * @eval: stateful object evaluation function + * @size: stateful object size * @init: initialize object from netlink attributes * @destroy: release existing stateful object * @dump: netlink dump stateful object */ -struct nft_object_type { +struct nft_object_ops { void (*eval)(struct nft_object *obj, struct nft_regs *regs, const struct nft_pktinfo *pkt); - struct list_head list; - u32 type; unsigned int size; - unsigned int maxattr; - struct module *owner; - const struct nla_policy *policy; int (*init)(const struct nft_ctx *ctx, const struct nlattr *const tb[], struct nft_object *obj); @@ -1072,6 +1084,7 @@ struct nft_object_type { int (*dump)(struct sk_buff *skb, struct nft_object *obj, bool reset); + const struct nft_object_type *type; }; int nft_register_obj(struct nft_object_type *obj_type); diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 149785ff1c7b..b37c178897f3 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -4248,7 +4248,7 @@ struct nft_object *nf_tables_obj_lookup(const struct nft_table *table, list_for_each_entry(obj, &table->objects, list) { if (!nla_strcmp(nla, obj->name) && - objtype == obj->type->type && + objtype == obj->ops->type->type && nft_active_genmask(obj, genmask)) return obj; } @@ -4270,6 +4270,7 @@ static struct nft_object *nft_obj_init(const struct nft_ctx *ctx, const struct nlattr *attr) { struct nlattr *tb[type->maxattr + 1]; + const struct nft_object_ops *ops; struct nft_object *obj; int err; @@ -4282,16 +4283,27 @@ static struct nft_object *nft_obj_init(const struct nft_ctx *ctx, memset(tb, 0, sizeof(tb[0]) * (type->maxattr + 1)); } + if (type->select_ops) { + ops = type->select_ops(ctx, (const struct nlattr * const *)tb); + if (IS_ERR(ops)) { + err = PTR_ERR(ops); + goto err1; + } + } else { + ops = type->ops; + } + err = -ENOMEM; - obj = kzalloc(sizeof(struct nft_object) + type->size, GFP_KERNEL); + obj = kzalloc(sizeof(*obj) + ops->size, GFP_KERNEL); if (obj == NULL) goto err1; - err = type->init(ctx, (const struct nlattr * const *)tb, obj); + err = ops->init(ctx, (const struct nlattr * const *)tb, obj); if (err < 0) goto err2; - obj->type = type; + obj->ops = ops; + return obj; err2: kfree(obj); @@ -4307,7 +4319,7 @@ static int nft_object_dump(struct sk_buff *skb, unsigned int attr, nest = nla_nest_start(skb, attr); if (!nest) goto nla_put_failure; - if (obj->type->dump(skb, obj, reset) < 0) + if (obj->ops->dump(skb, obj, reset) < 0) goto nla_put_failure; nla_nest_end(skb, nest); return 0; @@ -4418,8 +4430,8 @@ static int nf_tables_newobj(struct net *net, struct sock *nlsk, err3: kfree(obj->name); err2: - if (obj->type->destroy) - obj->type->destroy(obj); + if (obj->ops->destroy) + obj->ops->destroy(obj); kfree(obj); err1: module_put(type->owner); @@ -4446,7 +4458,7 @@ static int nf_tables_fill_obj_info(struct sk_buff *skb, struct net *net, if (nla_put_string(skb, NFTA_OBJ_TABLE, table->name) || nla_put_string(skb, NFTA_OBJ_NAME, obj->name) || - nla_put_be32(skb, NFTA_OBJ_TYPE, htonl(obj->type->type)) || + nla_put_be32(skb, NFTA_OBJ_TYPE, htonl(obj->ops->type->type)) || nla_put_be32(skb, NFTA_OBJ_USE, htonl(obj->use)) || nft_object_dump(skb, NFTA_OBJ_DATA, obj, reset)) goto nla_put_failure; @@ -4500,7 +4512,7 @@ static int nf_tables_dump_obj(struct sk_buff *skb, struct netlink_callback *cb) goto cont; if (filter && filter->type != NFT_OBJECT_UNSPEC && - obj->type->type != filter->type) + obj->ops->type->type != filter->type) goto cont; if (nf_tables_fill_obj_info(skb, net, NETLINK_CB(cb->skb).portid, @@ -4628,10 +4640,10 @@ err: static void nft_obj_destroy(struct nft_object *obj) { - if (obj->type->destroy) - obj->type->destroy(obj); + if (obj->ops->destroy) + obj->ops->destroy(obj); - module_put(obj->type->owner); + module_put(obj->ops->type->owner); kfree(obj->name); kfree(obj); } diff --git a/net/netfilter/nft_counter.c b/net/netfilter/nft_counter.c index 67a710ebde09..eefe3b409925 100644 --- a/net/netfilter/nft_counter.c +++ b/net/netfilter/nft_counter.c @@ -175,15 +175,21 @@ static const struct nla_policy nft_counter_policy[NFTA_COUNTER_MAX + 1] = { [NFTA_COUNTER_BYTES] = { .type = NLA_U64 }, }; -static struct nft_object_type nft_counter_obj __read_mostly = { - .type = NFT_OBJECT_COUNTER, +static struct nft_object_type nft_counter_obj_type; +static const struct nft_object_ops nft_counter_obj_ops = { + .type = &nft_counter_obj_type, .size = sizeof(struct nft_counter_percpu_priv), - .maxattr = NFTA_COUNTER_MAX, - .policy = nft_counter_policy, .eval = nft_counter_obj_eval, .init = nft_counter_obj_init, .destroy = nft_counter_obj_destroy, .dump = nft_counter_obj_dump, +}; + +static struct nft_object_type nft_counter_obj_type __read_mostly = { + .type = NFT_OBJECT_COUNTER, + .ops = &nft_counter_obj_ops, + .maxattr = NFTA_COUNTER_MAX, + .policy = nft_counter_policy, .owner = THIS_MODULE, }; @@ -271,7 +277,7 @@ static int __init nft_counter_module_init(void) for_each_possible_cpu(cpu) seqcount_init(per_cpu_ptr(&nft_counter_seq, cpu)); - err = nft_register_obj(&nft_counter_obj); + err = nft_register_obj(&nft_counter_obj_type); if (err < 0) return err; @@ -281,14 +287,14 @@ static int __init nft_counter_module_init(void) return 0; err1: - nft_unregister_obj(&nft_counter_obj); + nft_unregister_obj(&nft_counter_obj_type); return err; } static void __exit nft_counter_module_exit(void) { nft_unregister_expr(&nft_counter_type); - nft_unregister_obj(&nft_counter_obj); + nft_unregister_obj(&nft_counter_obj_type); } module_init(nft_counter_module_init); diff --git a/net/netfilter/nft_ct.c b/net/netfilter/nft_ct.c index 1678e9e75e8e..bd0975d7dd6f 100644 --- a/net/netfilter/nft_ct.c +++ b/net/netfilter/nft_ct.c @@ -904,15 +904,21 @@ static const struct nla_policy nft_ct_helper_policy[NFTA_CT_HELPER_MAX + 1] = { [NFTA_CT_HELPER_L4PROTO] = { .type = NLA_U8 }, }; -static struct nft_object_type nft_ct_helper_obj __read_mostly = { - .type = NFT_OBJECT_CT_HELPER, +static struct nft_object_type nft_ct_helper_obj_type; +static const struct nft_object_ops nft_ct_helper_obj_ops = { + .type = &nft_ct_helper_obj_type, .size = sizeof(struct nft_ct_helper_obj), - .maxattr = NFTA_CT_HELPER_MAX, - .policy = nft_ct_helper_policy, .eval = nft_ct_helper_obj_eval, .init = nft_ct_helper_obj_init, .destroy = nft_ct_helper_obj_destroy, .dump = nft_ct_helper_obj_dump, +}; + +static struct nft_object_type nft_ct_helper_obj_type __read_mostly = { + .type = NFT_OBJECT_CT_HELPER, + .ops = &nft_ct_helper_obj_ops, + .maxattr = NFTA_CT_HELPER_MAX, + .policy = nft_ct_helper_policy, .owner = THIS_MODULE, }; @@ -930,7 +936,7 @@ static int __init nft_ct_module_init(void) if (err < 0) goto err1; - err = nft_register_obj(&nft_ct_helper_obj); + err = nft_register_obj(&nft_ct_helper_obj_type); if (err < 0) goto err2; @@ -945,7 +951,7 @@ err1: static void __exit nft_ct_module_exit(void) { - nft_unregister_obj(&nft_ct_helper_obj); + nft_unregister_obj(&nft_ct_helper_obj_type); nft_unregister_expr(&nft_notrack_type); nft_unregister_expr(&nft_ct_type); } diff --git a/net/netfilter/nft_objref.c b/net/netfilter/nft_objref.c index 1dd428fbaaa3..7bcdc48f3d73 100644 --- a/net/netfilter/nft_objref.c +++ b/net/netfilter/nft_objref.c @@ -22,7 +22,7 @@ static void nft_objref_eval(const struct nft_expr *expr, { struct nft_object *obj = nft_objref_priv(expr); - obj->type->eval(obj, regs, pkt); + obj->ops->eval(obj, regs, pkt); } static int nft_objref_init(const struct nft_ctx *ctx, @@ -54,7 +54,8 @@ static int nft_objref_dump(struct sk_buff *skb, const struct nft_expr *expr) const struct nft_object *obj = nft_objref_priv(expr); if (nla_put_string(skb, NFTA_OBJREF_IMM_NAME, obj->name) || - nla_put_be32(skb, NFTA_OBJREF_IMM_TYPE, htonl(obj->type->type))) + nla_put_be32(skb, NFTA_OBJREF_IMM_TYPE, + htonl(obj->ops->type->type))) goto nla_put_failure; return 0; @@ -104,7 +105,7 @@ static void nft_objref_map_eval(const struct nft_expr *expr, return; } obj = *nft_set_ext_obj(ext); - obj->type->eval(obj, regs, pkt); + obj->ops->eval(obj, regs, pkt); } static int nft_objref_map_init(const struct nft_ctx *ctx, diff --git a/net/netfilter/nft_quota.c b/net/netfilter/nft_quota.c index 25e33159be57..0ed124a93fcf 100644 --- a/net/netfilter/nft_quota.c +++ b/net/netfilter/nft_quota.c @@ -151,14 +151,20 @@ static int nft_quota_obj_dump(struct sk_buff *skb, struct nft_object *obj, return nft_quota_do_dump(skb, priv, reset); } -static struct nft_object_type nft_quota_obj __read_mostly = { - .type = NFT_OBJECT_QUOTA, +static struct nft_object_type nft_quota_obj_type; +static const struct nft_object_ops nft_quota_obj_ops = { + .type = &nft_quota_obj_type, .size = sizeof(struct nft_quota), - .maxattr = NFTA_QUOTA_MAX, - .policy = nft_quota_policy, .init = nft_quota_obj_init, .eval = nft_quota_obj_eval, .dump = nft_quota_obj_dump, +}; + +static struct nft_object_type nft_quota_obj_type __read_mostly = { + .type = NFT_OBJECT_QUOTA, + .ops = &nft_quota_obj_ops, + .maxattr = NFTA_QUOTA_MAX, + .policy = nft_quota_policy, .owner = THIS_MODULE, }; @@ -209,7 +215,7 @@ static int __init nft_quota_module_init(void) { int err; - err = nft_register_obj(&nft_quota_obj); + err = nft_register_obj(&nft_quota_obj_type); if (err < 0) return err; @@ -219,14 +225,14 @@ static int __init nft_quota_module_init(void) return 0; err1: - nft_unregister_obj(&nft_quota_obj); + nft_unregister_obj(&nft_quota_obj_type); return err; } static void __exit nft_quota_module_exit(void) { nft_unregister_expr(&nft_quota_type); - nft_unregister_obj(&nft_quota_obj); + nft_unregister_obj(&nft_quota_obj_type); } module_init(nft_quota_module_init); -- cgit v1.2.3 From 6e323887565fe8a23c6c85faf9e395a24affd05c Mon Sep 17 00:00:00 2001 From: "Pablo M. Bermudo Garay" Date: Wed, 23 Aug 2017 22:41:24 +0200 Subject: netfilter: nft_limit: replace pkt_bytes with bytes Just a small refactor patch in order to improve the code readability. Signed-off-by: Pablo M. Bermudo Garay Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nft_limit.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index 14538b1d4d11..aae2d1ec27f3 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c @@ -168,9 +168,9 @@ static const struct nft_expr_ops nft_limit_pkts_ops = { .dump = nft_limit_pkts_dump, }; -static void nft_limit_pkt_bytes_eval(const struct nft_expr *expr, - struct nft_regs *regs, - const struct nft_pktinfo *pkt) +static void nft_limit_bytes_eval(const struct nft_expr *expr, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) { struct nft_limit *priv = nft_expr_priv(expr); u64 cost = div64_u64(priv->nsecs * pkt->skb->len, priv->rate); @@ -179,29 +179,29 @@ static void nft_limit_pkt_bytes_eval(const struct nft_expr *expr, regs->verdict.code = NFT_BREAK; } -static int nft_limit_pkt_bytes_init(const struct nft_ctx *ctx, - const struct nft_expr *expr, - const struct nlattr * const tb[]) +static int nft_limit_bytes_init(const struct nft_ctx *ctx, + const struct nft_expr *expr, + const struct nlattr * const tb[]) { struct nft_limit *priv = nft_expr_priv(expr); return nft_limit_init(priv, tb); } -static int nft_limit_pkt_bytes_dump(struct sk_buff *skb, - const struct nft_expr *expr) +static int nft_limit_bytes_dump(struct sk_buff *skb, + const struct nft_expr *expr) { const struct nft_limit *priv = nft_expr_priv(expr); return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES); } -static const struct nft_expr_ops nft_limit_pkt_bytes_ops = { +static const struct nft_expr_ops nft_limit_bytes_ops = { .type = &nft_limit_type, .size = NFT_EXPR_SIZE(sizeof(struct nft_limit)), - .eval = nft_limit_pkt_bytes_eval, - .init = nft_limit_pkt_bytes_init, - .dump = nft_limit_pkt_bytes_dump, + .eval = nft_limit_bytes_eval, + .init = nft_limit_bytes_init, + .dump = nft_limit_bytes_dump, }; static const struct nft_expr_ops * @@ -215,7 +215,7 @@ nft_limit_select_ops(const struct nft_ctx *ctx, case NFT_LIMIT_PKTS: return &nft_limit_pkts_ops; case NFT_LIMIT_PKT_BYTES: - return &nft_limit_pkt_bytes_ops; + return &nft_limit_bytes_ops; } return ERR_PTR(-EOPNOTSUPP); } -- cgit v1.2.3 From a691205571723cb0544110ca91653ac4b0eb5b17 Mon Sep 17 00:00:00 2001 From: "Pablo M. Bermudo Garay" Date: Wed, 23 Aug 2017 22:41:25 +0200 Subject: netfilter: nft_limit: add stateful object type Register a new limit stateful object type into the stateful object infrastructure. Signed-off-by: Pablo M. Bermudo Garay Signed-off-by: Pablo Neira Ayuso --- include/uapi/linux/netfilter/nf_tables.h | 3 +- net/netfilter/nft_limit.c | 122 ++++++++++++++++++++++++++++++- 2 files changed, 123 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/uapi/linux/netfilter/nf_tables.h b/include/uapi/linux/netfilter/nf_tables.h index b49da72efa68..871afa4871bf 100644 --- a/include/uapi/linux/netfilter/nf_tables.h +++ b/include/uapi/linux/netfilter/nf_tables.h @@ -1282,7 +1282,8 @@ enum nft_ct_helper_attributes { #define NFT_OBJECT_COUNTER 1 #define NFT_OBJECT_QUOTA 2 #define NFT_OBJECT_CT_HELPER 3 -#define __NFT_OBJECT_MAX 4 +#define NFT_OBJECT_LIMIT 4 +#define __NFT_OBJECT_MAX 5 #define NFT_OBJECT_MAX (__NFT_OBJECT_MAX - 1) /** diff --git a/net/netfilter/nft_limit.c b/net/netfilter/nft_limit.c index aae2d1ec27f3..a9fc298ef4c3 100644 --- a/net/netfilter/nft_limit.c +++ b/net/netfilter/nft_limit.c @@ -229,14 +229,133 @@ static struct nft_expr_type nft_limit_type __read_mostly = { .owner = THIS_MODULE, }; +static void nft_limit_obj_pkts_eval(struct nft_object *obj, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct nft_limit_pkts *priv = nft_obj_data(obj); + + if (nft_limit_eval(&priv->limit, priv->cost)) + regs->verdict.code = NFT_BREAK; +} + +static int nft_limit_obj_pkts_init(const struct nft_ctx *ctx, + const struct nlattr * const tb[], + struct nft_object *obj) +{ + struct nft_limit_pkts *priv = nft_obj_data(obj); + int err; + + err = nft_limit_init(&priv->limit, tb); + if (err < 0) + return err; + + priv->cost = div64_u64(priv->limit.nsecs, priv->limit.rate); + return 0; +} + +static int nft_limit_obj_pkts_dump(struct sk_buff *skb, + struct nft_object *obj, + bool reset) +{ + const struct nft_limit_pkts *priv = nft_obj_data(obj); + + return nft_limit_dump(skb, &priv->limit, NFT_LIMIT_PKTS); +} + +static struct nft_object_type nft_limit_obj_type; +static const struct nft_object_ops nft_limit_obj_pkts_ops = { + .type = &nft_limit_obj_type, + .size = NFT_EXPR_SIZE(sizeof(struct nft_limit_pkts)), + .init = nft_limit_obj_pkts_init, + .eval = nft_limit_obj_pkts_eval, + .dump = nft_limit_obj_pkts_dump, +}; + +static void nft_limit_obj_bytes_eval(struct nft_object *obj, + struct nft_regs *regs, + const struct nft_pktinfo *pkt) +{ + struct nft_limit *priv = nft_obj_data(obj); + u64 cost = div64_u64(priv->nsecs * pkt->skb->len, priv->rate); + + if (nft_limit_eval(priv, cost)) + regs->verdict.code = NFT_BREAK; +} + +static int nft_limit_obj_bytes_init(const struct nft_ctx *ctx, + const struct nlattr * const tb[], + struct nft_object *obj) +{ + struct nft_limit *priv = nft_obj_data(obj); + + return nft_limit_init(priv, tb); +} + +static int nft_limit_obj_bytes_dump(struct sk_buff *skb, + struct nft_object *obj, + bool reset) +{ + const struct nft_limit *priv = nft_obj_data(obj); + + return nft_limit_dump(skb, priv, NFT_LIMIT_PKT_BYTES); +} + +static struct nft_object_type nft_limit_obj_type; +static const struct nft_object_ops nft_limit_obj_bytes_ops = { + .type = &nft_limit_obj_type, + .size = sizeof(struct nft_limit), + .init = nft_limit_obj_bytes_init, + .eval = nft_limit_obj_bytes_eval, + .dump = nft_limit_obj_bytes_dump, +}; + +static const struct nft_object_ops * +nft_limit_obj_select_ops(const struct nft_ctx *ctx, + const struct nlattr * const tb[]) +{ + if (!tb[NFTA_LIMIT_TYPE]) + return &nft_limit_obj_pkts_ops; + + switch (ntohl(nla_get_be32(tb[NFTA_LIMIT_TYPE]))) { + case NFT_LIMIT_PKTS: + return &nft_limit_obj_pkts_ops; + case NFT_LIMIT_PKT_BYTES: + return &nft_limit_obj_bytes_ops; + } + return ERR_PTR(-EOPNOTSUPP); +} + +static struct nft_object_type nft_limit_obj_type __read_mostly = { + .select_ops = nft_limit_obj_select_ops, + .type = NFT_OBJECT_LIMIT, + .maxattr = NFTA_LIMIT_MAX, + .policy = nft_limit_policy, + .owner = THIS_MODULE, +}; + static int __init nft_limit_module_init(void) { - return nft_register_expr(&nft_limit_type); + int err; + + err = nft_register_obj(&nft_limit_obj_type); + if (err < 0) + return err; + + err = nft_register_expr(&nft_limit_type); + if (err < 0) + goto err1; + + return 0; +err1: + nft_unregister_obj(&nft_limit_obj_type); + return err; } static void __exit nft_limit_module_exit(void) { nft_unregister_expr(&nft_limit_type); + nft_unregister_obj(&nft_limit_obj_type); } module_init(nft_limit_module_init); @@ -245,3 +364,4 @@ module_exit(nft_limit_module_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Patrick McHardy "); MODULE_ALIAS_NFT_EXPR("limit"); +MODULE_ALIAS_NFT_OBJ(NFT_OBJECT_LIMIT); -- cgit v1.2.3 From d1c1e39de8357d66163da39e893e38ea1410e8f8 Mon Sep 17 00:00:00 2001 From: Florian Westphal Date: Tue, 29 Aug 2017 12:04:10 +0200 Subject: netfilter: remove unused hooknum arg from packet functions tested with allmodconfig build. Signed-off-by: Florian Westphal --- include/net/netfilter/nf_conntrack_l4proto.h | 1 - net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 1 - net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 6 ++---- net/netfilter/nf_conntrack_core.c | 2 +- net/netfilter/nf_conntrack_proto_dccp.c | 2 +- net/netfilter/nf_conntrack_proto_generic.c | 1 - net/netfilter/nf_conntrack_proto_gre.c | 1 - net/netfilter/nf_conntrack_proto_sctp.c | 1 - net/netfilter/nf_conntrack_proto_tcp.c | 1 - net/netfilter/nf_conntrack_proto_udp.c | 1 - 10 files changed, 4 insertions(+), 13 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack_l4proto.h b/include/net/netfilter/nf_conntrack_l4proto.h index d4933d56809d..738a0307a96b 100644 --- a/include/net/netfilter/nf_conntrack_l4proto.h +++ b/include/net/netfilter/nf_conntrack_l4proto.h @@ -43,7 +43,6 @@ struct nf_conntrack_l4proto { unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum, unsigned int *timeouts); /* Called when a new connection for this protocol found; diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index 434b4e20f6db..ce108a996316 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -82,7 +82,6 @@ static int icmp_packet(struct nf_conn *ct, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum, unsigned int *timeout) { /* Do not immediately delete the connection after the first diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 43544b975eae..30e34c4de003 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -95,7 +95,6 @@ static int icmpv6_packet(struct nf_conn *ct, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum, unsigned int *timeout) { /* Do not immediately delete the connection after the first @@ -129,8 +128,7 @@ static bool icmpv6_new(struct nf_conn *ct, const struct sk_buff *skb, static int icmpv6_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, - unsigned int icmp6off, - unsigned int hooknum) + unsigned int icmp6off) { struct nf_conntrack_tuple intuple, origtuple; const struct nf_conntrack_tuple_hash *h; @@ -214,7 +212,7 @@ icmpv6_error(struct net *net, struct nf_conn *tmpl, if (icmp6h->icmp6_type >= 128) return NF_ACCEPT; - return icmpv6_error_message(net, tmpl, skb, dataoff, hooknum); + return icmpv6_error_message(net, tmpl, skb, dataoff); } #if IS_ENABLED(CONFIG_NF_CT_NETLINK) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index c23df7c9cd59..ee5555dd7ebc 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -1414,7 +1414,7 @@ repeat: /* Decide what timeout policy we want to apply to this flow. */ timeouts = nf_ct_timeout_lookup(net, ct, l4proto); - ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, hooknum, timeouts); + ret = l4proto->packet(ct, skb, dataoff, ctinfo, pf, timeouts); if (ret <= 0) { /* Invalid: inverse of the return code tells * the netfilter core what to do */ diff --git a/net/netfilter/nf_conntrack_proto_dccp.c b/net/netfilter/nf_conntrack_proto_dccp.c index 188347571fc7..0f5a4d79f6b8 100644 --- a/net/netfilter/nf_conntrack_proto_dccp.c +++ b/net/netfilter/nf_conntrack_proto_dccp.c @@ -469,7 +469,7 @@ static unsigned int *dccp_get_timeouts(struct net *net) static int dccp_packet(struct nf_conn *ct, const struct sk_buff *skb, unsigned int dataoff, enum ip_conntrack_info ctinfo, - u_int8_t pf, unsigned int hooknum, + u_int8_t pf, unsigned int *timeouts) { struct net *net = nf_ct_net(ct); diff --git a/net/netfilter/nf_conntrack_proto_generic.c b/net/netfilter/nf_conntrack_proto_generic.c index 2993995b690d..9cd40700842e 100644 --- a/net/netfilter/nf_conntrack_proto_generic.c +++ b/net/netfilter/nf_conntrack_proto_generic.c @@ -61,7 +61,6 @@ static int generic_packet(struct nf_conn *ct, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum, unsigned int *timeout) { nf_ct_refresh_acct(ct, ctinfo, skb, *timeout); diff --git a/net/netfilter/nf_conntrack_proto_gre.c b/net/netfilter/nf_conntrack_proto_gre.c index c0e3a23ac23a..09a90484c27d 100644 --- a/net/netfilter/nf_conntrack_proto_gre.c +++ b/net/netfilter/nf_conntrack_proto_gre.c @@ -245,7 +245,6 @@ static int gre_packet(struct nf_conn *ct, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum, unsigned int *timeouts) { /* If we've seen traffic both ways, this is a GRE connection. diff --git a/net/netfilter/nf_conntrack_proto_sctp.c b/net/netfilter/nf_conntrack_proto_sctp.c index 890b5c73368d..6303a88af12b 100644 --- a/net/netfilter/nf_conntrack_proto_sctp.c +++ b/net/netfilter/nf_conntrack_proto_sctp.c @@ -307,7 +307,6 @@ static int sctp_packet(struct nf_conn *ct, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum, unsigned int *timeouts) { enum sctp_conntrack new_state, old_state; diff --git a/net/netfilter/nf_conntrack_proto_tcp.c b/net/netfilter/nf_conntrack_proto_tcp.c index 33c52d9ab2f5..cba1c6ffe51a 100644 --- a/net/netfilter/nf_conntrack_proto_tcp.c +++ b/net/netfilter/nf_conntrack_proto_tcp.c @@ -803,7 +803,6 @@ static int tcp_packet(struct nf_conn *ct, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum, unsigned int *timeouts) { struct net *net = nf_ct_net(ct); diff --git a/net/netfilter/nf_conntrack_proto_udp.c b/net/netfilter/nf_conntrack_proto_udp.c index dcf3030d2226..8af734cd1a94 100644 --- a/net/netfilter/nf_conntrack_proto_udp.c +++ b/net/netfilter/nf_conntrack_proto_udp.c @@ -74,7 +74,6 @@ static int udp_packet(struct nf_conn *ct, unsigned int dataoff, enum ip_conntrack_info ctinfo, u_int8_t pf, - unsigned int hooknum, unsigned int *timeouts) { /* If we've seen traffic both ways, this is some kind of UDP -- cgit v1.2.3 From 44d6e2f27328b254111dd716fde45b3b59b8a4f7 Mon Sep 17 00:00:00 2001 From: Varsha Rao Date: Wed, 30 Aug 2017 13:37:11 +0530 Subject: net: Replace NF_CT_ASSERT() with WARN_ON(). This patch removes NF_CT_ASSERT() and instead uses WARN_ON(). Signed-off-by: Varsha Rao --- include/net/netfilter/nf_conntrack.h | 2 +- net/ipv4/netfilter/nf_conntrack_proto_icmp.c | 2 +- net/ipv4/netfilter/nf_nat_l3proto_ipv4.c | 6 +++--- net/ipv4/netfilter/nf_nat_masquerade_ipv4.c | 8 ++++---- net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c | 2 +- net/ipv6/netfilter/nf_nat_l3proto_ipv6.c | 6 +++--- net/ipv6/netfilter/nf_nat_masquerade_ipv6.c | 4 ++-- net/netfilter/nf_conntrack_core.c | 11 +++++------ net/netfilter/nf_conntrack_expect.c | 4 ++-- net/netfilter/nf_conntrack_extend.c | 2 +- net/netfilter/nf_conntrack_standalone.c | 6 +++--- net/netfilter/nf_nat_core.c | 4 ++-- net/netfilter/nf_nat_redirect.c | 6 +++--- net/netfilter/xt_NETMAP.c | 8 ++++---- net/netfilter/xt_nat.c | 20 ++++++++++---------- 15 files changed, 45 insertions(+), 46 deletions(-) (limited to 'net') diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 6e6f678aaac7..0385cb08c478 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -159,7 +159,7 @@ nf_ct_get(const struct sk_buff *skb, enum ip_conntrack_info *ctinfo) /* decrement reference count on a conntrack */ static inline void nf_ct_put(struct nf_conn *ct) { - NF_CT_ASSERT(ct); + WARN_ON(!ct); nf_conntrack_put(&ct->ct_general); } diff --git a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c index ce108a996316..a046c298413a 100644 --- a/net/ipv4/netfilter/nf_conntrack_proto_icmp.c +++ b/net/ipv4/netfilter/nf_conntrack_proto_icmp.c @@ -126,7 +126,7 @@ icmp_error_message(struct net *net, struct nf_conn *tmpl, struct sk_buff *skb, enum ip_conntrack_info ctinfo; struct nf_conntrack_zone tmp; - NF_CT_ASSERT(!skb_nfct(skb)); + WARN_ON(skb_nfct(skb)); zone = nf_ct_zone_tmpl(tmpl, skb, &tmp); /* Are they talking about one of our connections? */ diff --git a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c index feedd759ca80..a0f37b208268 100644 --- a/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c +++ b/net/ipv4/netfilter/nf_nat_l3proto_ipv4.c @@ -190,7 +190,7 @@ int nf_nat_icmp_reply_translation(struct sk_buff *skb, struct nf_conntrack_tuple target; unsigned long statusbit; - NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY); + WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY); if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) return 0; @@ -306,8 +306,8 @@ nf_nat_ipv4_fn(void *priv, struct sk_buff *skb, default: /* ESTABLISHED */ - NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || - ctinfo == IP_CT_ESTABLISHED_REPLY); + WARN_ON(ctinfo != IP_CT_ESTABLISHED && + ctinfo != IP_CT_ESTABLISHED_REPLY); if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out)) goto oif_changed; } diff --git a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c index f39037fca923..0c366aad89cb 100644 --- a/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c +++ b/net/ipv4/netfilter/nf_nat_masquerade_ipv4.c @@ -34,12 +34,12 @@ nf_nat_masquerade_ipv4(struct sk_buff *skb, unsigned int hooknum, const struct rtable *rt; __be32 newsrc, nh; - NF_CT_ASSERT(hooknum == NF_INET_POST_ROUTING); + WARN_ON(hooknum != NF_INET_POST_ROUTING); ct = nf_ct_get(skb, &ctinfo); - NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED_REPLY)); + WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || + ctinfo == IP_CT_RELATED_REPLY))); /* Source address is 0.0.0.0 - locally generated packet that is * probably not supposed to be masqueraded. @@ -96,7 +96,7 @@ static int masq_device_event(struct notifier_block *this, * conntracks which were associated with that device, * and forget them. */ - NF_CT_ASSERT(dev->ifindex != 0); + WARN_ON(dev->ifindex == 0); nf_ct_iterate_cleanup_net(net, device_cmp, (void *)(long)dev->ifindex, 0, 0); diff --git a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c index 30e34c4de003..a9e1fd1a8536 100644 --- a/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c +++ b/net/ipv6/netfilter/nf_conntrack_proto_icmpv6.c @@ -136,7 +136,7 @@ icmpv6_error_message(struct net *net, struct nf_conn *tmpl, enum ip_conntrack_info ctinfo; struct nf_conntrack_zone tmp; - NF_CT_ASSERT(!skb_nfct(skb)); + WARN_ON(skb_nfct(skb)); /* Are they talking about one of our connections? */ if (!nf_ct_get_tuplepr(skb, diff --git a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c index b2b4f031b3a1..46d6dba50698 100644 --- a/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c +++ b/net/ipv6/netfilter/nf_nat_l3proto_ipv6.c @@ -196,7 +196,7 @@ int nf_nat_icmpv6_reply_translation(struct sk_buff *skb, struct nf_conntrack_tuple target; unsigned long statusbit; - NF_CT_ASSERT(ctinfo == IP_CT_RELATED || ctinfo == IP_CT_RELATED_REPLY); + WARN_ON(ctinfo != IP_CT_RELATED && ctinfo != IP_CT_RELATED_REPLY); if (!skb_make_writable(skb, hdrlen + sizeof(*inside))) return 0; @@ -319,8 +319,8 @@ nf_nat_ipv6_fn(void *priv, struct sk_buff *skb, default: /* ESTABLISHED */ - NF_CT_ASSERT(ctinfo == IP_CT_ESTABLISHED || - ctinfo == IP_CT_ESTABLISHED_REPLY); + WARN_ON(ctinfo != IP_CT_ESTABLISHED && + ctinfo != IP_CT_ESTABLISHED_REPLY); if (nf_nat_oif_changed(state->hook, ctinfo, nat, state->out)) goto oif_changed; } diff --git a/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c b/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c index d7b679037bae..98f61fcb9108 100644 --- a/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c +++ b/net/ipv6/netfilter/nf_nat_masquerade_ipv6.c @@ -36,8 +36,8 @@ nf_nat_masquerade_ipv6(struct sk_buff *skb, const struct nf_nat_range *range, struct nf_nat_range newrange; ct = nf_ct_get(skb, &ctinfo); - NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED_REPLY)); + WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || + ctinfo == IP_CT_RELATED_REPLY))); if (ipv6_dev_get_saddr(nf_ct_net(ct), out, &ipv6_hdr(skb)->daddr, 0, &src) < 0) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index ee5555dd7ebc..99c753c485ee 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -403,7 +403,7 @@ destroy_conntrack(struct nf_conntrack *nfct) const struct nf_conntrack_l4proto *l4proto; pr_debug("destroy_conntrack(%p)\n", ct); - NF_CT_ASSERT(atomic_read(&nfct->use) == 0); + WARN_ON(atomic_read(&nfct->use) != 0); if (unlikely(nf_ct_is_template(ct))) { nf_ct_tmpl_free(ct); @@ -756,12 +756,11 @@ __nf_conntrack_confirm(struct sk_buff *skb) * connections for unconfirmed conns. But packet copies and * REJECT will give spurious warnings here. */ - /* NF_CT_ASSERT(atomic_read(&ct->ct_general.use) == 1); */ /* No external references means no one else could have * confirmed us. */ - NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); + WARN_ON(nf_ct_is_confirmed(ct)); pr_debug("Confirming conntrack %p\n", ct); /* We have to check the DYING flag after unlink to prevent * a race against nf_ct_get_next_corpse() possibly called from @@ -1160,7 +1159,7 @@ void nf_conntrack_free(struct nf_conn *ct) /* A freed object has refcnt == 0, that's * the golden rule for SLAB_TYPESAFE_BY_RCU */ - NF_CT_ASSERT(atomic_read(&ct->ct_general.use) == 0); + WARN_ON(atomic_read(&ct->ct_general.use) != 0); nf_ct_ext_destroy(ct); nf_ct_ext_free(ct); @@ -1468,7 +1467,7 @@ void nf_conntrack_alter_reply(struct nf_conn *ct, struct nf_conn_help *help = nfct_help(ct); /* Should be unconfirmed, so not in hash table yet */ - NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); + WARN_ON(nf_ct_is_confirmed(ct)); pr_debug("Altering reply tuple of %p to ", ct); nf_ct_dump_tuple(newreply); @@ -1490,7 +1489,7 @@ void __nf_ct_refresh_acct(struct nf_conn *ct, unsigned long extra_jiffies, int do_acct) { - NF_CT_ASSERT(skb); + WARN_ON(!skb); /* Only update if this is not a fixed timeout */ if (test_bit(IPS_FIXED_TIMEOUT_BIT, &ct->status)) diff --git a/net/netfilter/nf_conntrack_expect.c b/net/netfilter/nf_conntrack_expect.c index dad2c0c22ad5..64778f9a8548 100644 --- a/net/netfilter/nf_conntrack_expect.c +++ b/net/netfilter/nf_conntrack_expect.c @@ -51,8 +51,8 @@ void nf_ct_unlink_expect_report(struct nf_conntrack_expect *exp, struct nf_conn_help *master_help = nfct_help(exp->master); struct net *net = nf_ct_exp_net(exp); - NF_CT_ASSERT(master_help); - NF_CT_ASSERT(!timer_pending(&exp->timeout)); + WARN_ON(!master_help); + WARN_ON(timer_pending(&exp->timeout)); hlist_del_rcu(&exp->hnode); net->ct.expect_count--; diff --git a/net/netfilter/nf_conntrack_extend.c b/net/netfilter/nf_conntrack_extend.c index 6c605e88ebae..9fe0ddc333fb 100644 --- a/net/netfilter/nf_conntrack_extend.c +++ b/net/netfilter/nf_conntrack_extend.c @@ -47,7 +47,7 @@ void *nf_ct_ext_add(struct nf_conn *ct, enum nf_ct_ext_id id, gfp_t gfp) struct nf_ct_ext_type *t; /* Conntrack must not be confirmed to avoid races on reallocation. */ - NF_CT_ASSERT(!nf_ct_is_confirmed(ct)); + WARN_ON(nf_ct_is_confirmed(ct)); old = ct->ext; diff --git a/net/netfilter/nf_conntrack_standalone.c b/net/netfilter/nf_conntrack_standalone.c index 9eb85858d764..5a101caa3e12 100644 --- a/net/netfilter/nf_conntrack_standalone.c +++ b/net/netfilter/nf_conntrack_standalone.c @@ -287,7 +287,7 @@ static int ct_seq_show(struct seq_file *s, void *v) struct net *net = seq_file_net(s); int ret = 0; - NF_CT_ASSERT(ct); + WARN_ON(!ct); if (unlikely(!atomic_inc_not_zero(&ct->ct_general.use))) return 0; @@ -304,9 +304,9 @@ static int ct_seq_show(struct seq_file *s, void *v) goto release; l3proto = __nf_ct_l3proto_find(nf_ct_l3num(ct)); - NF_CT_ASSERT(l3proto); + WARN_ON(!l3proto); l4proto = __nf_ct_l4proto_find(nf_ct_l3num(ct), nf_ct_protonum(ct)); - NF_CT_ASSERT(l4proto); + WARN_ON(!l4proto); ret = -ENOSPC; seq_printf(s, "%-8s %u %-8s %u %ld ", diff --git a/net/netfilter/nf_nat_core.c b/net/netfilter/nf_nat_core.c index b1d3740ae36a..40573aa6c133 100644 --- a/net/netfilter/nf_nat_core.c +++ b/net/netfilter/nf_nat_core.c @@ -414,8 +414,8 @@ nf_nat_setup_info(struct nf_conn *ct, if (nf_ct_is_confirmed(ct)) return NF_ACCEPT; - NF_CT_ASSERT(maniptype == NF_NAT_MANIP_SRC || - maniptype == NF_NAT_MANIP_DST); + WARN_ON(maniptype != NF_NAT_MANIP_SRC && + maniptype != NF_NAT_MANIP_DST); BUG_ON(nf_nat_initialized(ct, maniptype)); /* What we've got will look like inverse of reply. Normally diff --git a/net/netfilter/nf_nat_redirect.c b/net/netfilter/nf_nat_redirect.c index 86067560a318..25b06b959118 100644 --- a/net/netfilter/nf_nat_redirect.c +++ b/net/netfilter/nf_nat_redirect.c @@ -38,11 +38,11 @@ nf_nat_redirect_ipv4(struct sk_buff *skb, __be32 newdst; struct nf_nat_range newrange; - NF_CT_ASSERT(hooknum == NF_INET_PRE_ROUTING || - hooknum == NF_INET_LOCAL_OUT); + WARN_ON(hooknum != NF_INET_PRE_ROUTING && + hooknum != NF_INET_LOCAL_OUT); ct = nf_ct_get(skb, &ctinfo); - NF_CT_ASSERT(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); + WARN_ON(!(ct && (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED))); /* Local packets: make them go to loopback */ if (hooknum == NF_INET_LOCAL_OUT) { diff --git a/net/netfilter/xt_NETMAP.c b/net/netfilter/xt_NETMAP.c index e45a01255e70..58aa9dd3c5b7 100644 --- a/net/netfilter/xt_NETMAP.c +++ b/net/netfilter/xt_NETMAP.c @@ -77,10 +77,10 @@ netmap_tg4(struct sk_buff *skb, const struct xt_action_param *par) const struct nf_nat_ipv4_multi_range_compat *mr = par->targinfo; struct nf_nat_range newrange; - NF_CT_ASSERT(xt_hooknum(par) == NF_INET_PRE_ROUTING || - xt_hooknum(par) == NF_INET_POST_ROUTING || - xt_hooknum(par) == NF_INET_LOCAL_OUT || - xt_hooknum(par) == NF_INET_LOCAL_IN); + WARN_ON(xt_hooknum(par) != NF_INET_PRE_ROUTING && + xt_hooknum(par) != NF_INET_POST_ROUTING && + xt_hooknum(par) != NF_INET_LOCAL_OUT && + xt_hooknum(par) != NF_INET_LOCAL_IN); ct = nf_ct_get(skb, &ctinfo); netmask = ~(mr->range[0].min_ip ^ mr->range[0].max_ip); diff --git a/net/netfilter/xt_nat.c b/net/netfilter/xt_nat.c index 8107b3eb865f..0fd14d1eb09d 100644 --- a/net/netfilter/xt_nat.c +++ b/net/netfilter/xt_nat.c @@ -58,9 +58,9 @@ xt_snat_target_v0(struct sk_buff *skb, const struct xt_action_param *par) struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); - NF_CT_ASSERT(ct != NULL && - (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED_REPLY)); + WARN_ON(!(ct != NULL && + (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || + ctinfo == IP_CT_RELATED_REPLY))); xt_nat_convert_range(&range, &mr->range[0]); return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_SRC); @@ -75,8 +75,8 @@ xt_dnat_target_v0(struct sk_buff *skb, const struct xt_action_param *par) struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); - NF_CT_ASSERT(ct != NULL && - (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); + WARN_ON(!(ct != NULL && + (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED))); xt_nat_convert_range(&range, &mr->range[0]); return nf_nat_setup_info(ct, &range, NF_NAT_MANIP_DST); @@ -90,9 +90,9 @@ xt_snat_target_v1(struct sk_buff *skb, const struct xt_action_param *par) struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); - NF_CT_ASSERT(ct != NULL && - (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || - ctinfo == IP_CT_RELATED_REPLY)); + WARN_ON(!(ct != NULL && + (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED || + ctinfo == IP_CT_RELATED_REPLY))); return nf_nat_setup_info(ct, range, NF_NAT_MANIP_SRC); } @@ -105,8 +105,8 @@ xt_dnat_target_v1(struct sk_buff *skb, const struct xt_action_param *par) struct nf_conn *ct; ct = nf_ct_get(skb, &ctinfo); - NF_CT_ASSERT(ct != NULL && - (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED)); + WARN_ON(!(ct != NULL && + (ctinfo == IP_CT_NEW || ctinfo == IP_CT_RELATED))); return nf_nat_setup_info(ct, range, NF_NAT_MANIP_DST); } -- cgit v1.2.3 From 9efdb14f76f4d7591cd4d7a436ebd716b19703b6 Mon Sep 17 00:00:00 2001 From: Varsha Rao Date: Wed, 30 Aug 2017 13:37:12 +0530 Subject: net: Remove CONFIG_NETFILTER_DEBUG and _ASSERT() macros. This patch removes CONFIG_NETFILTER_DEBUG and _ASSERT() macros as they are no longer required. Replace _ASSERT() macros with WARN_ON(). Signed-off-by: Varsha Rao Signed-off-by: Pablo Neira Ayuso --- arch/parisc/configs/c3000_defconfig | 1 - arch/sh/configs/se7751_defconfig | 1 - include/net/netfilter/nf_conntrack.h | 6 ------ net/Kconfig | 7 ------- net/bridge/netfilter/ebtables.c | 20 ++++++++------------ net/ipv4/netfilter/ip_tables.c | 12 +++--------- net/ipv6/netfilter/ip6_tables.c | 12 +++--------- 7 files changed, 14 insertions(+), 45 deletions(-) (limited to 'net') diff --git a/arch/parisc/configs/c3000_defconfig b/arch/parisc/configs/c3000_defconfig index 0764d3971cf6..8d41a73bd71b 100644 --- a/arch/parisc/configs/c3000_defconfig +++ b/arch/parisc/configs/c3000_defconfig @@ -31,7 +31,6 @@ CONFIG_IP_PNP_BOOTP=y CONFIG_INET6_IPCOMP=m CONFIG_IPV6_TUNNEL=m CONFIG_NETFILTER=y -CONFIG_NETFILTER_DEBUG=y CONFIG_NET_PKTGEN=m CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" CONFIG_DEVTMPFS=y diff --git a/arch/sh/configs/se7751_defconfig b/arch/sh/configs/se7751_defconfig index 75c92fc1876b..56b5e4ce8d4a 100644 --- a/arch/sh/configs/se7751_defconfig +++ b/arch/sh/configs/se7751_defconfig @@ -28,7 +28,6 @@ CONFIG_IP_PNP_RARP=y # CONFIG_INET_LRO is not set # CONFIG_IPV6 is not set CONFIG_NETFILTER=y -CONFIG_NETFILTER_DEBUG=y CONFIG_IP_NF_QUEUE=y CONFIG_MTD=y CONFIG_MTD_PARTITIONS=y diff --git a/include/net/netfilter/nf_conntrack.h b/include/net/netfilter/nf_conntrack.h index 0385cb08c478..fdc9c64a1c94 100644 --- a/include/net/netfilter/nf_conntrack.h +++ b/include/net/netfilter/nf_conntrack.h @@ -44,12 +44,6 @@ union nf_conntrack_expect_proto { #include #include -#ifdef CONFIG_NETFILTER_DEBUG -#define NF_CT_ASSERT(x) WARN_ON(!(x)) -#else -#define NF_CT_ASSERT(x) -#endif - #include #include diff --git a/net/Kconfig b/net/Kconfig index e0e7c62c88f0..9dba2715919d 100644 --- a/net/Kconfig +++ b/net/Kconfig @@ -166,13 +166,6 @@ menuconfig NETFILTER if NETFILTER -config NETFILTER_DEBUG - bool "Network packet filtering debugging" - depends on NETFILTER - help - You can say Y here if you want to get additional messages useful in - debugging the netfilter code. - config NETFILTER_ADVANCED bool "Advanced netfilter configuration" depends on NETFILTER diff --git a/net/bridge/netfilter/ebtables.c b/net/bridge/netfilter/ebtables.c index 54c7ef4e970e..83951f978445 100644 --- a/net/bridge/netfilter/ebtables.c +++ b/net/bridge/netfilter/ebtables.c @@ -252,13 +252,11 @@ unsigned int ebt_do_table(struct sk_buff *skb, } if (verdict == EBT_RETURN) { letsreturn: -#ifdef CONFIG_NETFILTER_DEBUG - if (sp == 0) { - BUGPRINT("RETURN on base chain"); + if (WARN(sp == 0, "RETURN on base chain")) { /* act like this is EBT_CONTINUE */ goto letscontinue; } -#endif + sp--; /* put all the local variables right */ i = cs[sp].n; @@ -271,26 +269,24 @@ letsreturn: } if (verdict == EBT_CONTINUE) goto letscontinue; -#ifdef CONFIG_NETFILTER_DEBUG - if (verdict < 0) { - BUGPRINT("bogus standard verdict\n"); + + if (WARN(verdict < 0, "bogus standard verdict\n")) { read_unlock_bh(&table->lock); return NF_DROP; } -#endif + /* jump to a udc */ cs[sp].n = i + 1; cs[sp].chaininfo = chaininfo; cs[sp].e = ebt_next_entry(point); i = 0; chaininfo = (struct ebt_entries *) (base + verdict); -#ifdef CONFIG_NETFILTER_DEBUG - if (chaininfo->distinguisher) { - BUGPRINT("jump to non-chain\n"); + + if (WARN(chaininfo->distinguisher, "jump to non-chain\n")) { read_unlock_bh(&table->lock); return NF_DROP; } -#endif + nentries = chaininfo->nentries; point = (struct ebt_entry *)chaininfo->data; counter_base = cb_base + chaininfo->counter_offset; diff --git a/net/ipv4/netfilter/ip_tables.c b/net/ipv4/netfilter/ip_tables.c index ce1d97579ce8..576cba2b57e9 100644 --- a/net/ipv4/netfilter/ip_tables.c +++ b/net/ipv4/netfilter/ip_tables.c @@ -35,12 +35,6 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("IPv4 packet filter"); -#ifdef CONFIG_NETFILTER_DEBUG -#define IP_NF_ASSERT(x) WARN_ON(!(x)) -#else -#define IP_NF_ASSERT(x) -#endif - void *ipt_alloc_initial_table(const struct xt_table *info) { return xt_alloc_initial_table(ipt, IPT); @@ -263,7 +257,7 @@ ipt_do_table(struct sk_buff *skb, acpar.hotdrop = false; acpar.state = state; - IP_NF_ASSERT(table->valid_hooks & (1 << hook)); + WARN_ON(!(table->valid_hooks & (1 << hook))); local_bh_disable(); addend = xt_write_recseq_begin(); private = table->private; @@ -293,7 +287,7 @@ ipt_do_table(struct sk_buff *skb, const struct xt_entry_match *ematch; struct xt_counters *counter; - IP_NF_ASSERT(e); + WARN_ON(!e); if (!ip_packet_match(ip, indev, outdev, &e->ip, acpar.fragoff)) { no_match: @@ -312,7 +306,7 @@ ipt_do_table(struct sk_buff *skb, ADD_COUNTER(*counter, skb->len, 1); t = ipt_get_target(e); - IP_NF_ASSERT(t->u.kernel.target); + WARN_ON(!t->u.kernel.target); #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) /* The packet is traced: log it */ diff --git a/net/ipv6/netfilter/ip6_tables.c b/net/ipv6/netfilter/ip6_tables.c index 9f6644958e5e..54b1e75eded1 100644 --- a/net/ipv6/netfilter/ip6_tables.c +++ b/net/ipv6/netfilter/ip6_tables.c @@ -39,12 +39,6 @@ MODULE_LICENSE("GPL"); MODULE_AUTHOR("Netfilter Core Team "); MODULE_DESCRIPTION("IPv6 packet filter"); -#ifdef CONFIG_NETFILTER_DEBUG -#define IP_NF_ASSERT(x) WARN_ON(!(x)) -#else -#define IP_NF_ASSERT(x) -#endif - void *ip6t_alloc_initial_table(const struct xt_table *info) { return xt_alloc_initial_table(ip6t, IP6T); @@ -284,7 +278,7 @@ ip6t_do_table(struct sk_buff *skb, acpar.hotdrop = false; acpar.state = state; - IP_NF_ASSERT(table->valid_hooks & (1 << hook)); + WARN_ON(!(table->valid_hooks & (1 << hook))); local_bh_disable(); addend = xt_write_recseq_begin(); @@ -315,7 +309,7 @@ ip6t_do_table(struct sk_buff *skb, const struct xt_entry_match *ematch; struct xt_counters *counter; - IP_NF_ASSERT(e); + WARN_ON(!e); acpar.thoff = 0; if (!ip6_packet_match(skb, indev, outdev, &e->ipv6, &acpar.thoff, &acpar.fragoff, &acpar.hotdrop)) { @@ -335,7 +329,7 @@ ip6t_do_table(struct sk_buff *skb, ADD_COUNTER(*counter, skb->len, 1); t = ip6t_get_target_c(e); - IP_NF_ASSERT(t->u.kernel.target); + WARN_ON(!t->u.kernel.target); #if IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TRACE) /* The packet is traced: log it */ -- cgit v1.2.3 From 2c4a488a482d265df4505671f6051067524c4f76 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 3 Sep 2017 23:54:27 +0200 Subject: netfilter: nf_tables: add nf_tables_updchain() nf_tables_newchain() is too large, wrap the chain update path in a function to make it more maintainable. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 170 +++++++++++++++++++++++------------------- 1 file changed, 92 insertions(+), 78 deletions(-) (limited to 'net') diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index b37c178897f3..a910544acf59 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1335,6 +1335,97 @@ static void nft_chain_release_hook(struct nft_chain_hook *hook) dev_put(hook->dev); } +static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy, + bool create) +{ + const struct nlattr * const *nla = ctx->nla; + struct nft_table *table = ctx->table; + struct nft_chain *chain = ctx->chain; + struct nft_af_info *afi = ctx->afi; + struct nft_base_chain *basechain; + struct nft_stats *stats = NULL; + struct nft_chain_hook hook; + const struct nlattr *name; + struct nf_hook_ops *ops; + struct nft_trans *trans; + int err, i; + + if (nla[NFTA_CHAIN_HOOK]) { + if (!nft_is_base_chain(chain)) + return -EBUSY; + + err = nft_chain_parse_hook(ctx->net, nla, ctx->afi, &hook, + create); + if (err < 0) + return err; + + basechain = nft_base_chain(chain); + if (basechain->type != hook.type) { + nft_chain_release_hook(&hook); + return -EBUSY; + } + + for (i = 0; i < afi->nops; i++) { + ops = &basechain->ops[i]; + if (ops->hooknum != hook.num || + ops->priority != hook.priority || + ops->dev != hook.dev) { + nft_chain_release_hook(&hook); + return -EBUSY; + } + } + nft_chain_release_hook(&hook); + } + + if (nla[NFTA_CHAIN_HANDLE] && + nla[NFTA_CHAIN_NAME]) { + struct nft_chain *chain2; + + chain2 = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME], + genmask); + if (IS_ERR(chain2)) + return PTR_ERR(chain2); + } + + if (nla[NFTA_CHAIN_COUNTERS]) { + if (!nft_is_base_chain(chain)) + return -EOPNOTSUPP; + + stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); + if (IS_ERR(stats)) + return PTR_ERR(stats); + } + + trans = nft_trans_alloc(ctx, NFT_MSG_NEWCHAIN, + sizeof(struct nft_trans_chain)); + if (trans == NULL) { + free_percpu(stats); + return -ENOMEM; + } + + nft_trans_chain_stats(trans) = stats; + nft_trans_chain_update(trans) = true; + + if (nla[NFTA_CHAIN_POLICY]) + nft_trans_chain_policy(trans) = policy; + else + nft_trans_chain_policy(trans) = -1; + + name = nla[NFTA_CHAIN_NAME]; + if (nla[NFTA_CHAIN_HANDLE] && name) { + nft_trans_chain_name(trans) = + nla_strdup(name, GFP_KERNEL); + if (!nft_trans_chain_name(trans)) { + kfree(trans); + free_percpu(stats); + return -ENOMEM; + } + } + list_add_tail(&trans->list, &ctx->net->nft.commit_list); + + return 0; +} + static int nf_tables_newchain(struct net *net, struct sock *nlsk, struct sk_buff *skb, const struct nlmsghdr *nlh, const struct nlattr * const nla[], @@ -1403,91 +1494,14 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, } if (chain != NULL) { - struct nft_stats *stats = NULL; - struct nft_trans *trans; - if (nlh->nlmsg_flags & NLM_F_EXCL) return -EEXIST; if (nlh->nlmsg_flags & NLM_F_REPLACE) return -EOPNOTSUPP; - if (nla[NFTA_CHAIN_HOOK]) { - struct nft_base_chain *basechain; - struct nft_chain_hook hook; - struct nf_hook_ops *ops; - - if (!nft_is_base_chain(chain)) - return -EBUSY; - - err = nft_chain_parse_hook(net, nla, afi, &hook, - create); - if (err < 0) - return err; - - basechain = nft_base_chain(chain); - if (basechain->type != hook.type) { - nft_chain_release_hook(&hook); - return -EBUSY; - } - - for (i = 0; i < afi->nops; i++) { - ops = &basechain->ops[i]; - if (ops->hooknum != hook.num || - ops->priority != hook.priority || - ops->dev != hook.dev) { - nft_chain_release_hook(&hook); - return -EBUSY; - } - } - nft_chain_release_hook(&hook); - } - - if (nla[NFTA_CHAIN_HANDLE] && name) { - struct nft_chain *chain2; - - chain2 = nf_tables_chain_lookup(table, - nla[NFTA_CHAIN_NAME], - genmask); - if (IS_ERR(chain2)) - return PTR_ERR(chain2); - } - - if (nla[NFTA_CHAIN_COUNTERS]) { - if (!nft_is_base_chain(chain)) - return -EOPNOTSUPP; - - stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); - if (IS_ERR(stats)) - return PTR_ERR(stats); - } - nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla); - trans = nft_trans_alloc(&ctx, NFT_MSG_NEWCHAIN, - sizeof(struct nft_trans_chain)); - if (trans == NULL) { - free_percpu(stats); - return -ENOMEM; - } - - nft_trans_chain_stats(trans) = stats; - nft_trans_chain_update(trans) = true; - if (nla[NFTA_CHAIN_POLICY]) - nft_trans_chain_policy(trans) = policy; - else - nft_trans_chain_policy(trans) = -1; - - if (nla[NFTA_CHAIN_HANDLE] && name) { - nft_trans_chain_name(trans) = - nla_strdup(name, GFP_KERNEL); - if (!nft_trans_chain_name(trans)) { - kfree(trans); - free_percpu(stats); - return -ENOMEM; - } - } - list_add_tail(&trans->list, &net->nft.commit_list); - return 0; + return nf_tables_updchain(&ctx, genmask, policy, create); } if (table->use == UINT_MAX) -- cgit v1.2.3 From 4035285fe07d82ee78e314022541e619463aa408 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 3 Sep 2017 23:54:28 +0200 Subject: netfilter: nf_tables: add nf_tables_addchain() Wrap the chain addition path in a function to make it more maintainable. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 199 ++++++++++++++++++++++-------------------- 1 file changed, 106 insertions(+), 93 deletions(-) (limited to 'net') diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index a910544acf59..d95dfafea1e7 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1335,6 +1335,106 @@ static void nft_chain_release_hook(struct nft_chain_hook *hook) dev_put(hook->dev); } +static int nf_tables_addchain(struct nft_ctx *ctx, u8 family, u8 genmask, + u8 policy, bool create) +{ + const struct nlattr * const *nla = ctx->nla; + struct nft_table *table = ctx->table; + struct nft_af_info *afi = ctx->afi; + struct nft_base_chain *basechain; + struct nft_stats __percpu *stats; + struct net *net = ctx->net; + struct nft_chain *chain; + unsigned int i; + int err; + + if (table->use == UINT_MAX) + return -EOVERFLOW; + + if (nla[NFTA_CHAIN_HOOK]) { + struct nft_chain_hook hook; + struct nf_hook_ops *ops; + nf_hookfn *hookfn; + + err = nft_chain_parse_hook(net, nla, afi, &hook, create); + if (err < 0) + return err; + + basechain = kzalloc(sizeof(*basechain), GFP_KERNEL); + if (basechain == NULL) { + nft_chain_release_hook(&hook); + return -ENOMEM; + } + + if (hook.dev != NULL) + strncpy(basechain->dev_name, hook.dev->name, IFNAMSIZ); + + if (nla[NFTA_CHAIN_COUNTERS]) { + stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); + if (IS_ERR(stats)) { + nft_chain_release_hook(&hook); + kfree(basechain); + return PTR_ERR(stats); + } + basechain->stats = stats; + static_branch_inc(&nft_counters_enabled); + } + + hookfn = hook.type->hooks[hook.num]; + basechain->type = hook.type; + chain = &basechain->chain; + + for (i = 0; i < afi->nops; i++) { + ops = &basechain->ops[i]; + ops->pf = family; + ops->hooknum = hook.num; + ops->priority = hook.priority; + ops->priv = chain; + ops->hook = afi->hooks[ops->hooknum]; + ops->dev = hook.dev; + if (hookfn) + ops->hook = hookfn; + if (afi->hook_ops_init) + afi->hook_ops_init(ops, i); + } + + chain->flags |= NFT_BASE_CHAIN; + basechain->policy = policy; + } else { + chain = kzalloc(sizeof(*chain), GFP_KERNEL); + if (chain == NULL) + return -ENOMEM; + } + INIT_LIST_HEAD(&chain->rules); + chain->handle = nf_tables_alloc_handle(table); + chain->table = table; + chain->name = nla_strdup(nla[NFTA_CHAIN_NAME], GFP_KERNEL); + if (!chain->name) { + err = -ENOMEM; + goto err1; + } + + err = nf_tables_register_hooks(net, table, chain, afi->nops); + if (err < 0) + goto err1; + + ctx->chain = chain; + err = nft_trans_chain_add(ctx, NFT_MSG_NEWCHAIN); + if (err < 0) + goto err2; + + table->use++; + list_add_tail_rcu(&chain->list, &table->chains); + + return 0; +err2: + nf_tables_unregister_hooks(net, table, chain, afi->nops); +err1: + nf_tables_chain_destroy(chain); + + return err; +} + static int nf_tables_updchain(struct nft_ctx *ctx, u8 genmask, u8 policy, bool create) { @@ -1433,19 +1533,15 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, { const struct nfgenmsg *nfmsg = nlmsg_data(nlh); const struct nlattr * uninitialized_var(name); + u8 genmask = nft_genmask_next(net); + int family = nfmsg->nfgen_family; struct nft_af_info *afi; struct nft_table *table; struct nft_chain *chain; - struct nft_base_chain *basechain = NULL; - u8 genmask = nft_genmask_next(net); - int family = nfmsg->nfgen_family; u8 policy = NF_ACCEPT; + struct nft_ctx ctx; u64 handle = 0; - unsigned int i; - struct nft_stats __percpu *stats; - int err; bool create; - struct nft_ctx ctx; create = nlh->nlmsg_flags & NLM_F_CREATE ? true : false; @@ -1493,101 +1589,18 @@ static int nf_tables_newchain(struct net *net, struct sock *nlsk, } } + nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla); + if (chain != NULL) { if (nlh->nlmsg_flags & NLM_F_EXCL) return -EEXIST; if (nlh->nlmsg_flags & NLM_F_REPLACE) return -EOPNOTSUPP; - nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla); - return nf_tables_updchain(&ctx, genmask, policy, create); } - if (table->use == UINT_MAX) - return -EOVERFLOW; - - if (nla[NFTA_CHAIN_HOOK]) { - struct nft_chain_hook hook; - struct nf_hook_ops *ops; - nf_hookfn *hookfn; - - err = nft_chain_parse_hook(net, nla, afi, &hook, create); - if (err < 0) - return err; - - basechain = kzalloc(sizeof(*basechain), GFP_KERNEL); - if (basechain == NULL) { - nft_chain_release_hook(&hook); - return -ENOMEM; - } - - if (hook.dev != NULL) - strncpy(basechain->dev_name, hook.dev->name, IFNAMSIZ); - - if (nla[NFTA_CHAIN_COUNTERS]) { - stats = nft_stats_alloc(nla[NFTA_CHAIN_COUNTERS]); - if (IS_ERR(stats)) { - nft_chain_release_hook(&hook); - kfree(basechain); - return PTR_ERR(stats); - } - basechain->stats = stats; - static_branch_inc(&nft_counters_enabled); - } - - hookfn = hook.type->hooks[hook.num]; - basechain->type = hook.type; - chain = &basechain->chain; - - for (i = 0; i < afi->nops; i++) { - ops = &basechain->ops[i]; - ops->pf = family; - ops->hooknum = hook.num; - ops->priority = hook.priority; - ops->priv = chain; - ops->hook = afi->hooks[ops->hooknum]; - ops->dev = hook.dev; - if (hookfn) - ops->hook = hookfn; - if (afi->hook_ops_init) - afi->hook_ops_init(ops, i); - } - - chain->flags |= NFT_BASE_CHAIN; - basechain->policy = policy; - } else { - chain = kzalloc(sizeof(*chain), GFP_KERNEL); - if (chain == NULL) - return -ENOMEM; - } - - INIT_LIST_HEAD(&chain->rules); - chain->handle = nf_tables_alloc_handle(table); - chain->table = table; - chain->name = nla_strdup(name, GFP_KERNEL); - if (!chain->name) { - err = -ENOMEM; - goto err1; - } - - err = nf_tables_register_hooks(net, table, chain, afi->nops); - if (err < 0) - goto err1; - - nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla); - err = nft_trans_chain_add(&ctx, NFT_MSG_NEWCHAIN); - if (err < 0) - goto err2; - - table->use++; - list_add_tail_rcu(&chain->list, &table->chains); - return 0; -err2: - nf_tables_unregister_hooks(net, table, chain, afi->nops); -err1: - nf_tables_chain_destroy(chain); - return err; + return nf_tables_addchain(&ctx, family, genmask, policy, create); } static int nf_tables_delchain(struct net *net, struct sock *nlsk, -- cgit v1.2.3 From a8278400809dd99eb24468c02c8d4f0cb0003eae Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 3 Sep 2017 23:56:00 +0200 Subject: netfilter: nf_tables: use NLM_F_NONREC for deletion requests Bail out if user requests non-recursive deletion for tables and sets. This new flags tells nf_tables netlink interface to reject deletions if tables and sets have content. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index d95dfafea1e7..47fc7cd3f936 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -860,6 +860,10 @@ static int nf_tables_deltable(struct net *net, struct sock *nlsk, if (IS_ERR(table)) return PTR_ERR(table); + if (nlh->nlmsg_flags & NLM_F_NONREC && + table->use > 0) + return -EBUSY; + ctx.afi = afi; ctx.table = table; @@ -3225,7 +3229,9 @@ static int nf_tables_delset(struct net *net, struct sock *nlsk, set = nf_tables_set_lookup(ctx.table, nla[NFTA_SET_NAME], genmask); if (IS_ERR(set)) return PTR_ERR(set); - if (!list_empty(&set->bindings)) + + if (!list_empty(&set->bindings) || + (nlh->nlmsg_flags & NLM_F_NONREC && atomic_read(&set->nelems) > 0)) return -EBUSY; return nft_delset(&ctx, set); -- cgit v1.2.3 From 9dee1474121550b20542321f9e0579801c6b587c Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Sun, 3 Sep 2017 23:56:01 +0200 Subject: netfilter: nf_tables: support for recursive chain deletion This patch sorts out an asymmetry in deletions. Currently, table and set deletion commands come with an implicit content flush on deletion. However, chain deletion results in -EBUSY if there is content in this chain, so no implicit flush happens. So you have to send a flush command in first place to delete chains, this is inconsistent and it can be annoying in terms of user experience. This patch uses the new NLM_F_NONREC flag to request non-recursive chain deletion, ie. if the chain to be removed contains rules, then this returns EBUSY. This problem was discussed during the NFWS'17 in Faro, Portugal. In iptables, you hit -EBUSY if you try to delete a chain that contains rules, so you have to flush first before you can remove anything. Since iptables-compat uses the nf_tables netlink interface, it has to use the NLM_F_NONREC flag from userspace to retain the original iptables semantics, ie. bail out on removing chains that contain rules. Signed-off-by: Pablo Neira Ayuso --- net/netfilter/nf_tables_api.c | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/netfilter/nf_tables_api.c b/net/netfilter/nf_tables_api.c index 47fc7cd3f936..929927171426 100644 --- a/net/netfilter/nf_tables_api.c +++ b/net/netfilter/nf_tables_api.c @@ -1617,8 +1617,11 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk, struct nft_af_info *afi; struct nft_table *table; struct nft_chain *chain; + struct nft_rule *rule; int family = nfmsg->nfgen_family; struct nft_ctx ctx; + u32 use; + int err; afi = nf_tables_afinfo_lookup(net, family, false); if (IS_ERR(afi)) @@ -1631,11 +1634,30 @@ static int nf_tables_delchain(struct net *net, struct sock *nlsk, chain = nf_tables_chain_lookup(table, nla[NFTA_CHAIN_NAME], genmask); if (IS_ERR(chain)) return PTR_ERR(chain); - if (chain->use > 0) + + if (nlh->nlmsg_flags & NLM_F_NONREC && + chain->use > 0) return -EBUSY; nft_ctx_init(&ctx, net, skb, nlh, afi, table, chain, nla); + use = chain->use; + list_for_each_entry(rule, &chain->rules, list) { + if (!nft_is_active_next(net, rule)) + continue; + use--; + + err = nft_delrule(&ctx, rule); + if (err < 0) + return err; + } + + /* There are rules and elements that are still holding references to us, + * we cannot do a recursive removal in this case. + */ + if (use > 0) + return -EBUSY; + return nft_delchain(&ctx); } -- cgit v1.2.3 From fd0c88b70060e03a2215259ed29b4b31497ad205 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Tue, 5 Sep 2017 10:05:47 +0200 Subject: net/ncsi: fix ncsi_vlan_rx_{add,kill}_vid references We get a new link error in allmodconfig kernels after ftgmac100 started using the ncsi helpers: ERROR: "ncsi_vlan_rx_kill_vid" [drivers/net/ethernet/faraday/ftgmac100.ko] undefined! ERROR: "ncsi_vlan_rx_add_vid" [drivers/net/ethernet/faraday/ftgmac100.ko] undefined! Related to that, we get another error when CONFIG_NET_NCSI is disabled: drivers/net/ethernet/faraday/ftgmac100.c:1626:25: error: 'ncsi_vlan_rx_add_vid' undeclared here (not in a function); did you mean 'ncsi_start_dev'? drivers/net/ethernet/faraday/ftgmac100.c:1627:26: error: 'ncsi_vlan_rx_kill_vid' undeclared here (not in a function); did you mean 'ncsi_vlan_rx_add_vid'? This fixes both problems at once, using a 'static inline' stub helper for the disabled case, and exporting the functions when they are present. Fixes: 51564585d8c6 ("ftgmac100: Support NCSI VLAN filtering when available") Fixes: 21acf63013ed ("net/ncsi: Configure VLAN tag filter") Signed-off-by: Arnd Bergmann Signed-off-by: David S. Miller --- include/net/ncsi.h | 10 ++++++++++ net/ncsi/ncsi-manage.c | 2 ++ 2 files changed, 12 insertions(+) (limited to 'net') diff --git a/include/net/ncsi.h b/include/net/ncsi.h index 1f96af46df49..fdc60ff2511d 100644 --- a/include/net/ncsi.h +++ b/include/net/ncsi.h @@ -36,6 +36,16 @@ int ncsi_start_dev(struct ncsi_dev *nd); void ncsi_stop_dev(struct ncsi_dev *nd); void ncsi_unregister_dev(struct ncsi_dev *nd); #else /* !CONFIG_NET_NCSI */ +static inline int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) +{ + return -EINVAL; +} + +static inline int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) +{ + return -EINVAL; +} + static inline struct ncsi_dev *ncsi_register_dev(struct net_device *dev, void (*notifier)(struct ncsi_dev *nd)) { diff --git a/net/ncsi/ncsi-manage.c b/net/ncsi/ncsi-manage.c index 11904b3b702d..3fd3c39e6278 100644 --- a/net/ncsi/ncsi-manage.c +++ b/net/ncsi/ncsi-manage.c @@ -1453,6 +1453,7 @@ int ncsi_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid) return found ? ncsi_process_next_channel(ndp) : 0; } +EXPORT_SYMBOL_GPL(ncsi_vlan_rx_add_vid); int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) { @@ -1491,6 +1492,7 @@ int ncsi_vlan_rx_kill_vid(struct net_device *dev, __be16 proto, u16 vid) return found ? ncsi_process_next_channel(ndp) : 0; } +EXPORT_SYMBOL_GPL(ncsi_vlan_rx_kill_vid); struct ncsi_dev *ncsi_register_dev(struct net_device *dev, void (*handler)(struct ncsi_dev *ndev)) -- cgit v1.2.3 From 3a1214e8b06317b4e71cd3a36344df87b7858e19 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 1 Sep 2017 14:04:11 -0700 Subject: flow_dissector: Cleanup control flow __skb_flow_dissect is riddled with gotos that make discerning the flow, debugging, and extending the capability difficult. This patch reorganizes things so that we only perform goto's after the two main switch statements (no gotos within the cases now). It also eliminates several goto labels so that there are only two labels that can be target for goto. Reported-by: Alexander Popov Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- include/net/flow_dissector.h | 8 ++ net/core/flow_dissector.c | 223 ++++++++++++++++++++++++++++--------------- 2 files changed, 153 insertions(+), 78 deletions(-) (limited to 'net') diff --git a/include/net/flow_dissector.h b/include/net/flow_dissector.h index e2663e900b0a..fc3dce730a6b 100644 --- a/include/net/flow_dissector.h +++ b/include/net/flow_dissector.h @@ -19,6 +19,14 @@ struct flow_dissector_key_control { #define FLOW_DIS_FIRST_FRAG BIT(1) #define FLOW_DIS_ENCAPSULATION BIT(2) +enum flow_dissect_ret { + FLOW_DISSECT_RET_OUT_GOOD, + FLOW_DISSECT_RET_OUT_BAD, + FLOW_DISSECT_RET_PROTO_AGAIN, + FLOW_DISSECT_RET_IPPROTO_AGAIN, + FLOW_DISSECT_RET_CONTINUE, +}; + /** * struct flow_dissector_key_basic: * @thoff: Transport header offset diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index e2eaa1ff948d..e0ea17d1c7fc 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -115,12 +115,6 @@ __be32 __skb_flow_get_ports(const struct sk_buff *skb, int thoff, u8 ip_proto, } EXPORT_SYMBOL(__skb_flow_get_ports); -enum flow_dissect_ret { - FLOW_DISSECT_RET_OUT_GOOD, - FLOW_DISSECT_RET_OUT_BAD, - FLOW_DISSECT_RET_OUT_PROTO_AGAIN, -}; - static enum flow_dissect_ret __skb_flow_dissect_mpls(const struct sk_buff *skb, struct flow_dissector *flow_dissector, @@ -341,7 +335,7 @@ __skb_flow_dissect_gre(const struct sk_buff *skb, if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) return FLOW_DISSECT_RET_OUT_GOOD; - return FLOW_DISSECT_RET_OUT_PROTO_AGAIN; + return FLOW_DISSECT_RET_PROTO_AGAIN; } static void @@ -431,6 +425,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_dissector_key_icmp *key_icmp; struct flow_dissector_key_tags *key_tags; struct flow_dissector_key_vlan *key_vlan; + enum flow_dissect_ret fdret; bool skip_vlan = false; u8 ip_proto = 0; bool ret; @@ -482,14 +477,19 @@ bool __skb_flow_dissect(const struct sk_buff *skb, } proto_again: + fdret = FLOW_DISSECT_RET_CONTINUE; + switch (proto) { case htons(ETH_P_IP): { const struct iphdr *iph; struct iphdr _iph; -ip: + iph = __skb_header_pointer(skb, nhoff, sizeof(_iph), data, hlen, &_iph); - if (!iph || iph->ihl < 5) - goto out_bad; + if (!iph || iph->ihl < 5) { + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } + nhoff += iph->ihl * 4; ip_proto = iph->protocol; @@ -509,19 +509,25 @@ ip: key_control->flags |= FLOW_DIS_IS_FRAGMENT; if (iph->frag_off & htons(IP_OFFSET)) { - goto out_good; + fdret = FLOW_DISSECT_RET_OUT_GOOD; + break; } else { key_control->flags |= FLOW_DIS_FIRST_FRAG; - if (!(flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG)) - goto out_good; + if (!(flags & + FLOW_DISSECTOR_F_PARSE_1ST_FRAG)) { + fdret = FLOW_DISSECT_RET_OUT_GOOD; + break; + } } } __skb_flow_dissect_ipv4(skb, flow_dissector, target_container, data, iph); - if (flags & FLOW_DISSECTOR_F_STOP_AT_L3) - goto out_good; + if (flags & FLOW_DISSECTOR_F_STOP_AT_L3) { + fdret = FLOW_DISSECT_RET_OUT_GOOD; + break; + } break; } @@ -529,10 +535,11 @@ ip: const struct ipv6hdr *iph; struct ipv6hdr _iph; -ipv6: iph = __skb_header_pointer(skb, nhoff, sizeof(_iph), data, hlen, &_iph); - if (!iph) - goto out_bad; + if (!iph) { + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } ip_proto = iph->nexthdr; nhoff += sizeof(struct ipv6hdr); @@ -561,15 +568,17 @@ ipv6: target_container); key_tags->flow_label = ntohl(flow_label); } - if (flags & FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL) - goto out_good; + if (flags & FLOW_DISSECTOR_F_STOP_AT_FLOW_LABEL) { + fdret = FLOW_DISSECT_RET_OUT_GOOD; + break; + } } __skb_flow_dissect_ipv6(skb, flow_dissector, target_container, data, iph); if (flags & FLOW_DISSECTOR_F_STOP_AT_L3) - goto out_good; + fdret = FLOW_DISSECT_RET_OUT_GOOD; break; } @@ -585,12 +594,17 @@ ipv6: if (!vlan_tag_present || eth_type_vlan(skb->protocol)) { vlan = __skb_header_pointer(skb, nhoff, sizeof(_vlan), data, hlen, &_vlan); - if (!vlan) - goto out_bad; + if (!vlan) { + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } + proto = vlan->h_vlan_encapsulated_proto; nhoff += sizeof(*vlan); - if (skip_vlan) - goto proto_again; + if (skip_vlan) { + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; + break; + } } skip_vlan = true; @@ -613,7 +627,8 @@ ipv6: } } - goto proto_again; + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; + break; } case htons(ETH_P_PPP_SES): { struct { @@ -621,18 +636,27 @@ ipv6: __be16 proto; } *hdr, _hdr; hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr); - if (!hdr) - goto out_bad; + if (!hdr) { + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } + proto = hdr->proto; nhoff += PPPOE_SES_HLEN; switch (proto) { case htons(PPP_IP): - goto ip; + proto = htons(ETH_P_IP); + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; + break; case htons(PPP_IPV6): - goto ipv6; + proto = htons(ETH_P_IPV6); + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; + break; default: - goto out_bad; + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; } + break; } case htons(ETH_P_TIPC): { struct { @@ -640,8 +664,10 @@ ipv6: __be32 srcnode; } *hdr, _hdr; hdr = __skb_header_pointer(skb, nhoff, sizeof(_hdr), data, hlen, &_hdr); - if (!hdr) - goto out_bad; + if (!hdr) { + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } if (dissector_uses_key(flow_dissector, FLOW_DISSECTOR_KEY_TIPC_ADDRS)) { @@ -651,56 +677,62 @@ ipv6: key_addrs->tipcaddrs.srcnode = hdr->srcnode; key_control->addr_type = FLOW_DISSECTOR_KEY_TIPC_ADDRS; } - goto out_good; + fdret = FLOW_DISSECT_RET_OUT_GOOD; + break; } case htons(ETH_P_MPLS_UC): case htons(ETH_P_MPLS_MC): -mpls: - switch (__skb_flow_dissect_mpls(skb, flow_dissector, + fdret = __skb_flow_dissect_mpls(skb, flow_dissector, target_container, data, - nhoff, hlen)) { - case FLOW_DISSECT_RET_OUT_GOOD: - goto out_good; - case FLOW_DISSECT_RET_OUT_BAD: - default: - goto out_bad; - } + nhoff, hlen); + break; case htons(ETH_P_FCOE): - if ((hlen - nhoff) < FCOE_HEADER_LEN) - goto out_bad; + if ((hlen - nhoff) < FCOE_HEADER_LEN) { + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } nhoff += FCOE_HEADER_LEN; - goto out_good; + fdret = FLOW_DISSECT_RET_OUT_GOOD; + break; case htons(ETH_P_ARP): case htons(ETH_P_RARP): - switch (__skb_flow_dissect_arp(skb, flow_dissector, + fdret = __skb_flow_dissect_arp(skb, flow_dissector, target_container, data, - nhoff, hlen)) { - case FLOW_DISSECT_RET_OUT_GOOD: - goto out_good; - case FLOW_DISSECT_RET_OUT_BAD: - default: - goto out_bad; - } + nhoff, hlen); + break; + + default: + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } + + /* Process result of proto processing */ + switch (fdret) { + case FLOW_DISSECT_RET_OUT_GOOD: + goto out_good; + case FLOW_DISSECT_RET_PROTO_AGAIN: + goto proto_again; + case FLOW_DISSECT_RET_CONTINUE: + case FLOW_DISSECT_RET_IPPROTO_AGAIN: + break; + case FLOW_DISSECT_RET_OUT_BAD: default: goto out_bad; } ip_proto_again: + fdret = FLOW_DISSECT_RET_CONTINUE; + switch (ip_proto) { case IPPROTO_GRE: - switch (__skb_flow_dissect_gre(skb, key_control, flow_dissector, + fdret = __skb_flow_dissect_gre(skb, key_control, flow_dissector, target_container, data, - &proto, &nhoff, &hlen, flags)) { - case FLOW_DISSECT_RET_OUT_GOOD: - goto out_good; - case FLOW_DISSECT_RET_OUT_BAD: - goto out_bad; - case FLOW_DISSECT_RET_OUT_PROTO_AGAIN: - goto proto_again; - } + &proto, &nhoff, &hlen, flags); + break; + case NEXTHDR_HOP: case NEXTHDR_ROUTING: case NEXTHDR_DEST: { @@ -711,13 +743,16 @@ ip_proto_again: opthdr = __skb_header_pointer(skb, nhoff, sizeof(_opthdr), data, hlen, &_opthdr); - if (!opthdr) - goto out_bad; + if (!opthdr) { + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } ip_proto = opthdr[0]; nhoff += (opthdr[1] + 1) << 3; - goto ip_proto_again; + fdret = FLOW_DISSECT_RET_IPPROTO_AGAIN; + break; } case NEXTHDR_FRAGMENT: { struct frag_hdr _fh, *fh; @@ -728,8 +763,10 @@ ip_proto_again: fh = __skb_header_pointer(skb, nhoff, sizeof(_fh), data, hlen, &_fh); - if (!fh) - goto out_bad; + if (!fh) { + fdret = FLOW_DISSECT_RET_OUT_BAD; + break; + } key_control->flags |= FLOW_DIS_IS_FRAGMENT; @@ -738,34 +775,50 @@ ip_proto_again: if (!(fh->frag_off & htons(IP6_OFFSET))) { key_control->flags |= FLOW_DIS_FIRST_FRAG; - if (flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG) - goto ip_proto_again; + if (flags & FLOW_DISSECTOR_F_PARSE_1ST_FRAG) { + fdret = FLOW_DISSECT_RET_IPPROTO_AGAIN; + break; + } } - goto out_good; + + fdret = FLOW_DISSECT_RET_OUT_GOOD; + break; } case IPPROTO_IPIP: proto = htons(ETH_P_IP); key_control->flags |= FLOW_DIS_ENCAPSULATION; - if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) - goto out_good; + if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) { + fdret = FLOW_DISSECT_RET_OUT_GOOD; + break; + } + + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; + break; - goto ip; case IPPROTO_IPV6: proto = htons(ETH_P_IPV6); key_control->flags |= FLOW_DIS_ENCAPSULATION; - if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) - goto out_good; + if (flags & FLOW_DISSECTOR_F_STOP_AT_ENCAP) { + fdret = FLOW_DISSECT_RET_OUT_GOOD; + break; + } + + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; + break; + - goto ipv6; case IPPROTO_MPLS: proto = htons(ETH_P_MPLS_UC); - goto mpls; + fdret = FLOW_DISSECT_RET_PROTO_AGAIN; + break; + case IPPROTO_TCP: __skb_flow_dissect_tcp(skb, flow_dissector, target_container, data, nhoff, hlen); break; + default: break; } @@ -787,6 +840,20 @@ ip_proto_again: key_icmp->icmp = skb_flow_get_be16(skb, nhoff, data, hlen); } + /* Process result of IP proto processing */ + switch (fdret) { + case FLOW_DISSECT_RET_PROTO_AGAIN: + goto proto_again; + case FLOW_DISSECT_RET_IPPROTO_AGAIN: + goto ip_proto_again; + case FLOW_DISSECT_RET_OUT_GOOD: + case FLOW_DISSECT_RET_CONTINUE: + break; + case FLOW_DISSECT_RET_OUT_BAD: + default: + goto out_bad; + } + out_good: ret = true; -- cgit v1.2.3 From 1eed4dfb81b193af3299edeed2827337f9999e78 Mon Sep 17 00:00:00 2001 From: Tom Herbert Date: Fri, 1 Sep 2017 14:04:12 -0700 Subject: flow_dissector: Add limit for number of headers to dissect In flow dissector there are no limits to the number of nested encapsulations or headers that might be dissected which makes for a nice DOS attack. This patch sets a limit of the number of headers that flow dissector will parse. Headers includes network layer headers, transport layer headers, shim headers for encapsulation, IPv6 extension headers, etc. The limit for maximum number of headers to parse has be set to fifteen to account for a reasonable number of encapsulations, extension headers, VLAN, in a packet. Note that this limit does not supercede the STOP_AT_* flags which may stop processing before the headers limit is reached. Reported-by: Hannes Frederic Sowa Signed-off-by: Tom Herbert Signed-off-by: David S. Miller --- net/core/flow_dissector.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/core/flow_dissector.c b/net/core/flow_dissector.c index e0ea17d1c7fc..0a977373d003 100644 --- a/net/core/flow_dissector.c +++ b/net/core/flow_dissector.c @@ -396,6 +396,18 @@ __skb_flow_dissect_ipv6(const struct sk_buff *skb, key_ip->ttl = iph->hop_limit; } +/* Maximum number of protocol headers that can be parsed in + * __skb_flow_dissect + */ +#define MAX_FLOW_DISSECT_HDRS 15 + +static bool skb_flow_dissect_allowed(int *num_hdrs) +{ + ++*num_hdrs; + + return (*num_hdrs <= MAX_FLOW_DISSECT_HDRS); +} + /** * __skb_flow_dissect - extract the flow_keys struct and return it * @skb: sk_buff to extract the flow from, can be NULL if the rest are specified @@ -427,6 +439,7 @@ bool __skb_flow_dissect(const struct sk_buff *skb, struct flow_dissector_key_vlan *key_vlan; enum flow_dissect_ret fdret; bool skip_vlan = false; + int num_hdrs = 0; u8 ip_proto = 0; bool ret; @@ -714,7 +727,9 @@ proto_again: case FLOW_DISSECT_RET_OUT_GOOD: goto out_good; case FLOW_DISSECT_RET_PROTO_AGAIN: - goto proto_again; + if (skb_flow_dissect_allowed(&num_hdrs)) + goto proto_again; + goto out_good; case FLOW_DISSECT_RET_CONTINUE: case FLOW_DISSECT_RET_IPPROTO_AGAIN: break; @@ -843,9 +858,13 @@ ip_proto_again: /* Process result of IP proto processing */ switch (fdret) { case FLOW_DISSECT_RET_PROTO_AGAIN: - goto proto_again; + if (skb_flow_dissect_allowed(&num_hdrs)) + goto proto_again; + break; case FLOW_DISSECT_RET_IPPROTO_AGAIN: - goto ip_proto_again; + if (skb_flow_dissect_allowed(&num_hdrs)) + goto ip_proto_again; + break; case FLOW_DISSECT_RET_OUT_GOOD: case FLOW_DISSECT_RET_CONTINUE: break; -- cgit v1.2.3 From f1c2eddf4cb6a6c8ca5f6e728e7328b9c5bc3e8a Mon Sep 17 00:00:00 2001 From: Ido Schimmel Date: Sun, 3 Sep 2017 17:44:13 +0300 Subject: bridge: switchdev: Use an helper to clear forward mark Instead of using ifdef in the C file. Signed-off-by: Ido Schimmel Suggested-by: Nikolay Aleksandrov Tested-by: Yotam Gigi Acked-by: Nikolay Aleksandrov Signed-off-by: David S. Miller --- net/bridge/br_device.c | 4 +--- net/bridge/br_private.h | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) (limited to 'net') diff --git a/net/bridge/br_device.c b/net/bridge/br_device.c index 5a7be3bddfa9..f6b6a92f1c48 100644 --- a/net/bridge/br_device.c +++ b/net/bridge/br_device.c @@ -53,9 +53,7 @@ netdev_tx_t br_dev_xmit(struct sk_buff *skb, struct net_device *dev) brstats->tx_bytes += skb->len; u64_stats_update_end(&brstats->syncp); -#ifdef CONFIG_NET_SWITCHDEV - skb->offload_fwd_mark = 0; -#endif + br_switchdev_frame_unmark(skb); BR_INPUT_SKB_CB(skb)->brdev = dev; skb_reset_mac_header(skb); diff --git a/net/bridge/br_private.h b/net/bridge/br_private.h index fd9ee73e0a6d..e870cfc85b14 100644 --- a/net/bridge/br_private.h +++ b/net/bridge/br_private.h @@ -1091,6 +1091,11 @@ int br_switchdev_set_port_flag(struct net_bridge_port *p, unsigned long mask); void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type); + +static inline void br_switchdev_frame_unmark(struct sk_buff *skb) +{ + skb->offload_fwd_mark = 0; +} #else static inline int nbp_switchdev_mark_set(struct net_bridge_port *p) { @@ -1119,6 +1124,10 @@ static inline void br_switchdev_fdb_notify(const struct net_bridge_fdb_entry *fdb, int type) { } + +static inline void br_switchdev_frame_unmark(struct sk_buff *skb) +{ +} #endif /* CONFIG_NET_SWITCHDEV */ #endif -- cgit v1.2.3 From 55199df6d2af55be414f40856efcb527811001bb Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 3 Sep 2017 20:27:00 -0700 Subject: net: dsa: Allow switch drivers to indicate number of TX queues Let switch drivers indicate how many TX queues they support. Some switches, such as Broadcom Starfighter 2 are designed with 8 egress queues. Future changes will allow us to leverage the queue mapping and direct the transmission towards a particular queue. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- include/net/dsa.h | 3 +++ net/dsa/slave.c | 8 ++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/include/net/dsa.h b/include/net/dsa.h index 398ca8d70ccd..dd44d6ce1097 100644 --- a/include/net/dsa.h +++ b/include/net/dsa.h @@ -243,6 +243,9 @@ struct dsa_switch { /* devlink used to represent this switch device */ struct devlink *devlink; + /* Number of switch port queues */ + unsigned int num_tx_queues; + /* Dynamically allocated ports, keep last */ size_t num_ports; struct dsa_port ports[]; diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 78e78a6e6833..2afa99506f8b 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -1259,8 +1259,12 @@ int dsa_slave_create(struct dsa_port *port, const char *name) cpu_dp = ds->dst->cpu_dp; master = cpu_dp->netdev; - slave_dev = alloc_netdev(sizeof(struct dsa_slave_priv), name, - NET_NAME_UNKNOWN, ether_setup); + if (!ds->num_tx_queues) + ds->num_tx_queues = 1; + + slave_dev = alloc_netdev_mqs(sizeof(struct dsa_slave_priv), name, + NET_NAME_UNKNOWN, ether_setup, + ds->num_tx_queues, 1); if (slave_dev == NULL) return -ENOMEM; -- cgit v1.2.3 From 0f15b0986996a4d45090c0010728055c6aa2b458 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Sun, 3 Sep 2017 20:27:01 -0700 Subject: net: dsa: tag_brcm: Set output queue from skb queue mapping We originally used skb->priority but that was not quite correct as this bitfield needs to contain the egress switch queue we intend to send this SKB to. Signed-off-by: Florian Fainelli Signed-off-by: David S. Miller --- net/dsa/tag_brcm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'net') diff --git a/net/dsa/tag_brcm.c b/net/dsa/tag_brcm.c index de74c3f77818..dbb016434ace 100644 --- a/net/dsa/tag_brcm.c +++ b/net/dsa/tag_brcm.c @@ -62,6 +62,7 @@ static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev) { struct dsa_slave_priv *p = netdev_priv(dev); + u16 queue = skb_get_queue_mapping(skb); u8 *brcm_tag; if (skb_cow_head(skb, BRCM_TAG_LEN) < 0) @@ -78,7 +79,7 @@ static struct sk_buff *brcm_tag_xmit(struct sk_buff *skb, struct net_device *dev * deprecated */ brcm_tag[0] = (1 << BRCM_OPCODE_SHIFT) | - ((skb->priority << BRCM_IG_TC_SHIFT) & BRCM_IG_TC_MASK); + ((queue & BRCM_IG_TC_MASK) << BRCM_IG_TC_SHIFT); brcm_tag[1] = 0; brcm_tag[2] = 0; if (p->dp->index == 8) -- cgit v1.2.3 From fdade4f69e4ca29753963f858dc42c5f8faf1d0b Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 4 Sep 2017 15:28:28 +0100 Subject: rxrpc: Make service connection lookup always check for retry When an RxRPC service packet comes in, the target connection is looked up by an rb-tree search under RCU and a read-locked seqlock; the seqlock retry check is, however, currently skipped if we got a match, but probably shouldn't be in case the connection we found gets replaced whilst we're doing a search. Make the lookup procedure always go through need_seqretry(), even if the lookup was successful. This makes sure we always pick up on a write-lock event. On the other hand, since we don't take a ref on the object, but rely on RCU to prevent its destruction after dropping the seqlock, I'm not sure this is necessary. Signed-off-by: David Howells Signed-off-by: David S. Miller --- net/rxrpc/conn_service.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'net') diff --git a/net/rxrpc/conn_service.c b/net/rxrpc/conn_service.c index e60fcd2a4a02..f6fcdb3130a1 100644 --- a/net/rxrpc/conn_service.c +++ b/net/rxrpc/conn_service.c @@ -50,12 +50,11 @@ struct rxrpc_connection *rxrpc_find_service_conn_rcu(struct rxrpc_peer *peer, else if (conn->proto.index_key > k.index_key) p = rcu_dereference_raw(p->rb_right); else - goto done; + break; conn = NULL; } } while (need_seqretry(&peer->service_conn_lock, seq)); -done: done_seqretry(&peer->service_conn_lock, seq); _leave(" = %d", conn ? conn->debug_id : -1); return conn; -- cgit v1.2.3 From 2c8468dcf8303c68712f11c782d7d8724de07a73 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 5 Sep 2017 08:31:23 -0700 Subject: net: sched: don't use GFP_KERNEL under spin lock The new TC IDR code uses GFP_KERNEL under spin lock. Which leads to: [ 582.621091] BUG: sleeping function called from invalid context at ../mm/slab.h:416 [ 582.629721] in_atomic(): 1, irqs_disabled(): 0, pid: 3379, name: tc [ 582.636939] 2 locks held by tc/3379: [ 582.641049] #0: (rtnl_mutex){+.+.+.}, at: [] rtnetlink_rcv_msg+0x92e/0x1400 [ 582.650958] #1: (&(&tn->idrinfo->lock)->rlock){+.-.+.}, at: [] tcf_idr_create+0x2f0/0x8e0 [ 582.662217] Preemption disabled at: [ 582.662222] [] tcf_idr_create+0x2f0/0x8e0 [ 582.672592] CPU: 9 PID: 3379 Comm: tc Tainted: G W 4.13.0-rc7-debug-00648-g43503a79b9f0 #287 [ 582.683432] Hardware name: Dell Inc. PowerEdge R730/072T6D, BIOS 2.3.4 11/08/2016 [ 582.691937] Call Trace: ... [ 582.742460] kmem_cache_alloc+0x286/0x540 [ 582.747055] radix_tree_node_alloc.constprop.6+0x4a/0x450 [ 582.753209] idr_get_free_cmn+0x627/0xf80 ... [ 582.815525] idr_alloc_cmn+0x1a8/0x270 ... [ 582.833804] tcf_idr_create+0x31b/0x8e0 ... Try to preallocate the memory with idr_prealloc(GFP_KERNEL) (as suggested by Eric Dumazet), and change the allocation flags under spin lock. Fixes: 65a206c01e8e ("net/sched: Change act_api and act_xxx modules to use IDR") Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Acked-by: Eric Dumazet Signed-off-by: David S. Miller --- net/sched/act_api.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/sched/act_api.c b/net/sched/act_api.c index 0eb545bcb247..a306974e2fb4 100644 --- a/net/sched/act_api.c +++ b/net/sched/act_api.c @@ -296,10 +296,12 @@ err2: spin_lock_init(&p->tcfa_lock); /* user doesn't specify an index */ if (!index) { + idr_preload(GFP_KERNEL); spin_lock_bh(&idrinfo->lock); err = idr_alloc_ext(idr, NULL, &idr_index, 1, 0, - GFP_KERNEL); + GFP_ATOMIC); spin_unlock_bh(&idrinfo->lock); + idr_preload_end(); if (err) { err3: free_percpu(p->cpu_qstats); @@ -307,10 +309,12 @@ err3: } p->tcfa_index = idr_index; } else { + idr_preload(GFP_KERNEL); spin_lock_bh(&idrinfo->lock); err = idr_alloc_ext(idr, NULL, NULL, index, index + 1, - GFP_KERNEL); + GFP_ATOMIC); spin_unlock_bh(&idrinfo->lock); + idr_preload_end(); if (err) goto err3; p->tcfa_index = index; -- cgit v1.2.3 From f530f39f5ff97209cc6f1bf66e634685954ad741 Mon Sep 17 00:00:00 2001 From: Håkon Bugge Date: Tue, 5 Sep 2017 17:42:01 +0200 Subject: rds: Fix non-atomic operation on shared flag variable MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The bits in m_flags in struct rds_message are used for a plurality of reasons, and from different contexts. To avoid any missing updates to m_flags, use the atomic set_bit() instead of the non-atomic equivalent. Signed-off-by: Håkon Bugge Reviewed-by: Knut Omang Reviewed-by: Wei Lin Guay Acked-by: Santosh Shilimkar Signed-off-by: David S. Miller --- net/rds/send.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'net') diff --git a/net/rds/send.c b/net/rds/send.c index 41b9f0f5bb9c..058a40743041 100644 --- a/net/rds/send.c +++ b/net/rds/send.c @@ -273,7 +273,7 @@ restart: len = ntohl(rm->m_inc.i_hdr.h_len); if (cp->cp_unacked_packets == 0 || cp->cp_unacked_bytes < len) { - __set_bit(RDS_MSG_ACK_REQUIRED, &rm->m_flags); + set_bit(RDS_MSG_ACK_REQUIRED, &rm->m_flags); cp->cp_unacked_packets = rds_sysctl_max_unacked_packets; @@ -829,7 +829,7 @@ static int rds_send_queue_rm(struct rds_sock *rs, struct rds_connection *conn, * throughput hits a certain threshold. */ if (rs->rs_snd_bytes >= rds_sk_sndbuf(rs) / 2) - __set_bit(RDS_MSG_ACK_REQUIRED, &rm->m_flags); + set_bit(RDS_MSG_ACK_REQUIRED, &rm->m_flags); list_add_tail(&rm->m_sock_item, &rs->rs_send_queue); set_bit(RDS_MSG_ON_SOCK, &rm->m_flags); -- cgit v1.2.3